Errors
One machine-readable problem document for every failure, and what each type means for your retry logic.
Every failure — including a route that does not exist — answers with
application/problem+json, the shape defined by RFC 9457. There is no path
through this API that returns an HTML error page, so a parser that assumes JSON
on a non-2xx response is safe.
{
"type": "https://api.backresto.com/problems/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "The authenticated client lacks the required scope.",
"instance": "/v1/restaurants/restaurant-1/deliveries/8f2c/images",
"requestId": "b1c8b0a4-6c1e-4f6b-9e1c-3b2a5d7f0e91"
}
type is the stable identifier: branch on it, not on detail, which is prose
and may be reworded. requestId is the value to quote in a support message —
it is also returned in the X-Request-ID response header, and you can set that
header yourself to correlate with your own traces.
The catalogue
type suffix | Status | What happened | Retry? |
|---|---|---|---|
invalid-request | 400 | A parameter is missing or malformed: an inverted or over-long delivery range, an unknown collection, or a cursor that does not match the restaurant, collection and limit you are paging with. | No — fix the request. |
unauthorized | 401 | No Authorization header, or a key that is malformed, unknown, revoked, expired, or left with no grants at all. | No — the key needs attention. |
forbidden | 403 | The key holds a grant for this restaurant but not the scope this endpoint needs. | No — a key carrying that scope has to be requested. |
not-found | 404 | No grant for this restaurant, or no such delivery or record. The cases are deliberately indistinguishable. | No. |
conflict | 409 | The request conflicts with an existing resource. | No. |
payload-too-large | 413 | The request body exceeds the limit. | No. |
rate-limit-exceeded | 429 | Your client passed its request budget. | Yes, after Retry-After. |
dependency-unavailable | 503 | A component the API needs is briefly unavailable. | Yes, with backoff. |
internal-error | 500 | An unexpected failure, already recorded on our side. | Once, with backoff; then tell us the requestId. |
What a client should do
Three buckets, and nothing else is worth the code:
Do not retry — 400, 401, 403, 404, 409, 413. The request will fail again
identically. Log the type and the requestId, and surface it: a 401 in
particular means a human at your customer's end has revoked something, and a
retry loop will only hide that for a week.
Retry with backoff — 429, 503. Honour Retry-After on a 429; it is the
number of seconds until your budget refills, and it is not a suggestion. For
503, exponential backoff with jitter, and give up rather than hammering.
Retry once, then escalate — 500. If it recurs on the same request, it is not transient.
Every endpoint in this API is a GET, so retrying is always safe from the
API's point of view; the reason not to retry a 4xx is that it wastes your
budget, not that it risks a double write.
Correlating with your own logs
Send your own identifier and it comes back:
curl "https://api.backresto.com/v1/restaurants/$RESTAURANT_ID/deliveries?from=1787846400000&to=1787932800000" \
-H "Authorization: Bearer $BACKRESTO_PARTNER_API_KEY" \
-H "X-Request-ID: 3f7a1c9e-2b4d-4a86-9f0c-5e1d8b6a2c40" \
--include
The value is echoed in the response header and in the requestId of a problem
document, which makes a single grep answer "what did BackResto see when my job
failed at 04:12?".