/api/*, takes and returns JSON, and authenticates with an API key. The
per-endpoint pages in this section render directly from the server’s OpenAPI 3.1
specification, so they never drift from the code.
Base URL
The API is served by the running server, alongside the MCP endpoint and the Studio. Prefix every path with the server’s address:tai serve answers at http://127.0.0.1:8000/api.
Authentication
Every request carries an API key in thex-api-key header. Mint and manage keys
with tai keys or the /api/auth/api-keys routes.
401 with the error envelope below.
A key’s access-control condition scopes what it may call. See
access control for the policy model.
Response envelope
Successful responses wrap their payload in adata field:
error field carrying a human-readable message:
data or error, never both.
Expression fields
Some string properties in the schema hold a jq expression rather than an ordinary value. Those properties declare themselves: each carries anx-tai42-expression vendor annotation, so a client recognizes the field as an
expression and learns its input and output without guessing from the field name.
language is always present and is jq. The rest describe the expression’s
context and are emitted only when the declaring surface supplies them: label is a
short name for what . is, blurb says what the input document is, keys glosses
the input’s known top-level keys as name/gloss pairs, returns says what the
result is consumed as, caveats lists evaluation edge cases, and sample carries a
representative input document.
The annotation is metadata only. An x--prefixed keyword is ignored by JSON Schema
validation, so it can never change which values a schema accepts, and a field
without one generates a byte-identical schema to a plain string declaration. A
client that does not recognize the annotation, or the language it declares,
renders the field as a plain string input.
The Studio SDK’s
SchemaForm acts on this
annotation directly: an annotated field renders the
visual jq editor instead of a text box, with no per-field code.Error model
The reload-gated 503
When the server applies a configuration reload, routes that mutate state return503 until the reload completes. The body sets reloading to true and the
response carries a Retry-After header with the seconds to wait:
Retry-After interval. Read-only routes stay
available throughout a reload.
Unexpected errors and validation
Every error is JSON with anerror field and nothing beyond it that could leak.
An unexpected, unhandled server error answers a uniform 500 on every serving
path — the body is a fixed envelope carrying a correlation id and no internal
detail:
error_id correlates the response to the server-log line that records the
full traceback under the same id; the internal exception text — hosts, file paths,
stack frames — never reaches the client. Quote the error_id when reporting a
fault.
On the generic operation body-validation path (the adapter’s 422) and the
connectors mutation doors, a validation failure names which fields failed and
how, never what you sent: the body lists each offending field’s path and
its error type only, with the rejected input values stripped, so a secret
submitted in a rejected field there does not ride back out. This stripping is not
universal — some named-entity doors interpolate the raw validation error into
their 400 body, and pydantic includes the submitted value in that text, so do
not rely on the error response to withhold rejected input everywhere.

