Skip to content
Dashboard

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 ParametersExpand Collapse
"Featurebase-Version": optional "2026-08-19.orbit" or "2026-01-01.nova" or "2025-12-12.clover"
One of the following:
"2026-08-19.orbit"
"2026-01-01.nova"
"2025-12-12.clover"
Body ParametersJSONExpand Collapse
answer: string

The answer the AI agent should give

minLength1
maxLength100000
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.

One of the following:
"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.

minLength1
maxLength255
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.

One of the following:
"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.

minLength1
maxLength100
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.

minLength1
maxLength500
ReturnsExpand Collapse
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.

One of the following:
"pending"
"indexed"
"failed"
object: "qna"

Object type identifier

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.

One of the following:
"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.

One of the following:
"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.

One of the following:
"created"
"updated"
"unchanged"

Create a Q&A entry

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"
        }'
{
  "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"
}
Returns Examples
{
  "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"
}