Pepelen
← Systems Analysis: APIs, Integrations and Data

Lesson

Status codes: choose a code the client will understand

Choose a status code for each outcome of an endpoint — success, request error, no permission, state conflict, overload, failure — and describe the error body so that the client can react to it.

1 / 7

The status code is part of the contract

The status code is part of the contract

Code classes: • 2xx — success; • 3xx — redirection; • 4xx — client error: retrying unchanged won't help, except 429 after the Retry-After pause; • 5xx — server error: a later retry may help. A refresher from the testing course; the analyst picks the contract's code for each outcome. 401 or 403? Ask: would logging in again help? If yes, 401: the client is unidentified (no token, or it expired). If no, 403: the client is known but lacks permissions. If even the resource's existence is secret, answer 404 instead of 403, as the order service does for someone else's order. 400, 409 and 422: • 400 — the request can't be parsed: broken JSON, “two” instead of a number; • 422 — parsed, but the data breaks a rule: an empty cart, an address outside the zone; • 409 — the resource's current state blocks it: cancelling a delivered order. APIs draw the 400/422 line differently, so the contract records it. Anti-pattern: “200 with an error inside”: code 200, body {"success": false}. Retries, monitoring and caches look at the code and see success. Error bodies use one format, e.g. Problem Details (RFC 9457, application/problem+json): type, title, status, detail plus your own fields. A 409 for cancelling an order in status in_delivery: {"type": "https://api.romashka.example/problems/order-not-cancellable", "title": "Order cannot be cancelled", "status": 409, "currentStatus": "in_delivery"}
Status codes most often written into a contract
CodeWhenRomashka example
200 OKsuccess with a bodyGET an order
201 Createdresource created, its address is in LocationPOST /orders
202 Acceptedaccepted, will be done laterstarting a refund
204 No Contentsuccess without a bodysaved address deleted
400 Bad Requestrequest cannot be parsed"quantity": "two"
401 Unauthorizedtoken missing or invalidtoken expired
403 Forbiddenidentity known, no permissionrefund without the “finance” role
404 Not Foundno such resource (or it is hidden)a nonexistent order or someone else's
409 Conflictthe current state gets in the waycancelling a delivered order
422 Unprocessable Contentthe data breaks a ruleempty cart
429 Too Many Requestslimit exceeded, pause given in Retry-After300 requests per second with a limit of 100
500 Internal Server Errorfailure on our sidea bug in the order service code
502 Bad Gatewaythe called system returned an invalid responsethe provider sent a broken response
503 Service Unavailableoverload or maintenanceorder service under maintenance
504 Gateway Timeoutthe called system did not respond in timethe provider is silent
Lesson notes
The status code is part of the contract
Code classes: • 2xx — success; • 3xx — redirection; • 4xx — client error: retrying unchanged won't help, except 429 after the Retry-After pause; • 5xx — server error: a later retry may help. A refresher from the testing course; the analyst picks the contract's code for each outcome. 401 or 403? Ask: would logging in again help? If yes, 401: the client is unidentified (no token, or it expired). If no, 403: the client is known but lacks permissions. If even the resource's existence is secret, answer 404 instead of 403, as the order service does for someone else's order. 400, 409 and 422: • 400 — the request can't be parsed: broken JSON, “two” instead of a number; • 422 — parsed, but the data breaks a rule: an empty cart, an address outside the zone; • 409 — the resource's current state blocks it: cancelling a delivered order. APIs draw the 400/422 line differently, so the contract records it. Anti-pattern: “200 with an error inside”: code 200, body {"success": false}. Retries, monitoring and caches look at the code and see success. Error bodies use one format, e.g. Problem Details (RFC 9457, application/problem+json): type, title, status, detail plus your own fields. A 409 for cancelling an order in status in_delivery: {"type": "https://api.romashka.example/problems/order-not-cancellable", "title": "Order cannot be cancelled", "status": 409, "currentStatus": "in_delivery"}