From Philosophy to Practice

In the first part of this series I argued that compression and composability aren’t opposites. They sit on a spectrum.

APIs were historically designed for humans: small operations that developers could compose into larger workflows. Agents operate differently. They’re goal-driven, token-constrained, and probabilistic. In many cases, it’s more efficient to expose a single outcome-oriented call and let the deterministic system handle the orchestration.

That raises the real design question: where should complexity live?

The first article focused on that idea. This one focuses on the practical side — when compression actually makes sense, how to avoid turning endpoints into rigid monoliths, how to handle edge cases, and how to evolve compressed workflows without breaking everything that depends on them.


A Checklist: When to Compress and When to Compose

The first step is recognising that not every chain of API calls is a candidate for compression. Here is a pragmatic checklist for evaluating whether a multi‑step workflow should be turned into a single, outcome‑driven endpoint:

  • Stable and well‑defined workflows. If the orchestration logic changes rarely (e.g. less than once a quarter) and the steps are well known, compression can pay off. Frequent changes or experimental flows favour composability so that clients can adapt without backend changes.
  • Repeated agent behaviour. When you observe agents consistently chaining the same three or more operations, consider compressing. If each agent call triggers five HTTP requests in the same order, the overhead in tokens, latency and state management accumulates. Compression removes that orchestration cost.
  • Determinism and governance matter. In regulated domains – payments, healthcare, compliance – probabilistic orchestration can be risky. A compressed, deterministic endpoint ensures that business rules are enforced centrally and that failures are handled consistently.
  • Partial failure creates messy state. If a five‑step flow can leave resources in an inconsistent state when step three fails, grouping the steps into a transactional call avoids partial success problems. As part 1 noted, deterministic execution reduces error surfaces and simplifies recovery.

And when not to compress:

  • Rapidly evolving workflows. If the sequence of operations is still being discovered – for example, in a new product or during active experimentation – a compressed endpoint can lock you into a rigid path. Composability allows agents and developers to explore new combinations without backend redeployments.
  • User‑driven branching. Some flows genuinely require human input mid‑stream (e.g. manual approval, custom split‑payment arrangements). Forcing them into a single call either removes necessary decisions or creates unwieldy APIs with dozens of optional parameters. Keep these flows granular.
  • Low‑volume usage. Building and maintaining a compressed endpoint is an investment. If only one agent makes ten calls a day, the token savings may not justify the engineering cost. Watch your traffic; compress the hotspots, not the outliers.
  • Diverse consumer needs. If different agents need different subsets of a workflow, a single compressed path might fit none of them. For example, one agent may update inventory while another only processes payments. In such cases, separate compressed intents or composable primitives are better than an overloaded all‑in‑one call.

This checklist encourages you to build composable first, observe how agents actually use your APIs, and then compress the chains they assemble repeatedly. Compression becomes an optimisation driven by data, not speculation.


Escape Hatches: Handling Partial Success and Edge Cases

One criticism of compression is that real‑world workflows don’t always proceed in straight lines. Orders sometimes require manual approval mid‑flow; a discount might be invalidated; a payment might split across methods. What happens when the compressed endpoint doesn’t quite fit?

The answer is not to abandon compression altogether, but to design escape hatches. A compressed endpoint should be able to return a structured partial result and guide the agent to the next composable action when it cannot complete the entire workflow.

We already have a model for this in REST: partial success handling in bulk operations. Bulk APIs that create or update multiple resources in one request often return a 207 Multi‑Status response when some items succeed and others fail. The response body includes individual status codes for each item so the client knows what to do next. Your compressed endpoint can use a similar pattern: return a result object that includes the completed steps, the pending steps and instructions for continuing.

For example, imagine a compressed checkout endpoint that processes four steps: create order, reserve items, apply discounts and charge payment. If the discount code is expired, the endpoint should not return a generic error. Instead it can respond:

{
   "status":"partial_success",
   "completed":[
      "order_created",
      "items_reserved"
   ],
   "pending":[
      "apply_discount",
      "charge_payment"
   ],
   "errors":[
      {
         "type":"discount_invalid",
         "message":"Discount code SPRINGSALE has expired",
         "suggestions":[
            "SUMMER10",
            "AUTUMN15"
         ]
      }
   ],
   "next_action":{
      "type":"call_primitive",
      "endpoint":"/orders/{id}/discount",
      "params":{
         "discount_code":"SUMMER10"
      }
   }
}

The agent receives explicit guidance: which operations succeeded, what went wrong and which granular endpoint to call next. In other words, compression does not have to be all‑or‑nothing. It can degrade gracefully into composability when required. Implementing these escape hatches requires careful response design, but it avoids leaving the agent blind when the happy path breaks.


MCP Is More Than Plumbing: Designing Tool Descriptions

In part 1 we wrote that MCP is a transport layer, not a design philosophy. But that does not mean it’s neutral. The way you describe tools in MCP – the natural language summary, the parameter descriptions, the sample calls – shapes how the agent reasons about them. In this sense, a tool description is itself a form of compression: it condenses the semantics of an endpoint into a few sentences the model can internalise.

Poorly described compressed endpoints can be worse than clear, composable ones. If the description merely parrots the endpoint name (e.g. “Checkout endpoint that processes an order”), the agent may misinterpret its capabilities, pass the wrong parameters or choose it for tasks it can’t handle. Conversely, a rich description that outlines the workflow, the constraints and the supported scenarios can make compression far more effective. For example:

checkout_order — Creates an order, reserves items, applies discounts and charges the customer. Accepts an array of items with quantities, an optional discount code and payment details. Returns a receipt on success. Fails if inventory is insufficient or payment is declined. Emits partial results with suggestions if discounts are invalid or items are out of stock. Use /orders/{id}/items and /orders/{id}/payment to handle pending steps.

This description tells the agent exactly what the compressed endpoint does, when it might return partial results, and which primitives to call if it cannot finish. Designing tool descriptions with this level of semantic richness is part of operationalising compression. It ensures that the transport layer and the content strategy align.

In some cases you may also combine compressed calls with read primitives or prompt templates. Before calling a compressed endpoint, the agent could read a resource (e.g. /inventory/{sku} ) to check stock levels, or use a custom prompt primitive to verify that the requested operation fits the scenario. These intermediate steps act as sanity checks without forcing the entire workflow to be decomposed.


Beyond Schemas: Intent Validation and Semantic Translation

Another challenge with compressed endpoints arises not from missing parameters but from semantic conflicts. In the first article we mentioned the "translation gap" – the idea that agents sometimes pass a string where an enum is expected or skip required fields. Those are structural problems and can be caught by schema validation. The harder problem is when the input is structurally valid but semantically incoherent.

Consider an agent that calls POST /checkout with a discount code tied to the purchaser’s loyalty account, but the order is a gift for someone else. The schema is correct, but the business rule forbids applying a loyalty discount to a non‑owner. If the backend silently fails or returns a generic error, the agent has no way to correct its plan. Compressed endpoints must therefore validate intent as well as structure and explain the conflict clearly.

This is where idempotency and robust error reporting come in. Idempotency keys, as explained in Zuplo’s guide, let a client retry an operation without causing duplicate side effects. Clients generate a unique key and include it in the request; the server stores the key and response, skipping duplicate processing if the key is reused. On the server side, idempotency means checking whether the same key has been processed and returning the stored result. This mechanism ensures that if the agent must modify its request after receiving a semantic error, it can do so safely without creating duplicate orders or charges.

But idempotency alone is not enough. Compressed endpoints need a richer validation layer that evaluates business rules before performing side effects and returns actionable errors. Instead of “invalid request,” the response should explain: “Loyalty discount cannot be applied to gift orders. Apply the discount to the purchaser’s own cart or proceed without it.” Including preconditions in the schema (e.g. "discount_code applies only when buyer_id equals loyalty_account.owner_id") helps agents pre‑check feasibility.


Versioning and Evolvability: Keeping Compression Agile

Compressed endpoints are essentially pre‑baked workflows. That raises a natural question: how do we evolve them as requirements change? Traditional API versioning is notoriously challenging. The Nordic APIs introduction notes that API versioning can have complex implications for downstream products and that new versions often represent significant milestones with potential reimplementation costs. There is no single best practice; different providers adopt different schemes. Compressing workflows makes versioning even more critical, because breaking changes to a compressed endpoint can disrupt many clients at once.

Here are some strategies to maintain agility:

  • Intent versioning. Instead of creating a new URL like /v2/checkout , allow the client to specify an intent_version in the request. The server can route to different orchestration logic based on this field. New capabilities can be added without changing the endpoint URI, and clients can opt in when ready. This is similar to GraphQL’s approach of clients declaring their desired shape.
  • Additive evolution. Whenever possible, evolve compressed endpoints in a way that does not break existing clients. Add new optional fields or behaviours rather than altering the meaning of existing ones. Removing fields or changing semantics should be rare and signalled via versioning.
  • Decomposition triggers. If a compressed endpoint grows to support many mutually exclusive branches or optional parameters, it may be over‑compressed. A practical heuristic is to split the endpoint when the input schema has more than a handful of conditional paths. For example, separate POST /checkout (consumer purchases) and POST /invoice-payment (business invoices) rather than one giant checkout that handles both B2C and B2B flows. Decomposing endpoints before they become unwieldy keeps each intent focused.
  • Document supported scenarios. Make the limitations of compressed endpoints explicit in their documentation and tool descriptions. If the endpoint only supports single‑currency payments or does not handle split shipments, say so. This transparency allows agents to decide whether to use the compressed path or fall back to primitives.

Versioning is not just a technical detail; it has business implications. Launching a new version of a compressed endpoint can be akin to launching a new product, with new SLAs and new clients to support. Being disciplined about additive changes and clear about deprecation schedules helps avoid fragmentation and ensures a smooth evolution.


Cost Discipline: Build, Measure, Compress

Finally, compression should be treated as an optimization, not a default. There is an engineering cost to building and maintaining deterministic orchestration. The returns on that investment come from reduced token usage, lower latency and improved reliability at scale.

If your API traffic shows that agents repeatedly chain the same operations, and the cost of those chains dominates your usage, compressing them makes sense. If you don’t yet have the data or if the workflow is rare, leave it composable. Build instrumentation to observe patterns and measure where agents spend tokens. Use that data to prioritise which workflows to compress. The goal is to compress the hotspots, not the entire surface.

Moreover, compression is not a one‑time decision. Monitor usage after releasing a compressed endpoint. If you see that agents frequently hit escape hatches or that a previously stable workflow is changing, re‑evaluate whether the compression still serves its purpose or if it needs to be refined.

In short: build composable primitives, measure real usage, then compress intentionally. This discipline prevents over‑engineering and ensures that your investment in deterministic orchestration produces tangible benefits.


Conclusion: Compression as a Tool, Not a Dogma

Operationalising compression is about choosing the right tools for the job. Composability remains indispensable for human‑driven and experimental workflows. Compression offers a powerful alternative when the consumer is an autonomous agent operating under token constraints, cost pressures and governance requirements. The key is to decide deliberately.

By applying the checklist above, designing escape hatches, writing rich tool descriptions, validating intent semantically, evolving endpoints thoughtfully and keeping an eye on cost, architects can reap the benefits of compression without sacrificing flexibility. The future of APIs is not a war between composability and compression but a tiered ecosystem of capabilities, where each layer serves its consumer best.

The agentic era demands that we rethink not just the shape of our endpoints but also the responsibility boundaries they imply. Compression is one powerful tool in that design kit. Use it wisely.