API at a glance
Conventions and the endpoint map. The machine-readable OpenAPI spec is the field-by-field reference; these docs teach the flows.
Conventions
- Base URL
https://api.cloud.ingram.tech: everything under/v1. - Headers:
Authorization: Bearer <token>(Auth & tokens), plusIC-Api-Version: 2026-05-01to pin the request/response vocabulary.2026-05-01is the only live version today. Omit the header and you get the latest; send one we don't know and the request is refused withunsupported_api_version. Every response echoes the version it served. - Errors: the OpenAI-standard envelope, always — so the
openaiSDK and any OpenAI-compatible client parse our failures unchanged:
{ "error": { "message": "…", "type": "invalid_request_error",
"param": null, "code": "model_key_missing" } }
Match on code (our stable slug); type is the OpenAI category
(invalid_request_error, authentication_error, permission_error,
rate_limit_error, api_error). Every possible code is enumerated on the
Error schema in the OpenAPI spec — the catalog to
generate exhaustive client handling from. The request id is the
X-Request-Id response header — log it; show message to humans.
- Idempotency: send an
Idempotency-Keyheader on creates; replays return the original response instead of duplicating. This covers streaming run creation too: a retried key returns arun.duplicatepointer rather than starting a second run. Resources with a natural key also upsert on it: re-POST /v1/smithswith an existing liveexternal_idreturns that smith (200) rather than erroring. - One turn per smith: a smith runs one turn at a time, on every run surface —
its memory is a single doc shared by all its conversations. A second concurrent
run returns
400 conversation_locked; retry in a few seconds. See one run at a time. - Two dialects, one rule: OpenAI-mirrored resources (
/v1/files,/v1/vector_stores,/v1/conversations,/v1/embeddings, and the model surfaces) speak the OpenAI dialect end-to-end — unix-second timestamps,200on create,200with a*.deletedack on delete, and the OpenAI list envelope. Every other resource is native — ISO-8601 timestamps,201on create,204on delete, and the keyset list envelope. - Pagination, native dialect: list endpoints take
limit+cursorand return{ data, next_cursor, has_more }, newest first. Thecursoris opaque — pass the previous page'snext_cursorstraight back; never parse it. Pull the next page whilehas_moreis true. The run and event feeds add their own filters (since,status,external_id_prefix, …) on top of the same cursor. Fixed-size config lists (providers, model keys, the catalogs) return bare{ data }— they are bounded by construction. - Pagination, OpenAI dialect: lists take
limit+order+after(an object id) and return{ object: "list", data, first_id, last_id, has_more }; passlast_idback asafterwhilehas_moreis true. - Filtering:
GET /v1/smithsfilters by exactexternal_id,customer_id, oragent_id, and segments byexternal_id_prefix(matches one namespace, e.g.anon:).GET /v1/runstakes the sameexternal_id_prefixto pull one segment's conversation history (plussmith_id,agent_id— every run across one agent's smiths — andstatus). - Ids: prefixed and sortable:
smt_(smith),agt_(agent),run_,thr_,cnv_(conversation),apr_,evt_,cus_,trc_. A run and its trace share a suffix (run_X↔trc_X). Thread ids are yours if you pass them, minted withthr_if you don't. - Limits: no published rate limits yet (fair use); synchronous runs hold the connection for the whole turn, so prefer streaming for long ones.
Endpoint map
| Resource | Endpoints |
|---|---|
| Smiths | POST/GET /v1/smiths · GET/PATCH/DELETE /v1/smiths/{sid} |
| Config revisions | GET /v1/smiths/{sid}/revisions · POST …/revisions/{n}/restore |
| Agents | POST/GET /v1/agents · GET/PATCH/DELETE /v1/agents/{aid} · POST/GET …/versions · POST …/rollout · POST …/attach · POST /v1/agents/import · POST/GET …/{aid}/ui · GET/DELETE …/ui/{name} · GET …/ui/{name}/content (MCP Apps UI templates, multipart upload; /content serves the bundle bytes with a content-hash ETag — see MCP Apps) |
| Runs | POST/GET /v1/smiths/{sid}/runs · GET …/runs/{rid} · GET …/runs/{rid}/events (SSE) · POST …/runs/{rid}/submit · POST …/runs/{rid}/replay · GET /v1/runs |
| Embeddings | POST /v1/embeddings (OpenAI-compatible text→vector; runs on your OpenAI key, scope embeddings:write — see embeddings) |
| OpenAI-compatible | POST /v1/chat/completions · POST /v1/responses · GET /v1/responses/{id} · POST /v1/responses/{id}/cancel (retrieve or stop a response — the path to background: true, see openai-compat) (stream + non-stream; smith token = the smith, the user field = its external_id, or IC-Smith-Id header; user + an IC-Agent-Id header lazily provisions the (external_id, agent) smith). Honors the standard request fields: model (inference LLM override), system/instructions (appended per turn), tools (client-side function-call loop, or a hosted {"type":"file_search"} declaration — see retrieval) with tool_choice (auto/none/required/a named function), reasoning_effort / reasoning.effort (reasoning depth, mapped to each provider's native control — see models), include (file_search_call.results), response_format/text.format (enforced json_schema structured outputs — alone against a toolless agent it's a stateless one-shot, otherwise it types the final message only and the turn keeps its tools), and multimodal image and file parts. A cut-off turn finishes length on Chat Completions and incomplete (with incomplete_details) on Responses — see openai-compat. |
| Conversations | POST/GET /v1/conversations · GET/POST/DELETE /v1/conversations/{cnvId} · GET …/{cnvId}/items (OpenAI Conversations API over a thread of runs; pass the cnv_ id as conversation on /v1/responses — see conversations) |
| Files | POST/GET /v1/files (multipart upload · list uploads) · GET/DELETE /v1/files/{fid} · GET …/files/{fid}/content (bytes; inline conversation files stay reachable by id but unlisted; scopes files:read/files:write) |
| Vector stores | POST/GET /v1/vector_stores · GET/POST/DELETE /v1/vector_stores/{vsId} · POST/GET …/files · GET/POST/DELETE …/files/{fid} · GET …/files/{fid}/content · POST/GET …/file_batches, POST …/file_batches/{bid}/cancel, GET …/file_batches/{bid}/files · POST …/search (scopes vector_stores:read/vector_stores:write — see retrieval) |
| Memory | GET/PUT /v1/smiths/{sid}/memory (working-memory doc) · POST …/memory/recall (semantic recall over past messages) |
| Tools (MCP) | GET /v1/tenant/mcp · GET/PUT/DELETE /v1/tenant/mcp/{name} · POST …/mcp/{name}/refresh · GET /v1/tenant/hosted_tools |
| Sandbox | GET /v1/tenant/sandbox_secrets · PUT/DELETE …/sandbox_secrets/{name} (environment the run_command box is launched with; values are never echoed, admin-only — see sandbox) |
| Catalog | GET /v1/catalog · GET /v1/catalog/{slug} (curated MCP integration presets) |
| Approvals | GET /v1/approvals[?status=] · GET /v1/approvals/{id} |
| Connections | GET/POST /v1/smiths/{sid}/connections · GET/PATCH/DELETE …/{cid} · POST …/{cid}/refresh · POST …/connections/authorize (start OAuth) · GET /v1/oauth/callback/{provider} (browser redirect) |
| Deployments | GET/POST /v1/deployments · GET/PATCH/DELETE /v1/deployments/{depid} (target: a smith or an agent catch-all) · POST /v1/deployments/{depid}/mcp (the deployment's MCP server, JSON-RPC 2.0 over Streamable HTTP; every method past initialize runs as one smith — a smith token is that smith, a tenant-admin token names one with IC-Smith-Id, optional only on a smith target) · GET /v1/hosted/{depid} · POST /v1/hosted/{depid}/chat (public hosted-page metadata + chat, no auth) · GET/PUT/DELETE /v1/tenant/telegram · GET/PUT/DELETE /v1/tenant/slack · GET /v1/slack/oauth/{tenant} (browser redirect) · GET/PUT/DELETE /v1/tenant/whatsapp · GET/POST /v1/whatsapp/webhook (Meta verify + inbound, app-level) · GET/PUT/DELETE /v1/tenant/discord · POST /v1/discord/webhook/{tenant} (Ed25519-verified interactions) · GET/PUT/DELETE /v1/tenant/email · GET/POST/DELETE /v1/tenant/email/suppressions |
| Inbound events | GET /v1/inbound_events[?source=&smith_id=&since=] · GET /v1/inbound_events/{ievid} (what arrived on a channel, before interpretation — read-only) |
| Connector OAuth | GET /.well-known/oauth-protected-resource/v1/deployments/{depid}/mcp · GET /.well-known/oauth-authorization-server · GET /.well-known/jwks.json · POST /oauth/register · GET/POST /oauth/authorize (consent page, or a 302 to the tenant's authorize_url) · POST /oauth/token · GET /v1/oauth/authorize-requests/{rid} · POST …/{rid}/complete · POST …/{rid}/decline (delegated consent, tenant-admin) — see Connectors |
| Schedules | GET/POST /v1/smiths/{sid}/schedules · PATCH/DELETE …/{schid} · POST …/{schid}/run_now |
| Events & webhooks | GET /v1/events · GET/POST /v1/tenant/webhooks · PATCH/DELETE …/{wid} · POST …/{wid}/rotate_secret · POST …/{wid}/test · GET …/{wid}/deliveries · POST …/{wid}/deliveries/{did}/redeliver |
| Usage & cost | GET /v1/tenant/usage · GET /v1/usage?group_by= · POST/GET /v1/usage/events |
| Budgets | POST/GET /v1/budgets · GET/PATCH/DELETE /v1/budgets/{bid} · GET …/{bid}/status |
| Customers | POST/GET /v1/customers · GET/PATCH/DELETE /v1/customers/{cid} |
| Tokens | POST/GET /v1/tenant/tokens · DELETE …/tokens/{tid} |
| Projects (org key) | POST/GET /v1/organization/projects · GET/DELETE …/projects/{pid} · POST …/projects/{pid}/tokens (mint a project tenant:* token) |
| Credits (org key) | GET /v1/organization/billing/balance · GET …/billing/ledger · GET …/billing/usage (per-project draw this month) · GET …/billing/usage/series (daily draw per project) · POST …/billing/setup (add a card, unlock €10 free) · POST …/billing/checkout (Stripe top-up) · POST …/billing/confirm (credit a returned checkout session) · POST …/billing/redeem (redeem a credit code) · GET/PUT …/billing/autoreload · POST …/billing/reload (charge saved card) · GET …/billing/portal (Stripe billing portal) |
| Model keys | GET /v1/tenant/model_keys · PUT/DELETE …/model_keys/{provider} · GET /v1/tenant/models · GET /v1/smiths/{sid}/model_keys · PUT/DELETE …/model_keys/{provider} (end-user keys) |
| Providers (OAuth) | GET /v1/tenant/providers · GET/PUT/DELETE …/providers/{provider} |
| Traces | GET /v1/traces[?smith_id=&app_id=&run_id=&status=&since=] · GET /v1/traces/{tid} · GET /v1/runs/{rid}/trace (the trace for one run) · POST /v1/traces:ingest |
Early-access surfaces
One resource appears in scopes and usage breakdowns but is early access and
not yet documented here: trace ingestion (POST /v1/traces:ingest: push
your own spans from an external runtime, optionally tagged with an app_id
that group_by=app then attributes usage by). Ask us before building on it.
Two things to know if you already do. Ingesting the same span_id twice is a
no-op on the trace's totals, so an at-least-once exporter can retry safely. And
a span you don't attribute to a smith produces a trace with smith_id: null —
readable with a tenant-admin token, never with a smith token.
Using the OpenAPI spec
GET https://api.cloud.ingram.tech/openapi.json: feed it to your codegen
or API client of choice. The spec is generated from the live API, so it's
always in sync with the deployment. (https://cloud.ingram.tech/docs/openapi.json
redirects here, if you have it wired up already.)
It covers every endpoint this table lists — including the ones that aren't
plain JSON in and out. Streaming turns publish both bodies of the one response
(text/event-stream and application/json, chosen by stream); uploads publish
their multipart/form-data shape; downloads publish application/octet-stream;
the browser endpoints publish their redirects; and the provider webhooks publish
what a Slack, Meta, Discord, Telegram or Stripe request URL receives. If an
endpoint answers it, the spec describes it.