IncarnaDocsGitHubConsole

Errors

Every error response uses one envelope:

{ "error": { "code": "not_found", "message": "agent 00000000-... not found" } }

code is a stable, machine-readable string you can branch on. It is deliberately not the HTTP status number: the status is already in the status line, and it cannot distinguish two different failures that share it. 422 covers both a malformed body and a reused idempotency key, and those need different handling.

message is for humans and logs. Do not parse it.

Codes

HTTP code Meaning What to do
400 bad_request Rejected by us or by an upstream provider. The message carries the provider's own text. Read the message. Provider capacity and policy errors land here.
401 unauthorized Missing, malformed, invalid or revoked credential. Check the bearer. Revoked keys are indistinguishable from wrong ones, on purpose.
403 forbidden Authenticated, but not permitted. Most commonly: an API key tried to mint an API key. Use a console session.
404 not_found No such resource in your organization. A resource in another tenant also returns 404 — existence is not disclosed.
405 method_not_allowed Wrong verb.
409 idempotency_in_progress A request with this Idempotency-Key is still running. Retry after a short backoff. Do not change the body.
422 validation_error Body failed validation. Fix the request.
422 idempotency_key_reuse Same key, different body. A key is bound to one request. Use a new key.
422 idempotency_key_invalid Malformed key.
429 rate_limited Over 120 requests/minute for the organization. Back off. Per-org, so more keys will not help.
500 internal_error Our fault. Retry with backoff. Report if it persists.
503 unavailable A required subsystem is off. x402 routes return this when the paid surface is not configured — closed rather than free.

402 is not an error

402 on an /x402/* route is the protocol working. It carries signable payment terms, not a failure. See x402.

Notable behaviours

404 hides existence. Requesting an agent that belongs to another organization returns the same 404 as one that never existed. This is intentional: distinguishing them would let anyone enumerate other tenants' resources.

Revoked and invalid are the same 401. For the same reason.

Upstream errors are not laundered. When an email provider refuses to create an inbox, you get 400 with the provider's actual message. The alternative — swallowing it and storing a dead address — fails later, further away, and harder to diagnose.

Bookkeeping never fails a request. If a payment settles but the ledger write fails, the request still returns 200. The action happened and the money moved; reporting failure would be the false answer.

Retrying safely

Unsafe POSTs accept an Idempotency-Key header. Send the same key on a retry and you get the first request's response instead of a second agent, a second wallet, or a second payment.

curl -X POST $BASE/agents \
  -H "Authorization: Bearer $INCARNA_KEY" \
  -H 'Idempotency-Key: 8f14e45f-ea6a-4f7e-9c1b-1a2b3c4d5e6f' \
  -H 'Content-Type: application/json' \
  -d '{"name":"..."}'

Rules worth knowing:

  • Records expire after 24 hours.
  • A key is bound to the body it first saw. Reusing it with a different body is 422 idempotency_key_reuse, not a silent replay.
  • Concurrency is resolved in the database, so two simultaneous retries cannot both do the work. The loser gets 409 idempotency_in_progress.

Degraded is not an error

An agent in degraded returns 200 like any other. It means a provisioning job ran out of retries — recoverable, and it recovers on its own. See the lifecycle.