Errors
An error is a JSON body. error is a message for a person reading a log, and kind is for your code:
{"error": "propose req-4f2a9c1b8d7e6f50: payments.deploy denies self-approval, leaving 1 of 2 eligible operators for a quorum of 2",
"kind": "refused"}
A refused key (401, 403 or 429) carries error alone, because the key check happens before the route reads anything. Branch on the status, then on kind where there is one.
Statuses
Section titled “Statuses”These are the statuses POST /v1/requests answers.
| STATUS | WHEN |
|---|---|
| 400 | The body is unreadable, has a field this route doesn't take, names no action or requester, has a param that isn't a string, exceeds the parameter limits, carries a malformed intent or one without its digest in `params.intent`, sends `execute_within` without an intent, or the Idempotency-Key header is empty, too long or not printable ASCII. Kind `invalid`. |
| 401 | No live key. The body names neither the key nor the reason. |
| 403 | The key is live and doesn't hold the propose scope. |
| 409 | The Idempotency-Key already created a request for a different operation. Kind `idempotency_conflict`. Use a new key for a new operation. |
| 413 | The body is larger than 128 KiB. Kind `too_large`. |
| 415 | This route reads application/json. Kind `unsupported_media_type`. |
| 422 | The core refused the proposal, which is a real answer rather than a server fault, and sending it again gets the same one: an action your organization doesn't hold, a quorum outside the action's bounds, a roster too short for the quorum once self approval is struck, a param key starting `outis.`, or a callback URL it won't post to, all kind `refused`. A param the action marks required that's absent or blank is kind `invalid`. |
| 429 | Too many refused attempts from this peer. Carries `Retry-After`. |
| 500 | Something failed on the server's side, not in your request. The body says nothing more. Retry after a pause with the same Idempotency-Key, so the retry can't create a second request. Kind `internal`. |
A 4xx won’t succeed on retry with the same input. A 429 carries Retry-After in seconds. A 5xx is ours; retry after a pause, with the same Idempotency-Key so the retry can’t make a second request.
| KIND | MEANS |
|---|---|
| invalid | The body or the Idempotency-Key header is malformed, a 400, or a param the action requires is missing, a 422. |
| idempotency_conflict | The Idempotency-Key already names a different operation. A 409. |
| too_large | The body is over 64 KiB. A 413. |
| unsupported_media_type | The body isn't application/json. A 415. |
| refused | The core refused the proposal under policy. A 422. |
| internal | Something failed on the server's side. A 500; retry with the same Idempotency-Key. |
A 422 is a policy answer
Section titled “A 422 is a policy answer”The server understood the request and refused it under the action’s policy: your organization doesn’t hold the action, self approval is denied and the roster is short, the request asked for a quorum outside the action’s bounds, or it left out a param the action requires (that one’s kind invalid). Fix the policy or the roster on the dashboard, or the request, and create it again.
The execute routes have their own kinds, listed on Execution.
What isn’t an error
Section titled “What isn’t an error”A request that ends denied, expired or aborted is a normal outcome, delivered on the request rather than as an error status. Expect all four outcomes and act on authorized only.