Integration and APIs20 min

HTTP status codes in a business API: what to return when someone looks up a contact, and what a wrong answer costs

By · founder and CEO of Hábil ·

When to return 200, 404, 400, 422, 409, 429 or 503 on a contact lookup, and what a wrong choice costs: retries, misleading dashboards and agents.

An integrator at your company calls an API to look up a contact by email address. The response comes back with a 200 code and a body that says {"error": "contact not found"}. The integrator's program sees the 200, treats the operation as a success and moves on with a contact that does not exist. Nobody notices until, weeks later, someone asks why orders are missing.

Nothing failed in the network, the server or the database. What failed was a decision that looks minor: which number goes on the first line of the response. The status code is the only thing that load balancers, monitoring dashboards, retry libraries, API gateways and your partners' programs read without interpreting. A sentence in the body is understood by a person; the number is understood by everything else.

This article is for CTOs and technology leaders, and it is written so that a director skimming it can take away the essentials from the tables and the section closings. It walks through the codes that are actually used in a business API with a single example, looking up a contact, and explains when each one applies and what a wrong choice costs. The technical claims rest on the HTTP standards and are cited at the end; the design criteria that are our own are marked as such.

An HTTP response carries a code, some headers and a body. The code and the headers are standardized signals that many tools use to make decisions; the body is read by a person or, if the contract defines it, by a program.

HTTP status codes in a business API: what to return when someone looks up a contact, and what a wrong answer costs
If the response is…The family is…General rule
A real success2xxMove on
A request that must be corrected4xxCorrect it, identify yourself or wait, depending on the code and the contract
A failure of the server or of a dependency5xxRetry only if the operation can be repeated or the contract offers verifiable idempotency

The three parts of a response, and why the number rules

In HTTP/1.1 the response starts with a status line: the code and, optionally, a phrase; in HTTP/2 and later versions the code travels as response metadata, with the same meaning. The standard makes clear that the phrases are only recommendations: they can be replaced with local equivalents or left out without affecting the protocol [1, §15.1]. The number is what counts.

The first digit defines the family, and the standard requires every client to understand it even if it does not know the exact code [1, §15]:

The three parts of a response, and why the number rules
FamilyMeaning in the standardWhat it tells the caller
2xxThe request was received, understood and acceptedMove on
3xxFurther action is needed to complete itGo somewhere else or reuse what you have
4xxThe request contains an error or cannot be fulfilledCheck the specific code and the contract; repeating the identical request rarely helps
5xxThe server failed to handle an apparently valid requestCheck the specific code and the contract; do not blindly retry a non-idempotent write

One rule explains why choosing correctly matters more than choosing in detail: a client that receives a code it does not recognize must treat it as the x00 of its family. The standard itself illustrates it: a hypothetical 471 is treated as a 400 [1, §15]. In other words, the family is the minimum that every machine will understand. If you put a 200 on an error, the family the machines read is the one for success.

Headers complete the number. Some are mandatory with certain codes: a 401 must carry WWW-Authenticate and a 405 must carry Allow with the methods the resource does accept [1, §15.5.2 and §15.5.6]. And the body, as we will see later, should preferably have a standard format, so that a program does not have to guess.

How many of the error responses of your current APIs can be told apart from a success by looking only at the first line?

The case: looking up a contact

Imagine a company's contacts API, for use by its channels and its partners. It has two ways to find someone:

GET /contacts/{id}                      -> get a contact by its identifier
GET /contacts?email=ana@example.com     -> search contacts by a criterion

They look like the same thing. They are not, and the difference already produces the first decision. With the identifier, the caller asserts "this contact exists; give it to me." With the search, the caller asks "is there any that meets this?" And the right answer to a question that admits "none" is not an error.

An OpenAPI-style contract, simplified, to have the whole map before walking through it:

paths:
  /contacts/{id}:
    get:
      summary: Get a contact by identifier
      responses:
        "200": { description: The contact }
        "400": { description: The identifier is not validly formed }
        "401": { description: Authentication is missing }
        "403": { description: Authenticated, but not permitted }
        "404": { description: Does not exist, or its existence is not revealed }
        "410": { description: Existed and was permanently withdrawn }
        "429": { description: Too many requests; see Retry-After }
        "503": { description: Service unavailable for now; see Retry-After }
  /contacts:
    get:
      summary: Search contacts by criterion
      responses:
        "200": { description: List of matches (may be empty) }
        "400": { description: Unreadable criterion or unknown parameter }
        "422": { description: Well-formed criterion that the business does not accept }
    post:
      summary: Create a contact
      responses:
        "201": { description: Created; the Location header points to it }
        "409": { description: One already exists that clashes with this one }
        "422": { description: Well-formed data that breaks a rule }

We walk through them from least to most serious, starting with what goes well.

If your team had to draw this map for the customer or policy API, would everyone agree on how many distinct responses it should be able to give?

When it goes well: 200, and the temptation to use it for everything

A search with no matches deserves care. A request like this one:

curl -i "https://api.example.com/contacts?email=nobody@example.com"

should respond:

HTTP/1.1 200 OK
Content-Type: application/json

{ "results": [], "total": 0 }

200 is the success code for a query, and the standard defines its content for GET as a representation of the target resource [1, §15.3.1]. Here the target resource is the search, which exists and ran correctly; its result is an empty list. This is a reasonable reading of the definition, and it is a design criterion of our own: what matters is that it be decided, documented and applied the same way on every route. If one route returns an empty list and another returns 404 for the same kind of question, the client will have to write a special case per route.

The opposite, the error disguised as a success, is the source of the failures that are most expensive to detect:

HTTP/1.1 200 OK
Content-Type: application/json

{ "error": "contact not found" }

A monitor that looks only at the status code will mark this as healthy. A retry library will consider it resolved. A partner integrating your API will validate the body only if someone told them to. A 200 with an error inside is a failure that walked through the front door wearing a success badge.

There are three siblings of 200 worth telling apart when the case calls for it, and we will give each one a single line: 201 when something was created (normally with the location of the new resource in Location), 202 when the work was accepted but has not finished yet (if the contract publishes a status query, it is checked there) and 204 when it went well and there is nothing to return, for example when removing a contact [1, §15.3].

Does your organization know what percentage of what your dashboards call "success" includes bodies with errors inside?

When the client got it wrong: the 4xx family

The 4xx family says "the problem is in the request." To choose within it, a precedence policy can help, that is, an order of questions that reduces arguments. It is our own criterion, not a mandate of the standard, and a contract can alter it (for example, authenticating before validating is a legitimate security decision):

  1. Is the request understood? (400)
  2. Do we know who is calling? (401)
  3. Does the caller have permission? (403)
  4. Does what it asks for exist? (404, 410)
  5. Is this method allowed on this resource? (405)
  6. Is it in a format we accept? (415)
  7. Does the content, now understood, respect the business rules? (422)
  8. Does it clash with what is already stored? (409)

With this policy, when a request has two defects, the first one on that list that fails is the one answered. That makes responses predictable, and what is predictable can be tested and documented.

400: it is not understood

The standard describes 400 as the response when the server cannot or will not process the request because of something it perceives as a client error: for example, malformed syntax [1, §15.5.1]. In the contact case:

curl -i "https://api.example.com/contacts/abc%20%20%20"      # unreadable identifier
curl -i "https://api.example.com/contacts?email=&color=blue"  # unknown parameter

If the contract rejects those values, both can respond 400. The signal is that the request could not be read as what it claims to be. Repeating it as is would give the same result, and that is why a well-built client does not retry it: it corrects it or shows it to a person.

401 and 403: who you are and what you can do

These are two different questions and they are often confused. 401 says the request does not include valid credentials, and the standard requires including WWW-Authenticate [1, §15.5.2]. 403 says the server understood the request but refuses to fulfill it; if credentials were already sent, the client should not automatically repeat the request with the same ones [1, §15.5.4].

The difference has operational consequences. On a 401, a client can renew its session and repeat. On a 403, repeating with the same credentials does not change the result. A server that confuses the two puts clients in a loop of renewing credentials that resolves nothing.

There is a security nuance that matters to us in a contacts API: the standard allows a server that wants to hide that a forbidden resource exists to respond 404 instead of 403 [1, §15.5.4]. If one customer's contact is visible only to its owner, telling another customer "forbidden" confirms that this identifier exists. Answering "not found" confirms nothing. It is a policy decision that is worth writing down, not improvising route by route.

404 and 410: it is not there

404 indicates that no current representation of the resource was found or that the server does not want to reveal that it exists; it does not say whether the absence is temporary or permanent [1, §15.5.5]. If the server knows it is permanent, the standard prefers 410 [1, §15.5.11].

For a contact, that difference has business value. "That identifier was never created" (404) and "it existed and was merged into another or withdrawn" (410) lead to different actions on the client side: in the first, it checks whether it made a data-entry mistake; in the second, it stops asking for it and looks for the contact that replaced it. If your operation does not distinguish the two cases, use 404, which is the default, just as the standard itself recommends when the server does not know whether the condition is permanent [1, §15.5.11].

A practical detail that surprises people: 404 is among the codes the standard classifies as heuristically cacheable [1, §15.1]. A "does not exist" response can be stored in an intermediate cache, so that a newly created contact keeps not showing up for a while. If creation and lookup happen in immediate sequence, it is worth reviewing the cache headers of that response.

405 and 415: the route exists, the form does not

A 405 says the method is known but this resource does not support it, and requires the Allow header with what it does support [1, §15.5.6]. A 415 says the format of the content sent is not accepted. These are integration errors, not business errors, and they show up mostly in the first days of a new connection.

When a partner receives an error from your API, can they tell from the first line whether they should correct the request, renew their session or stop asking for that resource?

400 versus 422: the difference that is argued the most

The most frequent discussion in teams is which one to use when the data comes in wrong. The policy we suggest for this contract (it is not a universal HTTP rule), supported by the standard's definitions, is short:

400 versus 422: the difference that is argued the most
SituationCodeWhy
The JSON is broken, a closing brace is missing or a parameter does not exist400The request cannot be read [1, §15.5.1]
The JSON is valid and the format is accepted, but the email has no @ sign or the phone number does not meet the rule of its country422The server understands the content and the syntax is correct, but it cannot process what is asked [1, §15.5.21]
The content is valid, but it clashes with what is already stored (another contact with the same email)409Conflict with the current state of the resource [1, §15.5.10]

422 is defined as the response in which the server understands the content type of the request (that is why 415 does not apply) and its syntax is correct, but it could not process the instructions it contains [1, §15.5.21]. Many teams use 400 for everything, and that is not a protocol error: 400 is broad. The advantage of separating them is operational: with 400 the client knows that its code produced something unreadable (an integration defect), and with 422 it knows that the data did not pass a rule (a case to show to a user). Two different causes, two different repairs, two different teams that handle the alert.

An example of a creation with invalid data:

curl -i -X POST https://api.example.com/contacts \
  -H "Content-Type: application/json" \
  -d '{"name": "Ana Pérez", "email": "ana-at-example"}'
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/invalid-email",
  "title": "The email does not have a valid format",
  "status": 422,
  "detail": "The 'email' field must contain an email address.",
  "instance": "/contacts"
}

The body format is not accidental; it is explained in the Problem Details section.

If today you respond 400 to everything, can your support team tell a badly built integration from a badly entered piece of data without opening the logs?

409: when it clashes with what already exists

409 is reserved for a request that cannot be completed because of a conflict with the current state of the resource, in situations where the caller could resolve it and resubmit; the standard asks the server to include enough information to recognize the source of the conflict [1, §15.5.10].

In contacts it shows up in two forms. The first is the obvious one: creating a contact whose email already belongs to another. The second is more expensive and less visible: two channels edit the same contact at almost the same time. If both do a PUT with the full version they read, the one that arrives second silently erases the first one's change. Nobody saw an error; the system stored what it was asked to store.

The HTTP standard has tools for this: conditional requests. A contract can return a version tag (ETag) on each read and require the client to send it back in If-Match when modifying; if the version has already changed, the response is 412 Precondition Failed [1, §15.5.13], and if the client did not even send the condition and the server requires it, 428 Precondition Required, designed precisely to prevent the "lost update" [3, §3]. It is the way to turn a lost change into a visible error instead of a silent gap. The detail of each of these two codes is outside the scope of this article; what matters here is the principle: a conflict can be detected, and detecting it is cheaper than discovering it in an audit.

What happens in your systems when two channels modify the same customer at the same time: is there a visible error, or does the last one win?

429, 500, 502, 503 and 504: when the problem is the server's, or its capacity

The 5xx family is where a wrong choice costs the most money, because its practical effect is to tell machines "it may be a good idea to retry." Retrying well saves an operation; retrying badly duplicates it or brings the service down.

429: too many requests

Although it is not in the main HTTP standard but in an extension, 429 is the right tool for limiting the rate. It indicates that the caller sent too many requests in a period, and it may include Retry-After to say how long to wait; the same specification clarifies that it does not define how the user is identified or how requests are counted, and that these responses must not be stored in a cache [3, §4].

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json

{ "type": "https://api.example.com/problems/rate-limit-exceeded",
  "title": "The request limit was exceeded",
  "status": 429 }

Note that 429 belongs to the client-error family even though it is about capacity; it may reflect a shared quota or policy, not only the caller's own rate. And note Retry-After, which is optional: if it comes, respect it; if not, follow the documented policy, because without any signal the client guesses. In an integration that queries contacts in batches, the difference is the one between a client that adjusts and one that insists every second and worsens the saturation it was meant to avoid.

500: we did not expect this

500 indicates an unexpected condition that prevented the request from being fulfilled [1, §15.6.1]. It is the code for "an unexpected condition occurred on our side"; the server may know the cause, but the response should not disclose it. Two rules for a healthy 500. First: the body must not show the stack trace, the query, file paths or keys; the Problem Details standard expressly warns against exposing implementation details such as a stack dump, because they can reveal exploitable information [2, §5]. Second: the message should be general and carry an identifier that your team can look up in its logs.

502 and 504: an intermediary got no answer

When your API depends on another system (the CRM, the ERP, the core system) and acts as a gateway or proxy, two codes describe what happens between the two (in other topologies the contract may choose another response). 502 is the response when the server received an invalid response from the system behind it; 504, when it received no response in time [1, §15.6.3 and §15.6.5].

A 502 or a 504 on a write is especially delicate, and the same goes for a 500 or a 503 that arrives after the operation was applied, because it does not say whether the operation was applied or not. If the client blindly retries the creation of a contact, it may create two. The way out is in the section on retries.

503: I cannot right now, come back in a while

503 indicates that the server cannot handle the request at this moment because of temporary overload or scheduled maintenance, something that will probably ease with time; it may come with Retry-After [1, §15.6.4]. The same standard clarifies that its existence does not oblige an overloaded server to use it: some simply refuse the connection [1, §15.6.4].

Retry-After accepts a number of seconds or a date [1, §10.2.3]. With a 503 it indicates how long the service is expected to be unavailable to the client [1, §10.2.3]. In planned maintenance, that piece of information lets your partners' integrations pause in an orderly way instead of failing in a cascade.

A summary of the family, with the question that hurts most in each case:

503: I cannot right now, come back in a while
CodeWhat it saysWhat the caller should doThe typical mistake
429You are going too fastWait as long as Retry-After says and come backNot sending Retry-After; the client insists
500Something unexpected failedRetry with caution if the operation is repeatable; raise an alertShowing the stack trace, query or paths
502The system behind responded with something invalidCheck whether the operation was applied before repeatingBlindly retrying a creation
503I cannot handle this right nowWait and retryReturning 200 with "under maintenance" in the body
504The system behind did not respond in timeCheck the state before repeatingMistaking it for a client failure

When one of your internal systems slows down, do your APIs tell the caller how long to wait, or do they leave the caller to guess?

Problem Details (RFC 9457): an error body that a program can actually read

With the codes chosen, the body is still missing. Without an agreement, every API invents its own: {"error": "..."}, {"message": "..."}, {"code": 7}. The RFC 9457 standard (which replaces 7807) defines a common format for this [2]. Identified by the media type application/problem+json, it defines five base members that it may include [2, §3.1]:

Problem Details (RFC 9457): an error body that a program can actually read
MemberWhat it is for
typeAn identifier of the problem type, in the form of a URI; consumers should use it as the primary identifier of the problem [2, §3.1.1]
titleA short, human-readable summary of the problem type [2, §3.1.3]
statusThe HTTP code; it is informational, and the generator must use the same code in the actual response [2, §3.1.2]
detailAn explanation of this specific occurrence; consumers should not parse it to extract information [2, §3.1.4]
instanceAn identifier of this occurrence, to trace it [2, §3.1.5]

If there is no type, about:blank is assumed, which means the problem has no semantics beyond that of the HTTP code [2, §3.1.1 and §4.2.1]. Clients must ignore extension members they do not recognize [2, §3.2], which allows adding custom fields (for example, the list of invalid fields) without breaking anyone.

The value for the business lies in two rules. First: the program decides by type, not by the phrase. If the detail sentence changes from "try again" to "retry later," the client's behavior must not change. Second: the status in the body and the code on the first line must match; the standard itself warns that duplicating the information opens the possibility that the two will disagree [2, §5]. An error with status: 404 in the body on a 200 response is the case above, dressed up in a standard format.

With a stable type (and normally a stable title), the same problem produces the same identifier; detail, instance and the extensions can vary per occurrence. The generator of the response is the one who must make status match the code; an intermediary may alter it afterwards. This makes it possible to count occurrences by type on a dashboard, and not by text.

Could your organization today count how many times the same type of error occurred in a week, without reading messages one by one?

Retries: what is repeated and what is not

Choosing the code is half the work. The other half is what the client does with it. The underlying rule comes from the methods standard: a request is idempotent if repeating it has the same effect as making it once. PUT, DELETE and the safe methods, such as GET, are; POST is not [1, §9.2.2]. And the standard indicates that a client should not automatically retry a non-idempotent method, unless it has a way of knowing that the semantics are idempotent in that case or of detecting that the original request was never applied [1, §9.2.2].

Applied to the contact:

Retries: what is repeated and what is not
RequestHTTP property of the methodConsequence of getting it wrong
GET /contacts/{id}Safe and idempotentExtra load
PUT /contacts/{id} with the full contactIdempotentSame intended effect
DELETE /contacts/{id}IdempotentThe second time it may respond 404/410, and that is correct
POST /contacts (creation)Non-idempotent by definitionTwo identical contacts, unless the contract prevents it

To make creation safe there is a widespread practice: the client sends an idempotency key in an Idempotency-Key header, so that the server recognizes the retry. A word of caution: the header is described in an IETF draft (revision 07) that, as of October 20, 2026, is listed as expired (Apr 18, 2026) and is not a published standard [4]; use it as a reference, not as a norm. If your contract adopts the pattern, document key retention and responses. What the draft proposes: the same key with the same request returns the original result; the same key with a different payload, 422; and a repetition while the original is still in progress, 409 [4].

Retry safety also depends on the contract, on side effects and on network uncertainty, not only on the method. On that basis, the client can have a decision table per code. A commented version for our case:

Retries: what is repeated and what is not
ResponseClient actionWhy
200, 201, 204ContinueIt went well
400, 422Do not retry; correct or showRepeating the same request gives the same result
401Renew credentials once; if it comes back, stopA renewal loop resolves nothing
403Do not retry; escalateA policy denies it
404, 410Do not retry; treat "does not exist" as a resultIt is information, not a failure
409, 412Re-read the state, decide, resubmitThe state changed since it was read
429Wait for Retry-After and come backRespects the server's capacity
502, 504 on a writeCheck the state before repeatingIt may have been applied
500, 503Only if the method is idempotent or the contract offers verifiable idempotency: retry with increasing, randomized and capped waits, respecting Retry-After if it comes; otherwise, check the stateProbably a temporary failure, but a write may have been applied
Unrecognized codeTreat it as the x00 of its family [1, §15]Rule of the standard

The 500 and 503 row calls for a nuance: increasing waits with a random component and a cap are a common engineering practice, not a mandate of the standard. Their purpose is that a thousand clients do not all return at the exact same second and knock down again a service that was recovering.

If your provider responds 503 for fifteen minutes, what do your integrations do: wait in an orderly way, insist non-stop or stop without warning?

What a wrong answer costs: three scenarios

What follows are illustrative scenarios, with no figures; they serve to see the mechanism of the damage.

1. The integration that retries what it should not. A sales application creates contacts in your API. One day the database slows down and the API responds 504 on some creations. The application's code treats every 5xx as "retry," and some creations had already been applied. Result: duplicate contacts, campaigns that write twice to the same customer and a debugging effort that nobody planned. If client, server and contract apply the policy of "check the state before repeating" and an idempotency key on creation, the same incident is much less likely to leave duplicates.

2. The dashboard that looks green. A team measures the health of its API by the rate of 5xx responses. The API returns 200 with an error in the body when the CRM does not respond. The dashboard shows green while the channels cannot query contacts. In the opposite direction there is also a cost: if every search without results is returned as 404, the API's consumers accumulate "errors" that are not errors, and the on-call team learns to ignore the alert. A useful dashboard needs the code to mean something: 4xx for what the caller corrects, 5xx for what the team that owns the service corrects.

3. The agent that does not know what to do. More and more APIs are consumed by a program that decides on its own, including AI assistants and agents that call tools. An agent that receives 200 with the sentence "I could not find it" has to interpret natural language to know whether it failed. One that receives 404 with a stable type knows it can report "does not exist." One that receives 429 with Retry-After knows how long to wait; one that receives 503 with no indication may insist or give up, depending on how it was programmed. A free-language detail should not be parsed to make decisions [2, §3.1.4]; the type should. A well-chosen code and type reduce the ambiguity the agent has to handle. We cover the same subject, seen from what your system needs so that agents can use it, in "APIs ready for AI agents."

Which of these three scenarios could be happening today in one of your integrations without your team knowing?

A complete walk-through: the contact, end to end

To close the example, a sequence with the responses a well-designed API would give. Each row is a hypothetical call against the contract above.

A complete walk-through: the contact, end to end
#CallResponseReading
1GET /contacts?email=ana@example.com (exists)200, list with one contactQuery success
2GET /contacts?email=nobody@example.com200, empty listThe search worked; there are no matches
3GET /contacts/c-0001 (exists)200, the contact, with ETagSuccess with a version
4GET /contacts/c-9999 (never created)404, Problem DetailsA result, not a failure
5GET /contacts/c-0002 (merged into another)410, Problem Details with the replacementStop asking for it
6GET /contacts/%%%400Unreadable request
7POST /contacts with an email without @422Data that does not meet the rule
8POST /contacts with an email already registered409Clashes with what is stored
9PUT /contacts/c-0001 with an outdated If-Match412Someone changed it first
10A burst of searches exceeding the agreed limit429 with Retry-AfterRate exceeded
11GET /contacts/c-0001 with the CRM down503 with Retry-AfterCome back in a while
12POST /contacts and the CRM does not answer in time504Check before repeating

Twelve rows and ten distinct codes. Rows 11 and 12 are one possible contract policy, for an API that acts as an intermediary. A documented, tested contract table reduces ambiguity between teams, and turns a verbal agreement into something that can be verified.

For your most-used APIs, is there such a table today —scenario, code, reading— or does each integrator rebuild it by trial and error?

Six questions for your next API review

To take to the next architecture meeting, without needing to open any code:

  1. Does any error go out with a 200? Ask for a sample of error responses and look at the first line.
  2. Does "not found" have a single policy? Does the policy distinguish a search with no matches (200), a nonexistent resource (404) and a withdrawn resource (410), and is it applied consistently to equivalent cases on every route?
  3. Are 400, 422 and 409 told apart? If everything is 400, your support teams pay for the difference.
  4. Do the 429 and 503 say how long to wait? Retry-After is optional; when it is missing, each client decides on its own, so it is worth documenting the policy.
  5. Do errors have a common format and a stable type? That is what allows a dashboard, a partner and an agent to decide without reading prose.
  6. Can creations be repeated without duplicating? If not, every 504 is a risk of double data.

Many decisions begin in the contract; implementing them may require application or platform changes (rate limits, persistence of idempotency keys, observability).

How many of the six questions would you feel comfortable with today in front of an audit or a partner integrating your API?

What Hábil does

Hábil works in the space between the systems that already run in your company and the new layers that need them: channels, partners, assistants and agents. Part of this topic —that every API response say clearly what happened and what to do— is what makes an integration work when something goes wrong, and it is part of what we offer in Middleware and API platforms. For an AI assistant or agent to use your systems with judgment and clear limits, see Enterprise AI.

The first conversation, at no cost, starts from the map of your integrations and the points where an ambiguous response could cost you money, time or evidence. Write to us on WhatsApp.

Accessed on Oct 20, 2026 (the RFC texts were read in their official version from the RFC Editor and the cited passages were cross-checked).

References

  1. IETF, RFC 9110, HTTP Semantics (June 2022). Sections: §9.2.2 idempotent methods; §10.2.3 Retry-After; §15 and §15.1 code classes, recommended phrases, unrecognized code treated as x00, heuristically cacheable codes; §15.3.1 200; §15.5.1 400; §15.5.2 401; §15.5.4 403; §15.5.5 404; §15.5.6 405; §15.5.10 409; §15.5.11 410; §15.5.13 412; §15.5.21 422; §15.6.1 500; §15.6.3 502; §15.6.4 503; §15.6.5 504. https://www.rfc-editor.org/rfc/rfc9110.html
  2. IETF, RFC 9457, Problem Details for HTTP APIs (replaces RFC 7807). §3 and §3.1 members and media type application/problem+json; §3.1.1 type; §3.1.2 status; §3.1.4 detail; §3.2 extensions; §4.2.1 about:blank; §5 security considerations. https://www.rfc-editor.org/rfc/rfc9457.html
  3. IETF, RFC 6585, Additional HTTP Status Codes, §3 (428 Precondition Required) and §4 (429 Too Many Requests, Retry-After, no caching). https://www.rfc-editor.org/rfc/rfc6585.html
  4. IETF HTTPAPI, The Idempotency-Key HTTP Header Field (Internet-Draft draft-ietf-httpapi-idempotency-key-header, revision 07; draft, not a published standard; expiry recorded Apr 18, 2026 in the Datatracker). https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/