In this chapter
We'll learn how APIs know who's calling and what they may do — API keys, tokens, scopes — and how they report problems: status codes, error bodies, timeouts, retries with backoff and idempotency keys, explained as a building's visitor passes and a delivery company's return notes.
The Problem in Real Life
Anna reads the payment provider's documentation about callbacks for the first time. One paragraph stands out: "Your endpoint must respond with a 2xx status within 10 seconds. Otherwise the delivery is treated as failed and retried up to 3 times over the next hour."
She checks the logs: BlueTicket's handler took about 14 seconds on Friday. It created the order, generated the ticket PDF and sent the email — all before replying. Each of the three retries took just as long. After the third, the provider gave up. "It wasn't an error," she says slowly. "We just answered too late — four times."
Read the API's rules before you write a single line against it.
John
"It Either Works or It Doesn't" vs. Clear Rules for Every Kind of Failure
Who's calling?
An API must know which program is calling, and what that program is allowed to do.
Many kinds of failure
Bad input, no permission, overloaded server, timeout — each needs a different reaction.
Retries can duplicate
Retrying a request that actually succeeded can charge a card twice.
API Authentication, Authorization, Errors and Status Codes
The office building analogy: visitors to an office building get a visitor pass at reception — it says who they are and which floors they may visit. And when a delivery can't be completed, the courier leaves a note saying exactly why: "wrong address", "nobody home — we'll try again tomorrow", "refused". APIs need both: passes and clear notes.
- API authentication — the visitor pass: APIs check who is calling, usually with one of: an API key (a secret string for a program, Act 22), a bearer token such as a JWT for a logged-in user (Act 22), or OAuth client credentials — a program logs in with its own ID and secret and gets a short-lived token, the standard for machine-to-machine calls. Webhooks are usually authenticated with a signature (chapter five).
- API authorization — which floors: the pass also limits what the caller may do, often with scopes: a venue's API token has the scope
sales:readfor its own venue only — it can't read other venues or issue refunds (least privilege, Act 22). - Status codes — the note's headline: 2xx = it worked: 200 OK, 201 Created, 202 Accepted ("got it, will do it later"), 204 No Content. 4xx = the caller did something wrong — don't retry the same request: 400 Bad Request, 401 Unauthorized (who are you?), 403 Forbidden (not allowed), 404 Not Found, 409 Conflict (the seat is already taken), 422 Unprocessable (valid JSON, invalid values), 429 Too Many Requests (slow down). 5xx = the server had a problem — trying again later may work: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout.
- Error bodies — the note's details: a good API returns a consistent JSON error with a machine-readable code, a human message, which field was wrong, and the request ID for support. Never include stack traces or secrets (Act 17, Act 22).
- Timeouts — how long to wait: every API call needs a timeout: the longest the caller will wait. Without one, a slow provider can freeze your whole app. The payment provider waits 10 seconds for BlueTicket; BlueTicket waits 5 seconds for the SMS provider.
- Retries with backoff — try again, politely: for timeouts, 429 and 5xx, retrying often works. But retry with exponential backoff — wait 1 second, then 2, then 4, then 8 — plus a little randomness, so thousands of clients don't all retry at the same instant. Don't retry most 4xx errors: the same wrong request will fail the same way.
- Idempotency keys — retries without double charges: a POST that timed out might have worked — the answer just got lost. Retrying could create a second charge. So payment APIs accept an idempotency key (Act 12's idempotency): a unique ID sent with the request. If the same key arrives twice, the provider returns the first result instead of doing it again.
| Code | Delivery-note version | Should the caller retry? |
|---|---|---|
| 200 / 201 | Delivered | No — done |
| 202 Accepted | Received, will deliver later | No — wait for the result |
| 400 / 422 | Address incomplete or wrong | No — fix the request |
| 401 | No valid ID shown | No — get valid credentials |
| 403 | Not allowed in this building | No |
| 404 | No such address | No |
| 409 | Someone else got it first | No — choose again |
| 429 | Too many deliveries — slow down | Yes, after waiting |
| 500 / 502 / 503 / 504 | Problem at the depot | Yes, with backoff |
| Timeout (no answer) | Nobody came to the door | Yes, with backoff + idempotency key |
| Method | Who uses it | BlueTicket example |
|---|---|---|
| API key | A program, simple setups | BlueTicket → SMS provider |
| Bearer token (JWT) | A logged-in user's app | Mobile app → BlueTicket |
| OAuth client credentials | Program to program, short-lived tokens | Venue systems → BlueTicket venue API |
| Signature (HMAC) | Callbacks / webhooks | Payment provider → BlueTicket |
HTTP/1.1 409 ConflictContent-Type: application/json{"error": {"code": "seat_unavailable","message": "Seat C14 is no longer available.","field": "seatIds","requestId": "req_9f2a71"}}
const idempotencyKey = `order-${order.id}`; // same key on every retryfor (let attempt = 1; attempt <= 4; attempt++) {try {return await payments.createPayment({ amount: order.total, orderId: order.id },{ idempotencyKey, timeoutMs: 5000 });} catch (err) {const retryable = err.isTimeout || err.status === 429 || err.status >= 500;if (!retryable || attempt === 4) throw err;await sleep(2 ** attempt * 500 + Math.random() * 250); // 1s, 2s, 4s... + jitter}}
What went wrong, in these words: BlueTicket was the provider of the callback endpoint, and broke the caller's rule: reply with 2xx within 10 seconds. To the payment provider, our slow handler looked like a timeout, so it retried with backoff three times — exactly as documented — and gave up. And Anna spots a second problem in the code: if a retry ever had arrived while the first attempt was still running, both could have created an order. Two bugs, one handler. Chapter five fixes both.
Key Takeaway
APIs authenticate callers with API keys, bearer tokens or OAuth client credentials, and authorize them with scopes. Status codes say what happened: 2xx success, 4xx the caller's mistake (don't retry blindly), 5xx the server's problem (retry later). Good errors have a consistent JSON body with a code, message and request ID. Every call needs a timeout; retries use exponential backoff; and idempotency keys make retried POSTs safe from duplicate charges.
Why This Matters
Choosing the right status code, writing clear errors and handling other APIs' failures correctly are everyday backend skills — and the difference between a system that recovers by itself and one that charges customers twice. Timeouts, retries with backoff and idempotency come up in almost every system-design interview.
Meanwhile, Riverside Arena's accounts team reports another problem with BlueTicket's own API: when their system asks for "all orders", the request takes a minute and sometimes fails. There are 48,000 of them.
