---
title: "Contracts: one API, four audiences"
description: "Partners, internal teams, the public, and agents all need different views of the same endpoints. Contracts make that explicit."
canonical: "https://getelva.ai/blog/contracts-one-api-four-audiences"
author: "Elva Team"
published: "2026-06-18"
lastModified: "2026-08-27"
tags: "Product, Contracts, Governance"
---

# Contracts: one API, four audiences

**TL;DR**

- One API serves at least four audiences: partners, internal teams, the public, and agents, and each holds different promises.
- A contract writes one audience's promises down as a machine-checked view: endpoints, fields, auth, and change policy, diffed on every commit.
- Start with one consumer in warn mode for two weeks, then enforce; median time to a first enforced contract is eight days.

A contract in Elva is a named, versioned view of your API for a specific consumer: which endpoints they see, which fields exist for them, how they authenticate, and what happens when something they depend on changes. One API almost always has at least four consumers — partners, internal teams, the public, and now agents — and pretending they can all live off one undifferentiated spec is how breaking changes ship by accident. Contracts write each audience's promises down separately and make every one of them machine-checkable.

## Four consumers, four sets of promises

Run through who actually calls a payments API on a normal Tuesday:

- **Partners** integrate slowly and update rarely. They need stability guarantees, long deprecation windows, and a changelog someone maintains.
- **Internal teams** move fast and read source. They tolerate churn but need to know which fields are safe to build on and which are experiments.
- **The public** sees a curated subset. A field like `internalRiskScore` should not merely be undocumented for them; in their view it should not exist.
- **Agents** read schemas literally and act immediately. They need typed outputs, explicit auth, and error shapes they can branch on — and they punish ambiguity with retries.

The same `GET /payouts/{id}` serves all four. The endpoint is shared; the promises are not. A contract is where each promise is recorded against the same underlying catalog entry.

## A contract is a filtered view with teeth

Mechanically, a contract is a subset of the catalog — endpoints in or out, fields visible or hidden, auth scheme pinned, rate limits declared — expressed as an overlay on the service's [OpenAPI 3.1](https://spec.openapis.org/oas/latest.html) document. "With teeth" is the important part: the contract is not documentation about the filtered view, it is the thing CI checks against. When a commit changes the API's real shape, Elva diffs that shape against every contract touching the affected endpoints. A field removal that only ever existed in the internal view breaks one contract, not four, and the partner contract does not even get a notification, because for partners nothing changed. Scoping blast radius this way is most of the value: in a typical month, fewer than a third of shape changes touch any external contract at all.

## Versioning without version sprawl

The alternative teams usually try first is versioning the whole API: v1 for partners, v2 for internal, v3 for the experiment. It fails the same way every time — versions multiply, each fork drifts, and three years later four incompatible copies of the same endpoint each have their own bugs. Contracts invert this. There is one implementation and one catalog entry per endpoint; what varies per audience is visibility and guarantees, not code. When an endpoint genuinely must change shape, the contract records which consumers still hold the old promise, which turns "can we delete v1 yet" into an answerable query instead of a standing argument. One team we onboarded retired 31 zombie endpoints in their first quarter because, for the first time, they could prove no active contract referenced them.

## Breaking changes become decisions

When a diff does hit a contract, the change stops being a merge and becomes a decision with named owners. The flow is deliberately boring: the commit triggers a contract check, the check classifies the change against each affected contract — breaking, compatible, cosmetic — stakeholders on those contracts are notified, and a breaking change blocks publish until someone with authority approves it. On the teams we host, median approval takes under a day; the point was never speed, it was that "approved by whom" has an answer in a log. Deprecations follow the same path with a timer attached: the field stays in the contract, marked deprecated with a sunset date, and consumers get the window the contract promised. Ninety days is the default partners get most often.

## The audit question

Six months in, contracts quietly become the answer to questions nobody built them for. Security review asks who could see `accountBalance` in March; the contract history answers it, per audience, with dates. A partner disputes that a field was removed without notice; the deprecation record shows the sunset date, the notification, and who acknowledged it. An incident review asks why an internal consumer was reading an experimental field; the contract shows it was never in their stable set. None of this required extra bookkeeping, because the enforcement path and the paper trail are the same system. The alternative — reconstructing history from Git archaeology and Slack scrollback — is how these questions usually get answered today, at ten times the cost and half the confidence.

## Agents read the fine print

Agents turned out to be the strictest audience, and the best argument for contracts. A human partner who hits an undocumented quirk sends a support email; an agent that hits one acts on a wrong assumption at machine speed. So the agent contract is where teams get precise. Every tool exposed over [MCP](https://modelcontextprotocol.io/specification) is generated from a contract view, which means an agent cannot see an endpoint that was not deliberately granted, and every schema it receives is one the contract check gates. Field descriptions stop being optional prose and become behavior — they are what the model reads when deciding how to call. Teams that write them for agents report the same side effect every time: the docs got better for humans too, because something was finally reading them.

## Rollout: contracts in an afternoon

Adoption fails when it starts with a governance summit, so the on-ramp is deliberately small. Pick the consumer that scares you most — usually one partner — and snapshot their current view of the API as contract v1: the endpoints they call, the fields they receive, the auth they use, exactly as production behaves today. The snapshot takes minutes because it is derived from the catalog, not authored. Run the contract check in warn mode for two weeks so the team sees what would have been flagged; the log is usually persuasive on its own. Then turn enforcement on for that single contract and let the others follow as they earn attention. Median time from first snapshot to first enforced contract on the teams we host: eight days, most of it social rather than technical.

## Four defaults worth stealing

If you adopt one idea from this post, adopt explicit defaults per audience. The ones our users converge on: partners get additive-only changes and a 90-day sunset on anything removed; internal consumers get a seven-day notice and no field-level guarantees outside declared stable sets; the public view is allowlist-only, with nothing visible by default; agents get typed outputs, mandatory descriptions, and no tool without an owner. None of this requires our product — a wiki page and discipline get you halfway. What tooling adds is enforcement: the wiki page cannot block a publish at 6pm on a Friday, and the contract check does not care that everyone was in a hurry.

## FAQ

**What is an API contract?**

A named, versioned view of your API for one consumer: which endpoints they see, which fields exist for them, how they authenticate, and what happens when something they depend on changes.

**Which audiences does one API typically serve?**

At least four: partners who need stability, internal teams who tolerate churn, the public who see a curated subset, and agents who read schemas literally and act immediately.

**How do you adopt contracts without a governance project?**

Snapshot one consumer's current view as contract v1, run the check in warn mode for two weeks, then enforce that single contract. Median time to first enforced contract is eight days.
