What an API Should Return If the Caller Is an Agent
A REST endpoint that answers a browser and a REST endpoint that answers a language model are doing two different jobs, and most teams only staff the first one. The browser call is a request for data. The agent call is a request for the same data plus a set of instructions about what to do with it, what it costs, how to retry, and where the caller should stop. Hand an agent the same JSON a dashboard consumes and it will do something reasonable most of the time, then do something expensive and wrong the rest of the time.
The question worth asking is not "what should the payload contain." It is "who is on the other end of the socket, and what does that caller actually need to avoid making a mess."
The caller that cannot see the page
A human using an API console has context the machine does not. She knows that a 404 from the customers endpoint usually means a typo in the ID. She knows that a 429 with a Retry-After header means back off. She reads the error message, applies ten years of accumulated intuition, and tries again differently.
An agent has none of that. It has the response body, the headers it was told to look at, and the tools in its context window. When a call fails, the model does not shrug and adjust. It either retries the identical call, because the status code did not tell it anything useful, or it invents a plausible next step, because nothing in the response told it what the correct next step was.
That gap is where most agent failures happen. The API did its job by HTTP standards. It returned a status code and a body. The caller could not interpret either one well enough to change behavior.
Status codes alone are a coin flip for a model
Ask an agent to book a meeting room and have the API return 409 Conflict with an empty body. The model has been trained on enough REST documentation to guess that 409 means a conflict, but it has no information about which conflict, whether the conflict is retryable, or whether the right move is to pick a different room, a different time, or to ask a human. So it tries again, changes the time at random, and burns three tool calls before returning confusion.
The status code is a category. The agent needs a verdict.
The 200 case has the same problem in reverse. A 200 with a body the model cannot parse into a decision is worse than a 400, because at least a 400 tells the caller something broke. A successful response that quietly omits the field the agent needed produces a confident, wrong answer downstream, and by then the call is buried under four more.
A response that names the next move
The single most useful thing an API can return to an agent is an explicit statement of what to do next. Not a suggestion. A directive field, structured, with a bounded set of values.
Something like a "next_action" key on every response, with values drawn from a small vocabulary: "retry_same", "retry_with_backoff", "do_not_retry", "escalate_to_human", "use_alternative_endpoint", "request_missing_field". The model does not have to infer intent from a status code and a prose message. It reads the field and acts.
This is not exotic. It is the same thing a good CLI does when it prints "run this command to fix it." The API already knows what went wrong. It should say what to do about it in a form the caller can branch on.
The cost of the call belongs in the response
An agent making decisions does not just need to know whether a call succeeded. It needs to know what the call cost, because cost is part of the decision. A search endpoint that returns 40,000 tokens of JSON is cheap for a human to skim and expensive for a model to hold in context. If the response does not tell the caller how big it is, the caller cannot budget.
Return a token estimate or a byte count. Return the number of records before the records themselves, so the agent can decide whether to paginate. Return a "truncated" flag when the payload was clipped, so the model does not assume it has the whole picture.
Most APIs already send Content-Length. That header was designed for transport, not for decision-making. A response that says "here are 12 of 4,300 results, and here is the cursor for the rest" is a response an agent can reason about. A 200 with a giant array and no pagination hint is a trap.
Idempotency is not optional when the caller forgets
Agents retry. They retry when a request times out, when a 5xx comes back, when the connection drops, and sometimes when they simply lose track of whether the last call landed. The API can be perfectly correct and still end up with five duplicate orders because the caller replayed the same request five times.
The response should make retry safety visible. If an endpoint accepts an idempotency key, the response should echo which key was used and whether this was the first execution or a replay. If the endpoint is not idempotent, the response should say so, ideally with a header like Idempotency-Status: not_supported, so the agent knows a retry is a new transaction.
This is the difference between an API that survives an agent and an API that produces a support ticket. The agent cannot be trusted to remember. The response is the only memory the two parties share.
Errors that explain the shape of the fix
When a required field is missing, the error string "field 'email' is required" helps a human. It helps an agent only if the agent knows the schema well enough to construct a valid value. Most of the time the agent has the value sitting in its context, and the error message did not tell it which field name to map to.
A well-formed error body for an agent caller looks like this: a machine-readable error code, a human-readable message, the exact field path that failed, the expected type or format, and a hint about where to find a valid example in the API documentation. Redundant, yes. But the redundancy is what lets the model correct itself on the first retry instead of the fourth.
The same principle covers validation errors in bulk. If an agent submits 50 records and three fail validation, the response should list all three with the same field path detail. Returning the first error and stopping forces a serial retry loop the agent will run until it hits a budget cap or a rate limit, and the human watching the logs will get to explain why the nightly job took nine hours.
The permission boundary should be legible in the payload
Agents often act with scoped credentials, sometimes a service account, sometimes a token provisioned for a specific workflow. When a call is rejected for permissions, the model needs to know whether the scope it lacks can be granted, whether the endpoint is entirely off-limits, or whether the resource is visible but not writable by this caller.
A bare 403 Forbidden is the worst possible answer. It tells the agent "no" without telling it which "no." The agent will try a different endpoint, then a different method, then a different payload, and eventually work around the permission wall in a way nobody intended.
A 403 with a body that names the missing scope and the resource type gives the agent a clean decision: request the scope, escalate to a human, or stop. The API has told it which wall it hit. The agent can now choose not to climb.
What to strip out for agent callers
Some of what a human-facing response carries is noise to a model, and noise is expensive. Marketing copy in the error message ("Oops! Something went wrong on our end") costs tokens and communicates nothing. Stack traces leak internals and confuse the model into thinking the failure is in its own code. Timestamps in three formats across the same body make the model guess which one is authoritative.
A response for agents should be dry. Short keys. Consistent types. One date format. No decorative fields. The model does not need a "message" field that says "success" when the status code already said 200. It needs the data, the next action, the cost, and the retry semantics, in a shape it can parse without ambiguity.
There is a version of this where the API returns both: a verbose human-facing body for the dashboard, and a terse agent-facing body for the model, selected by an Accept header or a query parameter. That is more work to maintain, but it keeps two very different callers from fighting over the same payload.
The test is whether the model gets it right on the first call
The practical way to know whether an API is ready for agent callers is to run a small experiment. Give a capable model the endpoint documentation, a goal, and no further coaching. Count how many tool calls it takes to complete the task, and how many of those calls were caused by a response the model misread.
Then read the responses the model got wrong and ask one question about each: did the response contain enough information for a careful reader to choose the correct next step? If the answer is no, the API has a documentation problem baked into its runtime behavior, and no amount of prompt engineering on the agent side will fix it. The response is the interface. If the response is ambiguous, the caller will be too.
A good agent-facing API is not a different product. It is the same product with the ambiguity squeezed out, because the caller on the other end has no way to ask a follow-up question. The response either carries the decision or it does not. The next_action field, the cost estimate, the idempotency echo, the structured error: each one is a small piece of the answer to a question the model would otherwise have to guess. Guess wrong once, and the agent does not just fail. It fails confidently, at scale, in production, on a Tuesday, with the retry loop already running.

