HTTP API reference
Every invocation endpoint — flow invoke, SSE streaming, single-node calls — with authentication, request and response fields, rate limits and daily quotas, and the structured error envelope.
View as MarkdownThis page is the reference for Axiom's invocation HTTP surface: invoking a
flow (POST /v1/flows/invoke), streaming a pipeline-mode flow
(POST /v1/flows/invoke/stream), invoking a single node, the error
envelope, and rate limits. For a guided walkthrough, start with
Invoke a flow via API. For the exact
input and output schema of your flow or package, use the
live OpenAPI specs — they are generated from the
compiled artifact and are always current.
Base URL and authentication
All invocation endpoints are served on your deployment's origin under the
/invocations path prefix. For example:
POST https://<your-axiom-origin>/invocations/v1/flows/invokeEvery request must carry an API key as a bearer token:
Authorization: Bearer <your 64-character API key>API keys are 64-character hex strings created under Console → API Keys
in the app, or minted automatically by axiom login (named cli). The raw
key is shown exactly once at creation; the platform stores only its SHA-256
hash. See Create and manage API keys.
The key resolves to your tenant. Every execution it starts is scoped to that tenant — the platform enforces isolation, so a key can never invoke another tenant's flows or read another tenant's data (Sandboxing and tenancy).
A missing, invalid, or revoked key gets HTTP 401 with the body:
{"error": "unauthorized"}Revocation takes effect on the key's very next use — there is no key cache.
Rate limits
Two per-tenant token buckets apply to authenticated requests:
- All routes: 100 requests/second, burst 200.
- Invocation routes (everything under
/invocations/): an additional 5 requests/second, burst 10 — a beta guardrail.
Exceeding either returns HTTP 429 with a Retry-After: 1 header and the
body {"error":"rate limit exceeded"}.
Daily quotas
On top of the per-second rate limits, each tenant has two daily budgets. Both reset at midnight UTC. The beta defaults are:
- 2,000 invocations per day. An invocation is one authenticated request that triggers compute: a flow invoke (plain or streaming), a single-node call, a paused-flow resume (including resume webhooks), or a debug-session fork.
- 500 executions per day. An execution is one flow run. Most invocations start exactly one, but executions a flow spawns internally — parallel fan-out branches that run as child executions, runtime graph mutations, debug forks — each draw from the same budget. A fan-out into 10 branches costs 10 executions.
A request that would exceed either budget is rejected before any compute
runs, with HTTP 429, a Retry-After header, and a structured body whose
retry_after_seconds counts down to the next UTC midnight:
{
"error": "quota_exceeded",
"message": "daily invocations quota exceeded (cap 2000)",
"retry_after_seconds": 74380
}If a running flow exhausts the execution budget mid-run (for example at a
large fan-out), the flow fails with the same quota exceeded message as its
error.
The defaults are per-tenant and the operator can raise or lower them for your tenant — if you hit them with a legitimate workload, get in touch. Two related responses you may also see on invocation routes:
| Status | Body error | Meaning |
|---|---|---|
| 403 | tenant_suspended | Your tenant has been disabled by the operator. No invocations or resumes are accepted. Contact the operator. |
| 503 | quota_unavailable | The quota service could not be reached, so the request was rejected as a safety measure. Transient — retry after retry_after_seconds (5 s). |
Invoke a flow
POST /v1/flows/invoke executes a compiled artifact once and returns the
result inline (with wait) or an execution ID immediately (without).
curl -X POST 'https://flows.example-axiom-host.com/invocations/v1/flows/invoke' \
-H "Authorization: Bearer $AXIOM_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"graph_id": "01JX3F8Q4ZJ4M9W4Y0B8T2K7RD",
"input": { "text": "hello" },
"wait": true
}'Request fields
| Field | Type | Required | Meaning |
|---|---|---|---|
graph_id | string | yes | The compiled artifact to execute. Get it from the Use via API dialog or the flow's live OpenAPI spec; editing a flow produces a new artifact with a new ID. |
input | object | one of input/payload | The entry node's input message as a JSON object. The platform converts it to the typed message, and decodes the result back to JSON. |
payload | string (base64) | one of input/payload | The entry node's input message as protobuf-encoded bytes, base64-encoded. For callers that already speak protobuf. input takes precedence if both are set. |
wait | boolean | no | true blocks until the execution completes and populates result. Default false: the response returns immediately with just accepted and execution_id. |
timeout_seconds | integer | no | How long a waiting call blocks before giving up (default 30). |
config_id | string | no | Named flow config profile to apply; when omitted the default hierarchy applies (tenant default → flow default). See Flow configs. |
debug_session_id | string | no | Streams per-node debug events for this execution to the named debug session. Can also be sent as the X-Debug-Session-Id header, which takes precedence over the body field. See Debug a flow. |
Response
An accepted invocation returns HTTP 202:
{
"accepted": true,
"execution_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"result": {
"success": true,
"output": { "text": "hello" },
"completed_at": 1765432100000
}
}execution_id— a 32-character hex ID assigned when the request is accepted. It is also the execution's trace ID, so one ID references the execution everywhere: results, debug events, and traces.result— present only withwait: true.result.outputis the terminal node's output message decoded to JSON (when you invoked withinput);result.payloadcarries base64-encoded protobuf bytes instead when you invoked withpayloador when JSON decoding was not possible.- Without
wait, the body is just{"accepted": true, "execution_id": "..."}and the flow runs asynchronously.
Failures return a non-202 status with an error body — see Error envelope.
Stream a flow over SSE
POST /v1/flows/invoke/stream invokes a flow in pipeline mode and streams
result frames back as Server-Sent Events. Use it for flows that emit a
sequence of output frames rather than a single result
(Execution model).
curl -N -X POST 'https://flows.example-axiom-host.com/invocations/v1/flows/invoke/stream' \
-H "Authorization: Bearer $AXIOM_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"graph_id": "01JX3F8Q4ZJ4M9W4Y0B8T2K7RD",
"input": { "text": "hello" }
}'The request body accepts graph_id, input (or payload),
timeout_seconds, config_id, and debug_session_id exactly as in
Invoke a flow. wait does not apply — frames are always
delivered as they are produced.
The response is Content-Type: text/event-stream. Each event is one
data: line of JSON:
data: {"execution_id":"4bf92f3577b34da6a3ce929d0e0e4736","frame_index":0,"payload":{"text":"chunk 1"},"is_final":false}
data: {"execution_id":"4bf92f3577b34da6a3ce929d0e0e4736","frame_index":1,"payload":{"text":"chunk 2"},"is_final":true,"success":true}Frame fields:
| Field | Meaning |
|---|---|
execution_id | Same ID as the unary endpoint — also the trace ID. |
frame_index | Position of this frame in the stream, starting at 0. |
payload | The terminal node's output message for this frame, decoded to JSON. Omitted when the frame carries no decodable payload. |
is_final | true on the last frame; the stream ends after it. |
success | true on the final frame of a successful execution. On failure the field is omitted entirely (it is never serialized as false) — treat a final frame without success: true as failed and read error. |
error | Error message when a frame reports failure. |
The stream times out after 30 seconds by default; set timeout_seconds to
extend it. A timeout ends the stream with a final frame whose error is
"timeout waiting for pipeline result".
Invoke a single node
A published node can be called directly, without composing a flow. This is the endpoint that Client Builder SDKs use — one method per node.
curl -X POST 'https://flows.example-axiom-host.com/invocations/v1/nodes/axiom-official/pdf-chunker/1.0.0/Chunk' \
-H "Authorization: Bearer $AXIOM_API_KEY" \
-H 'Content-Type: application/json' \
-d '{ "url": "https://example.com/doc.pdf" }'- Path forms:
POST /v1/nodes/{owner}/{package}/{version}/{node}(name-based, as in the example) orPOST /v1/nodes/{node_ulid}(the node's 26-character registry ID). - Request body: the node's input message as a plain JSON object — no envelope.
- Response: HTTP 200 with the node's output message as JSON. A node
that returns an error gets HTTP 422 with
{"error_message": "..."}; an unknown or undeployed node gets HTTP 404. - Streaming nodes: a pipeline-mode node (or any request with
Accept: text/event-stream) streams its output frames as Server-Sent Events instead of a single JSON response.
A binary-protobuf variant exists at POST /v1/nodes/invoke with the JSON
body {"node_id": "...", "payload": "<base64 protobuf bytes>"}, returning
{"success": ..., "payload": ..., "error_message": ...} — for callers that
serialize the messages themselves.
Error envelope
Invocation failures with a known cause return a structured body with a
stable, machine-readable error class:
{
"error": "payload_too_large",
"message": "payload exceeds hard cap: actual=18874368 max=16777216",
"max_bytes": 16777216,
"actual_bytes": 18874368
}| Status | error class | Extra fields | When |
|---|---|---|---|
| 400 | — | {"accepted": false, "error_message": "invalid request body: ..."} | The request body is not valid JSON. |
| 401 | unauthorized | — | Missing, invalid, or revoked API key. |
| 413 | payload_too_large | max_bytes, actual_bytes (when the rejecting code path knows the sizes) | The input payload exceeds the platform's 16 MiB hard cap. |
| 422 | — | {"error_message": "..."} | Single-node invocation only: the node itself returned an error. |
| 429 | rate limit exceeded | Retry-After: 1 header | Per-tenant rate limit exceeded. |
| 429 | quota_exceeded | retry_after_seconds to next UTC midnight, mirrored in the Retry-After header | Per-tenant daily quota (invocations or executions) exhausted. |
| 403 | tenant_suspended | — | Your tenant has been disabled by the operator. |
| 503 | quota_unavailable | retry_after_seconds: 5 | The quota service is unreachable; the request is rejected as a safety measure. Retry with backoff. |
| 503 | upstream_unavailable | retry_after_seconds: 5 | The flow could not be queued; a platform dependency was temporarily unavailable. Retry with backoff. |
| 503 | blob_storage_unavailable | retry_after_seconds: 5 | A large input payload could not be stored; a platform dependency was temporarily unavailable. Retry with backoff. |
| 500 | — | {"accepted": false, "error_message": "..."} | Any failure that does not classify into the rows above (for example, a missing graph_id). |
Errors inside a successful HTTP exchange surface differently: a waited
invoke returns 202 with result.success: false, and a stream delivers a
final frame with error set and no success: true (the frame's success
field is omitted on failure, never serialized as false). The
error catalog lists the error messages
themselves.
Live OpenAPI specs
Hand-maintained docs cannot know your flow's input schema — the live, generated OpenAPI 3.0 specs can. Use them as the authoritative request and response schemas:
- Per flow:
GET /api/graphs/{artifact_id}/openapi.json(authenticated, same bearer key). Generated on demand from the compiled artifact: the entry node's input message is the documented input schema, the terminal node's output message is the result schema, andgraph_idis pinned to that exact artifact. Pipeline-mode flows document/v1/flows/invoke/stream; unary flows document/v1/flows/invoke. The same spec backs the Open interactive docs button in the flow inspector's API section — see Use the interactive API docs. - Per package:
GET /api/packages/{name}@{version}/openapi.json(public, no auth). OnePOST /v1/nodes/{owner}/{package}/{version}/{node}operation per node, with input and output schemas and examples. An interactive try-it page for the same spec is served atGET /api/packages/{name}@{version}/docs.