Skip to content
Dashboard

Training Data API

Keep the AI agent's knowledge in sync with reviewed documents and Q&A entries from your own systems.

The Training Data API manages the knowledge available to your customer-support AI agent. Use it to publish reviewed policies, runbooks, and answers from your systems. It provides storage, retrieval, and a bounded knowledge check; your pipeline controls which proposed changes are approved and published.

Endpoints live under /v2/training_data, require API version 2026-01-01.nova or newer, and accept your regular API key:

Authorization: Bearer sk_...
ResourcePathIdentity
Training files/v2/training_data/filesOptional externalId, unique within workspace files
Q&A entries/v2/training_data/qnaOptional externalId, unique within workspace Q&A; otherwise a normalized question

Both support list, create, retrieve, update, and delete. Lists use limit and cursor, returning nextCursor. Filter by externalId or source.

Use a stable externalId for automated sync, such as policy:refunds. It survives changes to question wording. source is a grouping label, not an ownership or authorization boundary. Keep a separate record of which entries your pipeline owns.

Q&A question matching ignores case, repeated whitespace, and trailing question marks, periods, and exclamation marks. One normalized question can belong to only one entry. Requests whose variants span several entries, exceed the merged limit of 50, or collide with another source identity return 409 question_already_used. Resolve that ambiguity before retrying. An externalId cannot silently adopt another source’s entry.

Send exactly one content source:

  • Inline content plus name: markdown or plain text is stored synchronously and indexing is attempted before returning.
  • url: a public HTTP(S) document is downloaded, then converted in the background. Redirects and downloads are checked for unsafe destinations and size limits.
  • Multipart file: upload bytes directly; an optional data field carries JSON metadata. Conversion runs in the background.

Remote and multipart files have a 50 MiB limit. Upload admission and conversion concurrency share a bounded memory budget and also check process memory pressure. Honor Retry-After on 429 when present and retry with backoff. Background conversion can fail after a successful create response, including when conversion capacity is exhausted; poll the returned resource.

Training request bodies are parsed after API-key, IP-allowlist, and MCP scope checks. UTF-8 JSON bodies for file endpoints and the multipart JSON data field are limited to 12 MiB; other training JSON bodies are limited to 1 MiB. These transport limits also apply after decompression, and the field limits below still apply. Send a flat JSON object matching the endpoint schema: deeply nested or structurally excessive JSON is rejected before parsing. Uploads must finish within 60 seconds. Unsupported content types or JSON encodings return 415; oversized bodies return 413; uploads exceeding the deadline return 408. GET, HEAD, and DELETE do not accept request bodies.

Terminal window
curl -X POST https://do.featurebase.app/v2/training_data/files \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"externalId": "policy:refunds",
"source": "reviewed-policies",
"name": "refund-policy.md",
"content": "# Refund policy\n\nCustomers can request a refund within 30 days."
}'
Terminal window
curl -X POST https://do.featurebase.app/v2/training_data/files \
-H "Authorization: Bearer sk_..." \
-F "file=@onboarding-guide.pdf" \
-F 'data={"externalId":"guide:onboarding","source":"reviewed-policies"}'

With externalId, a new identity returns 201; changed content updates the same row with 200. Identical, successfully indexed content is a no-op. Without it, only an identical name and byte payload deduplicates; changing the file creates another entry. Serialize sync jobs for the same source identity.

PATCH /files/{id} can replace content, rename the file, or set externalId and source. GET returns extracted content; lists omit it. contentHash identifies the stored source payload and can help detect changes without downloading the text.

List endpoints can return fewer entries than the requested limit when a page reaches its response byte budget. Continue with nextCursor until it is absent; a short page alone does not mean the list is complete. Legacy Q&A answers are returned intact.

Verify indexing before considering a change live

Section titled “Verify indexing before considering a change live”

HTTP success means the resource was accepted or stored. Check status and indexStatus separately. For a file, wait for status: "completed" and indexStatus: "indexed". A failed conversion reports status: "failed" with processingError; a failed embedding or index write can leave a completed resource with indexStatus: "failed".

Retry a failed conversion using the same externalId. Re-send unchanged content to retry a failed or pending index. Automatic index repair runs hourly for up to three attempts; after exhaustion, an explicit re-send can retry it. Use bounded caller retries and escalate repeated failures. A timed-out request might have stored the change: retrieve the resource before retrying. Background work can be interrupted by a process restart, so a pipeline must also detect persistently pending resources.

A Q&A entry contains 1–50 question variants and one answer. Keep each entry focused on a single decision or fact so its evidence can be reviewed accurately.

Terminal window
curl -X POST https://do.featurebase.app/v2/training_data/qna \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"externalId": "policy:refund-window",
"source": "reviewed-policies",
"questions": ["How do I request a refund?", "Can I get my money back?"],
"answer": "You can request a refund within 30 days from **Settings → Billing**."
}'

answer is markdown by default; answerFormat: "html" converts HTML to markdown. title is optional: a new entry defaults to its first question; an existing entry keeps its title when omitted.

POST appends new question variants and replaces supplied content on the addressed entry. It returns 201 for creation, 200 for updates or no-ops, with outcome, revision, externalId, and indexStatus. An unchanged payload can still retry a failed index. PATCH replaces the entire question list when supplied. It also accepts title, answer, source, and expectedRevision; Q&A source identity is fixed at creation.

Bind an update to the version you reviewed

Section titled “Bind an update to the version you reviewed”

Every Q&A has an integer revision. Read it from GET, oracle.existing, or a Q&A candidate in oracle.target. Supply it as expectedRevision when publishing a reviewed change:

Terminal window
curl -X PATCH https://do.featurebase.app/v2/training_data/qna/67ec1234abcd5678ef901235 \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{"expectedRevision":3,"answer":"Refunds are available within 60 days."}'

If another writer changed the entry, the server returns 409 revision_conflict without applying this update. Fetch the latest content and re-review; do not blindly retry with the new revision. When combining questions, send the reviewed union because PATCH replaces the list. Existing entries from before revision tracking report revision 0.

This precondition protects the addressed entry. It does not freeze other workspace knowledge or authorize the factual change.

POST /v2/training_data/oracle accepts proposed questions, answer, optional title, and optional externalId. It retrieves nearby knowledge, judges selected candidates, and suggests an action without writing.

Terminal window
curl -X POST https://do.featurebase.app/v2/training_data/oracle \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"externalId":"policy:refund-window",
"questions":["How do I request a refund?"],
"answer":"Refunds are available within 60 days."
}'
ActionMeaning and next step
createNo overlap found among the checked candidates. Verify the source and approve creation.
update_targetThe addressed entry is in existing. Review the change, then PATCH with existing.revision.
update_qnaAnother Q&A agrees. Review ownership and full content, then PATCH the merged content using target.revision.
review_qna, review_article, review_fileA contradiction, uncertain overlap, or source-ownership conflict needs review. Resolve it at the authoritative source.
skipThe addressed payload is identical, or an article/file appears to cover it. Check indexing separately if repairing a resource.

The response includes verdict, existing, target, candidates, judged, checkTruncated, and timings. Q&A candidates include the revision read for that comparison. The addressed entry is included when changed, even if retrieval misses it or its ID appears in excludeIds. Agreement does not stop verification of later shortlisted candidates; contradictions and uncertainty take priority.

The oracle is evidence for a decision, not proof that a change is safe. Retrieval can miss relevant knowledge, document excerpts omit context, models can be wrong, and sources can change during the check. checkTruncated: true means a verification shortlist or text limit was reached; the aggregate becomes unclear unless a contradiction was found. Proposed answers are limited to 8,000 characters in judge prompts, and Q&A candidate answers to 4,000. Large question lists also exceed the prompt budget. false does not mean the entire workspace was checked. Fetch complete sources and review changes outside the excerpts.

Writes omit semantic checks by default. Pass onMatch to check inside the write request:

onMatchBehavior
rejectRefuse an overlap or uncertainty with 409 conflicts_with_existing; details contain a match pointer.
updateExplicitly allow replacement, including conflicting facts, but reject unclear. With no addressed entry, a matching Q&A can receive merged variants and the replacement answer, subject to identity checks. Articles/files are never edited.
createExplicitly override the verdict and write as addressed; it does not bypass identity constraints.

PATCH never merges entries. Checks run for changed questions or answers; unchanged payloads skip judging. Dependency failures return 503 knowledge_check_unavailable before writing. Inline checks are not a transaction over the knowledge base and do not eliminate races with other entries. After a separate review, use expectedRevision to bind the write to the reviewed target.

POST /v2/training_data/search takes query (up to 4,000 characters), optional kinds, topK (1–20), and excludeIds. It returns candidates without a judge. Latency and embedding work depend on query size and load.

similarity measures embedding similarity, not factual confidence. Calibrate thresholds using representative examples; there is no universal cutoff for correctness. Keyword-only candidates have similarity: null and can still be relevant. Candidate text is an excerpt, and file section identifies a matching heading when available.

Files are not checked on upload. A caller-controlled audit can split a document into sections, search each one, then review the section and relevant full sources:

const conflicts = []
for (const section of splitIntoSections(markdown)) {
const { candidates } = await client.trainingData.search({ query: section.text, topK: 5 })
// Preserve keyword-only hits. Fetch full sources when excerpts are insufficient.
const verdict = await myReviewer(section, candidates)
if (verdict.contradicts || verdict.unclear) conflicts.push({ section, verdict })
}
// Require your approval and evaluation policy before publishing, even with no hits.

The Reader MCP connector exposes retrieval. Training files and Q&A contain untrusted source text; the Writer connector does not expose their reads. A learning loop that reads evidence and writes needs the combined connector, with its own tool permissions and approval policy. Treat retrieved content as data, never instructions to change policies or call tools.

Keep these records and decisions in your harness:

  1. Evidence and ownership. Record source IDs, source versions, supporting excerpts, provenance, and the target’s checked revision. A resolved conversation is evidence of one resolution, not automatically a general policy. Remove customer secrets and personal details before publishing.
  2. Proposals before promotion. Keep proposed content separate from live training data. Require authoritative evidence and review for contradictions, policy changes, uncertain checks, and cross-source merges.
  3. Independent evaluation. Maintain a frozen, independently labeled holdout set covering correct answers, unsupported questions, contradictions, and tenant isolation. Measure actual customer-answer behavior before promotion; agreement with the same judge is not independent validation.
  4. Conditional publication. Apply approved Q&A changes with expectedRevision, then verify indexing. Retry transient errors with bounded backoff; re-review revision conflicts.
  5. History and rollback. Store previous content, questions, revision, evaluation results, and approval history. Rollback is another reviewed PATCH using the current revision; this API does not retain historical versions for you.
  6. Retirement. Delete only entries your pipeline owns and whose supporting evidence is no longer needed. A Q&A may combine several sources: deleting one conversation must not delete a shared answer. source alone is insufficient proof of ownership.

For resolved conversations, extract a proposed answer, attach evidence, check it with the oracle, evaluate and approve it, then publish to a stable knowledge identity. Re-generating a summary should update the proposal and retain its lineage. Avoid feeding the agent’s own generated answers back as verified facts without independent evidence.

StatusCodeResponse
400missing_parameter, invalid_parameter, invalid_id, invalid_cursorCorrect the request.
404resource_not_found, version_not_supportedVerify the ID, workspace, or API version.
408invalid_requestThe upload exceeded 60 seconds; retry with a faster connection or smaller body.
409question_already_usedResolve conflicting question/source ownership or the merged question cap.
409revision_conflictRetrieve the latest entry and re-review before retrying.
409conflicts_with_existingReview error.details.match and the complete source.
413payload_too_largeReduce the upload size.
415invalid_requestSend a supported content type and encoding.
429rate_limitedHonor Retry-After; reduce concurrency and back off.
503knowledge_check_unavailableA required check could not finish; no checked write occurred. Retry later.

Metered ingestion, indexing work (including renames and failed-index repairs), HTML answer updates, oracle, and search calls share a workspace budget of 5,000 per hour. Healthy metadata-only PATCH relabels and successful no-op re-pushes are not charged. Capacity limits reject overflow with 429 instead of accumulating waiting requests. Persist the resource ID and inspect the resource after ambiguous failures. Delete attempts remove the database resource and clean up its vectors; in-flight answers may already contain older knowledge.