Requirement contracts for agent API calls turn a buying instruction into a stable set of machine-readable limits. The same contract should reach discovery, comparison, and decision so each operation answers the question the buyer originally asked.
Without that artifact, an agent can reinterpret the request at every step. It discovers providers using a task description, compares them with a newly inferred budget, and chooses one under a different latency preference. The final provider may look reasonable, but the route cannot be reproduced because there was never one durable statement of the requirements.
A contract is the input to selection
An endpoint input schema and a requirement contract do different jobs. The schema says which fields a provider accepts for the work itself. The requirement contract says which providers may receive that work and how the eligible set should be ordered.
The contract is therefore an input to the selection process, not an informal note beside it. In Apiosk, the shared requirement parameters are:
- `max_price` for the maximum payment amount the call may accept.
- `max_latency_ms` for the measured latency ceiling.
- `min_reliability` for the measured success-rate floor.
- `settlement=apiosk|direct` for the required settlement path.
- `require_all_inputs` for whether every supplied input must be supported.
- `optimize_for=price|latency|reliability|balanced` for ranking the eligible candidates.
The first five fields constrain the acceptable set. The last field orders the candidates that survive. Keeping that distinction visible prevents a preference from becoming a hidden rejection rule. The operational mechanics of removing candidates belong to constraint-based API filtering; the contract is the stable payload that tells every filter which limits to apply.
Build it before discovery
The contract should be assembled before the first provider is returned. That timing matters. A requirement selected after seeing the candidates can encode a preference for a result that is already known.
Start with the buyer's instruction and normalize each supported condition into one field. Do not fill gaps with convenient defaults unless those defaults are part of an explicit policy. A request with no latency ceiling is different from a request with a guessed ceiling. Both can be processed, but they should not produce the same audit record.
The completed contract needs a representation your system can retain unchanged. A local routing envelope might store the exact parameter values, the job identifier, the time it was created, and the source of each value. Those envelope fields are implementation choices, not Apiosk response fields. Their purpose is to preserve provenance: which limit came from the buyer, which came from account policy, and which was absent.
Once built, freeze the contract for that selection run. If the buyer changes the budget, create a revised contract. Do not edit the old object in place.
Pass the values, not the prose
Natural-language instructions are useful for discovering the job. They are a poor transport format for requirements that must survive several calls. Each fresh interpretation creates another chance for units, comparison operators, or defaults to change.
Pass the normalized values once and reuse them down the chain:
- `GET /v1/discover?q=…` finds candidates for the job under the shared requirements and returns stable `candidate_id` values.
- `GET /v1/compare?candidates={id},{id}` compares those candidates under the same limits and ranking preference.
- `GET /v1/decide?candidates={id},{id}` chooses from that set, names the rule, and returns the exact constraint for every rejection.
Over MCP, the equivalent sequence is `apiosk_discover`, `apiosk_compare`, and `apiosk_decide`. The transport changes, but the contract should not. A REST discovery followed by an MCP decision still needs the same values if it is meant to be one selection run.
This is also why stable candidate identifiers matter. The contract identifies the buying question; the candidate IDs identify the offers being evaluated. Replacing either without recording a new run breaks the chain of evidence.
Detect requirement drift as an error
Requirement drift occurs when two steps that claim to belong to one selection use different contract values. Some drift is obvious, such as raising `max_price`. Other cases are quieter: dropping `require_all_inputs`, switching settlement, or changing `optimize_for` after comparison.
Compare the contract presented at each boundary with the frozen original. A canonical serialization or local fingerprint can make that check mechanical. The specific storage design is yours. What matters is that these cases do not pass silently:
- A field changes value between discovery and comparison.
- A field is present at discovery but absent at decision.
- A default is applied in one client but not another.
- Units are converted without retaining the original value and rule.
- The candidate set changes while the run keeps the same identity.
When a mismatch occurs, either stop or start a new selection run. Continuing and calling the outcome reproducible is worse than returning no decision, because it attaches a clean explanation to a changed question.
Revise one requirement explicitly
An empty candidate set does not make the contract wrong. It says no reviewed candidate met that version of the buying requirements. The buyer can stop, inspect external options separately, or authorize a revision.
A revision should name exactly what changed. Raising `max_price` creates a new acceptable set. Relaxing `max_latency_ms` does the same. Switching `optimize_for` may keep the same eligible set but change its order. Each revision deserves its own record because each answers a different question.
Do not use missing measurements as an automatic relaxation. Apiosk drops an unmeasured dimension from relative scoring and names it rather than assigning zero. That scoring rule does not prove that a candidate satisfies a hard measurement floor. Price, settlement, and input compatibility can be checked from the offer and request; latency and success rate require enough proxied calls. Result quality and provider terms are not measured anywhere.
This boundary makes the eventual comparison of paid APIs inspectable instead of pretending every candidate has the same evidence.
Keep the contract with the decision record
A selected provider alone is not enough for an audit. Retain the requirement contract with the winner, the rule that picked it, the rejected candidates and their failed constraints, and the ordered runners-up.
That bundle answers separate operational questions. The contract shows what was requested. The rejection list shows how hard limits were enforced. The runners-up show which alternatives remained eligible. The weights and per-dimension contributions show how the relative 0–100 comparison score was produced.
`POST /v1/decisions` records which provider was chosen and which alternatives were rejected. It is free and unauthenticated. Your own record can associate that event with the frozen contract and its provenance. For the broader evidence chain, see the audit trail for agent API selection.
External x402 results must remain outside this bundle. Discovery returns them in `external_candidates` without a `candidate_id`. They are unreviewed, unmeasured, and not settleable through Apiosk, so they cannot continue into comparison or decision.
Where Apiosk fits
Apiosk accepts the same shared requirement parameters across discovery, comparison, and decision. It returns stable candidate IDs, checkable relative scores, named missing dimensions, exact rejection constraints, and ordered runners-up. That gives your system the pieces needed to prove that one contract governed the whole selection.
Apiosk does not create your buyer policy or infer requirements you did not state. You decide where each value came from and when a revision is authorized. The Apiosk documentation provides the live integration surface; your routing layer keeps the requirement contract immutable until the run ends.
Frequently asked questions
What belongs in a requirement contract for an agent API call?
The contract should contain the hard limits and ranking preference that define an acceptable provider. In Apiosk, those shared values cover maximum price, maximum latency, minimum reliability, settlement, input compatibility, and the optimization rule.
Why should the requirement contract stay unchanged across selection steps?
Reusing the same values ensures discovery, comparison, and decision answer the same buying question. If a value changes, the system should treat that as a new contract rather than silently continuing the original selection.
Is a requirement contract the same as an API input schema?
No. An input schema describes the data an endpoint accepts, while a requirement contract describes which provider is eligible and how eligible providers are ranked. Input compatibility can be one condition inside the requirement contract.
How should an agent relax a requirement when no provider qualifies?
It should create a revised contract with the changed value and retain the earlier version. The revision should name the relaxed field so an operator can see why the new candidate set differs.