Give failures stable meaning
An HTTP status alone rarely tells a client enough to recover. Define which failures are validation, authentication, authorization, conflict, rate limit, dependency failure, or internal fault, and state whether the requested effect occurred. RFC 9457 provides a standard problem-details shape with fields such as type, title, status, detail, and instance, while allowing domain extensions. A stable problem type lets client code branch on an identifier instead of parsing human prose.
Keep public details useful but non-sensitive. A trace or occurrence identifier can connect a caller to server logs; a database exception, secret, internal hostname, or stack trace should not become part of the response contract. Document which fields are stable and which are diagnostic.
Separate transport uncertainty from business rejection
A timeout means the client did not receive a conclusive response; it does not prove the server did nothing. A 422-style validation response can be final for that payload, while a connection reset after submission leaves the effect unknown. Model these states explicitly. The client needs to know whether to correct input, authenticate again, wait, query operation status, retry, or stop for human review.
For a hypothetical document-export API, return a stable validation problem for an unsupported format, a rate-limit response with server guidance, and an operation identifier once work is accepted. If the connection drops after acceptance, the client checks that identifier instead of creating a second export. This is a design example, not observed service behavior.
Retry only when effects are controlled
RFC 9110 defines idempotent methods by intended server effect and warns against automatically retrying non-idempotent requests without knowledge that the semantics are idempotent or that the original request was not applied. For create or charge operations, an application-level idempotency key can provide that knowledge if the server stores and checks it correctly. Stripe's official API documentation illustrates one implementation that associates repeated requests with the first result and compares parameters.
Do not copy a vendor policy without defining your own key scope, retention period, collision behavior, concurrent-request handling, and response to changed parameters. Cap retries, add jitter where appropriate, honor server retry guidance, and stop on permanent classes.
Test a failure matrix
Build contract cases for success, malformed input, unauthorized and forbidden access, timeout before acceptance, timeout after possible acceptance, rate limit, duplicate key, key reuse with changed input, malformed upstream response, and revoked credentials. Assert both the client-visible result and the server-side effect count where the harness allows it. Verify that logs redact secrets and carry correlation fields.
Generated clients and mocks can drift from the real provider, so retain at least one controlled integration check against the official contract. Report which failures were simulated, which were observed against a sandbox, and which remain assumptions. A complete matrix still cannot establish provider availability or behavior outside the tested version and environment.
What to carry into the work
- Define stable machine-readable problem types.
- Represent unknown outcome separately from confirmed failure.
- Specify idempotency scope and retry limits.
- Test effect counts, redaction, and recovery paths.
Sources & dates
- RFC 9457: Problem Details for HTTP APIs ↗IETF · 1 Jul 2023 · Checked 19 Sept 2026
- RFC 9110: HTTP Semantics ↗IETF · 1 Jun 2022 · Checked 19 Sept 2026
- Idempotent requests ↗Stripe · Undated source · Checked 19 Sept 2026
Unknown source dates stay undated. Preparation is not publication; no historical byline or interview is implied.