API strategy

Platform integration

API contracts between teams: the internal boundary agents will expose

API contracts between teams look fine until agents start calling them. What contract-first design misses, what contract testing catches, and what breaks next.

9 minute read
Decorative imagery showcasing Pontil's brand

When two teams inside the same company ship against each other's APIs, the contract between them is doing more work than anyone admits. It's the interface. It's the deployment coordination. It's the incident boundary when something breaks at 2am. And now it's the surface an agent will reach through when someone wires up a tool call.

So here's the question this piece answers: what does a good API contract between teams actually look like when the caller might be another team's service today and an agent tomorrow? Pontil's view in one sentence — internal contracts have always been undertested and under-owned, agents don't create that problem but they do make it impossible to ignore. The next 2,000 words walk through what a contract really is beyond the OpenAPI file, why internal contracts rot faster than external ones, what contract testing between microservices actually catches (and misses), how ownership breaks in practice, and what changes when agents start calling the same endpoints.

A contract is not the OpenAPI file

The first mistake teams make is treating the OpenAPI spec as the contract. It isn't. The spec is a partial, machine-readable description of the contract. The contract is the full set of expectations two teams hold about each other's behaviour — some written down, most not.

The written parts are the easy parts: endpoint paths, request shapes, response schemas, status codes. The unwritten parts are where production incidents live. What happens on a timeout — does the server complete the write and return 504, or roll back? What does the rate limiter do at the 429 boundary — hard cutoff, or soft with a Retry-After? Is the id field on the response the same id you passed in, or a new one the server minted? Does pagination stability hold if a record is deleted mid-cursor? None of these are in the spec. All of them are in the contract.

This matters because contract-first API design — the practice of writing the spec before the code — solves the wrong half of the problem. It aligns teams on shape. It does nothing to align them on behaviour. And behaviour is where the friction accumulates. Two teams can agree on POST /orders returning 201 Created with an Order object, and still disagree fundamentally on what the endpoint guarantees when called twice with the same idempotency key. Both teams will swear they're honouring the contract. Both will be right, by their own reading.

The implication for internal APIs: the contract lives partly in the spec, partly in shared test suites, partly in tribal knowledge, and partly in the on-call runbook. Any change to any of those changes the contract. Most teams only version the first one.

Why internal contracts rot faster than external ones

Public APIs get treated with more care than internal ones, and the reason is economic, not technical. A breaking change to a public API costs support tickets, integration partner escalations, and sometimes churn. A breaking change to an internal API costs a Slack message and a follow-up PR from the consuming team. The blast radius feels smaller.

It isn't. The blast radius is the same or worse — it's just borne by a team you have lunch with, so nobody counts it. Internal contracts rot in three specific ways.

Undocumented additions. A field gets added to a response because the consuming team asked for it in Slack. The spec never gets updated. Six months later, a third team starts consuming the same endpoint from the spec and can't figure out why they're missing data. The contract now has two versions — one in the file, one in reality.

Silent semantic drift. The endpoint behaviour changes but the shape doesn't. GET /users used to return active users only; now it returns everyone with an active boolean. Old consumers that assumed the filter now show deactivated users in their UI. The spec looks unchanged. This is the same failure mode we covered in OpenAPI spec drift: why your contract lies before your agents break — internal APIs get it worse because nobody's watching.

Behavioural coupling nobody agreed to. The producing team refactors an endpoint that was O(n) into one that's O(n log n). The consuming team's batch job that ran nightly now takes 40 minutes instead of 3 and misses its SLA. Nothing in any contract was violated. The consuming team had a performance expectation the producing team didn't know existed.

The common thread: internal contracts contain a lot of implicit terms, and the mechanisms for surfacing them are weak. External APIs have changelogs, versioning policies, and paying customers who file tickets. Internal APIs have a Slack channel and good intentions.

What contract testing between microservices actually catches

The standard answer to internal contract rot is API contract testing between microservices — consumer-driven contract testing, usually with Pact or a homegrown equivalent. It works, within limits, and it's worth being honest about both sides.

What it catches: shape violations. Producer removes a field, contract test fails in the producer's CI before the change ships. Producer changes a required field to optional, contract test fails. Producer changes snake_case to camelCase, contract test fails. This is the 80% of contract violations that cause 20% of the pain, and catching them at CI time is a real win.

What it doesn't catch: everything in the "unwritten contract" bucket above. Pact tests can assert response shape, status code, and header presence and values. They can't assert that the endpoint is idempotent, that pagination is stable, that the rate limiter behaves consistently, or that the timeout semantics match what the consumer assumes. Those live in integration tests, chaos tests, or production incidents.

Contract tests (Pact-style)
Integration tests
Production observability

Catches shape drift

Yes

Sometimes

After breakage

Catches semantic drift

No

Sometimes

Yes, usually via customer report

Catches performance regressions

No

Rarely

Yes

Runs pre-merge

Yes

Yes, if fast enough

No

Requires producer buy-in

Yes

Producer test env

No

False confidence risk

High

Medium

Low


The false confidence risk is the one to watch. Teams that adopt Pact often stop there and assume "we have contract testing" means "our contracts are safe." It means the shape is safe. The behaviour is not.

A reasonable stack: consumer-driven contract tests in CI for shape, a shared integration test suite for the behaviour both teams care about (idempotency, ordering, error handling), and production observability that alerts on latency and error-rate regressions before consumers notice. Three layers, each catching what the others miss.

Team API ownership: the invisible half of the contract

Contracts don't hold themselves. Someone has to own them, and internal API ownership is where most of this falls apart.

The common pattern in mid-sized SaaS companies: the team that first built the endpoint owns it. Fine, until that team is reorganised, split, or absorbed. Then the endpoint becomes orphaned — technically owned by a team on paper, actually understood by nobody currently there. Consuming teams file tickets that sit. Changes stop shipping. The endpoint becomes a fossil in the codebase that everyone works around.

The less common but healthier pattern: the API is owned by whichever team owns the underlying domain, and ownership travels with the domain. If billing gets reorganised, the billing API goes with it, and the reorg isn't done until the new team can answer questions about the endpoint. This requires two things most orgs don't have: a clear domain ownership map, and a rule that domain reorgs include their APIs.

Ownership shows up in the contract through five concrete responsibilities. If your producing team can't name who does each of these, the contract is under-owned:

  1. Who approves breaking changes and communicates the deprecation window
  2. Who runs the on-call rotation for the endpoint
  3. Who owns the shape (the spec) and the behaviour (the tests)
  4. Who consuming teams escalate to when something's wrong
  5. Who decides what makes it into the next version

It's fine for these to be the same person or split across a team. It's not fine for any of them to be unowned. When we've seen agent projects stall on internal APIs, it's often because the endpoint an agent needs is owned by a team that doesn't have capacity to make it agent-ready — and no forcing function to prioritise it.

What agents change about internal contracts

Here's where the argument gets specific to the moment we're in. Agents don't create new contract problems. They amplify existing ones, and they do it in three specific ways.

Volume. A human developer calls an internal API a few dozen times during integration and then their code calls it in production at some steady rate. An agent calls it during every reasoning loop, sometimes speculatively, sometimes to explore what's available. Volume goes up by one or two orders of magnitude. Any latency assumption in the contract that was fine at human-integration-volume gets exposed.

Interpretation ambiguity. A human developer reads the docs, reads the response, and infers what the endpoint does. When the inference is wrong, they file a ticket and get it clarified. An agent reads the tool description, calls the endpoint, gets a response, and moves on. It has no mechanism for "this looks wrong, let me ask." Any ambiguity in the contract — every unwritten term — becomes a source of silent wrong behaviour. This is the problem we cover in how to write tool descriptions for LLM agents: the description isn't documentation, it's part of the contract.

Identity. Internal APIs between teams usually authenticate as the calling service, not the end user. Team A's service calls Team B's endpoint using a service account. Fine, when the call chain terminates in a backend job. Not fine when the call chain starts with a specific human whose permissions matter, gets picked up by an agent, and reaches Team B's endpoint through Team A's service. The user identity gets lost at the internal boundary. The consuming team's endpoint now runs with more privilege than the originating user should have. Security review will find this. See agent identity vs user identity for the pattern that holds.

Each of these is fixable. Volume gets fixed by treating internal endpoints with the same rate-limit discipline you'd apply to a public API — see API rate limiting best practices for SaaS in the agent era. Ambiguity gets fixed by writing the unwritten terms down, ideally as executable tests. Identity gets fixed by propagating user context through the whole call chain, not terminating it at the service boundary.

What doesn't get fixed is the fundamental asymmetry: internal contracts were designed for a world where the caller is another team's code, written by a human who read the docs. Agents are a different caller with a different failure mode. The contracts either get tightened, or the failures show up in production.

Where does this leave a team trying to do it right?

The honest answer is that most internal API contracts are one abstraction level less rigorous than they need to be, and the gap was tolerable until it wasn't. The forcing function is either an agent project, a security review, or a bad incident. Waiting for the third is the most expensive option.

The practical starting point isn't a new tool. It's a conversation between two teams about a single high-traffic endpoint, working through the five ownership questions and the unwritten terms. What does timeout mean here? What does idempotency guarantee? Who approves the next breaking change? The endpoints where both teams can answer those quickly are the ones ready for agents. The endpoints where the answers take a week are the ones an agent project will stall on.

The deeper shift is that internal contracts are becoming as consequential as external ones, and the operating model hasn't caught up. Contract testing between microservices helps. Ownership discipline helps more. Neither is a substitute for two teams knowing what they actually promised each other — and writing enough of it down that the next caller, human or otherwise, can read it and act on it correctly.

Join our weekly newsletter

Stay up to date on the ever changing agentic landscape.

POSTS

Related content

API strategy

Agent infrastructure

OpenAPI spec drift: why your contract lies before your agents break

9 minute read

Agents in production

API strategy

Agent projects stall at the same point. Here's why

5 minute read

API strategy

Platform integration

API discoverability for AI agents: why your docs aren't the interface anymore

5 minute read