33 operations in 8 groups, rendered from the OpenAPI document TAVONEL serves at /api/openapi. Nothing here is written beside the contract: if an operation is on this page, the contract publishes it, and a build where the two disagree does not ship.

Base URL https://tavonel.com/api/v1. The compile routes carry their own, https://tavonel.com/api, declared per path in the document so a generated client resolves each operation against the right one. Send a key as Authorization: Bearer tvnl_live_…; keys are created in the workspace under Developers and the plaintext is shown once. Activation and rollback are absent from this page because they are absent from the contract — they are browser-session decisions, and no scope grants them.

Try it, with no key

Three unauthenticated reads, run from this page against TAVONEL. No signup, no credential, nothing that spends. The same three are a copy-pasteable curl recipe on Integration recipes.

  • What TAVONEL can read

    GET /api/v1/capabilities

    The same list the upload route validates against. A format absent from it is refused at upload rather than accepted and dropped.

  • What is open right now

    GET /api/status

    The deployment's own state, including whether compiling your own files is open here.

  • The shape of an answer

    GET /reproducibility/sample-world

    A deterministic product fixture, unsigned and labelled as such in its own disclosure field. Build against the shape; do not judge extraction from it.

Capabilities

What TAVONEL can read, and who signs what it exports. No key required.

GET/capabilitiesNo key required

Read the capability manifest

Every source format TAVONEL can read, with its support tier, what survives into the compiled World, its known limitations and its qualification receipt when one exists. A verified tier without a receipt is not representable. Anything absent from the manifest is refused at upload. The two per-source ceilings TAVONEL enforces — bytes and pages — are published here as knownLimitations tokens.

Parameters, request and responses200
Request
curl -sS -X GET https://tavonel.com/api/v1/capabilities

Responses

200The capability manifest and the sha256 of its serialized form. contentSha256 is taken over the manifest without that field: delete it, re-serialize with the key order unchanged, and hash.

Example response
200
{
  "schemaVersion": "tavonel.capability_manifest.v1",
  "defaultStatus": "UNSUPPORTED",
  "entries": [
    {
      "sourceFamily": "pdf",
      "mime": "application/pdf",
      "extensions": [
        "pdf"
      ],
      "status": "BEST_EFFORT",
      "preserved": [
        "page",
        "paragraph_text",
        "bbox1000"
      ],
      "knownLimitations": [
        "read_through_cdr_sanitized_pdf_and_ocr",
        "no_table_or_formula_extraction",
        "at_most_5_mib_per_source",
        "at_most_80_pages_per_source"
      ],
      "qualifiedAt": null,
      "qualificationReceipt": null
    }
  ],
  "contentSha256": "sha256:<64 hex>"
}
GET/export/trustNo key required

Read the export signing key

The Ed25519 public key every signed export is signed with, and its sha256 fingerprint. Fetched here rather than read out of an archive, because an archive that vouches for its own key has proven nothing. A deployment with no signing key configured answers 503 EXPORT_SIGNER_NOT_CONFIGURED rather than a fingerprint nobody can verify against.

Parameters, request and responses200 · 503
Request
curl -sS -X GET https://tavonel.com/api/export/trust

Responses

200The trust record. Verify a downloaded archive against publicKeySpkiSha256 from here, never against a fingerprint inside the archive.

Example response
200
{
  "schemaVersion": "tavonel.export_trust.v1",
  "algorithm": "Ed25519",
  "keyId": "<key id>",
  "publicKeySpkiDerBase64": "<base64 SPKI>",
  "publicKeySpkiSha256": "sha256:<64 hex>"
}

503EXPORT_SIGNER_NOT_CONFIGURED — No signing key is configured, so neither a signed archive nor a fingerprint can be produced. GET /export/trust answers this rather than a fingerprint nobody can verify against. EXPORT_SIGNER_INVALID — The configured signer did not produce a usable key.

EXPORT_SIGNER_NOT_CONFIGUREDEXPORT_SIGNER_INVALID

Example response
503
{
  "code": "EXPORT_SIGNER_NOT_CONFIGURED"
}
GET/collections/{id}/downloadScope collections:download

Download the signed package

The signed, hash-verifiable ZIP. Signed or refused: there is no third outcome, and you never receive an archive still in the candidate state. Verify it against the fingerprint from GET /export/trust, never against one inside the archive.

Parameters, request and responses200 · 400 · 401 · 404 · 422 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
Request
curl -sS -X GET https://tavonel.com/api/v1/collections/{id}/download \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200The signed package. manifest/export-manifest.json carries a digest for every file and signatures/export-manifest.ed25519.json is the detached Ed25519 signature over those bytes.

400COLLECTION_ID_INVALID — The collection id did not match collection-<32 hex>.

COLLECTION_ID_INVALID

Example response
400
{
  "code": "COLLECTION_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404NOT_FOUND — The addressed resource does not exist in this workspace. Deliberately uniform across tenants: the same answer for an id that is not yours and an id that is nobody's.

NOT_FOUND

Example response
404
{
  "code": "NOT_FOUND"
}

422COLLECTION_PACKAGE_INVALID — The package failed its own validation, so it was not served. INVALID_SIGNATURE — The signature did not verify against the key it names. EVIDENCE_DANGLING — An object cites evidence that is not in the package.

COLLECTION_PACKAGE_INVALIDINVALID_SIGNATUREEVIDENCE_DANGLING

Example response
422
{
  "code": "COLLECTION_PACKAGE_INVALID"
}

503EXPORT_SIGNER_NOT_CONFIGURED — No signing key is configured, so neither a signed archive nor a fingerprint can be produced. GET /export/trust answers this rather than a fingerprint nobody can verify against. SIGNATURE_READ_FAILED — The detached signature could not be read. R2_NOT_CONFIGURED — Object storage is not configured.

EXPORT_SIGNER_NOT_CONFIGUREDSIGNATURE_READ_FAILEDR2_NOT_CONFIGURED

Example response
503
{
  "code": "EXPORT_SIGNER_NOT_CONFIGURED"
}

Documents

Direct-to-storage upload, and the immutable document inventory it produces.

POST/uploads/capabilityScope documents:intake

Request a direct upload URL

Returns a short-lived browser/agent-direct R2 PUT URL. Document bytes never pass through the application server. requestedBytes is checked against the deployment's per-source ceiling here, before any byte is stored: above it the answer is 413 SOURCE_EXCEEDS_PROCESSING_CEILING carrying maxBytes, maxPages and a sentence you can show a customer. Send x-tavonel-source-idempotency-key (a sha256 hex digest of the source event) to make the document id deterministic, so a retried intake converges on one document rather than two.

Parameters, request and responses200 · 400 · 401 · 402 · 409 · 413 · 429 · 503

Parameters

NameInRequiredShape
x-tavonel-source-idempotency-keyheadernostring (^[a-f0-9]{64}$) Tenant-bound source-event key. Makes the document id deterministic so a retried intake converges on one document.
Request
curl -sS -X POST https://tavonel.com/api/v1/uploads/capability \
  -H "Authorization: Bearer $TAVONEL_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "originalFilename": "manual.pdf", "declaredMimeType": "application/pdf", "requestedBytes": 184320 }'

Responses

200A qualified direct upload capability and the immutable document id the bytes will be registered under. PUT the bytes to url with the headers in headers and nothing else; the URL is the credential and it is short-lived.

Example response
200
{
  "documentId": "00000000-0000-4000-8000-000000000000",
  "url": "https://<storage-host>/quarantine/...?X-Amz-Signature=...",
  "method": "PUT",
  "headers": {
    "content-type": "application/pdf",
    "content-length": "184320"
  },
  "expiresAt": "2026-09-16T00:15:00.000Z"
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

400UNQUALIFIED_INPUT — A required field was missing or was not of the declared type. UNQUALIFIED_MIME — The declared MIME type is not in the capability manifest. FILENAME_MIME_MISMATCH — The extension and the declared MIME type disagree. FILE_NAME_INVALID — The filename carried a path separator or a character the store refuses. SOURCE_IDEMPOTENCY_KEY_INVALID — x-tavonel-source-idempotency-key was present and was not 64 hex characters.

UNQUALIFIED_INPUTUNQUALIFIED_MIMEFILENAME_MIME_MISMATCHFILE_NAME_INVALIDSOURCE_IDEMPOTENCY_KEY_INVALID

Example response
400
{
  "code": "UNQUALIFIED_INPUT"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns. API_KEY_INVALID — The bearer token was well-formed but did not match a stored key. API_KEY_EXPIRED — The key matched and its expiry has passed. API_KEY_REVOKED — The key matched and was revoked. A rotation revokes the key it replaces.

AUTH_REQUIREDAPI_KEY_INVALIDAPI_KEY_EXPIREDAPI_KEY_REVOKED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

402STUDIO_SUBSCRIPTION_REQUIRED — The operation needs the higher plan tier — activation, rollback and retrieval-index rebuild are the three. TRIAL_ARCHIVE_NOT_INCLUDED — ZIP upload is not included in the free evaluation. TRIAL_FILE_LIMIT_EXCEEDED — The free evaluation's file count is spent. GPU_CREDITS_REQUIRED — The workspace has no processing balance left to reserve against.

STUDIO_SUBSCRIPTION_REQUIREDTRIAL_ARCHIVE_NOT_INCLUDEDTRIAL_FILE_LIMIT_EXCEEDEDGPU_CREDITS_REQUIRED

Example response
402
{
  "code": "STUDIO_SUBSCRIPTION_REQUIRED"
}

409INTAKE_IDEMPOTENCY_CONFLICT — The same source idempotency key was already used for different bytes. COMPUTE_IDEMPOTENCY_CONFLICT — The processing reservation ledger already holds a different reservation under this request's idempotency key.

INTAKE_IDEMPOTENCY_CONFLICTCOMPUTE_IDEMPOTENCY_CONFLICT

Example response
409
{
  "code": "INTAKE_IDEMPOTENCY_CONFLICT"
}

413SOURCE_EXCEEDS_PROCESSING_CEILING — requestedBytes is above what every processor in the chain can read. The body carries maxBytes, maxPages and a limit sentence. This is the refusal the 5 MB per-source ceiling produces. TRIAL_FILE_TOO_LARGE — Above the free-evaluation per-file bound, which is lower than the deployment ceiling.

SOURCE_EXCEEDS_PROCESSING_CEILINGTRIAL_FILE_TOO_LARGE

Example response
413
{
  "code": "SOURCE_EXCEEDS_PROCESSING_CEILING"
}

429API_RATE_LIMITED — The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits. INTAKE_RATE_LIMITED — Too many upload capabilities were requested in the window. INTAKE_DAILY_QUOTA_EXCEEDED — The workspace's daily intake quota is spent.

API_RATE_LIMITEDINTAKE_RATE_LIMITEDINTAKE_DAILY_QUOTA_EXCEEDED

Example response
429
{
  "code": "API_RATE_LIMITED"
}

503SIGNER_NOT_CONFIGURED — The upload URL signer is not configured, so no capability can be issued. INTAKE_DISABLED — Intake is closed today. API_RATE_LIMIT_UNAVAILABLE — The allowance could not be read, so the request was refused rather than run unbounded.

SIGNER_NOT_CONFIGUREDINTAKE_DISABLEDAPI_RATE_LIMIT_UNAVAILABLE

Example response
503
{
  "code": "SIGNER_NOT_CONFIGURED"
}
GET/documentsScope documents:read

List documents

The workspace's immutable document inventory with each document's processing state and version key. Uploading the same file twice produces two documents that share one content digest; nothing merges them. This operation takes no paging parameters — the inventory is returned whole.

Parameters, request and responses200 · 401 · 403 · 429 · 503
Request
curl -sS -X GET https://tavonel.com/api/v1/documents \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200{ code: OK, workspaceId, documents }. A document whose source could not be resolved is present with its refusal rather than omitted.

Example response
200
{
  "code": "OK",
  "workspaceId": "pilot-<workspace>",
  "documents": [
    {
      "documentId": "00000000-0000-4000-8000-000000000000",
      "originalFilename": "manual.pdf",
      "mimeType": "application/pdf",
      "state": "ready",
      "sourceVersionKey": "src_v_<id>",
      "createdAt": "2026-09-16T00:00:00.000Z"
    }
  ]
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns. API_KEY_INVALID — The bearer token was well-formed but did not match a stored key.

AUTH_REQUIREDAPI_KEY_INVALID

Example response
401
{
  "code": "AUTH_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL. CONNECTOR_SOURCE_ACCESS_DENIED — The provider refused the stored credential. AUTHORIZATION_CHANGED_RETRY — The caller's authorization changed while the request was in flight, so the request was abandoned rather than finished under a permission that may no longer hold.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIREDCONNECTOR_SOURCE_ACCESS_DENIEDAUTHORIZATION_CHANGED_RETRY

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

429API_RATE_LIMITED — The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits.

API_RATE_LIMITED

Example response
429
{
  "code": "API_RATE_LIMITED"
}

503CONNECTOR_SOURCE_ACCESS_UNAVAILABLE — The provider could not be reached. READ_FAILED — A backing store could not be read. API_RATE_LIMIT_UNAVAILABLE — The allowance could not be read, so the request was refused rather than run unbounded.

CONNECTOR_SOURCE_ACCESS_UNAVAILABLEREAD_FAILEDAPI_RATE_LIMIT_UNAVAILABLE

Example response
503
{
  "code": "CONNECTOR_SOURCE_ACCESS_UNAVAILABLE"
}

Compile

Turning a document set into a candidate Compiled World, and following the run.

POST/collections/compileScope collections:compile

Compile one document set, synchronously

Compiles a single document set and waits for the artifact. Bounded to what one compile carries; a larger selection belongs on POST /compile-jobs, which partitions it server-side and survives a closed tab. The result is a candidate: candidatePromotion is always false, because activation is a human decision in a signed-in session and no key holds it.

Parameters, request and responses200 · 400 · 401 · 402 · 403 · 409 · 415 · 429 · 503
Request
curl -sS -X POST https://tavonel.com/api/v1/collections/compile \
  -H "Authorization: Bearer $TAVONEL_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "documentIds": [ "00000000-0000-4000-8000-000000000000" ] }'

Responses

200An immutable candidate package receipt. candidatePromotion is always false.

Example response
200
{
  "code": "COLLECTION_CANDIDATE_READY",
  "collectionId": "collection-<32 hex>",
  "manifestDigest": "sha256:<64 hex>",
  "candidatePromotion": false,
  "documentsTotal": 1
}

400DOCUMENT_IDS_REQUIRED — The request carried no document id array. DOCUMENT_SET_EMPTY — Nothing was selected to compile. DOCUMENT_SET_UNQUALIFIED — A value in documentIds was not a document id. DOCUMENT_SET_TOO_LARGE — More documents than one compile carries were sent to the single-compile route. INVALID_JSON — The body was not parseable JSON.

DOCUMENT_IDS_REQUIREDDOCUMENT_SET_EMPTYDOCUMENT_SET_UNQUALIFIEDDOCUMENT_SET_TOO_LARGEINVALID_JSON

Example response
400
{
  "code": "DOCUMENT_IDS_REQUIRED"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

402STUDIO_SUBSCRIPTION_REQUIRED — The operation needs the higher plan tier — activation, rollback and retrieval-index rebuild are the three. TRIAL_DURABLE_COMPILE_REQUIRED — The free evaluation compiles through the durable job route only. GPU_CREDITS_REQUIRED — The workspace has no processing balance left to reserve against.

STUDIO_SUBSCRIPTION_REQUIREDTRIAL_DURABLE_COMPILE_REQUIREDGPU_CREDITS_REQUIRED

Example response
402
{
  "code": "STUDIO_SUBSCRIPTION_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

409OCR_NOT_READY — The sources have not finished being read. SOURCE_VERSION_AMBIGUOUS — Two source versions carry the same identity and the request did not say which.

OCR_NOT_READYSOURCE_VERSION_AMBIGUOUS

Example response
409
{
  "code": "OCR_NOT_READY"
}

415METADATA_ONLY_ENDPOINT — Document bytes were POSTed to a route that accepts only metadata. Bytes go direct to storage, never through the application server.

METADATA_ONLY_ENDPOINT

Example response
415
{
  "code": "METADATA_ONLY_ENDPOINT"
}

429API_RATE_LIMITED — The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits. WORKSPACE_CONCURRENCY_LIMIT — The workspace already has as many compiles in flight as it may.

API_RATE_LIMITEDWORKSPACE_CONCURRENCY_LIMIT

Example response
429
{
  "code": "API_RATE_LIMITED"
}

503CORE_NOT_CONFIGURED — The compile runtime is unavailable. The request was not charged. CORE_UNAVAILABLE — The compile runtime was reachable and did not answer.

CORE_NOT_CONFIGUREDCORE_UNAVAILABLE

Example response
503
{
  "code": "CORE_NOT_CONFIGURED"
}
GET/compile-jobsScope collections:read

List recent compiles

The workspace's recent compiles, newest first, so a client that lost its job id can pick a run back up. Takes no paging parameters.

Parameters, request and responses200 · 401 · 403 · 429 · 503
Request
curl -sS -X GET https://tavonel.com/api/compile-jobs \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200{ code: OK, jobs }, newest first.

Example response
200
{
  "code": "OK",
  "jobs": [
    {
      "jobId": "cjob-<32 hex>",
      "state": "compiled",
      "collectionId": "collection-<32 hex>",
      "documentIds": [
        "00000000-0000-4000-8000-000000000000"
      ],
      "documentsTotal": 1,
      "documentsReady": 1,
      "blocked": [],
      "blockedResolution": null,
      "errorCode": null,
      "corpusId": null,
      "batchIndex": null,
      "batchCount": null,
      "createdAt": "2026-09-16T00:00:00.000Z",
      "updatedAt": "2026-09-16T00:02:00.000Z",
      "settledAt": "2026-09-16T00:02:00.000Z"
    }
  ]
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

429API_RATE_LIMITED — The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits.

API_RATE_LIMITED

Example response
429
{
  "code": "API_RATE_LIMITED"
}

503COMPILE_JOB_STORE_READ_FAILED — The durable job store could not be read. COMPILE_JOB_STORE_NOT_CONFIGURED — The durable job store is not configured.

COMPILE_JOB_STORE_READ_FAILEDCOMPILE_JOB_STORE_NOT_CONFIGURED

Example response
503
{
  "code": "COMPILE_JOB_STORE_READ_FAILED"
}
POST/compile-jobsScope collections:compile

Start a durable compile

Records the intent to compile and returns immediately. The job advances on the server whether or not the caller stays connected, which is the difference between this and /v1/collections/compile. Submitting the same document set again returns the job that already exists rather than starting a second compile. A selection larger than one compile can carry is partitioned server-side into parts and answered with COMPILE_CORPUS_ACCEPTED and a corpusId instead of a jobId; each part is an ordinary compile job with its own id, state and event stream.

Parameters, request and responses202 · 400 · 401 · 402 · 403 · 409 · 415 · 429 · 503
Request
curl -sS -X POST https://tavonel.com/api/compile-jobs \
  -H "Authorization: Bearer $TAVONEL_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "documentIds": [ "00000000-0000-4000-8000-000000000000", "00000000-0000-4000-8000-000000000001" ] }'

Responses

202Accepted. A single job returns { code: COMPILE_JOB_ACCEPTED, jobId, state, documentsTotal }; a partitioned selection returns { code: COMPILE_CORPUS_ACCEPTED, corpusId, batchCount, parts } instead, and each part is an ordinary compile job with its own id. Location names whichever resource was created.

Example response
202
{
  "code": "COMPILE_JOB_ACCEPTED",
  "jobId": "cjob-<32 hex>",
  "state": "queued",
  "documentsTotal": 2
}

400DOCUMENT_IDS_REQUIRED — The request carried no document id array. DOCUMENT_SET_EMPTY — Nothing was selected to compile. DOCUMENT_SET_UNQUALIFIED — A value in documentIds was not a document id. CORPUS_TOO_LARGE — More documents than one run carries. SPLIT_PART_LIMIT_EXCEEDED — Partitioning the selection would make more parts than a run holds. INVALID_JSON — The body was not parseable JSON.

DOCUMENT_IDS_REQUIREDDOCUMENT_SET_EMPTYDOCUMENT_SET_UNQUALIFIEDCORPUS_TOO_LARGESPLIT_PART_LIMIT_EXCEEDEDINVALID_JSON

Example response
400
{
  "code": "DOCUMENT_IDS_REQUIRED"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

402STUDIO_SUBSCRIPTION_REQUIRED — The operation needs the higher plan tier — activation, rollback and retrieval-index rebuild are the three. TRIAL_WORLD_LIMIT_REACHED — The free evaluation's compiled-World count is spent. GPU_CREDITS_REQUIRED — The workspace has no processing balance left to reserve against.

STUDIO_SUBSCRIPTION_REQUIREDTRIAL_WORLD_LIMIT_REACHEDGPU_CREDITS_REQUIRED

Example response
402
{
  "code": "STUDIO_SUBSCRIPTION_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

409COMPILE_JOB_SLOT_CONFLICT — A part of this corpus is already held by a job over a different document set. Retrying does not clear it. SOURCE_VERSION_AMBIGUOUS — Two source versions carry the same identity and the request did not say which.

COMPILE_JOB_SLOT_CONFLICTSOURCE_VERSION_AMBIGUOUS

Example response
409
{
  "code": "COMPILE_JOB_SLOT_CONFLICT"
}

415METADATA_ONLY_ENDPOINT — Document bytes were POSTed to a route that accepts only metadata. Bytes go direct to storage, never through the application server.

METADATA_ONLY_ENDPOINT

Example response
415
{
  "code": "METADATA_ONLY_ENDPOINT"
}

429API_RATE_LIMITED — The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits. WORKSPACE_CONCURRENCY_LIMIT — The workspace already has as many compiles in flight as it may. COMPILE_JOB_WORKSPACE_LIMIT_REACHED — The workspace is at its compile-job ceiling.

API_RATE_LIMITEDWORKSPACE_CONCURRENCY_LIMITCOMPILE_JOB_WORKSPACE_LIMIT_REACHED

Example response
429
{
  "code": "API_RATE_LIMITED"
}

503COMPILE_JOB_STORE_NOT_CONFIGURED — The durable job store is not configured. COMPILE_JOB_STORE_WRITE_FAILED — The durable job store could not be written, so the intent was not recorded. COMPILE_JOB_RPC_UNDEFINED — The job store is missing a procedure this build expects — a deployment mismatch.

COMPILE_JOB_STORE_NOT_CONFIGUREDCOMPILE_JOB_STORE_WRITE_FAILEDCOMPILE_JOB_RPC_UNDEFINED

Example response
503
{
  "code": "COMPILE_JOB_STORE_NOT_CONFIGURED"
}
GET/compile-jobs/corpus/{corpusId}Scope collections:read

Read a partitioned run

A partitioned run, summarised from its parts. There is no stored roll-up: the state is computed from the part rows every time, so it cannot disagree with them. partial means some parts compiled and at least one did not -- the Worlds that exist are usable, and reporting that as ready would hide missing sources.

Parameters, request and responses200 · 400 · 401 · 404 · 503

Parameters

NameInRequiredShape
corpusIdpathyesstring (^corpus-[a-f0-9]{32}$)
Request
curl -sS -X GET https://tavonel.com/api/compile-jobs/corpus/{corpusId} \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200Corpus state, part list and aggregate progress.

Example response
200
{
  "code": "OK",
  "corpusId": "corpus-<32 hex>",
  "state": "partial",
  "documentsTotal": 20,
  "documentsReady": 12,
  "partsEnqueued": 2,
  "batchCount": 2,
  "incompleteReason": null,
  "parts": [
    {
      "jobId": "cjob-<32 hex>",
      "batchIndex": 0,
      "batchCount": 2,
      "state": "compiled",
      "collectionId": "collection-<32 hex>",
      "documentsTotal": 12,
      "documentsReady": 12,
      "errorCode": null
    },
    {
      "jobId": "cjob-<32 hex>",
      "batchIndex": 1,
      "batchCount": 2,
      "state": "blocked",
      "collectionId": null,
      "documentsTotal": 8,
      "documentsReady": 0,
      "errorCode": null
    }
  ]
}

400CORPUS_ID_INVALID — The corpus id did not match corpus-<32 hex>.

CORPUS_ID_INVALID

Example response
400
{
  "code": "CORPUS_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404NOT_FOUND — The addressed resource does not exist in this workspace. Deliberately uniform across tenants: the same answer for an id that is not yours and an id that is nobody's.

NOT_FOUND

Example response
404
{
  "code": "NOT_FOUND"
}

503COMPILE_JOB_STORE_READ_FAILED — The durable job store could not be read.

COMPILE_JOB_STORE_READ_FAILED

Example response
503
{
  "code": "COMPILE_JOB_STORE_READ_FAILED"
}
GET/compile-jobs/{jobId}Scope collections:read

Read one compile job

The durable current state. A poller against this sees exactly what a stream subscriber sees, because both read the same row. A job id from another workspace answers not found, which is also all it should reveal.

Parameters, request and responses200 · 400 · 401 · 404 · 503

Parameters

NameInRequiredShape
jobIdpathyesstring (^cjob-[a-f0-9]{32}$)
Request
curl -sS -X GET https://tavonel.com/api/compile-jobs/{jobId} \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200{ code: OK, job }, including any blocked documents and the resolution recorded against them.

Example response
200
{
  "code": "OK",
  "job": {
    "jobId": "cjob-<32 hex>",
    "state": "blocked",
    "collectionId": null,
    "documentIds": [
      "00000000-0000-4000-8000-000000000000"
    ],
    "documentsTotal": 1,
    "documentsReady": 0,
    "blocked": [
      {
        "documentId": "00000000-0000-4000-8000-000000000000",
        "reason": "security",
        "retryable": false
      }
    ],
    "blockedResolution": null,
    "errorCode": null,
    "corpusId": null,
    "batchIndex": null,
    "batchCount": null,
    "createdAt": "2026-09-16T00:00:00.000Z",
    "updatedAt": "2026-09-16T00:01:00.000Z",
    "settledAt": null
  }
}

400COMPILE_JOB_SCOPE_INVALID — The job exists and belongs to another workspace.

COMPILE_JOB_SCOPE_INVALID

Example response
400
{
  "code": "COMPILE_JOB_SCOPE_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404COMPILE_JOB_NOT_FOUND — No such job in this workspace. Job ids are workspace-scoped, so this is also the answer for another tenant's id.

COMPILE_JOB_NOT_FOUND

Example response
404
{
  "code": "COMPILE_JOB_NOT_FOUND"
}

503COMPILE_JOB_STORE_READ_FAILED — The durable job store could not be read.

COMPILE_JOB_STORE_READ_FAILED

Example response
503
{
  "code": "COMPILE_JOB_STORE_READ_FAILED"
}
GET/compile-jobs/{jobId}/eventsScope collections:read

Stream compile transitions

Replays the persisted transition log after Last-Event-ID, then follows it. The server closes the stream at its own wall clock, so reconnecting is the normal case rather than an error path; every frame carries the durable sequence to resume from. Nothing is lost when a connection drops, because the events are written before they are sent and nothing was ever only in the connection.

Parameters, request and responses200 · 400 · 401 · 404

Parameters

NameInRequiredShape
jobIdpathyesstring (^cjob-[a-f0-9]{32}$)
Last-Event-IDheadernostring The durable sequence to resume after. Set automatically by EventSource on reconnect.
Request
curl -sS -X GET https://tavonel.com/api/compile-jobs/{jobId}/events \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200text/event-stream of persisted compile transitions. Each frame's id is the durable sequence; echo it back as Last-Event-ID to resume.

Example response
200
"id: 42\nevent: state\ndata: {\"jobId\":\"cjob-<32 hex>\",\"state\":\"compiling\",\"documentsTotal\":1,\"documentsReady\":0}\n\n"

400COMPILE_JOB_SCOPE_INVALID — The job exists and belongs to another workspace.

COMPILE_JOB_SCOPE_INVALID

Example response
400
{
  "code": "COMPILE_JOB_SCOPE_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404COMPILE_JOB_NOT_FOUND — No such job in this workspace. Job ids are workspace-scoped, so this is also the answer for another tenant's id.

COMPILE_JOB_NOT_FOUND

Example response
404
{
  "code": "COMPILE_JOB_NOT_FOUND"
}
POST/compile-jobs/{jobId}/blockersScope collections:compile

Answer a partial failure

Answers a partial failure. A job with blocked documents stops and waits; nothing skips them by itself. continue is refused while any blocker is a security blocker -- those leave the set through remove_blocked, which records who removed them.

Parameters, request and responses200 · 400 · 401 · 404 · 409 · 415

Parameters

NameInRequiredShape
jobIdpathyesstring (^cjob-[a-f0-9]{32}$)
Request
curl -sS -X POST https://tavonel.com/api/compile-jobs/{jobId}/blockers \
  -H "Authorization: Bearer $TAVONEL_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "resolution": "remove_blocked" }'

Responses

200The recorded resolution and the job it applies to.

Example response
200
{
  "code": "OK",
  "job": {
    "jobId": "cjob-<32 hex>",
    "state": "queued",
    "collectionId": null,
    "documentIds": [
      "00000000-0000-4000-8000-000000000000"
    ],
    "documentsTotal": 1,
    "documentsReady": 0,
    "blocked": [],
    "blockedResolution": "remove_blocked",
    "errorCode": null,
    "corpusId": null,
    "batchIndex": null,
    "batchCount": null,
    "createdAt": "2026-09-16T00:00:00.000Z",
    "updatedAt": "2026-09-16T00:03:00.000Z",
    "settledAt": null
  }
}

400RESOLUTION_REQUIRED — A blocker resolution was requested with no resolution field. INVALID_JSON — The body was not parseable JSON. COMPILE_JOB_SCOPE_INVALID — The job exists and belongs to another workspace.

RESOLUTION_REQUIREDINVALID_JSONCOMPILE_JOB_SCOPE_INVALID

Example response
400
{
  "code": "RESOLUTION_REQUIRED"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404COMPILE_JOB_NOT_FOUND — No such job in this workspace. Job ids are workspace-scoped, so this is also the answer for another tenant's id.

COMPILE_JOB_NOT_FOUND

Example response
404
{
  "code": "COMPILE_JOB_NOT_FOUND"
}

409SECURITY_BLOCKER_REQUIRES_EXPLICIT_REMOVAL — continue was sent while a source was held by a safety check. RESOLUTION_NOT_APPLIED — The resolution was understood and the job's state refused it. COMPILE_JOB_ALREADY_SETTLED — The job had already finished. Nothing was discarded — a cancel arriving a second after a compile finished does not destroy the result.

SECURITY_BLOCKER_REQUIRES_EXPLICIT_REMOVALRESOLUTION_NOT_APPLIEDCOMPILE_JOB_ALREADY_SETTLED

Example response
409
{
  "code": "SECURITY_BLOCKER_REQUIRES_EXPLICIT_REMOVAL"
}

415METADATA_ONLY_ENDPOINT — Document bytes were POSTed to a route that accepts only metadata. Bytes go direct to storage, never through the application server.

METADATA_ONLY_ENDPOINT

Example response
415
{
  "code": "METADATA_ONLY_ENDPOINT"
}
POST/compile-jobs/{jobId}/cancelScope collections:compile

Cancel a compile job

Marks the job cancelled. A job that had already settled is left alone -- a cancel arriving a second after a compile finished does not destroy the result.

Parameters, request and responses200 · 401 · 404 · 409

Parameters

NameInRequiredShape
jobIdpathyesstring (^cjob-[a-f0-9]{32}$)
Request
curl -sS -X POST https://tavonel.com/api/compile-jobs/{jobId}/cancel \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200The cancelled job.

Example response
200
{
  "code": "OK",
  "job": {
    "jobId": "cjob-<32 hex>",
    "state": "cancelled",
    "collectionId": null,
    "documentIds": [
      "00000000-0000-4000-8000-000000000000"
    ],
    "documentsTotal": 1,
    "documentsReady": 0,
    "blocked": [],
    "blockedResolution": null,
    "errorCode": null,
    "corpusId": null,
    "batchIndex": null,
    "batchCount": null,
    "createdAt": "2026-09-16T00:00:00.000Z",
    "updatedAt": "2026-09-16T00:04:00.000Z",
    "settledAt": "2026-09-16T00:04:00.000Z"
  }
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404COMPILE_JOB_NOT_FOUND — No such job in this workspace. Job ids are workspace-scoped, so this is also the answer for another tenant's id.

COMPILE_JOB_NOT_FOUND

Example response
404
{
  "code": "COMPILE_JOB_NOT_FOUND"
}

409COMPILE_JOB_ALREADY_SETTLED — The job had already finished. Nothing was discarded — a cancel arriving a second after a compile finished does not destroy the result.

COMPILE_JOB_ALREADY_SETTLED

Example response
409
{
  "code": "COMPILE_JOB_ALREADY_SETTLED"
}

Worlds

Reading an active World, its lenses, its retrieval index and its version state.

GET/collectionsScope collections:read

List active Compiled Worlds

The calling workspace's active Compiled Worlds, keyset-paginated on collection id. page.nextCursor is the last collection id on this page, or null on the last page. The workspace comes from the credential; there is no workspace parameter.

Parameters, request and responses200 · 400 · 401 · 403 · 503

Parameters

NameInRequiredShape
limitquerynointeger (1–50, default 25)
cursorquerynostring (^collection-[a-f0-9]{32}$)
Request
curl -sS -X GET https://tavonel.com/api/v1/collections \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200One page of active Worlds.

Example response
200
{
  "code": "COLLECTIONS_LISTED",
  "collections": [
    {
      "collectionId": "collection-<32 hex>",
      "manifestDigest": "sha256:<64 hex>",
      "revision": 3,
      "updatedAt": "2026-09-16T00:00:00.000Z"
    }
  ],
  "page": {
    "limit": 25,
    "cursor": null,
    "nextCursor": null
  }
}

400WORLD_PAGE_LIMIT_INVALID — limit was outside 1–50. WORLD_PAGE_CURSOR_INVALID — The cursor named an id this lens does not contain. A wrong cursor is refused rather than answered with an empty page, so a paging bug is visible instead of silent.

WORLD_PAGE_LIMIT_INVALIDWORLD_PAGE_CURSOR_INVALID

Example response
400
{
  "code": "WORLD_PAGE_LIMIT_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

503WORLD_STORE_READ_FAILED — The World store could not be read. WORLD_STORE_NOT_CONFIGURED — The World store is not configured.

WORLD_STORE_READ_FAILEDWORLD_STORE_NOT_CONFIGURED

Example response
503
{
  "code": "WORLD_STORE_READ_FAILED"
}
GET/collections/{id}Scope collections:read

Read a candidate package

The reviewable candidate artifact — the raw compile package, before anyone activated it. This is not the World read model: GET /world/{id} is that, and it answers only for a version a person activated.

Parameters, request and responses200 · 400 · 401 · 404 · 422 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
Request
curl -sS -X GET https://tavonel.com/api/v1/collections/{id} \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200The candidate artifact and its validation report.

Example response
200
{
  "collectionId": "collection-<32 hex>",
  "manifestDigest": "sha256:<64 hex>",
  "candidatePromotion": false,
  "validation": {
    "status": "valid",
    "reasons": []
  }
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

400COLLECTION_ID_INVALID — The collection id did not match collection-<32 hex>.

COLLECTION_ID_INVALID

Example response
400
{
  "code": "COLLECTION_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404NOT_FOUND — The addressed resource does not exist in this workspace. Deliberately uniform across tenants: the same answer for an id that is not yours and an id that is nobody's.

NOT_FOUND

Example response
404
{
  "code": "NOT_FOUND"
}

422COLLECTION_PACKAGE_INVALID — The package failed its own validation, so it was not served. COLLECTION_SOURCE_BINDING_INVALID — A package entry cites a source version that cannot be resolved.

COLLECTION_PACKAGE_INVALIDCOLLECTION_SOURCE_BINDING_INVALID

Example response
422
{
  "code": "COLLECTION_PACKAGE_INVALID"
}

503READ_FAILED — A backing store could not be read. R2_NOT_CONFIGURED — Object storage is not configured.

READ_FAILEDR2_NOT_CONFIGURED

Example response
503
{
  "code": "READ_FAILED"
}
POST/collections/{id}/retrieval-indexScope collections:compile

Rebuild the retrieval index

Compiles the retrieval index for the collection's ACTIVE World, and is a no-op returning alreadyCompiled: true when a completed run for that World version already exists. The manifest comes from the active pointer, so this cannot index a candidate nobody activated. Requires the workspace owner or admin role in addition to the scope, and the same plan bar activating a World takes. A rebuild that does not reach a queryable index answers 503 with RETRIEVAL_INDEX_NOT_COMPILED and the failure class in retrievalIndex.errorClass -- never 200.

Parameters, request and responses200 · 400 · 401 · 402 · 403 · 404 · 409 · 429 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
Request
curl -sS -X POST https://tavonel.com/api/v1/collections/{id}/retrieval-index \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200The index state after the run. alreadyCompiled: true means nothing was recompiled.

Example response
200
{
  "code": "RETRIEVAL_INDEX_COMPILED",
  "alreadyCompiled": false,
  "activeWorld": {
    "collectionId": "collection-<32 hex>",
    "manifestDigest": "sha256:<64 hex>",
    "revision": 3,
    "activatedAt": "2026-09-16T00:00:00.000Z"
  },
  "retrievalIndex": {
    "status": "compiled",
    "errorClass": null,
    "runId": "rrun-<id>",
    "retrievalProfileId": "rprof-<id>"
  }
}

400COLLECTION_ID_INVALID — The collection id did not match collection-<32 hex>.

COLLECTION_ID_INVALID

Example response
400
{
  "code": "COLLECTION_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

402STUDIO_SUBSCRIPTION_REQUIRED — The operation needs the higher plan tier — activation, rollback and retrieval-index rebuild are the three.

STUDIO_SUBSCRIPTION_REQUIRED

Example response
402
{
  "code": "STUDIO_SUBSCRIPTION_REQUIRED"
}

403RETRIEVAL_COMPILE_ROLE_REQUIRED — Rebuilding the retrieval index needs the workspace owner or admin role, on top of the scope. API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

RETRIEVAL_COMPILE_ROLE_REQUIREDAPI_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "RETRIEVAL_COMPILE_ROLE_REQUIRED"
}

404NOT_FOUND — The addressed resource does not exist in this workspace. Deliberately uniform across tenants: the same answer for an id that is not yours and an id that is nobody's.

NOT_FOUND

Example response
404
{
  "code": "NOT_FOUND"
}

409ACTIVE_WORLD_NOT_FOUND — Nothing has been activated for this collection, so there is no World to read, index or answer from. A candidate nobody accepted is not a smaller answer — it is a different one.

ACTIVE_WORLD_NOT_FOUND

Example response
409
{
  "code": "ACTIVE_WORLD_NOT_FOUND"
}

429ACTIVATION_RATE_LIMITED — The workspace has used its hour's allowance of World activations, rollbacks or retrieval-index rebuilds. Nothing was charged.

ACTIVATION_RATE_LIMITED

Example response
429
{
  "code": "ACTIVATION_RATE_LIMITED"
}

503RETRIEVAL_INDEX_NOT_COMPILED — A rebuild did not reach a queryable index. retrievalIndex.errorClass names the failure class. Never answered as a 200. RETRIEVAL_COMPILE_NO_UNITS — The World produced no retrievable units, so there was nothing to index. RETRIEVAL_COMPILE_EMBEDDING_PROVIDER_FAILED — The embedding provider failed during the rebuild. RETRIEVAL_COMPILE_SOURCE_BINDING_UNRESOLVED — A retrieval unit cited a source version that could not be resolved, so the index was refused rather than compiled with a dangling citation. ACTIVATION_RATE_LIMIT_UNAVAILABLE — That hourly allowance could not be read, so the request was refused rather than run unbounded.

RETRIEVAL_INDEX_NOT_COMPILEDRETRIEVAL_COMPILE_NO_UNITSRETRIEVAL_COMPILE_EMBEDDING_PROVIDER_FAILEDRETRIEVAL_COMPILE_SOURCE_BINDING_UNRESOLVEDACTIVATION_RATE_LIMIT_UNAVAILABLE

Example response
503
{
  "code": "RETRIEVAL_INDEX_NOT_COMPILED"
}
GET/collections/{id}/worldScope worlds:read

Read the active World pointer

Which version this collection currently answers from, and which prior versions are retained. Use GET /world/{id} for the read model itself and GET /world/{id}/{lens} for one lens of it.

Parameters, request and responses200 · 400 · 401 · 404 · 409 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
Request
curl -sS -X GET https://tavonel.com/api/v1/collections/{id}/world \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200The active pointer and the retained versions.

Example response
200
{
  "activeWorld": {
    "collectionId": "collection-<32 hex>",
    "manifestDigest": "sha256:<64 hex>",
    "revision": 3,
    "activatedAt": "2026-09-16T00:00:00.000Z"
  },
  "versions": [
    {
      "manifestDigest": "sha256:<64 hex>",
      "revision": 2,
      "lifecycleStatus": "superseded"
    }
  ]
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

400COLLECTION_ID_INVALID — The collection id did not match collection-<32 hex>.

COLLECTION_ID_INVALID

Example response
400
{
  "code": "COLLECTION_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404NOT_FOUND — The addressed resource does not exist in this workspace. Deliberately uniform across tenants: the same answer for an id that is not yours and an id that is nobody's. WORLD_NOT_FOUND — No World for that collection id in this workspace.

NOT_FOUNDWORLD_NOT_FOUND

Example response
404
{
  "code": "NOT_FOUND"
}

409ACTIVE_WORLD_NOT_FOUND — Nothing has been activated for this collection, so there is no World to read, index or answer from. A candidate nobody accepted is not a smaller answer — it is a different one.

ACTIVE_WORLD_NOT_FOUND

Example response
409
{
  "code": "ACTIVE_WORLD_NOT_FOUND"
}

503WORLD_STORE_READ_FAILED — The World store could not be read.

WORLD_STORE_READ_FAILED

Example response
503
{
  "code": "WORLD_STORE_READ_FAILED"
}
GET/world/{id}Scope worlds:read

Read the whole World

The whole World read model: contract, freshness, objects, relations, evidence, directory, ontology, history, files and review state. manifest reads a specific version, which is what a two-version diff needs.

Parameters, request and responses200 · 400 · 401 · 404 · 422 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
manifestquerynostring (^sha256:[a-f0-9]{64}$)
Request
curl -sS -X GET https://tavonel.com/api/v1/world/{id} \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200{ code: OK, model }.

Example response
200
{
  "code": "OK",
  "model": {
    "world": {
      "collectionId": "collection-<32 hex>",
      "manifestDigest": "sha256:<64 hex>",
      "revision": 3
    },
    "contract": {
      "schemaVersion": "tavonel.world.v1"
    },
    "freshness": {
      "observedAt": null,
      "processedAt": "2026-09-16T00:00:00.000Z",
      "reviewedAt": null,
      "activatedAt": "2026-09-16T00:00:00.000Z",
      "activeManifestDigest": "sha256:<64 hex>",
      "candidateAwaitingActivation": false,
      "candidateManifestDigest": null
    },
    "objects": [
      {
        "objectId": "obj-01",
        "label": "Revenue recognition policy"
      }
    ],
    "relations": [],
    "evidence": [],
    "history": [],
    "files": [],
    "review": []
  }
}

400WORLD_ID_INVALID — The World id did not match the collection id pattern. MANIFEST_DIGEST_INVALID — The manifest digest did not match sha256:<64 hex>.

WORLD_ID_INVALIDMANIFEST_DIGEST_INVALID

Example response
400
{
  "code": "WORLD_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404WORLD_NOT_FOUND — No World for that collection id in this workspace.

WORLD_NOT_FOUND

Example response
404
{
  "code": "WORLD_NOT_FOUND"
}

422WORLD_READ_MODEL_INVALID — The read model failed validation, so nothing was served.

WORLD_READ_MODEL_INVALID

Example response
422
{
  "code": "WORLD_READ_MODEL_INVALID"
}

503WORLD_STORE_READ_FAILED — The World store could not be read. WORLD_STORE_NOT_CONFIGURED — The World store is not configured.

WORLD_STORE_READ_FAILEDWORLD_STORE_NOT_CONFIGURED

Example response
503
{
  "code": "WORLD_STORE_READ_FAILED"
}
GET/world/{id}/{lens}Scope worlds:read

Read one lens of a World

One lens of the World read model. objects, relations and evidence accept limit and cursor; history, files and review do not and answer WORLD_LENS_NOT_PAGEABLE if asked. Omitting limit returns the whole lens and page.limit: null. The cursor is the last item id from the previous page -- keyset, not an offset -- and a cursor naming an id the lens does not contain is a 400 rather than an empty page.

Parameters, request and responses200 · 400 · 401 · 404 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
lenspathyes"objects" | "relations" | "evidence" | "history" | "files" | "review"
limitquerynointeger (1–50)
cursorquerynostring
Request
curl -sS -X GET https://tavonel.com/api/v1/world/{id}/{lens} \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200{ code: OK, world, contract, freshness, <lens>, page }. The lens array is keyed by the lens name, so objects comes back under objects.

Example response
200
{
  "code": "OK",
  "world": {
    "collectionId": "collection-<32 hex>",
    "manifestDigest": "sha256:<64 hex>",
    "revision": 3,
    "activatedAt": "2026-09-16T00:00:00.000Z"
  },
  "evidence": [
    {
      "evidenceId": "ev-01",
      "sourceVersionKey": "src_v_01",
      "page": 4,
      "bbox1000": [
        118,
        214,
        886,
        374
      ]
    }
  ],
  "page": {
    "limit": 50,
    "cursor": null,
    "nextCursor": "ev-50",
    "total": 212
  }
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

400WORLD_ID_INVALID — The World id did not match the collection id pattern. WORLD_LENS_NOT_PAGEABLE — history, files and review return whole; limit and cursor are refused on them. WORLD_PAGE_LIMIT_INVALID — limit was outside 1–50. WORLD_PAGE_CURSOR_INVALID — The cursor named an id this lens does not contain. A wrong cursor is refused rather than answered with an empty page, so a paging bug is visible instead of silent.

WORLD_ID_INVALIDWORLD_LENS_NOT_PAGEABLEWORLD_PAGE_LIMIT_INVALIDWORLD_PAGE_CURSOR_INVALID

Example response
400
{
  "code": "WORLD_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404WORLD_NOT_FOUND — No World for that collection id in this workspace. WORLD_LENS_NOT_FOUND — The lens name is not one of objects, relations, evidence, history, files, review.

WORLD_NOT_FOUNDWORLD_LENS_NOT_FOUND

Example response
404
{
  "code": "WORLD_NOT_FOUND"
}

503WORLD_STORE_READ_FAILED — The World store could not be read.

WORLD_STORE_READ_FAILED

Example response
503
{
  "code": "WORLD_STORE_READ_FAILED"
}
GET/world/{id}/manifest-statusScope worlds:read

Check whether a held version is still current

Whether a manifest digest is the one this workspace currently answers from. active: false means a different version is active now; it does not mean the held copy was withdrawn or deleted, and this endpoint deletes nothing. knownToWorkspace: false means this workspace has no record of ever activating that digest, which is a different answer from 'it was superseded'.

Parameters, request and responses200 · 400 · 401 · 409 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
digestqueryyesstring (^sha256:[a-f0-9]{64}$)
Request
curl -sS -X GET https://tavonel.com/api/v1/world/{id}/manifest-status \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200The status of that digest against the workspace's current state.

Example response
200
{
  "code": "MANIFEST_STATUS",
  "manifestStatus": {
    "collectionId": "collection-<32 hex>",
    "manifestDigest": "sha256:<64 hex>",
    "active": false,
    "activeManifestDigest": "sha256:<64 hex>",
    "knownToWorkspace": true,
    "lifecycleStatus": "superseded",
    "activatedAt": "2026-09-10T00:00:00.000Z"
  }
}

400WORLD_ID_INVALID — The World id did not match the collection id pattern. MANIFEST_DIGEST_INVALID — The manifest digest did not match sha256:<64 hex>.

WORLD_ID_INVALIDMANIFEST_DIGEST_INVALID

Example response
400
{
  "code": "WORLD_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

409ACTIVE_WORLD_NOT_FOUND — Nothing has been activated for this collection, so there is no World to read, index or answer from. A candidate nobody accepted is not a smaller answer — it is a different one.

ACTIVE_WORLD_NOT_FOUND

Example response
409
{
  "code": "ACTIVE_WORLD_NOT_FOUND"
}

503WORLD_STORE_READ_FAILED — The World store could not be read.

WORLD_STORE_READ_FAILED

Example response
503
{
  "code": "WORLD_STORE_READ_FAILED"
}

Questions

Grounded answers and retrieval-only search over the active World.

POST/collections/{id}/askScope ask:read

Ask the active World a question

Grounded answer with exact page and bbox citations, or an explicit abstention. Both retrieval paths return the same fields: answer, reason, citations, receipt, activeWorld, freshness, answerMode and retrievalPath. answerMode is evidence_excerpts on both -- the answer is the cited excerpts, concatenated in rank order, and no language model writes any part of it. retrievalPath names which runtime answered: compiled-retrieval-v1 (lexical + dense + structure, RRF-fused, reranked and World Gate filtered; also returns contextPacket and retrieval diagnostics) or excerpt-concatenation-fallback when the active World has no queryable compiled index, which also returns retrievalIndex and a human-readable retrievalNotice. Per-citation scoring differs by path and is not normalized across them: the fallback carries relevance with its lexical/graph/temporal/authority breakdown, the compiled path carries per-source ranks and the reranker score. An abstention is a 200 with code: ANSWER_ABSTAINED — a result, not a failure.

Parameters, request and responses200 · 400 · 401 · 403 · 409 · 413 · 429 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
Request
curl -sS -X POST https://tavonel.com/api/v1/collections/{id}/ask \
  -H "Authorization: Bearer $TAVONEL_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "question": "What is the stated revenue recognition policy?" }'

Responses

200{ code, answer, reason, citations, receipt, activeWorld, freshness, answerMode, retrievalPath }. answerMode is evidence_excerpts on both paths. retrievalPath is compiled-retrieval-v1 (which also returns contextPacket and retrieval) or excerpt-concatenation-fallback (which also returns retrievalIndex and retrievalNotice). An abstention is a 200 with code ANSWER_ABSTAINED and a reason — a result, not a failure. Read retrievalPath before comparing answers across Worlds: a difference between two answers can be a difference between two runtimes rather than between two corpora.

Example response
200
{
  "code": "GROUNDED_ANSWER",
  "answer": "Revenue is recognized when control transfers to the customer.",
  "reason": null,
  "answerMode": "evidence_excerpts",
  "retrievalPath": "compiled-retrieval-v1",
  "citations": [
    {
      "evidenceId": "ev-01",
      "sourceVersionKey": "src_v_01",
      "page": 4,
      "bbox1000": [
        118,
        214,
        886,
        374
      ]
    }
  ],
  "activeWorld": {
    "collectionId": "collection-<32 hex>",
    "manifestDigest": "sha256:<64 hex>",
    "revision": 3,
    "activatedAt": "2026-09-16T00:00:00.000Z"
  },
  "freshness": {
    "observedAt": null,
    "processedAt": "2026-09-16T00:00:00.000Z",
    "reviewedAt": null,
    "activatedAt": "2026-09-16T00:00:00.000Z",
    "activeManifestDigest": "sha256:<64 hex>",
    "candidateAwaitingActivation": false,
    "candidateManifestDigest": null
  },
  "receipt": {
    "runId": "ask-<id>",
    "askedAt": "2026-09-16T00:05:00.000Z"
  }
}

400QUESTION_INVALID — The question was absent, or shorter than the minimum. RETRIEVAL_QUESTION_INVALID — The retrieval runtime refused the question text itself. COLLECTION_ID_INVALID — The collection id did not match collection-<32 hex>. INVALID_JSON — The body was not parseable JSON.

QUESTION_INVALIDRETRIEVAL_QUESTION_INVALIDCOLLECTION_ID_INVALIDINVALID_JSON

Example response
400
{
  "code": "QUESTION_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

409ACTIVE_WORLD_NOT_FOUND — Nothing has been activated for this collection, so there is no World to read, index or answer from. A candidate nobody accepted is not a smaller answer — it is a different one. ACTIVE_WORLD_CHANGED_RETRY — The active version changed while the request was in flight.

ACTIVE_WORLD_NOT_FOUNDACTIVE_WORLD_CHANGED_RETRY

Example response
409
{
  "code": "ACTIVE_WORLD_NOT_FOUND"
}

413QUESTION_TOO_LARGE — The question exceeded 500 characters.

QUESTION_TOO_LARGE

Example response
413
{
  "code": "QUESTION_TOO_LARGE"
}

429API_RATE_LIMITED — The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits.

API_RATE_LIMITED

Example response
429
{
  "code": "API_RATE_LIMITED"
}

503ACTIVE_WORLD_STORE_UNAVAILABLE — The World store could not be reached. WORLD_STORE_READ_FAILED — The World store could not be read.

ACTIVE_WORLD_STORE_UNAVAILABLEWORLD_STORE_READ_FAILED

Example response
503
{
  "code": "ACTIVE_WORLD_STORE_UNAVAILABLE"
}
POST/collections/{id}/searchScope ask:read

Search the active World

Retrieval-only search over the active World's compiled retrieval index: hybrid lexical + dense + structure retrieval, RRF-fused, reranked, then World Gate filtered. Returns the ContextPacket (the same runtime contract /ask, MCP and the CLI share) plus per-source retrieval telemetry, without generating an answer. There is no excerpt fallback here -- a World with no queryable compiled index is a 409, not a weaker answer. Read degradations before comparing two results: a full-pipeline result and a partial one otherwise look identical.

Parameters, request and responses200 · 400 · 401 · 403 · 409 · 413 · 429 · 503

Parameters

NameInRequiredShape
idpathyesstring (^collection-[a-f0-9]{32}$)
Request
curl -sS -X POST https://tavonel.com/api/v1/collections/{id}/search \
  -H "Authorization: Bearer $TAVONEL_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "query": "revenue recognition policy", "limit": 10 }'

Responses

200{ code, retrievalPath, contextPacket, degradations, retrieval, activeWorld, freshness }. The contextPacket carries the evidence-bound retrieval units with their lexical, dense and structure ranks, the reranker score and the World Gate decisions; retrieval carries the per-source candidate counts and gateRejections. retrievalPath is always compiled-retrieval-v1. degradations is a named list of what did not run — dense retrieval skipped with no embedder configured, or the reranker degrading to the fused order — and reading it is how a full-pipeline result is told from a partial one, because the two otherwise look identical.

Example response
200
{
  "code": "SEARCH_RESULTS",
  "retrievalPath": "compiled-retrieval-v1",
  "contextPacket": {
    "units": [
      {
        "evidenceId": "ev-01",
        "sourceVersionKey": "src_v_01",
        "page": 4,
        "bbox1000": [
          118,
          214,
          886,
          374
        ],
        "text": "Revenue is recognized when control transfers to the customer.",
        "lexicalRank": 1,
        "denseRank": 2,
        "structureRank": null,
        "rerankerScore": 0.82
      }
    ]
  },
  "degradations": [
    "dense retrieval skipped: no embedder configured"
  ],
  "retrieval": {
    "lexicalCandidates": 24,
    "denseCandidates": 0,
    "structureCandidates": 6,
    "rerankerApplied": true,
    "gateRejections": []
  },
  "activeWorld": {
    "collectionId": "collection-<32 hex>",
    "manifestDigest": "sha256:<64 hex>",
    "revision": 3,
    "activatedAt": "2026-09-16T00:00:00.000Z"
  },
  "freshness": {
    "observedAt": null,
    "processedAt": "2026-09-16T00:00:00.000Z",
    "reviewedAt": null,
    "activatedAt": "2026-09-16T00:00:00.000Z",
    "activeManifestDigest": "sha256:<64 hex>",
    "candidateAwaitingActivation": false,
    "candidateManifestDigest": null
  }
}

400QUERY_INVALID — The search query was absent, or shorter than the minimum. COLLECTION_ID_INVALID — The collection id did not match collection-<32 hex>. INVALID_JSON — The body was not parseable JSON.

QUERY_INVALIDCOLLECTION_ID_INVALIDINVALID_JSON

Example response
400
{
  "code": "QUERY_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

409RETRIEVAL_RUN_NOT_FOUND — The active World has no completed retrieval compile run, so there is no queryable index. Search has no fallback — a weaker answer presented as the real one is worse than a refusal you can act on. RETRIEVAL_PROFILE_NOT_FOUND — The retrieval profile the index was compiled against is not registered. ACTIVE_WORLD_NOT_FOUND — Nothing has been activated for this collection, so there is no World to read, index or answer from. A candidate nobody accepted is not a smaller answer — it is a different one.

RETRIEVAL_RUN_NOT_FOUNDRETRIEVAL_PROFILE_NOT_FOUNDACTIVE_WORLD_NOT_FOUND

Example response
409
{
  "code": "RETRIEVAL_RUN_NOT_FOUND"
}

413QUERY_TOO_LARGE — The search query exceeded 500 characters.

QUERY_TOO_LARGE

Example response
413
{
  "code": "QUERY_TOO_LARGE"
}

429API_RATE_LIMITED — The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits.

API_RATE_LIMITED

Example response
429
{
  "code": "API_RATE_LIMITED"
}

503ACTIVE_WORLD_STORE_UNAVAILABLE — The World store could not be reached.

ACTIVE_WORLD_STORE_UNAVAILABLE

Example response
503
{
  "code": "ACTIVE_WORLD_STORE_UNAVAILABLE"
}

Review

Append-only human decisions over evidence, and the observed run-event stream.

GET/runs/{runId}/eventsScope documents:read

Stream observed run events

Replays append-only observed run events after Last-Event-ID, then streams new events and a bounded heartbeat. This is the second of two event streams and the one over *observed* runs — a connector sync, an intake — where /compile-jobs/{jobId}/events follows a compile's own transitions. after is the query-parameter form of Last-Event-ID, for clients that cannot set the header.

Parameters, request and responses200 · 400 · 401 · 404 · 503

Parameters

NameInRequiredShape
runIdpathyesstring
afterquerynointeger (0–)
Last-Event-IDheadernostring The durable sequence to resume after. Set automatically by EventSource on reconnect.
Request
curl -sS -X GET https://tavonel.com/api/v1/runs/{runId}/events \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200Persisted run-event stream. Frames carry the durable sequence as id; a heartbeat frame keeps intermediaries from closing an idle connection.

Example response
200
"id: 7\nevent: run\ndata: {\"runId\":\"<run id>\",\"state\":\"running\",\"sequence\":7}\n\n"

400RUN_ID_REQUIRED — The run id path segment was empty. JOB_CURSOR_INVALID — The run's cursor did not validate.

RUN_ID_REQUIREDJOB_CURSOR_INVALID

Example response
400
{
  "code": "RUN_ID_REQUIRED"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404JOB_NOT_FOUND — No such run in this workspace. JOB_SCOPE_INVALID — The run exists and belongs to another workspace. Answered as not found rather than as forbidden, so an id cannot be probed across tenants.

JOB_NOT_FOUNDJOB_SCOPE_INVALID

Example response
404
{
  "code": "JOB_NOT_FOUND"
}

503JOB_STORE_READ_FAILED — The run store could not be read.

JOB_STORE_READ_FAILED

Example response
503
{
  "code": "JOB_STORE_READ_FAILED"
}
POST/reviewsBrowser session only

Record a decision over one piece of evidence

Records an append-only Accept, Edit, or Reject decision after revalidating the evidence against the persisted World. The digest is part of the request because a decision recorded against a version it does not describe is worse than no decision: if the World moved under you, this answers REVIEW_WORLD_CHANGED rather than writing it.

Parameters, request and responses201 · 400 · 401 · 404 · 409 · 413 · 503
Request
# Browser session only. A developer API key is refused on this route.
curl -sS -X POST https://tavonel.com/api/v1/reviews \
  -H "Authorization: Bearer $TAVONEL_SESSION_JWT" \
  -H "content-type: application/json" \
  -d '{ "collectionId": "collection-<32 hex>", "manifestDigest": "sha256:<64 hex>", "evidenceId": "ev-01", "action": "accept", "reason": "Checked against page 4 of the filing." }'

Responses

201An evidence-bound human decision receipt. Append-only: a second decision does not overwrite the first.

Example response
201
{
  "code": "RECORDED",
  "receipt": {
    "evidenceId": "ev-01",
    "action": "accept",
    "reason": "Checked against page 4 of the filing.",
    "manifestDigest": "sha256:<64 hex>",
    "recordedAt": "2026-09-16T00:06:00.000Z",
    "actor": "<user id>"
  }
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

400REVIEW_REQUEST_INVALID — The review body did not validate. REVIEW_PATCH_INVALID — An Edit decision carried a patch that did not validate. PATCH_NO_CHANGE — The patch would change nothing. INVALID_JSON — The body was not parseable JSON.

REVIEW_REQUEST_INVALIDREVIEW_PATCH_INVALIDPATCH_NO_CHANGEINVALID_JSON

Example response
400
{
  "code": "REVIEW_REQUEST_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404REVIEW_EVIDENCE_NOT_FOUND — The evidence id is not in the World version named by the digest. PATCH_TARGET_NOT_FOUND — The patch names a target the World does not contain.

REVIEW_EVIDENCE_NOT_FOUNDPATCH_TARGET_NOT_FOUND

Example response
404
{
  "code": "REVIEW_EVIDENCE_NOT_FOUND"
}

409REVIEW_WORLD_CHANGED — The World changed between reading the evidence and recording the decision, so the decision was not written against a version it may not describe. PATCH_BEFORE_MISMATCH — The patch's before value does not match what is stored now. PATCH_TARGET_NOT_EDITABLE — The target is not one a review decision may edit.

REVIEW_WORLD_CHANGEDPATCH_BEFORE_MISMATCHPATCH_TARGET_NOT_EDITABLE

Example response
409
{
  "code": "REVIEW_WORLD_CHANGED"
}

413REVIEW_REQUEST_TOO_LARGE — The review body exceeded its bound.

REVIEW_REQUEST_TOO_LARGE

Example response
413
{
  "code": "REVIEW_REQUEST_TOO_LARGE"
}

503REVIEW_STORE_WRITE_FAILED — The review store could not be written. REVIEW_STORE_NOT_CONFIGURED — The review store is not configured.

REVIEW_STORE_WRITE_FAILEDREVIEW_STORE_NOT_CONFIGURED

Example response
503
{
  "code": "REVIEW_STORE_WRITE_FAILED"
}

Connections

Durable source connections, their cursors, and the OAuth connectors that create them.

GET/connectionsScope connections:read

List source connections

Tenant-scoped durable source connections and their committed cursor state. Takes no paging parameters. The cursor is opaque: it is a sha256 over the collector's own position, and the only supported way to advance it is to send a batch whose previousCursorSha256 matches the committed one.

Parameters, request and responses200 · 401 · 403 · 503
Request
curl -sS -X GET https://tavonel.com/api/v1/connections \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

200The workspace's connections, with the committed cursor on each.

Example response
200
{
  "connections": [
    {
      "connectionId": "00000000-0000-4000-8000-000000000000",
      "provider": "s3",
      "mode": "local_agent",
      "displayName": "Contracts bucket",
      "status": "active",
      "cursorSha256": "sha256:<64 hex>",
      "lastSyncAt": "2026-09-16T00:00:00.000Z",
      "lastErrorCode": null,
      "createdAt": "2026-09-01T00:00:00.000Z",
      "updatedAt": "2026-09-16T00:00:00.000Z"
    }
  ]
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

503DEVELOPER_STORE_READ_FAILED — The developer store could not be read. DEVELOPER_STORE_NOT_CONFIGURED — The developer store is not configured. DEVELOPER_STORE_BINDING_INVALID — A stored row did not validate, so it was refused rather than returned partially.

DEVELOPER_STORE_READ_FAILEDDEVELOPER_STORE_NOT_CONFIGUREDDEVELOPER_STORE_BINDING_INVALID

Example response
503
{
  "code": "DEVELOPER_STORE_READ_FAILED"
}
POST/connectionsScope connections:write

Create a source connection

Registers a local-agent file-server, S3, R2 or MinIO source. Credentials stay in the customer environment: secretReference must be null, because a local agent uses its own workload credentials and TAVONEL never holds them. The agent pushes outward; nothing here reaches into your network.

Parameters, request and responses201 · 400 · 401 · 403 · 413 · 503
Request
curl -sS -X POST https://tavonel.com/api/v1/connections \
  -H "Authorization: Bearer $TAVONEL_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "provider": "s3", "mode": "local_agent", "displayName": "Contracts bucket", "configuration": { "bucket": "contracts", "prefix": "2026/", "region": "ap-northeast-2" }, "secretReference": null }'

Responses

201The durable connection record, with an empty cursor.

Example response
201
{
  "connectionId": "00000000-0000-4000-8000-000000000000",
  "provider": "s3",
  "mode": "local_agent",
  "displayName": "Contracts bucket",
  "status": "active",
  "cursorSha256": null,
  "lastSyncAt": null,
  "lastErrorCode": null,
  "createdAt": "2026-09-16T00:00:00.000Z",
  "updatedAt": "2026-09-16T00:00:00.000Z"
}

400CONNECTION_INPUT_INVALID — The connection body did not match ConnectionInput. INVALID_JSON — The body was not parseable JSON.

CONNECTION_INPUT_INVALIDINVALID_JSON

Example response
400
{
  "code": "CONNECTION_INPUT_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

403API_SCOPE_REQUIRED — The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. PILOT_ACCESS_REQUIRED — The credential is valid and the workspace it names is not admitted to TAVONEL.

API_SCOPE_REQUIREDPILOT_ACCESS_REQUIRED

Example response
403
{
  "code": "API_SCOPE_REQUIRED"
}

413REQUEST_TOO_LARGE — The JSON body exceeded the route's bound.

REQUEST_TOO_LARGE

Example response
413
{
  "code": "REQUEST_TOO_LARGE"
}

503CONNECTION_CREATE_FAILED — The connection could not be recorded. DEVELOPER_STORE_NOT_CONFIGURED — The developer store is not configured.

CONNECTION_CREATE_FAILEDDEVELOPER_STORE_NOT_CONFIGURED

Example response
503
{
  "code": "CONNECTION_CREATE_FAILED"
}
DELETE/connections/{id}Scope connections:write

Revoke a source connection

Revokes the connection. Access removal takes effect on the next request rather than waiting for a background reindex. Immutable outputs already compiled are retained: a revoke stops future reads, it does not rewrite history.

Parameters, request and responses204 · 400 · 401 · 404 · 503

Parameters

NameInRequiredShape
idpathyesstring (uuid)
Request
curl -sS -X DELETE https://tavonel.com/api/v1/connections/{id} \
  -H "Authorization: Bearer $TAVONEL_API_KEY"

Responses

204Connection revoked; immutable outputs retained. No body.

400CONNECTION_ID_INVALID — The connection id was not a UUID.

CONNECTION_ID_INVALID

Example response
400
{
  "code": "CONNECTION_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404CONNECTION_NOT_FOUND — No such connection in this workspace.

CONNECTION_NOT_FOUND

Example response
404
{
  "code": "CONNECTION_NOT_FOUND"
}

503CONNECTION_REVOKE_FAILED — The revoke could not be recorded, so it is not reported as done. Immutable outputs are retained by design when a revoke does succeed.

CONNECTION_REVOKE_FAILED

Example response
503
{
  "code": "CONNECTION_REVOKE_FAILED"
}
POST/connections/{id}/syncScope connections:sync

Advance a connection cursor

Applies one batch of source events and advances the cursor. previousCursorSha256 must match the committed cursor, so two collectors cannot both advance it; a mismatch is a 409 and nothing is applied. Replaying an identical batchId is idempotent — delivery is at-least-once and this is the consumer that makes it exactly-once.

Parameters, request and responses200 · 400 · 401 · 404 · 409 · 413 · 423 · 503

Parameters

NameInRequiredShape
idpathyesstring (uuid)
Request
curl -sS -X POST https://tavonel.com/api/v1/connections/{id}/sync \
  -H "Authorization: Bearer $TAVONEL_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "batchId": "00000000-0000-4000-8000-000000000000", "previousCursorSha256": null, "nextCursorSha256": "sha256:<64 hex>", "manifestSha256": "sha256:<64 hex>", "events": [ { "kind": "added", "nativeId": "s3://contracts/2026/a.pdf", "revision": "\"etag\"", "contentSha256": "<64 hex>", "sizeBytes": 184320, "mimeType": "application/pdf", "documentId": "00000000-0000-4000-8000-000000000001", "sourceIdempotencyKey": "<64 hex>" } ] }'

Responses

200The cursor transition, applied or idempotently replayed. status: replayed means this exact batch had already been applied and nothing changed.

Example response
200
{
  "code": "OK",
  "status": "applied",
  "batchId": "00000000-0000-4000-8000-000000000000",
  "cursorSha256": "sha256:<64 hex>",
  "eventCount": 1
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

400CONNECTION_BATCH_INVALID — The sync batch did not match ConnectionBatch. CONNECTION_ID_INVALID — The connection id was not a UUID. SOURCE_IDENTITY_INVALID — The event's nativeId/revision pair did not validate. SOURCE_DIGEST_REQUIRED — An event carried bytes with no contentSha256. SOURCE_METADATA_INVALID — Event metadata did not validate. INVALID_JSON — The body was not parseable JSON.

CONNECTION_BATCH_INVALIDCONNECTION_ID_INVALIDSOURCE_IDENTITY_INVALIDSOURCE_DIGEST_REQUIREDSOURCE_METADATA_INVALIDINVALID_JSON

Example response
400
{
  "code": "CONNECTION_BATCH_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404CONNECTION_NOT_FOUND — No such connection in this workspace.

CONNECTION_NOT_FOUND

Example response
404
{
  "code": "CONNECTION_NOT_FOUND"
}

409CONNECTION_BATCH_CONFLICT — The batch's previousCursorSha256 does not match the committed cursor — another sync moved it. CONNECTION_CURSOR_CONFLICT — Two batches tried to advance the same cursor. SOURCE_CURSOR_STALE — The cursor the batch carries is behind the committed one. SOURCE_DIGEST_CONFLICT — Two events declare different digests for the same revision. SOURCE_DIGEST_MISMATCH — The bytes do not hash to the digest the event declared. SOURCE_VERSION_DIGEST_CONFLICT — A version is already recorded with a different digest.

CONNECTION_BATCH_CONFLICTCONNECTION_CURSOR_CONFLICTSOURCE_CURSOR_STALESOURCE_DIGEST_CONFLICTSOURCE_DIGEST_MISMATCHSOURCE_VERSION_DIGEST_CONFLICT

Example response
409
{
  "code": "CONNECTION_BATCH_CONFLICT"
}

413REQUEST_TOO_LARGE — The JSON body exceeded the route's bound. SOURCE_CURSOR_TOO_LARGE — The cursor value exceeded its bound.

REQUEST_TOO_LARGESOURCE_CURSOR_TOO_LARGE

Example response
413
{
  "code": "REQUEST_TOO_LARGE"
}

423CONNECTION_NOT_SYNCABLE — The connection is revoked or in a state that does not accept a batch. SOURCE_LIFECYCLE_REVIEW_REQUIRED — A lifecycle transition on this source needs a person.

CONNECTION_NOT_SYNCABLESOURCE_LIFECYCLE_REVIEW_REQUIRED

Example response
423
{
  "code": "CONNECTION_NOT_SYNCABLE"
}

503CONNECTION_BATCH_FAILED — The batch could not be applied.

CONNECTION_BATCH_FAILED

Example response
503
{
  "code": "CONNECTION_BATCH_FAILED"
}
GET/oauth-connectorsBrowser session only

List OAuth connector state

Lists configured provider readiness and tenant OAuth connections. Provider credentials are never returned. A provider whose client is not configured reports configured: false rather than being hidden.

Parameters, request and responses200 · 401 · 503
Request
# Browser session only. A developer API key is refused on this route.
curl -sS -X GET https://tavonel.com/api/v1/oauth-connectors \
  -H "Authorization: Bearer $TAVONEL_SESSION_JWT"

Responses

200Provider readiness and the workspace's connections.

Example response
200
{
  "code": "OK",
  "providers": [
    {
      "provider": "google_drive",
      "configured": false
    },
    {
      "provider": "dropbox",
      "configured": false
    },
    {
      "provider": "microsoft_graph",
      "configured": false
    }
  ],
  "connections": []
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

503OAUTH_STORE_UNAVAILABLE — The OAuth store could not be reached. OAUTH_STORE_NOT_CONFIGURED — The OAuth store is not configured.

OAUTH_STORE_UNAVAILABLEOAUTH_STORE_NOT_CONFIGURED

Example response
503
{
  "code": "OAUTH_STORE_UNAVAILABLE"
}
POST/oauth-connectors/authorizeBrowser session only

Start an OAuth authorization

Creates a single-use PKCE authorization. Fails closed unless the provider client and the managed secret broker are both configured — an authorization that cannot store a refresh secret is not started.

Parameters, request and responses200 · 400 · 401 · 413 · 503
Request
# Browser session only. A developer API key is refused on this route.
curl -sS -X POST https://tavonel.com/api/v1/oauth-connectors/authorize \
  -H "Authorization: Bearer $TAVONEL_SESSION_JWT" \
  -H "content-type: application/json" \
  -d '{ "provider": "google_drive", "displayName": "Finance shared drive" }'

Responses

200A short-lived provider authorization URL. Single-use: a second visit to it is refused.

Example response
200
{
  "code": "AUTHORIZED_REDIRECT_READY",
  "authorizationUrl": "https://accounts.google.com/o/oauth2/v2/auth?...",
  "expiresAt": "2026-09-16T00:10:00.000Z"
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

400OAUTH_CONNECTOR_INPUT_INVALID — The authorization body did not match OAuthConnectorAuthorizationInput. INVALID_JSON — The body was not parseable JSON.

OAUTH_CONNECTOR_INPUT_INVALIDINVALID_JSON

Example response
400
{
  "code": "OAUTH_CONNECTOR_INPUT_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

413REQUEST_TOO_LARGE — The JSON body exceeded the route's bound.

REQUEST_TOO_LARGE

Example response
413
{
  "code": "REQUEST_TOO_LARGE"
}

503OAUTH_PROVIDER_NOT_CONFIGURED — The provider's client is not configured. Fails closed rather than starting an authorization that cannot complete. OAUTH_SECRET_BROKER_NOT_CONFIGURED — The managed secret broker is not configured, so no refresh secret could be stored — or, on a revoke, deleted. OAUTH_AUTHORIZATION_START_FAILED — The single-use PKCE authorization could not be created.

OAUTH_PROVIDER_NOT_CONFIGUREDOAUTH_SECRET_BROKER_NOT_CONFIGUREDOAUTH_AUTHORIZATION_START_FAILED

Example response
503
{
  "code": "OAUTH_PROVIDER_NOT_CONFIGURED"
}
DELETE/oauth-connectors/connections/{id}Browser session only

Revoke an OAuth connection

Deletes the stored refresh secret and revokes the connection. Reported as done only when the secret is actually gone — a revoke that cannot be proven answers 503 rather than 204.

Parameters, request and responses204 · 400 · 401 · 404 · 503

Parameters

NameInRequiredShape
idpathyesstring (uuid)
Request
# Browser session only. A developer API key is refused on this route.
curl -sS -X DELETE https://tavonel.com/api/v1/oauth-connectors/connections/{id} \
  -H "Authorization: Bearer $TAVONEL_SESSION_JWT"

Responses

204Refresh secret deleted and connection revoked. No body.

400OAUTH_CONNECTION_ID_INVALID — The OAuth connection id was not a UUID.

OAUTH_CONNECTION_ID_INVALID

Example response
400
{
  "code": "OAUTH_CONNECTION_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

404OAUTH_CONNECTION_NOT_FOUND — No such OAuth connection in this workspace.

OAUTH_CONNECTION_NOT_FOUND

Example response
404
{
  "code": "OAUTH_CONNECTION_NOT_FOUND"
}

503OAUTH_SECRET_REVOCATION_FAILED — The refresh secret could not be deleted, so the revoke was not reported as done. OAUTH_SECRET_BROKER_NOT_CONFIGURED — The managed secret broker is not configured, so no refresh secret could be stored — or, on a revoke, deleted.

OAUTH_SECRET_REVOCATION_FAILEDOAUTH_SECRET_BROKER_NOT_CONFIGURED

Example response
503
{
  "code": "OAUTH_SECRET_REVOCATION_FAILED"
}

Workspace administration

Key rotation and the audit trail. Browser session only — no API key calls these.

POST/developer/keys/{id}/rotateBrowser session only

Rotate an API key

Atomically creates a replacement key, revokes the source key and writes an audit event. Plaintext is returned once and is not recoverable afterwards. Either all three happened or none did — there is no state where the old key is revoked and no replacement exists.

Parameters, request and responses201 · 400 · 401 · 402 · 404 · 413 · 503

Parameters

NameInRequiredShape
idpathyesstring (uuid)
Request
# Browser session only. A developer API key is refused on this route.
curl -sS -X POST https://tavonel.com/api/v1/developer/keys/{id}/rotate \
  -H "Authorization: Bearer $TAVONEL_SESSION_JWT"

Responses

201The one-time replacement credential. Store it now; it is not shown again.

Example response
201
{
  "code": "ROTATED",
  "keyId": "00000000-0000-4000-8000-000000000002",
  "plaintext": "tvnl_live_<prefix>_<secret>",
  "scopes": [
    "documents:read",
    "worlds:read"
  ],
  "revokedKeyId": "00000000-0000-4000-8000-000000000001",
  "createdAt": "2026-09-16T00:07:00.000Z"
}

This schema names the fields a caller can rely on and is not closed. The handler composes the payload from a store row the contract does not own, so listing every field here would be transcribing a shape that can move — it is marked x-tavonel-status: best-effort in the document rather than closed on fields nobody verified.

400API_KEY_ID_INVALID — The key id was not a UUID. API_KEY_INPUT_INVALID — The rotation body did not validate. INVALID_JSON — The body was not parseable JSON.

API_KEY_ID_INVALIDAPI_KEY_INPUT_INVALIDINVALID_JSON

Example response
400
{
  "code": "API_KEY_ID_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

402TRIAL_FEATURE_NOT_INCLUDED — The operation — key rotation among them — is not in the free evaluation.

TRIAL_FEATURE_NOT_INCLUDED

Example response
402
{
  "code": "TRIAL_FEATURE_NOT_INCLUDED"
}

404API_KEY_NOT_FOUND — No such key in this workspace.

API_KEY_NOT_FOUND

Example response
404
{
  "code": "API_KEY_NOT_FOUND"
}

413REQUEST_TOO_LARGE — The JSON body exceeded the route's bound.

REQUEST_TOO_LARGE

Example response
413
{
  "code": "REQUEST_TOO_LARGE"
}

503API_KEY_ROTATE_FAILED — The rotation could not be completed. API_KEY_CREATE_FAILED — The replacement key could not be written, so nothing was revoked either. DEVELOPER_AUDIT_WRITE_FAILED — An audit event could not be written, so the action it describes was refused. Every key create, rotate and revoke writes an audit row or does not happen.

API_KEY_ROTATE_FAILEDAPI_KEY_CREATE_FAILEDDEVELOPER_AUDIT_WRITE_FAILED

Example response
503
{
  "code": "API_KEY_ROTATE_FAILED"
}
GET/developer/auditBrowser session only

Read the developer audit trail

The tenant-scoped developer and connector audit trail, newest first, bounded by limit. Every key create, rotate and revoke writes a row here or does not happen.

Parameters, request and responses200 · 400 · 401 · 503

Parameters

NameInRequiredShape
limitquerynointeger (1–200, default 50)
Request
# Browser session only. A developer API key is refused on this route.
curl -sS -X GET https://tavonel.com/api/v1/developer/audit \
  -H "Authorization: Bearer $TAVONEL_SESSION_JWT"

Responses

200{ code: OK, events }, newest first.

Example response
200
{
  "code": "OK",
  "events": [
    {
      "eventType": "developer_key_rotated",
      "occurredAt": "2026-09-16T00:07:00.000Z",
      "actor": "<user id>",
      "subject": "00000000-0000-4000-8000-000000000001",
      "detail": {}
    }
  ]
}

400AUDIT_LIMIT_INVALID — The audit limit was outside its range.

AUDIT_LIMIT_INVALID

Example response
400
{
  "code": "AUDIT_LIMIT_INVALID"
}

401AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns.

AUTH_REQUIRED

Example response
401
{
  "code": "AUTH_REQUIRED"
}

503DEVELOPER_AUDIT_READ_FAILED — The audit trail could not be read. DEVELOPER_STORE_NOT_CONFIGURED — The developer store is not configured.

DEVELOPER_AUDIT_READ_FAILEDDEVELOPER_STORE_NOT_CONFIGURED

Example response
503
{
  "code": "DEVELOPER_AUDIT_READ_FAILED"
}

Error codes

Every failure carries a stable machine code alongside the status. Branch on the code: the status says what kind of problem it is, and the code says which one. Each response above names the codes it can carry; the full catalogue, with what to do about every one of them, is on the Errors page and in the contract under x-tavonel-error-catalogue.

Concepts, worked flows and the quickstart are in the documentation; this page is the operation-by-operation reference. API version 2026-09-02.1 · support window and deprecation policy on Versioning and changes.