Agent API (/v1)
Used by: n8n, Zapier, MCP, and any HTTP client. Get X-Agent-Id and X-Api-Key from Credentials.
Per-request timeout overrides in the JSON body are REST-only. The MCP integration does not forward timeout; expiry always uses the agent’s dashboard defaults unless you call this API over HTTP.
All routes require agent authentication.
A machine-readable description of these endpoints is available as OpenAPI YAML. For plan and quota errors on create, see Automation limits & billing.
Approval status values
Thestatus field describes the approval lifecycle (human choice or expiry). Values you will see on GET /v1/approvals/:requestId, GET /v1/approvals/:requestId/result, and in callback bodies:
| Value | Meaning |
|---|---|
pending | Open; waiting for a human or for expiry. Poll responses may include only request_id and status. |
selected_option | Resolved: the human chose one of the configured options. |
custom_instruction | Resolved: the human provided free-text instruction instead of an option. |
expired | Closed by timeout (no human resolution in time). |
status is pending for a newly created row.
Result delivery fields
OnGET /v1/approvals/:requestId only, the API also returns how the final outcome was (or was not) handed off when a callback URL was configured on create. This is separate from approval status above.
| Field | Values | Meaning |
|---|---|---|
result_delivery_status | pending | No callback, or callback not completed successfully yet, or consumer is expected to poll for the result. |
delivered | Callback URL returned a successful HTTP response (2xx) after resolution. | |
failed | Callback retries were exhausted; see result_delivery_error. | |
result_delivery_error | string or null | Short error message when delivery failed (for example HTTP status or network error). |
callback.url on create, the service still tracks completion via polling; result_delivery_status typically stays pending from a “callback delivery” perspective even after the approval is resolved.
Callback webhooks
If you includecallback on POST /v1/approvals, the service sends the outcome to your URL when the request leaves pending (human resolution or timeout expiry).
Request the service makes to your URL
| Aspect | Behavior |
|---|---|
| HTTP method | POST |
| URL | The callback.url you supplied at create time |
| Headers | Content-Type: application/json, then any custom keys from callback.headers (if set), then optional agent-level callback authentication from the dashboard (same header name + value you configure for tools such as n8n Wait Header Auth). If the same header name appears in both places, the agent value wins so clients cannot strip auth by omitting it from the create payload. |
| Body | Same JSON object as GET /v1/approvals/:requestId/result returns for a resolved request (not the minimal pending shape). Same fields as described under Resolved (200) below. |
| Success | Any 2xx response is treated as success; no response body is required. |
| Retries | On non-2xx or network failure, the service retries with exponential backoff (defaults in the open-source server: base delay 1s, max delay 30s, up to 3 attempts—your operator can tune env vars). |
| Security | Prefer HTTPS and optional agent-level callback auth in the dashboard (merged at delivery; the secret is not part of POST /v1/approvals). Use callback.headers only for non-secret extras (for example tracing IDs). There is no HMAC body signature in the reference server; see n8n integration for Wait-compatible header auth. |
Rate limits
POST /v1/approvals is additionally limited by a per-agent sliding window in the API process to absorb bursts. Default in the reference server configuration: 60 successful create attempts per agent per 60 seconds (your deployment may override AGENT_CREATE_RATE_LIMIT_MAX and AGENT_CREATE_RATE_LIMIT_WINDOW_MS).
When exceeded, the API returns 429 with { "error": "rate_limited" }. Backoff and retry with jitter; do not spin in a tight loop.
POST /v1/approvals
Creates an approval request. The body must match the API validation rules below.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
request_id | string | no | 1–120 chars when set; omit or leave blank and the server assigns a UUID (see response request_id). Supply your own stable id for idempotent retries. |
correlation_id | string | no | max 120 |
question | string | yes | max 500 |
context_markdown | string | yes | max 2500 |
notification_summary | string | no | max 120 |
options | array | yes | 1–4 items |
options[].label | string | yes | max 120 |
options[].value | string | yes | max 500 |
timeout | object | no | Omit to use this agent’s default timeout from the dashboard. If present, each field you send overrides only that part of the defaults (partial override). |
timeout.expires_in_seconds | number | no* | integer, 30–86400; *required in the merged result (from body or agent default) |
timeout.mode | string | no* | expire_no_action or expire_with_predefined_instruction; *same as above |
timeout.predefined_instruction | string | null | no* | When non-null, max 1000 chars. After merge, required to be non-empty when mode is expire_with_predefined_instruction |
callback | object | no | |
callback.url | string (URL) | yes if callback set | Valid URL |
callback.headers | record of string | no | Optional extra headers (non-secret metadata). Do not rely on this for auth secrets when agent callback auth is enabled in the dashboard—the server merges the configured auth header at delivery. |
timeout: The server merges a missing timeout with the agent’s saved defaults (default_timeout_seconds, default_timeout_mode, default_timeout_instruction). If you include timeout, only the properties you set replace the corresponding defaults; omitted properties keep the agent values. The merged timeout is validated (including instruction required for expire_with_predefined_instruction) and used for expires_at and idempotency hashing.
Omitting request_id: The create response always includes request_id (either yours or the server-generated UUID). Use that value for polling. Repeating POST /v1/approvals without a client request_id assigns a new id each time, so blind retries can create duplicate approvals. For safe retries, send the same request_id you chose on the first attempt.
Success response (200)
idempotent is true when this call matched an existing equivalent request for the same idempotency identity (only when you supplied a request_id; server-assigned ids are never merged across calls).
Error responses
| HTTP | error | When |
|---|---|---|
| 400 | invalid_payload | Validation failed; issues may be present |
| 401 | missing_agent_auth, invalid_agent, invalid_api_key | Auth failure |
| 403 | USAGE_APPROVAL_LIMIT | Team is over the subscription/plan approval quota (when billing/usage limits are enabled). Body includes code: "USAGE_APPROVAL_LIMIT" and over_limit: true. |
| 409 | idempotency_conflict | Same request_id for this agent but incompatible canonical payload (see body below) |
| 429 | rate_limited | Too many creates per agent in the sliding window |
| 500 | approval_creation_failed | Unexpected failure |
idempotency_conflict body (in addition to error):
request_id when you intend a separate approval; repeat the same body to retry safely.
GET /v1/approvals/:requestId
Returns metadata for an approval identified by your external request_id (the path parameter is that same string). Use this for dashboards or to inspect callback delivery state; use /result for the full outcome payload.
Success response (200)
status follows Approval status values. result_delivery_status / result_delivery_error are described under Result delivery fields.
Error responses
| HTTP | error | When |
|---|---|---|
| 401 | (agent auth) | Invalid credentials |
| 404 | not_found | No approval for this agent + external id |
GET /v1/approvals/:requestId/result
Polls the outcome. The path parameter :requestId is your external id (request_id from creation).
Pending (200)
Resolved (200)
Includes fields such as:
request_id,agent_id,correlation_idstatus— one ofselected_option,custom_instruction, orexpired(see Approval status values)selected_option_index,selected_option_label(when applicable)custom_instructionresolved_by(user_id,name),resolved_at
callback.url, if configured.
Error responses
| HTTP | error | When |
|---|---|---|
| 401 | (agent auth) | Invalid credentials |
| 404 | not_found | Unknown approval for this agent + external id |