# API reference

> Every route the Immiscible server answers, generated from the router itself at start-up, so the reference cannot fall behind the code. Each endpoint has its authentication, parameters and curl, Node and Python examples.

Source: https://immiscible.fly.dev/docs/api

## Base URL

```text
https://immiscible.fly.dev
```

Self-hosted deployments use their own `PUBLIC_URL`. All requests and responses are JSON (`application/json; charset=utf-8`) unless an endpoint says otherwise: the OCSF export is NDJSON, the Shared Signals receiver takes `application/secevent+jwt`, and a few browser routes redirect.

## The surfaces

| Surface | Paths | Called by | Auth |
|---|---|---|---|
| Gateway | `/v1/chat/completions`, `/anthropic/v1/*`, `/v1/outcomes` and friends | model clients and SDKs | [workspace key](https://immiscible.fly.dev/docs/api/authentication.md#workspace-keys) |
| The gate | `/v1/actions/*`, `/mcp`, `/mcp/proxy/*` | agents | [agent key](https://immiscible.fly.dev/docs/api/authentication.md#agent-keys) |
| Verification | `/v1/verify`, `/.well-known/immiscible-keys.json` | merchants, auditors, anyone | none |
| Machine admin | `/v1/admin/*` | SOAR, CI, Terraform | [service token](https://immiscible.fly.dev/docs/api/authentication.md#service-tokens) |
| Console | `/api/*` | the web console, people | [session cookie](https://immiscible.fly.dev/docs/api/authentication.md#session-cookies) |
| Inbound | `/issuing/*`, `/ssf/*`, `/slack/*`, `/teams/*`, `/hooks/*`, `/webhooks/*` | card issuers, identity providers, chat, GitHub, Stripe | [signatures](https://immiscible.fly.dev/docs/api/authentication.md#signed-requests) |

The full list, grouped, is on [all endpoints](https://immiscible.fly.dev/docs/api/endpoints.md).

The API is also published as OpenAPI 3.1 at `https://immiscible.fly.dev/openapi.json`: the gate's public operations (ask, check, be called back, settle, verify), the gateway and machine admin are written out in full, and the rest of the public surface (the CLI, MCP, SCIM, the well-known documents and health checks) is generated from the router like this reference, with a link to its page and a schema read from its documented example. The console's own `/api/*` routes, sign-in pages and signed callbacks are left out: they are not the surface to build on, and may change. They are listed, marked `x-internal: true`, at `https://immiscible.fly.dev/openapi.json?include=internal`. See [agent builders](https://immiscible.fly.dev/docs/guides/openapi-actions.md).

## Conventions

- **Ids** are prefixed by kind: `ws_` workspace, `agt_` agent, `mdt_` mandate, `act_` action, `apr_` approval, `mcu_` MCP upstream, `stk_` service token.
- **Money** is an integer in minor units of its currency: `4200` is £42.00. Workspace-wide lines (tiers, the chat line) are in reference pence and converted.
- **Times** are ISO 8601 in UTC; signed tokens use seconds since the epoch.
- **Idempotency.** Action requests take an `idempotencyKey` (or the `idempotency-key` header). A retry with the same key and body gets the same answer; the same key with a different body is `409 idempotency_conflict`.
- **Tracing.** Send a W3C `traceparent` and it is continued; every response returns one. See [traces](https://immiscible.fly.dev/docs/guides/traces-and-reviews.md#traces).
- **Lists** return `{ "data": [...] }`. Endpoints that page take `limit` (and `since`, `until` where time matters).

## Decisions are not errors

An action that is refused is `200` with `"decision": "deny"`; one that needs a person is `200` with `"decision": "approval_required"`. HTTP errors are for requests that could not be evaluated. See [decisions](https://immiscible.fly.dev/docs/concepts/decisions.md) and [errors](https://immiscible.fly.dev/docs/api/errors.md).

## Rate limits

Limits are per key, per agent, per address for public routes, and per card on the card rail. A limited request is `429 rate_limited` with a `retry-after` header in seconds; honour it.

## CORS

Gateway, gate and OAuth routes allow any origin, because they authenticate with a key, never a cookie. Console routes allow none.

## How this reference is built

The server reads its own router once every route is registered, classifies each route's authentication from its path, works out its parameters, and renders a page per endpoint. Descriptions, request and response examples are merged in from Markdown files kept beside the server's source (the server is not open source; the SDKs and the CLI are, at [efr7-7/immiscible-sdks](https://github.com/efr7-7/immiscible-sdks)). A route without a written description still appears, marked as generated, so nothing the server answers is missing from this reference.
