Developers
Errors
Every error, from any endpoint, uses one envelope. Read the HTTP status for what to do and the code for why it happened.
The envelope
Every failure, from every endpoint, has this shape. There is no other error format to handle.
Any error
{
"error": {
"code": "not_found",
"message": "Delivery not found.",
"details": {}
}
}Codes
| Status | Code | Meaning |
|---|---|---|
| 400 | Codebad_request | MeaningThe request was understood but is not valid — a cancel on an already-delivered parcel, for instance. |
| 401 | Codeunauthorized | MeaningMissing, malformed or revoked API key. Not retryable: fix the key. |
| 404 | Codenot_found | MeaningNo such delivery on your account. A delivery belonging to another partner reads as missing, not forbidden. |
| 409 | Codeconflict | MeaningThe action collides with the delivery's current state. |
| 422 | Codevalidation_error | MeaningA field is missing or the wrong type. `details.errors` names each one. |
| 429 | Coderate_limited | MeaningRate limit exceeded. Wait the number of seconds in Retry-After, then retry. |
| 500 | Codeinternal_error | MeaningOur fault. Retry with backoff, and tell us if it persists. |
| 502 | Codeprovider_error | MeaningA downstream provider (maps, SMS, payments) failed. Safe to retry with backoff. |
Validation errors
A 422 names every field that failed, so you can surface the problem rather than a generic failure.
422 response
{
"error": {
"code": "validation_error",
"message": "Request validation failed.",
"details": {
"errors": [
{
"loc": ["body", "recipient_phone"],
"msg": "Field required",
"type": "missing"
}
]
}
}
}Retrying
Retry 429, 502 and 500 with exponential backoff and a jitter. Never retry a 4xx other than 429 — the request will fail identically every time.