Standard API Error Envelope
Application-level handlers return errors in this shape:Authentication Errors
The auth layer behaves like this:| Status | Code | Meaning |
|---|---|---|
401 | UNAUTHORIZED | Missing x-api-key or API key hash not found |
403 | FORBIDDEN | API key exists but is not active |
403 when you try to access a case owned by another user.
Validation And Conflict Errors
| Status | Code | Where it happens |
|---|---|---|
400 | INVALID_REQUEST | Malformed JSON or schema validation failure |
404 | CASE_NOT_FOUND | Case does not exist |
404 | ATTACHMENT_NOT_FOUND | Attachment ID is not on the case |
409 | CASE_NOT_WAITING_FOR_INPUT | POST /cases/{id}/input called when the case is not paused |
409 | INPUT_REQUEST_NOT_PENDING | Input request was already resolved |
409 | INPUT_REQUEST_MISMATCH | inputRequestId is stale or wrong |
429 Cases
There are two different ways to get throttled:
Usage Limit 429
POST /cases returns:
- status
429 - code
USAGE_LIMIT_REACHED
Gateway Throttling 429
API Gateway also has route throttling configured:
POST /caseshas a tighter limit than other routes- defaults vary by environment
429 responses may not use the standard JSON envelope.
500 Cases
Known handler behavior:
POST /casesreturnsINTERNAL_ERRORwhen persistence or queueing failsPOST /cases/{id}/inputreturnsINTERNAL_ERRORwhen queueing the input failsGET /cases/{id}/attachments/{attachmentId}returnsINTERNAL_ERRORwhen AgentMail attachment lookup fails
Transcript Fetch Caveat
GET /cases/{id} does not wrap transcript-fetch failures in a local try/catch.
If includeTranscript=true and the AgentMail thread fetch fails, the platform may return a generic 500 instead of the standard JSON error shape.
Webhook Failure Handling
Webhook delivery is best-effort:- one HTTP request
- no signature
- no retry
- failure only logged server-side
FAQ / Edge Cases
Does POST /cases support idempotency?
No. Sending the same request twice creates two cases.
Does 202 Accepted on POST /cases/{id}/input mean the case has resumed?
No. It only means the input was queued successfully.
Why is nextActionAt a huge number?
The code uses 253402300799999 as an “unscheduled” sentinel instead of null.
Is resultStatus a closed enum?
No. Known values exist, but the implementation types it as string and currently mixes lowercase workflow values with uppercase provider-delivery values.