Errors

Updated 9/17/2026

Errors

LetterAPI uses standard HTTP status codes. Error responses are JSON.

Shape

Most errors:
json
{ "error": "Human-readable message" }
Validation errors (Zod) return the flattened field errors instead of a plain string:
json
{
"error": {
"formErrors": [],
"fieldErrors": {
"filePageCount": ["Expected number, received string"],
"recipientAddress": ["Required"]
}
}
}
When an order fails a check (credits, addresses, pricing) the response also carries an errors list, one entry per problem, so you can fix them all at once. Address errors say which address (to or from) and which field:
json
{
"error": "Enter a valid UK postcode, for example SW1A 1AA.",
"errors": [
{ "code": "address_invalid", "address": "to", "field": "postcode", "message": "Enter a valid UK postcode, for example SW1A 1AA." }
]
}
Codes: shop_unavailable, post_not_offered, pricing_unavailable, insufficient_credits, file_unavailable (the PDF at filePath is not publicly accessible, so the shop could not print it), address_invalid. Run POST /api/v1/orders/quote first to see them without creating anything.
Insufficient-credit errors include the balances involved (see Creating orders):
json
{ "error": "Insufficient credit balance", "balancePence": 50, "requiredPence": 186 }

Status codes

StatusMeaningTypical cause
400 Bad RequestMalformed or invalid request body.Missing required field, wrong type/enum value.
401 UnauthorizedNot authenticated.Missing, malformed, or revoked API key.
402 Payment RequiredOrder blocked on funds.Credit balance below the order's price.
403 ForbiddenAuthenticated, but not allowed.Accessing another account's order/shop, or using a test key against a live-mode resource (and vice versa).
404 Not FoundResource doesn't exist.Bad id, or an order/key that was deleted.
413, 415, 422File upload rejected.PDF over 25 MB, not actually a PDF, or corrupted/password-protected.
409 ConflictValid request, wrong state.Paying an order that isn't pending_payment; an invalid status transition.
500 Internal Server ErrorSomething broke on our end.Retry with backoff; contact support if persistent.

Idempotency

Order creation is not currently idempotent — retrying a timed-out POST /api/v1/orders request can create a duplicate order. Check GET /api/v1/orders for an existing matching order before retrying, or add client-side de-duplication (e.g. don't retry automatically on ambiguous network errors).