HTTP status codes in a business API: what to return when someone looks up a contact, and what a wrong answer costs
By Dorian Chávez · 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.
| If the response is… | The family is… | General rule |
|---|---|---|
| A real success | 2xx | Move on |
| A request that must be corrected | 4xx | Correct it, identify yourself or wait, depending on the code and the contract |
| A failure of the server or of a dependency | 5xx | Retry 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]:
| Family | Meaning in the standard | What it tells the caller |
|---|---|---|
2xx | The request was received, understood and accepted | Move on |
3xx | Further action is needed to complete it | Go somewhere else or reuse what you have |
4xx | The request contains an error or cannot be fulfilled | Check the specific code and the contract; repeating the identical request rarely helps |
5xx | The server failed to handle an apparently valid request | Check 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 criterionThey 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):
- Is the request understood? (
400) - Do we know who is calling? (
401) - Does the caller have permission? (
403) - Does what it asks for exist? (
404,410) - Is this method allowed on this resource? (
405) - Is it in a format we accept? (
415) - Does the content, now understood, respect the business rules? (
422) - 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 parameterIf 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:
| Situation | Code | Why |
|---|---|---|
| The JSON is broken, a closing brace is missing or a parameter does not exist | 400 | The 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 country | 422 | The 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) | 409 | Conflict 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:
| Code | What it says | What the caller should do | The typical mistake |
|---|---|---|---|
429 | You are going too fast | Wait as long as Retry-After says and come back | Not sending Retry-After; the client insists |
500 | Something unexpected failed | Retry with caution if the operation is repeatable; raise an alert | Showing the stack trace, query or paths |
502 | The system behind responded with something invalid | Check whether the operation was applied before repeating | Blindly retrying a creation |
503 | I cannot handle this right now | Wait and retry | Returning 200 with "under maintenance" in the body |
504 | The system behind did not respond in time | Check the state before repeating | Mistaking 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]:
| Member | What it is for |
|---|---|
type | An 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] |
title | A short, human-readable summary of the problem type [2, §3.1.3] |
status | The HTTP code; it is informational, and the generator must use the same code in the actual response [2, §3.1.2] |
detail | An explanation of this specific occurrence; consumers should not parse it to extract information [2, §3.1.4] |
instance | An 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:
| Request | HTTP property of the method | Consequence of getting it wrong |
|---|---|---|
GET /contacts/{id} | Safe and idempotent | Extra load |
PUT /contacts/{id} with the full contact | Idempotent | Same intended effect |
DELETE /contacts/{id} | Idempotent | The second time it may respond 404/410, and that is correct |
POST /contacts (creation) | Non-idempotent by definition | Two 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:
| Response | Client action | Why |
|---|---|---|
200, 201, 204 | Continue | It went well |
400, 422 | Do not retry; correct or show | Repeating the same request gives the same result |
401 | Renew credentials once; if it comes back, stop | A renewal loop resolves nothing |
403 | Do not retry; escalate | A policy denies it |
404, 410 | Do not retry; treat "does not exist" as a result | It is information, not a failure |
409, 412 | Re-read the state, decide, resubmit | The state changed since it was read |
429 | Wait for Retry-After and come back | Respects the server's capacity |
502, 504 on a write | Check the state before repeating | It may have been applied |
500, 503 | Only 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 state | Probably a temporary failure, but a write may have been applied |
| Unrecognized code | Treat 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.
| # | Call | Response | Reading |
|---|---|---|---|
| 1 | GET /contacts?email=ana@example.com (exists) | 200, list with one contact | Query success |
| 2 | GET /contacts?email=nobody@example.com | 200, empty list | The search worked; there are no matches |
| 3 | GET /contacts/c-0001 (exists) | 200, the contact, with ETag | Success with a version |
| 4 | GET /contacts/c-9999 (never created) | 404, Problem Details | A result, not a failure |
| 5 | GET /contacts/c-0002 (merged into another) | 410, Problem Details with the replacement | Stop asking for it |
| 6 | GET /contacts/%%% | 400 | Unreadable request |
| 7 | POST /contacts with an email without @ | 422 | Data that does not meet the rule |
| 8 | POST /contacts with an email already registered | 409 | Clashes with what is stored |
| 9 | PUT /contacts/c-0001 with an outdated If-Match | 412 | Someone changed it first |
| 10 | A burst of searches exceeding the agreed limit | 429 with Retry-After | Rate exceeded |
| 11 | GET /contacts/c-0001 with the CRM down | 503 with Retry-After | Come back in a while |
| 12 | POST /contacts and the CRM does not answer in time | 504 | Check 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:
- Does any error go out with a
200? Ask for a sample of error responses and look at the first line. - 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? - Are
400,422and409told apart? If everything is400, your support teams pay for the difference. - Do the
429and503say how long to wait?Retry-Afteris optional; when it is missing, each client decides on its own, so it is worth documenting the policy. - 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. - Can creations be repeated without duplicating? If not, every
504is 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
- 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 asx00, heuristically cacheable codes; §15.3.1200; §15.5.1400; §15.5.2401; §15.5.4403; §15.5.5404; §15.5.6405; §15.5.10409; §15.5.11410; §15.5.13412; §15.5.21422; §15.6.1500; §15.6.3502; §15.6.4503; §15.6.5504. https://www.rfc-editor.org/rfc/rfc9110.html - IETF, RFC 9457, Problem Details for HTTP APIs (replaces RFC 7807). §3 and §3.1 members and media type
application/problem+json; §3.1.1type; §3.1.2status; §3.1.4detail; §3.2 extensions; §4.2.1about:blank; §5 security considerations. https://www.rfc-editor.org/rfc/rfc9457.html - 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 - 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/
- APIs ready for AI agents: what your system needs so an agent can use it with control →
- REST or gRPC: when to use each, and why your core speaks neither →
- Middleware and API platforms →
Does your operation face these challenges?
Prefer email? Write to us at hola@habil.mx