# Qna

## List Q&A entries

**get** `/v2/training_data/qna`

Returns Q&A entries, newest first, with cursor pagination. Filter by `externalId` or `source`. Entries include their full questions and answer.

### Query Parameters

- `cursor: optional string`

  An opaque cursor for pagination. Use the nextCursor value from a previous response to fetch the next page of results.

- `externalId: optional string`

  Only return the entry with this `externalId`

- `limit: optional number`

  A limit on the number of objects to be returned, between 1 and 100.

- `source: optional string`

  Only return entries with this `source` label

### Header Parameters

- `"Featurebase-Version": optional "2026-08-19.orbit" or "2026-01-01.nova" or "2025-12-12.clover"`

  - `"2026-08-19.orbit"`

  - `"2026-01-01.nova"`

  - `"2025-12-12.clover"`

### Returns

- `data: array of Qna`

  Array containing the actual response elements

  - `id: string`

    Q&A entry ID

  - `answer: string`

    The answer the AI agent gives (markdown)

  - `createdAt: string`

    ISO timestamp of creation

  - `externalId: string`

    Stable identifier supplied by the source system

  - `indexStatus: "pending" or "indexed" or "failed"`

    Whether the entry is searchable by the AI agent. `failed` entries are stored and indexing is retried hourly up to three times. Re-send the resource to retry after exhaustion.

    - `"pending"`

    - `"indexed"`

    - `"failed"`

  - `object: "qna"`

    Object type identifier

    - `"qna"`

  - `questions: array of string`

    Question variants the answer applies to

  - `revision: number`

    Q&A revision. Send it as expectedRevision on PATCH to reject stale changes.

  - `source: string`

    Pipeline label, if one was set

  - `title: string`

    Short label for the entry

  - `updatedAt: string`

    ISO timestamp of the last change

  - `match: optional object { entry, reason, verdict }`

    Present only when `onMatch` was passed and the check ran: the verdict and the closest existing item, even when the write went ahead. `null` otherwise (the default), including when the payload was unchanged.

    - `entry: object { id, kind, section, 3 more }`

      The existing item the verdict is about, or `null` when nothing was close

      - `id: string`

        ID of the item (Q&A entry, training file, or article)

      - `kind: "qna" or "training_file" or "help_center_article"`

        What kind of knowledge item matched: a Q&A entry, a training file / crawled page, or a live help-center article.

        - `"qna"`

        - `"training_file"`

        - `"help_center_article"`

      - `section: string`

        Heading breadcrumb of the matching section, for training files

      - `similarity: number`

        Cosine similarity between your entry and the item (0–1); `null` for a keyword-only hit

      - `title: string`

        Title of the item

      - `url: string`

        Public URL of the item when it has one (articles, crawled pages)

    - `reason: string`

      The judge's one-line explanation of the verdict

    - `verdict: "same_topic" or "contradicts" or "different" or 2 more`

      How the closest existing item relates to the entry. `same_topic`: it answers the same customer question (the entry would be a duplicate, rewording, or updated version). `contradicts`: same question, incompatible facts. `different`: a different question, even if the wording overlaps. `unclear`: the judge could not decide. `no_match`: nothing similar was found.

      - `"same_topic"`

      - `"contradicts"`

      - `"different"`

      - `"unclear"`

      - `"no_match"`

  - `outcome: optional "created" or "updated" or "unchanged"`

    What the write did — present on create and update responses. `created`: a new entry. `updated`: an entry already carried one of the questions (or was merged into with `onMatch: "update"`) and got new content or an indexing retry. `unchanged`: everything in the payload was already on that entry; nothing written, nothing re-indexed.

    - `"created"`

    - `"updated"`

    - `"unchanged"`

- `nextCursor: string`

  Cursor to use for fetching the next page. Null if there are no more results.

- `object: "list"`

  String representing the object type

  - `"list"`

### Example

```http
curl https://do.featurebase.app/v2/training_data/qna \
    -H "Authorization: Bearer $FEATUREBASE_API_KEY"
```

#### Response

```json
{
  "data": [
    {
      "id": "67ec1234abcd5678ef901235",
      "answer": "You can request a refund within 30 days from **Settings → Billing**.",
      "createdAt": "2026-09-03T10:15:00.000Z",
      "externalId": "resolution-1842",
      "indexStatus": "indexed",
      "object": "qna",
      "questions": [
        "How do I request a refund?",
        "Can I get my money back?"
      ],
      "revision": 1,
      "source": "resolved-conversations",
      "title": "Refund requests",
      "updatedAt": "2026-09-03T10:15:30.000Z",
      "match": {
        "entry": {
          "id": "67ec1234abcd5678ef901236",
          "kind": "qna",
          "section": null,
          "similarity": 0.61,
          "title": "Refund requests",
          "url": null
        },
        "reason": "Both answer how to request a refund; the new entry adds the 30-day window.",
        "verdict": "same_topic"
      },
      "outcome": "created"
    }
  ],
  "nextCursor": "eyJpZCI6IjUwN2YxZjc3YmNmODZjZDc5OTQzOTAxMSJ9",
  "object": "list"
}
```

## Create a Q&A entry

**post** `/v2/training_data/qna`

Creates or updates question variants and an answer. Optional externalId is a stable, caller-owned key, unique within workspace Q&A. Otherwise a normalized question addresses an existing entry. POST appends variants (maximum 50) and replaces supplied content; PATCH replaces the question list. Questions cannot belong to multiple entries: ambiguous ownership returns 409 question_already_used. New entries return 201; updates and no-ops return 200. Responses include externalId, revision, and indexStatus. Indexing is attempted before returning; verify indexStatus rather than assuming HTTP success means searchable.

Writes omit semantic checks by default. Optional onMatch runs a bounded check against the addressed entry and retrieved knowledge: reject refuses overlap; update explicitly permits answer replacement, including contradictions, but refuses unclear results; create explicitly overrides the check. Without an addressed entry, update can merge a matching Q&A, subject to source ownership. Articles and files are never edited. Check failures return 503 before writing. This is not a transaction over the knowledge base: other entries can change concurrently. After a separate oracle call, use PATCH with the checked expectedRevision; stale writes return 409 revision_conflict.

### Header Parameters

- `"Featurebase-Version": optional "2026-08-19.orbit" or "2026-01-01.nova" or "2025-12-12.clover"`

  - `"2026-08-19.orbit"`

  - `"2026-01-01.nova"`

  - `"2025-12-12.clover"`

### Body Parameters

- `answer: string`

  The answer the AI agent should give

- `questions: array of string`

  Question variants this answer applies to (1–50). Phrase them the way customers ask.

- `answerFormat: optional "markdown" or "html"`

  Format of `answer`. `markdown` (default) is stored as-is; `html` is converted to markdown.

  - `"markdown"`

  - `"html"`

- `externalId: optional string`

  Your own identifier for this entry (unique per workspace). When provided, the request creates the entry if the ID is new and otherwise updates the existing entry in place — an unchanged payload is a no-op. Use it to keep the AI agent in sync with a system of record without tracking Featurebase IDs.

- `onMatch: optional "reject" or "update" or "create"`

  Optional inline knowledge check. Omit it (the default) and the entry is written immediately without consulting existing knowledge — ask `POST /v2/training_data/oracle` first when you want a suggestion. Pass it to run the same check as the oracle inside this request and act on the result (this is not a transaction over the knowledge base): `reject`: respond `409 conflicts_with_existing` describing the match and write nothing. `update`: when an entry already carries one of your questions, write to that entry and report the match; otherwise, if the match is another Q&A entry, merge into it (question variants appended, answer replaced); an article or file is never edited — the entry is written as requested and the match reported. `create`: write as requested and report the match. `unclear` is treated as `reject` for `update`. With `onMatch` set, the request fails with `503 knowledge_check_unavailable` instead of writing when the check cannot run.

  - `"reject"`

  - `"update"`

  - `"create"`

- `source: optional string`

  Free-form label for the pipeline or system this entry came from. Filter lists by it to review or sweep everything from one source.

- `title: optional string`

  Short label for the entry. Optional: a new entry defaults to its first question; an existing entry keeps its title when omitted.

### Returns

- `Qna object { id, answer, createdAt, 10 more }`

  - `id: string`

    Q&A entry ID

  - `answer: string`

    The answer the AI agent gives (markdown)

  - `createdAt: string`

    ISO timestamp of creation

  - `externalId: string`

    Stable identifier supplied by the source system

  - `indexStatus: "pending" or "indexed" or "failed"`

    Whether the entry is searchable by the AI agent. `failed` entries are stored and indexing is retried hourly up to three times. Re-send the resource to retry after exhaustion.

    - `"pending"`

    - `"indexed"`

    - `"failed"`

  - `object: "qna"`

    Object type identifier

    - `"qna"`

  - `questions: array of string`

    Question variants the answer applies to

  - `revision: number`

    Q&A revision. Send it as expectedRevision on PATCH to reject stale changes.

  - `source: string`

    Pipeline label, if one was set

  - `title: string`

    Short label for the entry

  - `updatedAt: string`

    ISO timestamp of the last change

  - `match: optional object { entry, reason, verdict }`

    Present only when `onMatch` was passed and the check ran: the verdict and the closest existing item, even when the write went ahead. `null` otherwise (the default), including when the payload was unchanged.

    - `entry: object { id, kind, section, 3 more }`

      The existing item the verdict is about, or `null` when nothing was close

      - `id: string`

        ID of the item (Q&A entry, training file, or article)

      - `kind: "qna" or "training_file" or "help_center_article"`

        What kind of knowledge item matched: a Q&A entry, a training file / crawled page, or a live help-center article.

        - `"qna"`

        - `"training_file"`

        - `"help_center_article"`

      - `section: string`

        Heading breadcrumb of the matching section, for training files

      - `similarity: number`

        Cosine similarity between your entry and the item (0–1); `null` for a keyword-only hit

      - `title: string`

        Title of the item

      - `url: string`

        Public URL of the item when it has one (articles, crawled pages)

    - `reason: string`

      The judge's one-line explanation of the verdict

    - `verdict: "same_topic" or "contradicts" or "different" or 2 more`

      How the closest existing item relates to the entry. `same_topic`: it answers the same customer question (the entry would be a duplicate, rewording, or updated version). `contradicts`: same question, incompatible facts. `different`: a different question, even if the wording overlaps. `unclear`: the judge could not decide. `no_match`: nothing similar was found.

      - `"same_topic"`

      - `"contradicts"`

      - `"different"`

      - `"unclear"`

      - `"no_match"`

  - `outcome: optional "created" or "updated" or "unchanged"`

    What the write did — present on create and update responses. `created`: a new entry. `updated`: an entry already carried one of the questions (or was merged into with `onMatch: "update"`) and got new content or an indexing retry. `unchanged`: everything in the payload was already on that entry; nothing written, nothing re-indexed.

    - `"created"`

    - `"updated"`

    - `"unchanged"`

### Example

```http
curl https://do.featurebase.app/v2/training_data/qna \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $FEATUREBASE_API_KEY" \
    -d '{
          "answer": "You can request a refund within 30 days from **Settings → Billing**.",
          "questions": [
            "How do I request a refund?",
            "Can I get my money back?"
          ],
          "answerFormat": "markdown",
          "externalId": "kb-article-1842",
          "onMatch": "reject",
          "source": "resolved-conversations",
          "title": "Refund requests"
        }'
```

#### Response

```json
{
  "id": "67ec1234abcd5678ef901235",
  "answer": "You can request a refund within 30 days from **Settings → Billing**.",
  "createdAt": "2026-09-03T10:15:00.000Z",
  "externalId": "resolution-1842",
  "indexStatus": "indexed",
  "object": "qna",
  "questions": [
    "How do I request a refund?",
    "Can I get my money back?"
  ],
  "revision": 1,
  "source": "resolved-conversations",
  "title": "Refund requests",
  "updatedAt": "2026-09-03T10:15:30.000Z",
  "match": {
    "entry": {
      "id": "67ec1234abcd5678ef901236",
      "kind": "qna",
      "section": null,
      "similarity": 0.61,
      "title": "Refund requests",
      "url": null
    },
    "reason": "Both answer how to request a refund; the new entry adds the 30-day window.",
    "verdict": "same_topic"
  },
  "outcome": "created"
}
```

## Get a Q&A entry

**get** `/v2/training_data/qna/{id}`

Returns a single Q&A entry by ID.

### Path Parameters

- `id: string`

  The Q&A entry ID

### Header Parameters

- `"Featurebase-Version": optional "2026-08-19.orbit" or "2026-01-01.nova" or "2025-12-12.clover"`

  - `"2026-08-19.orbit"`

  - `"2026-01-01.nova"`

  - `"2025-12-12.clover"`

### Returns

- `Qna object { id, answer, createdAt, 10 more }`

  - `id: string`

    Q&A entry ID

  - `answer: string`

    The answer the AI agent gives (markdown)

  - `createdAt: string`

    ISO timestamp of creation

  - `externalId: string`

    Stable identifier supplied by the source system

  - `indexStatus: "pending" or "indexed" or "failed"`

    Whether the entry is searchable by the AI agent. `failed` entries are stored and indexing is retried hourly up to three times. Re-send the resource to retry after exhaustion.

    - `"pending"`

    - `"indexed"`

    - `"failed"`

  - `object: "qna"`

    Object type identifier

    - `"qna"`

  - `questions: array of string`

    Question variants the answer applies to

  - `revision: number`

    Q&A revision. Send it as expectedRevision on PATCH to reject stale changes.

  - `source: string`

    Pipeline label, if one was set

  - `title: string`

    Short label for the entry

  - `updatedAt: string`

    ISO timestamp of the last change

  - `match: optional object { entry, reason, verdict }`

    Present only when `onMatch` was passed and the check ran: the verdict and the closest existing item, even when the write went ahead. `null` otherwise (the default), including when the payload was unchanged.

    - `entry: object { id, kind, section, 3 more }`

      The existing item the verdict is about, or `null` when nothing was close

      - `id: string`

        ID of the item (Q&A entry, training file, or article)

      - `kind: "qna" or "training_file" or "help_center_article"`

        What kind of knowledge item matched: a Q&A entry, a training file / crawled page, or a live help-center article.

        - `"qna"`

        - `"training_file"`

        - `"help_center_article"`

      - `section: string`

        Heading breadcrumb of the matching section, for training files

      - `similarity: number`

        Cosine similarity between your entry and the item (0–1); `null` for a keyword-only hit

      - `title: string`

        Title of the item

      - `url: string`

        Public URL of the item when it has one (articles, crawled pages)

    - `reason: string`

      The judge's one-line explanation of the verdict

    - `verdict: "same_topic" or "contradicts" or "different" or 2 more`

      How the closest existing item relates to the entry. `same_topic`: it answers the same customer question (the entry would be a duplicate, rewording, or updated version). `contradicts`: same question, incompatible facts. `different`: a different question, even if the wording overlaps. `unclear`: the judge could not decide. `no_match`: nothing similar was found.

      - `"same_topic"`

      - `"contradicts"`

      - `"different"`

      - `"unclear"`

      - `"no_match"`

  - `outcome: optional "created" or "updated" or "unchanged"`

    What the write did — present on create and update responses. `created`: a new entry. `updated`: an entry already carried one of the questions (or was merged into with `onMatch: "update"`) and got new content or an indexing retry. `unchanged`: everything in the payload was already on that entry; nothing written, nothing re-indexed.

    - `"created"`

    - `"updated"`

    - `"unchanged"`

### Example

```http
curl https://do.featurebase.app/v2/training_data/qna/$ID \
    -H "Authorization: Bearer $FEATUREBASE_API_KEY"
```

#### Response

```json
{
  "id": "67ec1234abcd5678ef901235",
  "answer": "You can request a refund within 30 days from **Settings → Billing**.",
  "createdAt": "2026-09-03T10:15:00.000Z",
  "externalId": "resolution-1842",
  "indexStatus": "indexed",
  "object": "qna",
  "questions": [
    "How do I request a refund?",
    "Can I get my money back?"
  ],
  "revision": 1,
  "source": "resolved-conversations",
  "title": "Refund requests",
  "updatedAt": "2026-09-03T10:15:30.000Z",
  "match": {
    "entry": {
      "id": "67ec1234abcd5678ef901236",
      "kind": "qna",
      "section": null,
      "similarity": 0.61,
      "title": "Refund requests",
      "url": null
    },
    "reason": "Both answer how to request a refund; the new entry adds the 30-day window.",
    "verdict": "same_topic"
  },
  "outcome": "created"
}
```

## Update a Q&A entry

**patch** `/v2/training_data/qna/{id}`

Updates title, questions, answer, or source. Questions replaces the whole list; preserve the union yourself when merging. Supply expectedRevision from GET or oracle.existing / oracle.target to apply only to the version you reviewed. A stale revision returns 409 revision_conflict; a question owned by another entry returns 409 question_already_used. Content changes attempt indexing before returning; check indexStatus.

Optional onMatch checks changed questions or answer against the addressed entry and retrieved candidates. reject refuses overlap; update permits deliberate replacement but refuses unclear results; create overrides the result. PATCH never merges entries. Checks are bounded advice and do not lock the rest of the knowledge base. A missing onMatch applies the requested update without semantic judging.

### Path Parameters

- `id: string`

  The Q&A entry ID

### Header Parameters

- `"Featurebase-Version": optional "2026-08-19.orbit" or "2026-01-01.nova" or "2025-12-12.clover"`

  - `"2026-08-19.orbit"`

  - `"2026-01-01.nova"`

  - `"2025-12-12.clover"`

### Body Parameters

- `answer: optional string`

  Replacement answer

- `answerFormat: optional "markdown" or "html"`

  Format of `answer`. `markdown` (default) is stored as-is; `html` is converted to markdown.

  - `"markdown"`

  - `"html"`

- `expectedRevision: optional number`

  Apply only if the entry still has this revision. A stale revision returns 409 revision_conflict without writing. Get the revision from retrieve, oracle.existing, or oracle.target.

- `onMatch: optional "reject" or "update" or "create"`

  Optional inline knowledge check for a content change. Omit it (the default) and the change is applied immediately. Pass it to check the changed `questions` / `answer` against the addressed entry and retrieved workspace candidates first: `reject`: respond `409 conflicts_with_existing` and leave the entry untouched; `update` or `create`: apply the change and report the match in `match`. A PATCH never merges two entries. Ignored when neither `questions` nor `answer` changes.

  - `"reject"`

  - `"update"`

  - `"create"`

- `questions: optional array of string`

  Replacement question list (replaces all existing questions)

- `source: optional string`

  Free-form label for the pipeline or system this entry came from. Filter lists by it to review or sweep everything from one source.

- `title: optional string`

  New label

### Returns

- `Qna object { id, answer, createdAt, 10 more }`

  - `id: string`

    Q&A entry ID

  - `answer: string`

    The answer the AI agent gives (markdown)

  - `createdAt: string`

    ISO timestamp of creation

  - `externalId: string`

    Stable identifier supplied by the source system

  - `indexStatus: "pending" or "indexed" or "failed"`

    Whether the entry is searchable by the AI agent. `failed` entries are stored and indexing is retried hourly up to three times. Re-send the resource to retry after exhaustion.

    - `"pending"`

    - `"indexed"`

    - `"failed"`

  - `object: "qna"`

    Object type identifier

    - `"qna"`

  - `questions: array of string`

    Question variants the answer applies to

  - `revision: number`

    Q&A revision. Send it as expectedRevision on PATCH to reject stale changes.

  - `source: string`

    Pipeline label, if one was set

  - `title: string`

    Short label for the entry

  - `updatedAt: string`

    ISO timestamp of the last change

  - `match: optional object { entry, reason, verdict }`

    Present only when `onMatch` was passed and the check ran: the verdict and the closest existing item, even when the write went ahead. `null` otherwise (the default), including when the payload was unchanged.

    - `entry: object { id, kind, section, 3 more }`

      The existing item the verdict is about, or `null` when nothing was close

      - `id: string`

        ID of the item (Q&A entry, training file, or article)

      - `kind: "qna" or "training_file" or "help_center_article"`

        What kind of knowledge item matched: a Q&A entry, a training file / crawled page, or a live help-center article.

        - `"qna"`

        - `"training_file"`

        - `"help_center_article"`

      - `section: string`

        Heading breadcrumb of the matching section, for training files

      - `similarity: number`

        Cosine similarity between your entry and the item (0–1); `null` for a keyword-only hit

      - `title: string`

        Title of the item

      - `url: string`

        Public URL of the item when it has one (articles, crawled pages)

    - `reason: string`

      The judge's one-line explanation of the verdict

    - `verdict: "same_topic" or "contradicts" or "different" or 2 more`

      How the closest existing item relates to the entry. `same_topic`: it answers the same customer question (the entry would be a duplicate, rewording, or updated version). `contradicts`: same question, incompatible facts. `different`: a different question, even if the wording overlaps. `unclear`: the judge could not decide. `no_match`: nothing similar was found.

      - `"same_topic"`

      - `"contradicts"`

      - `"different"`

      - `"unclear"`

      - `"no_match"`

  - `outcome: optional "created" or "updated" or "unchanged"`

    What the write did — present on create and update responses. `created`: a new entry. `updated`: an entry already carried one of the questions (or was merged into with `onMatch: "update"`) and got new content or an indexing retry. `unchanged`: everything in the payload was already on that entry; nothing written, nothing re-indexed.

    - `"created"`

    - `"updated"`

    - `"unchanged"`

### Example

```http
curl https://do.featurebase.app/v2/training_data/qna/$ID \
    -X PATCH \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $FEATUREBASE_API_KEY" \
    -d '{
          "answer": "You can request a refund within 60 days from **Settings → Billing**.",
          "answerFormat": "markdown",
          "expectedRevision": 1,
          "onMatch": "reject",
          "questions": [
            "How do I request a refund?",
            "Can I get my money back?"
          ],
          "source": "resolved-conversations",
          "title": "Refund requests"
        }'
```

#### Response

```json
{
  "id": "67ec1234abcd5678ef901235",
  "answer": "You can request a refund within 30 days from **Settings → Billing**.",
  "createdAt": "2026-09-03T10:15:00.000Z",
  "externalId": "resolution-1842",
  "indexStatus": "indexed",
  "object": "qna",
  "questions": [
    "How do I request a refund?",
    "Can I get my money back?"
  ],
  "revision": 1,
  "source": "resolved-conversations",
  "title": "Refund requests",
  "updatedAt": "2026-09-03T10:15:30.000Z",
  "match": {
    "entry": {
      "id": "67ec1234abcd5678ef901236",
      "kind": "qna",
      "section": null,
      "similarity": 0.61,
      "title": "Refund requests",
      "url": null
    },
    "reason": "Both answer how to request a refund; the new entry adds the 30-day window.",
    "verdict": "same_topic"
  },
  "outcome": "created"
}
```

## Delete a Q&A entry

**delete** `/v2/training_data/qna/{id}`

Removes a Q&A entry from the workspace and attempts search-index cleanup. Delete only entries your pipeline owns; a merged entry may have several contributing sources.

### Path Parameters

- `id: string`

  The Q&A entry ID

### Header Parameters

- `"Featurebase-Version": optional "2026-08-19.orbit" or "2026-01-01.nova" or "2025-12-12.clover"`

  - `"2026-08-19.orbit"`

  - `"2026-01-01.nova"`

  - `"2025-12-12.clover"`

### Returns

- `id: string`

  ID of the deleted Q&A entry

- `deleted: true`

  Indicates the resource was deleted

  - `true`

- `object: "qna"`

  Object type identifier

  - `"qna"`

### Example

```http
curl https://do.featurebase.app/v2/training_data/qna/$ID \
    -X DELETE \
    -H "Authorization: Bearer $FEATUREBASE_API_KEY"
```

#### Response

```json
{
  "id": "67ec1234abcd5678ef901235",
  "deleted": true,
  "object": "qna"
}
```

## Domain Types

### Qna

- `Qna object { id, answer, createdAt, 10 more }`

  - `id: string`

    Q&A entry ID

  - `answer: string`

    The answer the AI agent gives (markdown)

  - `createdAt: string`

    ISO timestamp of creation

  - `externalId: string`

    Stable identifier supplied by the source system

  - `indexStatus: "pending" or "indexed" or "failed"`

    Whether the entry is searchable by the AI agent. `failed` entries are stored and indexing is retried hourly up to three times. Re-send the resource to retry after exhaustion.

    - `"pending"`

    - `"indexed"`

    - `"failed"`

  - `object: "qna"`

    Object type identifier

    - `"qna"`

  - `questions: array of string`

    Question variants the answer applies to

  - `revision: number`

    Q&A revision. Send it as expectedRevision on PATCH to reject stale changes.

  - `source: string`

    Pipeline label, if one was set

  - `title: string`

    Short label for the entry

  - `updatedAt: string`

    ISO timestamp of the last change

  - `match: optional object { entry, reason, verdict }`

    Present only when `onMatch` was passed and the check ran: the verdict and the closest existing item, even when the write went ahead. `null` otherwise (the default), including when the payload was unchanged.

    - `entry: object { id, kind, section, 3 more }`

      The existing item the verdict is about, or `null` when nothing was close

      - `id: string`

        ID of the item (Q&A entry, training file, or article)

      - `kind: "qna" or "training_file" or "help_center_article"`

        What kind of knowledge item matched: a Q&A entry, a training file / crawled page, or a live help-center article.

        - `"qna"`

        - `"training_file"`

        - `"help_center_article"`

      - `section: string`

        Heading breadcrumb of the matching section, for training files

      - `similarity: number`

        Cosine similarity between your entry and the item (0–1); `null` for a keyword-only hit

      - `title: string`

        Title of the item

      - `url: string`

        Public URL of the item when it has one (articles, crawled pages)

    - `reason: string`

      The judge's one-line explanation of the verdict

    - `verdict: "same_topic" or "contradicts" or "different" or 2 more`

      How the closest existing item relates to the entry. `same_topic`: it answers the same customer question (the entry would be a duplicate, rewording, or updated version). `contradicts`: same question, incompatible facts. `different`: a different question, even if the wording overlaps. `unclear`: the judge could not decide. `no_match`: nothing similar was found.

      - `"same_topic"`

      - `"contradicts"`

      - `"different"`

      - `"unclear"`

      - `"no_match"`

  - `outcome: optional "created" or "updated" or "unchanged"`

    What the write did — present on create and update responses. `created`: a new entry. `updated`: an entry already carried one of the questions (or was merged into with `onMatch: "update"`) and got new content or an indexing retry. `unchanged`: everything in the payload was already on that entry; nothing written, nothing re-indexed.

    - `"created"`

    - `"updated"`

    - `"unchanged"`

### Qna Delete Response

- `QnaDeleteResponse object { id, deleted, object }`

  - `id: string`

    ID of the deleted Q&A entry

  - `deleted: true`

    Indicates the resource was deleted

    - `true`

  - `object: "qna"`

    Object type identifier

    - `"qna"`
