The Composability Gap: When Valid APIs Fail Real Workflows
We tend to treat a documented API as a usable API. If the endpoints are described, the parameters are typed, and the responses are listed, the hard part should be done. That assumption mostly holds when a human integrator can read between the lines, patch over inconsistencies, and learn the quirks of each service over time. It becomes much weaker when the caller is an agent trying to complete a goal across multiple systems without that human safety net.
Imagine a simple workflow: fetch an order from an order management service, extract the customer/payment account reference, call a billing service to validate payment, then continue the process. On paper, nothing looks obviously broken. Each API can be valid in isolation. The order service returns a reference. The billing service accepts a reference. Both contracts may be correctly described at the endpoint level. And yet the workflow can still fail immediately: the reference emitted by one service does not match the naming, length, or regex expectations of the other, and the billing system responds with a vague HTTP 500 that hides the real cause. For a human, this is frustrating. For an agent, it is often unrecoverable.
That is the composability gap. OpenAPI can help validate whether a single endpoint is well specified, but it cannot by itself prove that a real workflow will hold together across service boundaries. Composability depends on shared design intent: what a cross-service handoff means, what format it must follow, what the next valid step is, and how failures should be communicated so the caller can adapt. When that intent is missing or only implicit, individually valid APIs can still fail real work. This article explores that gap through a concrete example and argues that API teams should audit end-to-end workflows, not just endpoints.
A simple workflow that should work
Consider a very ordinary workflow. An application needs to validate the payment status of an order before moving it forward. To do that, it first calls the order management service, fetches the order, and extracts the customer/payment account reference attached to it. It then calls the billing service with that reference to confirm whether payment is valid, pending, or blocked. If the answer is positive, the workflow continues. If not, the process stops or takes a different path.
Nothing about this looks unusual. There is no complex orchestration, no long chain of dependencies, and no exotic business rule. It is the kind of cross-service interaction that exists in almost every non-trivial system. That is precisely why it is useful as an example: if even this kind of workflow can fail despite well-described APIs, then the problem is deeper than a missing endpoint or a bad implementation detail.
At first glance, both APIs can look perfectly healthy. The order management API returns a structured order document. The billing API exposes a clear validation endpoint. Each team may even have done good work on its own contract. If you inspect them one at a time, there may be little to criticize. But workflows are not executed one endpoint at a time. They are executed through the handoff between endpoints, and that is where hidden incompatibilities start to matter.
Where the workflow breaks: identifier incompatibility
The break happens at the handoff object itself: the customer/payment account reference.
The order service returns a reference that is valid in its own context. Maybe it uses a string with a business prefix, or a value whose length reflects legacy constraints, or a pattern designed for readability in one domain. The billing service, meanwhile, expects a different format. Its validation endpoint may accept only identifiers that match another naming convention, length limit, or regex pattern. From the perspective of each individual service, those choices can be reasonable. From the perspective of the workflow, they are incompatible.
This is the important point: neither service has to be obviously wrong in isolation. The order API can truthfully say, “this is the customer/payment account reference associated with the order.” The billing API can truthfully say, “this is the format of the reference I accept.” The failure lives in the space between them. The API landscape contains all the required operations, but it does not contain a reliable path from one operation to the next.
That is a composability problem, not just a contract problem. The question is no longer whether each endpoint is valid. The question is whether a caller can navigate from one resource to the next without guessing, transforming, or reverse-engineering assumptions that were never made explicit.
Why the agent cannot recover: vague error semantics
Once the billing call fails, the next issue appears: the response does not help the caller recover. Instead of returning a precise explanation that the provided reference does not satisfy the expected format, the service responds with a vague HTTP 500 and an error body that says little more than that something went wrong.
A human developer can sometimes work around that. They can inspect logs, compare payloads, contact another team, or slowly build the missing mental model of how the two services are supposed to fit together. But an agent does not have that safety net. It has to decide what the error means and what to do next based on what the API actually communicates.
Should it retry? Should it transform the identifier? Should it stop the workflow? Should it escalate to a human? A vague 500 does not answer any of those questions. Worse, it hides the root cause. What looks like an internal server failure may actually be a predictable cross-service incompatibility.
That is what makes the workflow unrecoverable for an agent. The problem is not only that the handoff object is incompatible. It is also that the failure semantics do not expose that incompatibility in a machine-actionable way. The workflow breaks twice: first at the data boundary, then again at the error boundary.
The composability gap
This is the larger lesson. API quality has usually been discussed at the level of individual contracts: are the operations documented, are the payloads typed, are the response codes listed, are the schemas stable? Those are real concerns, and specs like OpenAPI have raised the floor significantly. But they focus on the behavior of a single interface at a time.
Agents expose a higher-order requirement. They do not just call endpoints; they pursue goals. To do that, they need to move from one operation to the next with as little implicit knowledge as possible. They need to know that an identifier returned here is acceptable there. They need to know which next step is valid in a given state. They need error semantics that help them adapt instead of dead-end. In other words, they need composability.
Composability is the capacity of an API landscape to support navigation across calls in a reliable and deliberate way. It is not only about whether APIs exist. It is about whether they fit together. A landscape can be rich in endpoints and still poor in workflows. It can be fully documented and still force callers to guess. That is why “valid API” and “usable workflow” are not the same claim.
This matters even if you are not building autonomous agents today. Agentic use cases simply make the weakness more visible. The same hidden assumptions that frustrate agents also slow down internal teams, increase integration cost, and create fragile delivery paths. Agents are not inventing the problem. They are revealing it.
What to audit in your API landscape
If this example feels familiar, the useful response is not to dismiss it as an edge case. It is to audit for the pattern.
Start with real workflows, not endpoint inventories. Pick a business task that crosses service boundaries and trace it step by step. Where does one system hand off meaning, identifiers, or state to another? Are those expectations explicit? Are they consistent? Could a caller follow that path without tribal knowledge?
Then inspect the handoff objects themselves. Do related services agree on identifier shape, allowed values, naming conventions, and semantic meaning? If they do not, is the transformation rule explicit and documented, or merely assumed?
Next, inspect error behavior. When a workflow fails, does the API communicate a machine-actionable reason? Can a caller tell whether it should retry, stop, correct the request, or choose a different next step? Or does everything collapse into generic failures that hide predictable design mismatches?
Finally, ask a harder question: where does your workflow intent actually live today? If the answer is “in code, in people’s heads, in tickets, and in Slack threads,” then your API landscape may be documented without being truly composable.
Conclusion
The point is not that OpenAPI is insufficient in its own domain. It is excellent at what it does: describing and validating individual API contracts. The point is that real work happens across boundaries. Once the caller is trying to achieve a goal rather than invoke a single method, another kind of quality becomes visible. The order service and billing service in this example are not necessarily broken on their own. The workflow is broken because the design intent between them was never made explicit enough to validate or recover.
That is the composability gap: the distance between having valid endpoints and having a usable workflow.
If you expect agents to operate across your systems, audit your APIs accordingly. Do not stop at endpoint coverage. Audit handoffs, identifier compatibility, valid next steps, and error semantics across real business flows.
In the next article, I will look at Arazzo as a candidate specification layer for that missing piece: a way to describe and validate workflow intent across APIs, not just the behavior of each operation in isolation.