API surface
Capability, authority, and protocol map for the complete generated endpoint reference.
The generated endpoint reference is the exhaustive inventory of the 370 operations registered by the engine. It is generated from the production Go route registrations; a route change makes the documentation check fail until the OpenAPI file is regenerated.
This page is the map: which family owns a capability, who may call it, and which guide explains the end-to-end task. It deliberately does not duplicate every method and path from the generated reference.
Authentication classes
| Class | Credential | Main surfaces |
|---|---|---|
| Public | none | /, /healthz, /readyz, RFC 9728 metadata, personal-connection OAuth hops |
| Tenant | machine or end-user OAuth2 token | compatibility APIs, datasets, projects, models, usage, readiness |
| User-owned | end-user OAuth2 token | conversations, memories, personal connections, agent runs, skills |
| Organization administrator | end-user token with engine-held org-admin | limits, guardrails, model grants, teams, integrations, webhooks, organization connectors |
| Application | application credential with application-admin | self-service signup, which models the application offers its tenants, the application's own model pricing, its own revenue report, the default spend caps of the organizations it owns, its guardrail policy, the chat model its memory infrastructure runs on, and the skill catalog it curates for its tenants |
| Platform administrator | operator OAuth2 token, admin session, or break-glass Basic | applications, organizations, identity, platform connectors, pricing, audit, setup backup |
| Billing service | BILLING_TOKEN | POST /v1/billing/topups only |
| Memory service | MEMORY_TOKEN | internal embeddings and reranking only |
An application credential is neither a tenant nor a platform administrator: it
reaches no organization's data, and platform-admin is stripped from every
token a tenant authorization server mints. Its surfaces are therefore
self-scoped — the credential names the application, and no path carries an
application id.
All /v1/* routes require one of these credentials except /v1/connect/start
and /v1/connect/callback. /.well-known/oauth-protected-resource is also
public. The engine serves no sign-in route of its own: clients authenticate
against the identity provider directly and present the token here.
Capability map
| Family | Surface prefixes | Authority | Task documentation |
|---|---|---|---|
| System | /, /healthz, /readyz, /v1/readiness, /v1/me | public or tenant | Health and observability, Authentication |
| Compatibility | /v1/models, /v1/chat/completions, /v1/messages, /v1/rerank | tenant; internal rerank uses memory service | OpenAI SDK, Anthropic SDK, OpenAI parameter compatibility |
| Datasets | /v1/datasets/*, /v1/me/storage | tenant; mutations follow ownership and grants; /v1/me/storage requires an end user and no role | Datasets and RAG |
| Projects | /v1/projects/* | tenant; mutations follow ownership and grants | Projects |
| Widgets | /v1/widgets/*, /v1/sources/{id}/widgets | end user; private to the creator in one organization, org-admin included — no sharing | Build a widget over a source, Widgets and sources |
| Sources | /v1/sources/* | end user; private to the creator in one organization, org-admin included — no sharing | Build a widget over a source, Widgets and sources |
| Conversations and memory | /v1/conversations/*, /v1/memory/*, /v1/memories/*, /v1/instructions* | end user; reading memory settings needs no role, writing them requires org-admin; /v1/instructions is each person's own response preferences, with a billable /v1/instructions/preview | Conversations and memory |
| Agents and runs | /v1/agents/*, /v1/runs/* | end user; management follows agent ownership | Agents and runs |
| Surfaces a run answered with | /v1/surfaces/*, /v1/conversations/{id}/surfaces | the revision's owner only, for every role; a stranger's revision is 404 and a stranger's thread lists nothing | Agents and runs |
| Skills | /v1/skills/* | end user; the application catalog plus the caller's own uploads and hash-bound enablements | Conversations and memory |
| Knowledge-agent run budget | /v1/agents/knowledge/policy, /v1/admin/agents/knowledge/policy, /v1/application/agents/knowledge/policy | org-admin, platform admin, or an application credential | Datasets and RAG |
| Teams | /v1/teams/* | members may read; org-admin mutates | Teams and sharing |
| Organizations | /v1/onboarding/*, /v1/orgs, /v1/org/members*, /v1/org/directory/members, /v1/org/invitations, /v1/org/machine-credentials* | end user creates; org-admin manages membership and machine credentials; /v1/org/directory/members needs only an enrolled member; /v1/onboarding/signups is application-credential | Organizations |
| Automation | /v1/schedules/*, /v1/schedule-events, /v1/webhook-* | tenant; endpoint management requires org-admin | Schedules and webhooks |
| Personal connections | /v1/integrations/*, /v1/connections/* | org-admin manages integrations; owners manage connections | Email connections |
| External MCP | /v1/mcp-servers/* | org-admin registers; users attach; /v1/mcp-servers/catalog is the member-safe read | MCP integrations |
| Deeplinq MCP | /mcp | tenant | MCP integrations |
| Models and connectors | /v1/connectors/*, /v1/org/connectors/*, /v1/model-grants/* | platform or org-admin, by prefix | Connectors |
| Billing and limits | /v1/usage*, /v1/me/usage, /v1/me/usage/tokens, /v1/limits*, /v1/billing/* | tenant, org-admin, or billing service; both /v1/me/usage* reads require an end user and no role | Credits and limits |
| Application models | /v1/application/models* | application credential; self-scoped, the credential names the application | Models and pricing |
| Application pricing | /v1/application/pricing* | application credential; self-scoped, the credential names the application | Credits and limits |
| Application usage | /v1/application/usage | application credential; self-scoped, the credential names the application | Credits and limits |
| Application organizations | /v1/application/orgs | application credential; external applications only, for an organization that already exists at their own identity provider | Bring your own identity provider |
| Application spend limits | /v1/application/orgs/{id}/limits | application credential; the credential names the application, and the organization must belong to it | Credits and limits |
| Application memory model | /v1/application/memory-model | application credential; self-scoped, the credential names the application | Conversations and memory |
| Application knowledge-agent policy | /v1/application/agents/knowledge/policy | application credential; self-scoped, the credential names the application | Datasets and RAG |
| Application skills | /v1/application/skills* | application credential; self-scoped, the credential names the application | Conversations and memory |
| Application widget-data erasures | /v1/application/widget-data-erasures* | application credential; asks the engine to erase one of the application's own closed accounts' widgets and sources | Account-closure erasure |
| Guardrails | /v1/guardrails/*, /v1/admin/guardrails/*, /v1/application/guardrails | org-admin, platform admin, or an application credential | Guardrails |
| Protected resource metadata | /.well-known/oauth-protected-resource | public; RFC 9728 pointer to the identity provider | Authentication |
| Platform administration | /v1/admin/* | platform admin | Applications, Organizations, Members |
The embedded console's browser-only /admin/api/* backend is intentionally not
part of the public API contract. It is a same-origin BFF for the shipped
console, not an integration surface.
Pagination
Inventory routes use opaque keyset cursors. Unless an endpoint says otherwise:
- omitted
limituses that surface's default; - a value below
1, above100, or non-integer is400, naming the1–100bound; - pass
next_cursorback unchanged and do not parse, compare, or synthesize it.
Out-of-range used to clamp silently (a value above 100 truncated to 100 with no
signal it happened) rather than reject. Every list surface now answers the same way:
a value outside 1–100 is a 400, not a silently-truncated 200.
Some provider-backed tools use their own opaque cursor field, such as
page_token. Treat it with the same opacity rule.
Strict query parameters
Every list endpoint rejects a query key outside its own accepted set with
400 {"error":{"type":"invalid_request","field":"<key>","message":"…"}}
naming it, rather than ignoring it. A misspelled or misremembered parameter
name is a client bug, not a silent no-op — GET /v1/admin/orgs?external_org_ref=…
(the wrong name; it's ref) used to fall through to the unfiltered listing
and answer with the first organization in sort order. There is no alias for
a wrong name: the 400 teaches the correct one instead of quietly accepting
both. Each endpoint's accepted set is its own — see that endpoint's entry in
the generated reference rather than assuming
every list shares the same limit/cursor pair (/v1/conversations takes
limit alone; /v1/admin/orgs also takes ref and status; /v1/models,
/v1/model-grants, and /v1/mcp-servers take no query parameters at all).
Conditional updates and idempotency
- Knowledge-agent policy updates return a strong
ETagand requireIf-Match; missing is428, stale or malformed is412. - Dataset and project grant writes are upserts. A create returns
201; an update or no-op repeat returns200and remains audited. - Do not retry any other mutating request unless its endpoint or task guide explicitly states that replay is safe.
Idempotency-Key — supported routes
These routes read and honor Idempotency-Key; every one is also marked on
its own entry in the generated reference with
whether the header is required or optional there. Every other route answers
400 naming the header rather than silently accepting and ignoring it — see
below.
| Route | Required? |
|---|---|
POST /v1/billing/topups | Required |
POST /v1/admin/orgs/{id}/credits | Optional — omitted, every call credits |
POST /v1/schedules | Optional — omitted, every call creates a schedule |
POST /v1/schedules/{id}/run | Required |
POST /v1/sources | Required |
POST /v1/sources/{id}/versions | Required |
POST /v1/sources/{id}/versions/{version}/restore | Required |
POST /v1/sources/{id}/documents | Required |
PUT /v1/sources/{id}/documents/{document} | Required |
DELETE /v1/sources/{id}/documents/{document} | Required |
POST /v1/widgets | Required |
POST /v1/widgets/{id}/draft | Conditional — required only when the body sets from_revision_id; an ordinary resume needs no key |
POST /v1/widgets/{id}/draft/publish | Required |
POST /v1/widgets/{id}/draft/update-source | Required |
POST /v1/widgets/{id}/draft/messages | Required |
POST /v1/widgets/{id}/refresh | Required |
POST /v1/widgets/{id}/views | Required |
POST /v1/application/widget-data-erasures | Optional — the body's operation_id is the real key; if sent, the header must equal it exactly |
On every route above except the application widget-data-erasure route, the
same shape holds — same key and the same input is a true replay that returns
the original result unchanged; same key and different input is a 409
refusal that leaves whatever the first call did untouched — but the exact
409 body and what counts as "the same input" both differ by family.
Branch on type (and code, where present), not on message text.
Credit-ledger routes (POST /v1/billing/topups,
POST /v1/admin/orgs/{id}/credits) compare the raw request body bytes with
SHA-256:
409 {"error":{"type":"idempotency_key_reuse","message":"Idempotency-Key was already used with a different request body"}}
A successful replay also sets an Idempotent-Replayed: true response header,
following Stripe's convention for
telling a deduped replay apart from a fresh write. Neither route enforces a
length limit on the header — only "present" is checked (required on
top-ups, optional on the credit grant). Retry with the identical bytes.
The comparison is over the raw body, not a canonicalized or re-parsed form
of it — a re-serialized but semantically equivalent payload (different key
order, different whitespace, 25000000 vs 25000000.0) is a different
body and hits the 409 above. (Stripe's own idempotency check compares
request parameters, not raw bytes — this engine's raw-byte comparison on
these two routes is a deliberate, stricter choice.)
Widget and source routes raise a conflict carrying the same code as a
distinct field, not a type of its own:
409 {"error":{"type":"conflict","code":"idempotency_key_reuse","message":"…"}}
(the message is fixed prose per resource — "widget changed or preview is
no longer current" for every widget route, "this key was used for different
input" for sources — not a description of what changed). Each route
compares only the specific fields that decide its outcome, at most 200
bytes on the key itself: a source creation compares name; a version
upload compares the file's own content and filename (never literal request
bytes — the multipart boundary differs on every request even for an
identical file); a version restore compares which historical version id is
being restored, independent of expected_source_revision; a widget creation
compares source_id/name/model; publish compares
expected_revision/preview_execution_id; update-source compares
expected_revision/source_version_id (the DRAFT's revision, not the
widget's — see Widgets and sources);
a draft message compares expected_revision/text/decision; a view
compares base_execution_id/parameters. POST /v1/widgets/{id}/refresh
takes no body, so a replay with the same key always matches — but the key is
not scoped to this route alone. Draft open/restore, update-source, a draft
message, a view and refresh share ONE receipt table per widget, so reusing a
key already used for a different one of those five is still the conflict
above. POST /v1/widgets (create) and .../draft/publish each keep their
own separate key column and are unaffected by a key already used on any of
the other five, or on each other. Each route's own entry in the
generated reference states its exact
comparison.
Schedule routes raise the same conflict type with no code field at
all:
409 {"error":{"type":"conflict","message":"Idempotency-Key reused with a different request"}}
POST /v1/schedules compares a hash of the request body after it is parsed
and re-encoded — not the literal bytes sent — so an unrelated formatting
difference collapses to the same hash while a changed field does not.
POST /v1/schedules/{id}/run takes no body, so a replay with the same key
always matches; a key already used for a different schedule's run is the
conflict above.
Sending Idempotency-Key to any other native /v1 route answers 400
naming the header —
{"error":{"type":"invalid_request","field":"Idempotency-Key","message":"…"}}
— instead of silently ignoring it. A retried POST /v1/datasets with the
same key, for example, is not deduplicated: each call creates a new dataset,
and the engine now says so up front rather than leaving that gap to be
discovered by trial and error. Support for further routes is tracked on the
product roadmap, not shipped.
Exception: the OpenAI-compatible (POST /v1/chat/completions) and
Anthropic-compatible (POST /v1/messages, POST /v1/messages/count_tokens)
routes are exempt from that 400 — a drop-in SDK or proxy targeting either
dialect may attach Idempotency-Key out of habit, and the engine tolerates
it there rather than breaking compatibility. Neither route deduplicates on
it: each call still processes independently.
Error and correlation contract
Native routes return a typed JSON error. OpenAI- and Anthropic-compatible routes use their protocol's envelope. Common statuses are:
| Status | Meaning |
|---|---|
400 | Invalid request or a user-owned route called without an end user |
401 | Missing, expired, invalid, unbound, or wrongly scoped OAuth2 token |
402 | Credit balance or hard spend limit exhausted |
403 | Authenticated but not entitled; retired credential schemes also end here |
404 | Missing resource, or a forbidden resource whose existence is concealed |
409 | State, uniqueness, or identity-binding conflict |
412 | Conditional update used a stale or invalid entity tag |
413 | Request body exceeds the transport or feature limit |
428 | Required conditional header is absent |
429 | Authentication, capacity, rate, or concurrency limit reached |
502 | Upstream provider or configured collaborator failed |
503 | Required configured capability is unavailable |
Every response carries an engine-generated X-Request-ID. Preserve it in
support telemetry; it correlates the response with provider calls, billing,
audit, conversation persistence, and structured logs.
Deliberate service boundaries
/v1/embeddingsand/v1/rerankare internal memory-service surfaces, not general tenant embedding or reranking APIs./v1/billing/topupsrecords settled payment outcomes; Deeplinq does not process the payment itself./mcpexposes a read-only Deeplinq server. Tenant-registered external MCP servers are managed separately under/v1/mcp-servers/*.- Hosted login and OAuth callback routes are browser hops. They return redirects and cookies, not API tokens in JSON bodies.