---
id: 7
title: "The cross-cutting contract of an API surface"
version: "1.0.0"
status: draft
date: 2026-08-30
authors:
  - "Theo Zourzouvillys"
tags: [api, architecture, design, http, interop, reliability]
summary: "The contract every operation on an API surface shares: operation taxonomy, request envelope, idempotent replay, state tokens, delegation chains, quota in two currencies, regional pinning, the request archive, and schema evolution — specified as day-one decisions."
use_when: "You are designing a new API surface, or retrofitting a cross-cutting concern — idempotency, quota, regions, delegation, versioning — onto one that already has callers you cannot break."
not_for:
  - "The design of any individual endpoint's payload or domain model, which this deliberately says nothing about."
  - "Outbound delivery — webhooks, push notifications, callbacks — which is a separate subsystem with its own guarantees."
  - "An internal service-to-service RPC surface with a single first-party caller, where most of this is overhead."
requires: [1, 2, 3]
provides:
  - "api-surface-contract"
  - "idempotent-mutations"
  - "client-state-token"
  - "work-based-quota"
  - "request-archive"
implements_notes: [12, 13, 14, 18, 19, 21, 25, 30, 51, 53, 56, 57, 58, 61, 62, 65, 67]
supersedes: null
superseded_by: null
aliases: []
crossrefs:
  ZFN-67: "The argument this document specifies. It makes the case that these concerns share a cost curve and must be decided together on day one; this document is what to build."
  ZFN-51: "Where the envelope concept comes from — context about a request rather than in it, given a home in the wire format before the first endpoint exists."
  ZFN-14: "Why the contract is a schema that generates clients rather than a document humans implement, which is what makes every requirement here reachable by a caller."
  ZFN-25: "The reasoning behind handing the client a token describing the freshness of its own view, rather than routing it to a primary."
  ZFN-58: "Why the error surface is designed and enumerated rather than accreted, and why retry correctness depends on it."
  ZFN-61: "The deadline as a budget that every hop inherits, and why a cancelled call has an unknown rather than a failed outcome."
  ZFN-19: "The case for marking read-only and idempotent operations in the contract so infrastructure can act on the classification."
  ZFN-18: "Why quota enforcement belongs at the edge rather than inside each service."
  ZFN-13: "Shedding and flow control, which is what a quota system degrades into when the limits are wrong or absent."
  ZFN-53: "The reasoning behind pricing work rather than requests: making the expensive path expensive for the caller who chose it."
  ZFN-21: "The discipline that keeps a cache from becoming an undurable database, which is why cacheability here is declared but never depended on."
  ZFN-56: "The argument for identifiers that carry their type and reveal nothing, and why the format can never be changed afterwards."
  ZFN-57: "Why deletion is a designed capability, which is the obligation the request archive creates once it holds request bodies."
  ZFN-62: "The three-phase mechanism every schema change here follows, and the reason a shape change is never one deploy."
  ZFN-65: "Why the archive is written as batched appends to a journal rather than a row per request."
  ZFN-12: "The distinction between a queue, a topic and a journal, which decides what the archive and the event surface each are."
  ZFN-30: "Why this document adopts existing header fields and token formats rather than minting private ones."
  ZBP-1: "The proof-of-possession mechanism this document assumes for every credential it discusses, rather than restating it."
  ZBP-2: "The workload identity and token exchange machinery an internal hop uses to add its own assertion without discarding the caller's."
  ZBP-3: "The tenant isolation this document assumes underneath it. The tenant named on a request here is an input to be verified against that context, never a value that establishes it."
  ZBP-4: "Where the credential presented to this surface comes from. Obtaining one is out of scope here; this document begins once it is presented."
references:
  - id: rfc8174
    title: "RFC 8174 — Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words"
    url: https://www.rfc-editor.org/rfc/rfc8174.html
    abstract: "Clarifies that the RFC 2119 requirement keywords carry their special meaning only when capitalised, so that lowercase prose can use the same English words without being read as normative."
  - id: rfc9110
    title: "RFC 9110 — HTTP Semantics"
    url: https://www.rfc-editor.org/rfc/rfc9110.html
    abstract: "The core HTTP semantics specification, including the definitions of safe and idempotent methods, conditional requests, and the response status classes this document builds its operation taxonomy on."
  - id: dpop
    title: "RFC 9449 — OAuth 2.0 Demonstrating Proof of Possession (DPoP)"
    url: https://www.rfc-editor.org/rfc/rfc9449.html
    abstract: "Defines a mechanism to bind an access token to a client-held key: the client sends a signed DPoP proof header on each request, so a stolen token cannot be replayed by a party that does not hold the corresponding private key."
  - id: rfc8693
    title: "RFC 8693 — OAuth 2.0 Token Exchange"
    url: https://www.rfc-editor.org/rfc/rfc8693.html
    abstract: "Defines exchanging one security token for another, and separates delegation from impersonation: an `act` claim naming the acting party in a chain, and a `may_act` claim stating who is permitted to act for whom."
  - id: rfc9457
    title: "RFC 9457 — Problem Details for HTTP APIs"
    url: https://www.rfc-editor.org/rfc/rfc9457.html
    abstract: "The standard shape for machine-readable HTTP error responses: a `type` URI identifying the error class, `title`, `status`, `detail`, `instance`, and extension members. Obsoletes RFC 7807."
  - id: idempotency
    title: "The Idempotency-Key HTTP Header Field (IETF draft)"
    url: https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/
    abstract: "Specifies an `Idempotency-Key` request header letting a client mark a mutating request with a unique key so servers can deduplicate retried requests, together with the expected server behaviour for concurrent, replayed, and mismatched-payload cases."
  - id: ratelimit
    title: "RateLimit header fields for HTTP (IETF draft)"
    url: https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/
    abstract: "Defines the `RateLimit` and `RateLimit-Policy` response header fields, expressing a server's quota policies and the caller's remaining quota as structured fields — including a quota-unit parameter, so a policy can be denominated in something other than requests."
  - id: servertiming
    title: "Server Timing (W3C)"
    url: https://www.w3.org/TR/server-timing/
    abstract: "Defines the `Server-Timing` response header field, letting a server report named metrics and durations for the work it did serving a request, and exposes them to the client through the browser performance APIs."
  - id: deprecation
    title: "RFC 9745 — The Deprecation HTTP Response Header Field"
    url: https://www.rfc-editor.org/rfc/rfc9745.html
    abstract: "Defines a `Deprecation` response header carrying the date a resource became, or becomes, deprecated. Designed to be used alongside the `Sunset` header field."
  - id: sunset
    title: "RFC 8594 — The Sunset HTTP Header Field"
    url: https://www.rfc-editor.org/rfc/rfc8594.html
    abstract: "Defines a `Sunset` response header field carrying the point in time at which a resource is expected to become unresponsive, so a client can detect a planned removal without out-of-band communication."
  - id: aip151
    title: "AIP-151 — Long-running operations (Google API Improvement Proposals)"
    url: https://google.aip.dev/151
    abstract: "The design pattern for calls that cannot complete within one request: the method returns an operation resource immediately, which the client polls or waits on, and which carries the eventual response or error as part of its own state."
---

## TL;DR

This specifies the part of an API that is identical across every operation on it: how an operation is
classified, what rides alongside the payload, what a retry means, how a caller learns the freshness of
its own view, who is really making the call, what the call cost, which region answered it, what was
recorded about it, and how any of it is allowed to change. It defines an **operation taxonomy** of
four kinds; a **request envelope** carried in the transport's metadata layer; **idempotency keys that
replay a stored response** rather than merely suppressing a second execution; an opaque **state token**
that makes read-your-writes a property of the protocol; a **delegation chain** rather than a single
principal; **quota denominated in both requests and work**, admitted against an estimate and reconciled
against a measurement; **regional endpoints** with failover as an explicitly new session; a **request
archive** written off the critical path; and a **schema** that is introspectable, per-caller filtered,
customer-extensible, and evolved by expand/migrate/contract.

Each of these is between free and a few days' work before the surface has callers, and a coordinated
migration across every client afterwards. That asymmetry — not any individual merit — is why they are
specified together and as day-one decisions. The rationale is argued in
[ZFN-67](/zfn/67-the-api-id-build-today/); this document is what to build.

## Applicability

**Use this when** you are designing a new API surface intended for callers you do not control, or when
you are retrofitting one of these concerns onto a surface that already has callers and cannot break
them. It applies whether the transport is HTTP, gRPC, or a message protocol; the requirements are
written against a generic envelope and mapped to concrete transports in *Wire formats and interfaces*.

**Do not use this when** the surface has exactly one first-party caller, deployed in lockstep with the
server, with no external integrations and no independent retry behaviour. Most of this document exists
because the caller is separately deployed, independently written, and cannot be fixed by you — and
where that is not true, the machinery is overhead. Do not use it as guidance for designing an
individual endpoint's domain model, which it deliberately does not address.

This document assumes three others rather than restating them: [ZBP-1](/zbp/1-dpop-bound-requests/)
for proof-of-possession on credentials, [ZBP-2](/zbp/2-workload-security-token-service/) for the
workload identity an internal hop asserts with, and [ZBP-3](/zbp/3-tenant-isolation/) for the tenant
context every requirement here operates inside.

## Scope and non-goals

**In scope.** The operation taxonomy and its lifecycle; the envelope and its per-hop versus propagated
split; idempotency and retry semantics; the error and retry-control contract; consistency tokens and
pagination; the identity, delegation and authorization surface of a request; quota and cost accounting;
cache declaration; regional partitioning and failover; degradation controls; the request archive and
its relationship to the audit log; schema publication, per-tenant extension, versioning and client
generation.

**Out of scope, deliberately:**

- **Domain modelling.** What an operation *does*, what its payload contains, and how its resources
  relate is the province of the team that owns it.
- **Outbound delivery.** Webhooks, push notifications, and callbacks are a separate subsystem with
  their own delivery guarantees, retry schedules, dead-lettering, signing, and subscription lifecycle.
  They are not the request surface with its arrow reversed, and the only requirement this document
  makes of them is a negative one: the request surface's availability must not depend on theirs
  ([ZFN-12](/zfn/12-queues-topics-journals/)).
- **Authentication ceremony.** How a human or workload obtains a credential in the first place is
  ZBP-2 and ZBP-4. This document begins once a credential is presented.
- **Storage isolation.** How tenant data is kept apart below the API is ZBP-3.
- **Transport security.** TLS configuration, certificate management, and network policy.

## Vocabulary

These terms are used with exactly these meanings throughout. Where an existing standard defines a
term, this document adopts that definition rather than a parallel one
([ZFN-30](/zfn/30-use-standards-dont-reinvent/)).

- **Surface** — the complete set of operations exposed under one contract, versioned and evolved
  together.
- **Operation** — one named, invocable unit of the surface, of exactly one *kind* (below). Called an
  endpoint in HTTP and a method in RPC; *operation* is used throughout to avoid transport-specific
  connotations.
- **Kind** — one of **query**, **mutation**, **subscription**, or **long-running operation (LRO)**.
- **Payload** — the domain data of a request or response.
- **Envelope** — metadata *about* a call rather than *in* it, carried in the transport's metadata
  layer. Split into **per-hop** fields, meaningful only between two adjacent parties, and
  **propagated** fields, which travel the whole causal chain including asynchronous hops.
- **Caller** — the party issuing a request. **Client** — the software library through which it does so,
  generated from the schema.
- **Principal chain** — the ordered list of parties responsible for a call: the party on whose behalf
  it is made, and each party acting for them, in order.
- **State token** — an opaque, server-minted value describing how far through the system's ordered
  history a caller's view has reached.
- **Idempotency key** — a caller-minted value identifying one *intent* to perform a mutation, across
  every attempt at it.
- **Idempotency record** — the server-side association of a key with the request fingerprint and the
  stored response.
- **Request fingerprint** — a deterministic digest of the semantically significant parts of a request,
  used to detect a key reused with different content.
- **Work unit** — the declared unit in which computational cost is denominated and charged, distinct
  from a count of requests.
- **Cost estimate** — the a-priori upper bound on the work an operation will consume, computed before
  execution. **Cost measurement** — the a-posteriori actual, computed after.
- **Region** — a failure and residency domain that serves a disjoint partition of the surface's state.
- **Session** — the period over which a caller's state token, idempotency records, cursors, and
  operation identifiers remain meaningful. A session is scoped to exactly one region.
- **Request archive** — the durable, append-only record of requests and their outcomes, held outside
  the transactional store.
- **Audit log** — the customer-visible, retained, access-controlled record of who did what to what. It
  is not the request archive.

Normative keywords — MUST, MUST NOT, SHOULD, SHOULD NOT, MAY — are to be interpreted per
[RFC 8174](ref:rfc8174) and carry their normative meaning **only when capitalised**.

## Architecture

The surface is a schema, a set of middleware that reads the envelope, and a set of handlers that see
only payloads. Nothing in the handler layer is expected to implement any requirement in this document.

```
                       discovery (global, anycast)
                                 │  returns regional endpoint
                                 ▼
  caller ── generated client ──► regional ingress ──► middleware chain ──► handler
                  │                    │                   │
                  │                    │                   ├─ credential + proof   (ZBP-1)
                  │                    │                   ├─ principal chain
                  │                    │                   ├─ tenant context       (ZBP-3)
                  │                    │                   ├─ scope enforcement
                  │                    │                   ├─ quota admission (estimate)
                  │                    │                   ├─ idempotency lookup / replay
                  │                    │                   ├─ deadline budget
                  │                    │                   └─ visibility filter (serialisation)
                  │                    │
                  │                    └─────► request archive (buffered, off critical path)
                  │
                  └─ retry budget, key reuse, state token, region pinning
```

Four properties of this arrangement are what the requirements below protect.

**The envelope is infrastructure's data, not the handler's.** Everything the middleware chain reads —
credential, principal chain, tenant, idempotency key, deadline, quota key, state token — rides the
transport's metadata layer, so an intermediary can route, admit, or reject on it without deserialising
a domain object it has no schema for ([ZFN-51](/zfn/51-design-the-request-envelope-first/)).

**The client is part of the implementation.** Key reuse across retries, retry budgets, region pinning,
state token echo, and deadline propagation are caller-side behaviours. A surface whose clients are
hand-written in each language has as many dialects of this contract as it has clients
([ZFN-14](/zfn/14-schema-first-apis-generate-clients/)), which is why generation is normative here
rather than a convenience.

**Everything durable that is not the write itself is off the critical path.** The request archive is a
buffered append ([ZFN-65](/zfn/65-journal-writes-micro-batches/)); the idempotency record is the one
exception, because it is the write's own receipt.

**A front door may translate transports.** Where an external HTTP surface maps onto an internal RPC
one, that gateway is a legitimate place for protocol-specific concerns — content negotiation, header
syntax, browser constraints — and an illegitimate place for business logic, because everything behind
it then deploys through it.

## Normative requirements

### Operation taxonomy

- **ZBP-7-R1** Every operation MUST be classified as exactly one of: **query**, **mutation**,
  **subscription**, or **long-running operation**. The classification MUST be declared in the schema
  and MUST be machine-readable, so that infrastructure can act on it without reading handler code.
- **ZBP-7-R2** A query MUST be safe and repeatable in the sense of [RFC 9110](ref:rfc9110): executing
  it any number of times MUST NOT produce a client-observable change of state. Counters, rate
  accounting, and access logging are not client-observable changes.
- **ZBP-7-R3** A mutation MUST accept an idempotency key. A query MUST NOT require one.
- **ZBP-7-R4** A subscription MUST declare its delivery guarantee (at-most-once, at-least-once, or
  exactly-once), its ordering guarantee, and its resumption semantics — including whether a resumption
  token exists and how long it remains valid.
- **ZBP-7-R5** A mutation that cannot be guaranteed to complete within its deadline MUST be declared a
  long-running operation and MUST return an operation resource rather than blocking
  ([AIP-151](ref:aip151)).
- **ZBP-7-R6** An operation resource MUST carry: a unique identifier, its current state, the time it
  was created, the time its state last changed, and — once in a terminal state — either its result or
  a structured error conforming to ZBP-7-R30 through ZBP-7-R32.
- **ZBP-7-R7** Each long-running operation *type* MUST declare its own state machine in the schema:
  the set of named states, which transitions between them are legal, which states are terminal, and
  which terminal states are retryable. A single state enumeration MUST NOT be imposed across
  semantically different operation types.
- **ZBP-7-R8** Every operation resource MUST additionally expose a coarse lifecycle common to all
  types — at minimum whether it is settled, and if settled whether it succeeded — so that a generic
  client can wait on any operation without understanding its domain states.
- **ZBP-7-R9** The surface MUST offer a wait mechanism that does not require repeated polling: a
  server-pushed event stream, a long-poll bounded by the caller's deadline, or an equivalent transport
  primitive. Polling MUST remain available as a fallback, and MUST NOT be the only mechanism offered.
- **ZBP-7-R10** Progress events SHOULD be emitted over the wait mechanism, carrying the current phase
  and, where one is meaningful, a completion measure. A caller MUST NOT be required to consume progress
  events to observe the terminal outcome.
- **ZBP-7-R11** The availability of the request surface MUST NOT depend on the availability of any
  outbound delivery subsystem. A failure to deliver a webhook, push notification, or callback MUST NOT
  fail, delay, or roll back the request that caused it.

### The request envelope

- **ZBP-7-R12** Envelope fields MUST be carried in the transport's metadata layer — HTTP header fields,
  RPC metadata, or message attributes — and MUST NOT be carried in the payload.
- **ZBP-7-R13** The envelope MUST be defined in the schema as a type distinct from any request or
  response message, and MUST be generated into every client and server stub.
- **ZBP-7-R14** Every envelope field MUST be declared as either **per-hop** or **propagated**, and
  MUST be declared as either client-settable or server-only. An idempotency key MUST be per-hop. A
  trace identifier, the principal chain, and the tenant MUST be propagated.
- **ZBP-7-R15** Every response MUST carry a request identifier, and every error response MUST carry the
  same identifier in its body as well as its envelope.
- **ZBP-7-R16** The request identifier MUST be generated at ingress when the caller did not supply one,
  MUST be unique within the retention period of the request archive, and MUST be the key under which
  that request is retrievable under ZBP-7-R95.
- **ZBP-7-R17** A caller MAY supply a deadline. When supplied, the server MUST NOT exceed it, MUST
  propagate the remaining budget to every downstream hop, and MUST fail with a distinct, classified
  error rather than returning a partial result on expiry.
- **ZBP-7-R18** The envelope MUST carry structured client identity: the client name, its version, and
  its platform, as separate fields. A server MUST NOT require parsing a free-form user-agent string to
  obtain them.
- **ZBP-7-R19** The response envelope MAY carry the support status of the calling client version, the
  current version, and whether upgrading is required or optional. Where the surface offers this, the
  generated client MUST expose it to the application rather than discarding it.

### Idempotency and retry safety

- **ZBP-7-R20** Every mutation MUST accept a caller-supplied idempotency key
  ([`Idempotency-Key`](ref:idempotency) on HTTP).
- **ZBP-7-R21** An idempotency key MUST be scoped to at least the principal chain and the operation. It
  MUST NOT be interpreted in a global namespace, where one caller's key could collide with another's.
- **ZBP-7-R22** On first use of a key, the server MUST durably record an idempotency record before the
  response is returned, associating the key with the request fingerprint and the response.
- **ZBP-7-R23** On reuse of a key with a matching fingerprint, the server MUST return the stored
  response — its status, its payload, and its state token — and MUST NOT re-execute the operation.
  Returning a conflict status instead of the stored response does not satisfy this requirement.
- **ZBP-7-R24** The request fingerprint MUST be computed over the semantically significant parts of the
  request, MUST be deterministic across attempts, and MUST NOT include values that legitimately differ
  between attempts, such as the request identifier, timestamps, proof headers, or trace context.
- **ZBP-7-R25** On reuse of a key whose fingerprint does not match the recorded one, the server MUST
  reject the request with a distinct, non-retryable error code, and MUST NOT execute it.
- **ZBP-7-R26** When two requests carrying the same key arrive concurrently, at most one MUST execute.
  The others MUST either block until the first completes and then return its stored response, or fail
  with a distinct retryable "in progress" error. They MUST NOT execute.
- **ZBP-7-R27** The surface MUST declare the retention period of idempotency records, and that period
  MUST exceed the maximum retry window of the generated client.
- **ZBP-7-R28** For a long-running operation, an idempotency key MUST resolve to the same operation
  identifier on every attempt. A server MUST NOT replay a stream of results byte-for-byte; it MUST
  return the handle and allow the caller to re-attach.
- **ZBP-7-R29** In a batch request, the idempotency key MUST apply per item, not per batch, so that a
  retry after a partial failure does not re-execute the items that already succeeded.
- **ZBP-7-R30** A generated client MUST mint the idempotency key before its first attempt and MUST
  reuse the identical key for every retry of that intent. It MUST NOT generate a new key per attempt.
- **ZBP-7-R31** A call that times out, is cancelled, or fails with an indeterminate transport error
  MUST be treated by the client as having an unknown outcome, and MUST be resolved by retrying with
  the same key rather than by assuming failure ([ZFN-61](/zfn/61-propagate-the-deadline/)).

### Errors and retry control

- **ZBP-7-R32** Every error the surface can return MUST be enumerated in the schema with a stable,
  machine-readable code. Codes MUST NOT be reused for a different meaning.
- **ZBP-7-R33** Every error code MUST declare whether it is retryable, and MUST attribute the fault to
  the caller, the surface, or an upstream dependency.
- **ZBP-7-R34** A client MUST NOT be required to parse human-readable message text to determine what
  happened. Message text MUST be free to change without notice.
- **ZBP-7-R35** Error responses MUST carry structured, machine-readable parameters identifying what was
  wrong — the offending field, the exceeded limit, the conflicting identifier — rather than embedding
  them only in prose. On HTTP the response SHOULD use [problem details](ref:rfc9457).
- **ZBP-7-R36** A response that rejects a request for quota or overload reasons MUST carry a retry-after
  indication, and the client MUST honour it in preference to its own backoff schedule.
- **ZBP-7-R37** A generated client MUST implement bounded exponential backoff with randomised jitter
  for retryable errors, and MUST NOT retry an error classified as non-retryable.
- **ZBP-7-R38** A generated client MUST implement a retry budget expressed as a bounded ratio of
  retries to successful requests, and MUST stop retrying when the budget is exhausted, even where
  individual errors remain retryable. Per-request backoff alone MUST NOT be the only control
  ([ZFN-13](/zfn/13-load-shedding-and-flow-control/)).

### Consistency, state tokens, and pagination

- **ZBP-7-R39** Every response MUST carry a state token describing the position of the served view in
  the system's ordered history.
- **ZBP-7-R40** A generated client MUST retain the most recent state token it has observed and MUST
  present it on subsequent requests within the same session.
- **ZBP-7-R41** When a request carries a state token, the server MUST serve a view at least as fresh as
  that token, or MUST fail with a distinct retryable error. It MUST NOT silently serve a staler view.
- **ZBP-7-R42** The state token MUST be opaque. Its internal structure MUST NOT be documented as a
  contract, and clients MUST NOT parse, construct, or compare it other than by returning it verbatim.
- **ZBP-7-R43** A state token MUST be scoped to the region that minted it, and MUST be rejected with a
  distinct error by any other region.
- **ZBP-7-R44** Every operation returning an unbounded collection MUST paginate using an opaque,
  server-minted cursor. Numeric offsets and page numbers MUST NOT be the contract, because they
  encode an ordering guarantee a distributed store cannot keep across pages.
- **ZBP-7-R45** A cursor MUST encode both position and the snapshot it is positioned within, so that
  concurrent writes cannot cause a page to be silently skipped or repeated.
- **ZBP-7-R46** The surface MUST use one cursor dialect across every paginated operation.

### Identity, delegation, and authorization

- **ZBP-7-R47** Credentials accepted by the surface MUST be sender-constrained, bound to a key the
  caller holds, per ZBP-1. A bearer credential accepted without proof of possession MUST NOT be the
  default for any operation that mutates state or reads customer data.
- **ZBP-7-R48** More than one credential MUST be able to be valid for a principal at the same time, so
  that rotation can overlap. A surface that permits only one active credential per principal MUST NOT
  be considered conforming, because it makes rotation a flag-day cutover for the caller.
- **ZBP-7-R49** The identity presented on a request MUST be able to express a principal chain rather
  than a single subject, using the `act` claim of [RFC 8693](ref:rfc8693).
- **ZBP-7-R50** Delegation, in which every party in the chain remains visible, MUST be the default
  representation. Impersonation, in which the acting party is not represented, MUST be explicitly
  marked where it is permitted at all, and MUST be recorded in the audit log with the acting party
  named.
- **ZBP-7-R51** Authorization MUST be evaluated against the whole principal chain. A request MUST be
  denied unless every link in the chain is permitted to act as it claims, and the effective authority
  MUST NOT exceed the intersection of the authorities held by the chain.
- **ZBP-7-R52** An internal hop MUST NOT discard the caller's original credential when forwarding a
  request. Replacing it with a service credential makes every service beyond that point authorise the
  forwarding service rather than the caller.
- **ZBP-7-R53** An internal hop that forwards a request MUST add its own assertion, naming itself and
  what it verified, over the preserved original, using the workload identity of ZBP-2.
- **ZBP-7-R54** A service receiving a forwarded request MUST verify both the original credential and
  each forwarding assertion, and MUST NOT accept an unauthenticated header field as evidence of who
  the original caller was.
- **ZBP-7-R55** Every scope MUST be declared in a machine-readable registry carrying: its identifier, a
  human description, what it grants, which other scopes it implies, which principals may grant it, and
  whether it is deprecated.
- **ZBP-7-R56** Every operation MUST declare the scopes it requires, in the schema, and enforcement
  MUST be generated from that declaration rather than written per handler.
- **ZBP-7-R57** Absent or unrecognised authority MUST deny.
- **ZBP-7-R58** Field-level visibility MUST be declared in the schema, per field, and MUST be evaluated
  against the caller. The filter MUST be applied by one shared mechanism at serialisation, and MUST
  NOT be left to individual handlers.
- **ZBP-7-R59** Schema introspection and generated documentation MUST apply the same visibility policy
  as data responses. A caller that cannot read a field MUST NOT be able to discover its existence
  through introspection or documentation.
- **ZBP-7-R60** A request MUST name the tenant it intends to act within. That value MUST be verified
  against the tenant context established from authenticated material per ZBP-3-R3, and MUST NOT be used
  to establish it. A mismatch MUST deny.

### Quota and cost

- **ZBP-7-R61** The surface MUST enforce quota in two currencies: a rate of requests, and a measure of
  the work those requests consume. Request rate alone MUST NOT be the only limit, because it prices a
  trivial lookup and an expensive aggregation identically
  ([ZFN-18](/zfn/18-enforce-quotas-at-ingress/)).
- **ZBP-7-R62** The work unit MUST be declared, documented, and stable. It MUST correspond to the
  resource that actually constrains the surface — processor time, rows examined, bytes returned, or a
  declared composite — and MUST NOT be redefined without a major version.
- **ZBP-7-R63** Every operation MUST declare an upper bound on the work a single invocation may
  consume. An operation whose worst case is unbounded MUST NOT be exposed.
- **ZBP-7-R64** Every operation returning a collection MUST declare and enforce a maximum page size,
  and every batch operation MUST declare and enforce a maximum batch size. A caller-supplied value
  above the maximum MUST be rejected or clamped, and the behaviour MUST be declared.
- **ZBP-7-R65** An operation accepting caller-supplied filters, projections, or traversals MUST declare
  and enforce a complexity limit evaluated before execution.
- **ZBP-7-R66** Admission control MUST evaluate a cost estimate computed *before* the operation
  executes. A limiter that can only act on measured cost MUST NOT be the sole control, because the
  work it would prevent has by then already been done.
- **ZBP-7-R67** Actual cost MUST be measured and reconciled against the estimate. A caller whose
  estimates are systematically below measurement MUST be throttleable on that evidence.
- **ZBP-7-R68** Every response MUST report the work its request consumed, in the declared work unit. On
  HTTP this SHOULD use [`Server-Timing`](ref:servertiming). This MUST be reported whether or not a
  work-based limit is currently enforced.
- **ZBP-7-R69** Quota state MUST be reported to the caller — the applicable policy, the remaining
  allowance, and the window. On HTTP this SHOULD use the [`RateLimit` and `RateLimit-Policy`
  fields](ref:ratelimit), whose quota-unit parameter expresses a non-request currency.
- **ZBP-7-R70** Quota exhaustion MUST be distinguishable, by error code, from surface overload. The
  first is the caller's own allowance and is retryable after a stated interval; the second is a
  shedding decision and carries different retry semantics.
- **ZBP-7-R71** Every operation's cost MUST be charged to a declared party in the principal chain, and
  which party bears it MUST be unambiguous. Work performed under delegation MUST NOT be chargeable to
  a party that cannot observe or limit it.

### Caching

- **ZBP-7-R72** Cacheability MUST be declared per operation in the schema: whether a response may be
  cached, keyed on what, and by what event it is invalidated.
- **ZBP-7-R73** Correctness MUST NOT depend on a cache hit. The surface MUST serve correct results with
  every cache empty; a cold cache MUST be a latency event and MUST NOT be an availability event
  ([ZFN-21](/zfn/21-caches-sparingly-immutable-only/)).
- **ZBP-7-R74** The cold-cache path MUST be exercised deliberately and periodically, not discovered
  during an incident.
- **ZBP-7-R75** A cache key MUST include every input that changes the response's visible content — at
  minimum the tenant, per ZBP-3-R22, and any principal attribute that affects the field-visibility
  filter of ZBP-7-R58.

### Regional partitioning and failover

- **ZBP-7-R76** Each region MUST be addressed by its own hostname.
- **ZBP-7-R77** A regional hostname MUST resolve to exactly one region. Requests to it MUST NOT be
  silently served by another region under any failure or capacity condition.
- **ZBP-7-R78** The surface MUST provide a discovery operation that returns the regional endpoint a
  caller should use. That operation MAY be served from an anycast or globally-routed address, since it
  establishes the region rather than operating within one.
- **ZBP-7-R79** Discovery MAY be piggybacked on authentication, so that the response conveying a
  credential also conveys the regional endpoint. Where it is, the generated client MUST use the
  returned endpoint for the remainder of the session.
- **ZBP-7-R80** Every response MUST name the region that served it.
- **ZBP-7-R81** A client that fails over to another region MUST treat the result as an entirely new
  session. Its state token, outstanding idempotency records, cursors, and operation identifiers MUST
  all be considered invalid, and MUST NOT be presented to the new region.
- **ZBP-7-R82** A generated client MUST NOT carry session state across a region change, and MUST
  surface the failover to the application rather than performing it transparently.
- **ZBP-7-R83** Data residency constraints MUST be enforced server-side. A surface MUST NOT rely on the
  client having chosen the correct region to keep data within a jurisdiction.

### Degradation controls

- **ZBP-7-R84** The surface MUST provide a control-plane mechanism to mark an operation, or a class of
  operations, as unavailable or restricted — by operation, by tenant, by caller class, or by cost.
- **ZBP-7-R85** A restricted operation MUST return a specific, enumerated, correctly classified error.
  It MUST NOT be expressed as a timeout, a connection reset, or a generic internal error.
- **ZBP-7-R86** Applying or removing a degradation control MUST be a configuration change and MUST NOT
  require a code deployment.
- **ZBP-7-R87** Degradation controls SHOULD be exercised outside incidents, on a stated schedule, so
  that their behaviour and the procedure for using them are known before they are needed.

### The request archive and the audit log

- **ZBP-7-R88** Every request MUST be recorded with, at minimum: its request identifier, its envelope,
  the principal chain, the tenant, the operation, the outcome, the measured cost, and the region.
- **ZBP-7-R89** Writing to the archive MUST NOT be on the critical path of the request. A failure,
  slowness, or unavailability of the archive MUST NOT fail, delay, or roll back the request.
- **ZBP-7-R90** Archive writes MUST be buffered and committed in batches. A per-request synchronous
  insert MUST NOT be the write path ([ZFN-65](/zfn/65-journal-writes-micro-batches/)).
- **ZBP-7-R91** The archive MUST NOT be held in the transactional store that serves the surface. It is
  append-only, high-volume, and read far less often than written.
- **ZBP-7-R92** The archive's retention period MUST be declared and MUST be enforced automatically by a
  lifecycle policy rather than by a deletion job that can silently stop running.
- **ZBP-7-R93** Fields declared sensitive in the schema MUST be redacted or tokenised at the point of
  writing to the archive, not at the point of reading from it.
- **ZBP-7-R94** Erasure obligations MUST reach the archive. A surface that can delete a subject's data
  from its transactional store but not from its archive MUST NOT be considered conforming
  ([ZFN-57](/zfn/57-deletion-is-a-feature/)).
- **ZBP-7-R95** An archived request MUST be retrievable by its request identifier through an operation
  of the surface, subject to the same authorization and field-visibility rules as the original data.
- **ZBP-7-R96** The audit log MUST be a distinct artifact from the request archive, with its own
  retention, its own access control, and its own schema. The two MUST NOT be the same store serving
  both purposes.
- **ZBP-7-R97** An audit record MUST identify the full principal chain, the tenant, the operation, the
  affected resource, the outcome, and the time. Audit records MUST be retained for a declared period,
  MUST be access-controlled at least as tightly as the data they describe, and MUST NOT be sampled.

### Schema, evolution, and clients

- **ZBP-7-R98** The schema MUST be published in a machine-readable form covering operations and their
  kinds, types, error codes, required scopes, declared cost bounds, and cacheability.
- **ZBP-7-R99** Every identifier exposed by the surface MUST be opaque and MUST carry a type prefix, so
  that its kind is determinable from the value alone.
- **ZBP-7-R100** Identifiers MUST NOT be sequential or otherwise enumerable, and the storage key of a
  record MUST NOT be its public identifier ([ZBP-3-R19](/zbp/3-tenant-isolation/)).
- **ZBP-7-R101** The identifier format MUST be treated as permanent. It MUST NOT be changed within a
  major version, and a surface SHOULD assume it can never be changed at all, because identifiers
  propagate into systems the surface's operator does not control ([ZFN-56](/zfn/56-typed-prefixed-ids/)).
- **ZBP-7-R102** A generated client MUST tolerate unknown fields and unknown enumeration members,
  preserving them where it round-trips a value and never failing on their presence. A client that
  rejects what it does not recognise makes every additive server change a breaking one.
- **ZBP-7-R103** Every change to the shape of a request, response, or stored record MUST follow
  expand, migrate, then contract as three separately deployable steps
  ([ZFN-62](/zfn/62-expand-migrate-contract/)).
- **ZBP-7-R104** A deprecated operation MUST be signalled in its responses with a deprecation date and,
  once scheduled, the date it stops answering. On HTTP this MUST use
  [`Deprecation`](ref:deprecation) and [`Sunset`](ref:sunset).
- **ZBP-7-R105** Deprecation MUST also be recorded in the schema and in generated documentation, so
  that a caller reading the contract sees the same facts as a caller reading a response.
- **ZBP-7-R106** Usage telemetry MUST be available per operation and per client version before a
  deprecated operation is removed. Removal without evidence of who is still calling it MUST NOT occur.
- **ZBP-7-R107** The schema mechanism MUST support per-tenant extension: fields and types defined by a
  customer MUST be declarable, validated against a declared type, introspectable through the same
  mechanism as first-party fields, and versioned.
- **ZBP-7-R108** Customer-defined data MUST NOT be modelled as an unvalidated, untyped, unindexed
  opaque document as the only extension mechanism.
- **ZBP-7-R109** Clients MUST be generated from the schema for every supported language. A
  hand-written client MUST NOT be the reference implementation of any requirement in this document.
- **ZBP-7-R110** The generated client MUST implement, without application code: idempotency key
  minting and reuse (ZBP-7-R30), state token retention and echo (ZBP-7-R40), deadline propagation
  (ZBP-7-R17), retry classification and budget (ZBP-7-R37, ZBP-7-R38), retry-after honouring
  (ZBP-7-R36), region pinning (ZBP-7-R79), and failover as a new session (ZBP-7-R82).
- **ZBP-7-R111** The surface MUST offer a test mode, selected by the credential presented rather than
  by a separate deployment, address, or schema.
- **ZBP-7-R112** Every side effect reachable from the surface — quota accounting, outbound delivery,
  ledgers, exports, notifications to people, and every internal service a request reaches — MUST
  honour test mode. A side effect that cannot distinguish test from live MUST NOT be reachable from a
  test-mode credential.
- **ZBP-7-R113** Test mode MUST allow a caller to deliberately provoke declared failure conditions —
  at minimum a chosen error code, a quota rejection, and a delayed or failed long-running operation —
  so that integration error handling can be exercised before it is needed.

## Wire formats and interfaces

The requirements above are transport-neutral. This section binds them to concrete syntax. An
implementation on a transport not covered here MUST provide an equivalent mapping and MUST document it.

### Envelope fields on HTTP

| Field | Header | Direction | Scope | Requirement |
| --- | --- | --- | --- | --- |
| Idempotency key | `Idempotency-Key` | request | per-hop | ZBP-7-R20 |
| Request identifier | `X-Request-Id` | both | per-hop | ZBP-7-R15 |
| Deadline | `X-Deadline-Ms` | request | propagated | ZBP-7-R17 |
| State token | `X-State-Token` | both | propagated | ZBP-7-R39 |
| Serving region | `X-Region` | response | per-hop | ZBP-7-R80 |
| Client identity | `X-Client-Name`, `X-Client-Version`, `X-Client-Platform` | request | per-hop | ZBP-7-R18 |
| Client support status | `X-Client-Support` | response | per-hop | ZBP-7-R19 |
| Tenant | `X-Tenant` | request | propagated | ZBP-7-R60 |
| Proof of possession | `DPoP` | request | per-hop | ZBP-1 |
| Trace context | `traceparent`, `tracestate` | both | propagated | — |
| Quota state | `RateLimit`, `RateLimit-Policy` | response | per-hop | ZBP-7-R69 |
| Measured cost | `Server-Timing` | response | per-hop | ZBP-7-R68 |
| Retry timing | `Retry-After` | response | per-hop | ZBP-7-R36 |
| Deprecation | `Deprecation`, `Sunset` | response | per-hop | ZBP-7-R104 |

The `X-` prefixed names above are placeholders for a surface's own namespace; where a standard field
exists it MUST be used in preference to a private one. A surface MUST NOT mint a private field whose
meaning duplicates a registered one.

### The operation resource

A long-running operation is represented as a resource carrying both its type-specific state and the
common lifecycle of ZBP-7-R8:

```json
{
  "id": "op_01J9ZK4Q2M7X8V3B",
  "type": "export.create",
  "state": "transcoding",
  "settled": false,
  "succeeded": null,
  "created_at": "2026-08-30T09:14:02Z",
  "updated_at": "2026-08-30T09:14:19Z",
  "progress": { "phase": "transcoding", "completed": 41, "total": 118 },
  "result": null,
  "error": null
}
```

`state` is drawn from the type's own declared state machine (ZBP-7-R7). `settled` and `succeeded` are
the common lifecycle every type exposes, so a generic client can wait without understanding
`transcoding`. A settled operation has `settled: true`, `succeeded` either `true` or `false`, and
exactly one of `result` or `error` populated.

The type's state machine is itself published in the schema:

```json
{
  "type": "export.create",
  "initial": "queued",
  "states": {
    "queued":      { "to": ["gathering", "cancelled"] },
    "gathering":   { "to": ["transcoding", "failed", "cancelled"] },
    "transcoding": { "to": ["uploading", "failed", "cancelled"] },
    "uploading":   { "to": ["complete", "failed"] },
    "complete":    { "terminal": true, "succeeded": true },
    "failed":      { "terminal": true, "succeeded": false, "retryable": true },
    "cancelled":   { "terminal": true, "succeeded": false, "retryable": false }
  }
}
```

### Error responses

Errors use [problem details](ref:rfc9457) with the extension members this document requires:

```json
{
  "type": "https://api.example.com/errors/quota-exceeded",
  "title": "Work quota exceeded",
  "status": 429,
  "detail": "This request would consume 4200 work units; 1180 remain in the current window.",
  "code": "quota_exceeded",
  "retryable": true,
  "fault": "client",
  "request_id": "req_01J9ZK4Q2M7X8V3B",
  "params": { "estimated_cost": 4200, "remaining": 1180, "window_seconds": 60 }
}
```

`code`, `retryable`, `fault`, `request_id` and `params` satisfy ZBP-7-R32 through ZBP-7-R35 and
ZBP-7-R15. `title` and `detail` are prose and MUST NOT be parsed (ZBP-7-R34).

### Discovery

The discovery operation establishes the region for a session, and MAY be combined with the exchange
that issues a credential (ZBP-7-R79):

```json
{
  "endpoint": "https://eu-west.api.example.com",
  "region": "eu-west",
  "session_ttl_seconds": 3600,
  "alternates": [
    { "region": "us-east", "endpoint": "https://us-east.api.example.com" }
  ]
}
```

`alternates` exists so a client can fail over deliberately. Presenting a state token, cursor, or
idempotency key minted in `eu-west` to `us-east` is a new session under ZBP-7-R81, and the alternate
endpoint MUST reject those values rather than reinterpret them.

### Envelope fields on gRPC

Every field in the HTTP table maps to a metadata key of the same name, lowercased, with binary values
suffixed `-bin`. Deadlines use the transport's native mechanism rather than a metadata key, and its
value takes precedence where both are present. Trace context uses the standard `traceparent` and
`tracestate` keys.

## Algorithms

Each algorithm is written as ordered steps with named outcomes. An implementation MUST produce the
named outcome for the named condition; the ordering between steps is normative where a later step
depends on an earlier one having rejected.

### Handling a mutation with an idempotency key

1. **Validate the credential and proof** per ZBP-1. Outcome on failure: `unauthenticated`. This
   precedes everything, so an unauthenticated caller can neither consume quota nor create records.
2. **Establish the tenant context** from authenticated material, then verify the tenant named on the
   request against it (ZBP-7-R60). Mismatch outcome: `permission_denied`.
3. **Enforce scope** for the operation (ZBP-7-R56). Outcome: `permission_denied`.
4. **Compute the request fingerprint** over the canonical form defined in *Test vectors*
   (ZBP-7-R24).
5. **Look up the idempotency record** for (principal chain, operation, key).
   - **No record:** attempt to insert a record in state `in_progress` with the fingerprint,
     conditionally on no record existing. If the insert loses the race, restart this step.
   - **Record exists in state `complete` with a matching fingerprint:** return the stored response
     verbatim (ZBP-7-R23). **Stop.**
   - **Record exists with a differing fingerprint:** outcome `idempotency_key_reused`, non-retryable
     (ZBP-7-R25). **Stop.**
   - **Record exists in state `in_progress`:** either wait for it to settle and then return its stored
     response, or return `idempotency_in_progress`, retryable (ZBP-7-R26). **Stop.**
6. **Estimate cost and admit** against both quota currencies (ZBP-7-R61, ZBP-7-R66). On rejection,
   mark the record `abandoned` so the key is reusable, and return `quota_exceeded` with a retry-after.
7. **Execute the operation** within the remaining deadline budget.
8. **Store the response** — status, payload, and the state token of the resulting view — in the
   idempotency record, and transition it to `complete`. This MUST be durable before the response is
   returned (ZBP-7-R22).
9. **Measure and reconcile cost**, debit the actual, and attach the cost and quota headers
   (ZBP-7-R67, ZBP-7-R68, ZBP-7-R69).
10. **Enqueue the archive record** on the buffered path (ZBP-7-R89), and return.

A failure between steps 7 and 8 is the case the whole mechanism exists for: the work may have
committed while the record says `in_progress`. The record MUST therefore be settled by the same
transaction as the write where the store permits it, and where it does not, a reconciliation process
MUST resolve records left `in_progress` beyond a bounded period by determining the write's actual
outcome — never by assuming failure.

### Quota admission

1. Compute the estimate `E` for the request from its declared bounds and its caller-supplied
   parameters — page size, batch size, filter complexity.
2. If `E` exceeds the operation's declared maximum (ZBP-7-R63), reject with `request_too_large`,
   non-retryable. The caller must ask for less, not ask again.
3. Read the caller's remaining allowance `A` for the window in each currency.
4. If `E > A` in any currency, reject with `quota_exceeded`, retryable, with a retry-after computed
   from the window's refill rate.
5. Provisionally debit `E`.
6. Execute; measure actual cost `M`.
7. Settle the debit to `M`. Where `M > E`, the excess MUST still be debited — the estimate bounds
   admission, not billing — and the discrepancy MUST be recorded for ZBP-7-R67.

### Waiting on an operation

1. The caller invokes the wait mechanism with the operation identifier and a deadline
   (ZBP-7-R9).
2. The server streams progress events as the operation's state advances (ZBP-7-R10).
3. On reaching a terminal state, the server emits a final event carrying `settled: true` and the
   result or error, then closes the stream.
4. If the caller's deadline expires first, the server closes the stream without a terminal event. This
   is not a failure of the operation, and the caller MUST re-attach rather than treat it as one.
5. If the stream breaks, the caller re-attaches using the same operation identifier. Where the wait
   mechanism cannot be used at all, the caller polls the operation resource — the fallback of
   ZBP-7-R9, never the only path.

### Region discovery and failover

1. The client resolves the discovery address and obtains its regional endpoint (ZBP-7-R78), caching
   it for the returned session lifetime.
2. All subsequent calls in the session go to that endpoint. The client MUST NOT re-resolve per request.
3. On repeated failure of the regional endpoint, the client MAY select an alternate.
4. Before issuing any request to the alternate, the client MUST discard its state token, cursors,
   operation identifiers, and outstanding idempotency keys (ZBP-7-R81).
5. The client MUST surface the region change to the application (ZBP-7-R82). Work in flight at the
   moment of failover has an unknown outcome in the original region and MUST NOT be assumed failed;
   it is resolved when that region returns, using the original key.

## Threat model and failure modes

The adversary assumed here is a caller — sometimes hostile, more often merely wrong. A caller with
valid credentials retrying aggressively causes most of the damage this document prevents, and it does
so without violating any rule the surface has stated.

**Token replay.** An access token observed in a log, a proxy, or a browser extension is replayable by
whoever holds it. Mitigated by proof of possession (ZBP-7-R47, ZBP-1). Residual risk: an attacker with
the private key, which is why ZBP-7-R48 requires rotation to be possible without a flag day.

**Confused deputy across tenants.** A caller authenticated for one tenant induces the surface to act
on another by supplying an identifier belonging to it. Mitigated by ZBP-7-R60 and the underlying
ZBP-3-R3: the tenant on the request is verified, never trusted. Residual risk sits in operations that
resolve an identifier before establishing context, which is why step 2 of the mutation algorithm
precedes lookup.

**Authority escalation through a forwarding hop.** A service that replaces the caller's credential
with its own makes every downstream authorization decision about itself. Mitigated by ZBP-7-R52
through ZBP-7-R54. This is the failure that is invisible in testing, because the forwarded request
succeeds — it simply succeeds for the wrong reason.

**Existence disclosure through introspection.** A schema that documents operator-only fields to every
caller leaks the shape of internal tooling without returning any data. Mitigated by ZBP-7-R59.
Field-level filtering applied only to data responses, and not to the schema, is the common
half-measure.

**Resource exhaustion within quota.** A caller stays inside its request rate while consuming a shard,
because the limit prices every request identically. Mitigated by the second currency and by
pre-execution estimation (ZBP-7-R61, ZBP-7-R66). A limiter acting only on measured cost cannot prevent
the request that exhausted the resource, only the next one.

**Retry amplification.** A partial failure multiplies every well-behaved client's traffic by its retry
factor, at the moment capacity is lowest, turning a degraded dependency into a total outage. Mitigated
by retry budgets (ZBP-7-R38) and by honouring retry-after (ZBP-7-R36). This is a client-side control,
which is why client generation is normative.

**Duplicate execution under retry.** A response lost in transit is indistinguishable from a request
never processed. Mitigated by ZBP-7-R20 through ZBP-7-R31. The dangerous window is between execution
and the durable idempotency record; the mutation algorithm names it and requires reconciliation rather
than assumption.

**Silent staleness after failover.** A client moved between regions presents state minted elsewhere and
receives an older view of the world, producing a data-consistency bug visible only during an incident.
Mitigated by ZBP-7-R43 and ZBP-7-R81, which make the failure loud instead.

**The archive as a disclosure surface.** A store of every request body is a store of every secret a
caller ever sent, retained long after the transactional record was deleted. Mitigated by write-time
redaction (ZBP-7-R93), declared retention (ZBP-7-R92), erasure reach (ZBP-7-R94), and the same
authorization on retrieval as on the original data (ZBP-7-R95).

## Anti-patterns

If an implementation does any of the following, it has misread this document.

- **Returning a conflict for a replayed idempotency key.** The caller still does not know whether the
  work happened, which was the entire problem. See ZBP-7-R23.
- **Generating a fresh idempotency key per attempt.** Every retry is then a new intent, and the
  mechanism guarantees nothing. See ZBP-7-R30.
- **Putting the state token, tenant, or trace context in the request body.** It cannot be routed on at
  ingress without deserialising a domain object, and it will not survive a hop that lacks the schema.
  See ZBP-7-R12.
- **Propagating the idempotency key downstream.** Two genuinely distinct internal writes then collide
  on one key. See ZBP-7-R14.
- **A single global operation state enumeration.** Every operation type is flattened into
  `pending`/`running`/`done`, and progress reporting loses the information that made it worth having.
  See ZBP-7-R7.
- **Polling as the only wait.** Every client invents its own interval, and the quota system charges
  them for asking. See ZBP-7-R9.
- **Rate limiting on requests alone.** The expensive operation is priced identically to the trivial
  one. See ZBP-7-R61.
- **Measuring cost without estimating it.** The limiter can only ever punish work already performed.
  See ZBP-7-R66.
- **Transparent cross-region failover.** Correctness is traded for an availability number, and the
  resulting bug appears only under the conditions that make it hardest to diagnose. See ZBP-7-R81.
- **A `metadata` blob as the only extension mechanism.** Customer data becomes unvalidated,
  unindexed, and permanently untyped. See ZBP-7-R108.
- **Test mode as a separate deployment.** It drifts from production, and the code paths that matter
  are the ones that differ. See ZBP-7-R111.
- **Rejecting unknown enumeration members.** Every additive change becomes breaking, discovered from
  the callers who upgrade least often. See ZBP-7-R102.
- **Writing the archive with a synchronous insert into the transactional store.** The observability
  system becomes a source of latency and a cause of outages. See ZBP-7-R89, ZBP-7-R90.

## Test vectors

These vectors are computed, not illustrative. Each canonical input is given as an exact byte string so
the digest can be reproduced.

### The request fingerprint

The fingerprint of ZBP-7-R24 is computed over the operation name, the tenant, and the canonical JSON
encoding of the payload — keys sorted, no insignificant whitespace — each followed by U+000A. Fields
that legitimately differ between attempts are excluded: the request identifier, timestamps, the proof
header, and trace context.

**Vector 1 — the canonical input.**

```
payments.create\nten_7Q2XW9\n{"amount":2500,"currency":"usd","reference":"inv_884"}\n
```

`sha256` = `d387a19fab503058f90f73005976fdf4b8abdb6ea15d354dd5557b2b7216ce6d`

**Vector 2 — a retry of the same intent.** The same request re-sent with a new request identifier, a
new `DPoP` proof, a new `traceparent`, and a later wall-clock time produces a byte-identical canonical
input, and therefore the identical digest. The idempotency record matches and the stored response is
replayed (ZBP-7-R23).

**Vector 3 — the same key with a changed amount.** `amount` becomes `9900`:

```
payments.create\nten_7Q2XW9\n{"amount":9900,"currency":"usd","reference":"inv_884"}\n
```

`sha256` = `4324a2874004663892d9831687b04823f6e6f8742d180190387ed7bc347d8c72`

This differs from Vector 1, so the request MUST be rejected with `idempotency_key_reused` and MUST NOT
execute (ZBP-7-R25).

**Vector 4 — the same key and payload under a different tenant.** `ten_ZZZZZZ` in place of
`ten_7Q2XW9`:

`sha256` = `66bcc0617e8c80c26c07ef0b19533b430a7bc9686a15f15e48e72b765404bf54`

The digest differs, but this case MUST already have been rejected at step 2 of the mutation algorithm
by ZBP-7-R60 and ZBP-3-R3. The vector exists to demonstrate that the fingerprint is not the tenant
control — it is a change detector, not an authorization mechanism.

### Quota admission

An operation declares a maximum of `5000` work units. The caller's window holds `1180` remaining.

| Case | Estimate | Remaining | Outcome | Code |
| --- | --- | --- | --- | --- |
| Page size 50 | 400 | 1180 | admitted, debit 400 | — |
| Page size 200 | 1600 | 1180 | rejected, retryable | `quota_exceeded` |
| Page size 1000 | 8000 | 1180 | rejected, non-retryable | `request_too_large` |
| Page size 50, actual 610 | 400 | 1180 | admitted, settled to 610 | — |

The third case is rejected before the allowance is consulted, because an estimate above the operation's
declared maximum can never be admitted by waiting (ZBP-7-R63, step 2 of quota admission). The fourth
shows ZBP-7-R67: admission bounds on the estimate, settlement charges the measurement, and the `+210`
discrepancy is recorded.

### The retry budget

A client with a budget of 20% of successful requests, over a window in which it has made 500
successful calls, may spend 100 retries.

| Retries spent | Next retryable error | Behaviour |
| --- | --- | --- |
| 0–99 | any | retry after backoff and jitter |
| 100 | any | do not retry; surface the error |
| 100 | error carrying retry-after | do not retry; surface the error |

The budget binds even where individual errors remain retryable (ZBP-7-R38). This is what prevents a
degraded dependency from being amplified into an outage by clients that are each behaving correctly.

## Conformance checklist

An implementation conforms when every item below holds. Each cites the requirements it covers.

**Operation taxonomy**

- [ ] Every operation declares exactly one kind, machine-readably (ZBP-7-R1).
- [ ] Queries are safe and repeatable; mutations accept an idempotency key and queries do not require
      one (ZBP-7-R2, ZBP-7-R3).
- [ ] Subscriptions declare delivery, ordering, and resumption semantics (ZBP-7-R4).
- [ ] Operations that may exceed their deadline return an operation resource (ZBP-7-R5) carrying every
      required field (ZBP-7-R6).
- [ ] Each long-running operation type declares its own state machine (ZBP-7-R7), and every operation
      exposes the common settled/succeeded lifecycle (ZBP-7-R8).
- [ ] A wait mechanism exists that does not require polling, with polling retained as a fallback
      (ZBP-7-R9), and progress events are emitted over it (ZBP-7-R10).
- [ ] Request-surface availability does not depend on outbound delivery (ZBP-7-R11).

**Envelope**

- [ ] Envelope fields ride the transport metadata layer, never the payload (ZBP-7-R12), are defined in
      the schema and generated into clients and servers (ZBP-7-R13), and each is declared per-hop or
      propagated and client-settable or server-only (ZBP-7-R14).
- [ ] Every response and every error carries a request identifier (ZBP-7-R15), generated at ingress
      when absent, unique, and usable as the archive key (ZBP-7-R16).
- [ ] Supplied deadlines are honoured, propagated, and failed cleanly on expiry (ZBP-7-R17).
- [ ] Client identity is structured, requiring no user-agent parsing (ZBP-7-R18), and any support
      status returned is surfaced by the generated client (ZBP-7-R19).

**Idempotency**

- [ ] Every mutation accepts a key (ZBP-7-R20), scoped to at least the principal chain and operation
      (ZBP-7-R21).
- [ ] A durable record is written before the response is returned (ZBP-7-R22), and a matching replay
      returns the stored response rather than a conflict (ZBP-7-R23).
- [ ] The fingerprint is deterministic and excludes per-attempt values (ZBP-7-R24); a mismatch is
      rejected without executing (ZBP-7-R25).
- [ ] Concurrent use of one key executes at most once (ZBP-7-R26).
- [ ] Record retention is declared and exceeds the client's retry window (ZBP-7-R27).
- [ ] A key resolves to the same operation identifier for a long-running operation, with no byte-wise
      stream replay (ZBP-7-R28), and applies per item within a batch (ZBP-7-R29).
- [ ] The generated client mints once and reuses across retries (ZBP-7-R30), treating timeouts and
      cancellations as unknown outcomes resolved by replay (ZBP-7-R31).

**Errors and retry control**

- [ ] Every error is enumerated with a stable code (ZBP-7-R32), classified for retryability and fault
      (ZBP-7-R33).
- [ ] No client behaviour depends on parsing prose (ZBP-7-R34); structured parameters are provided
      (ZBP-7-R35).
- [ ] Quota and overload responses carry a retry-after that clients honour in preference to their own
      backoff (ZBP-7-R36).
- [ ] The generated client implements bounded jittered backoff and never retries a non-retryable error
      (ZBP-7-R37), under a retry budget that can be exhausted (ZBP-7-R38).

**Consistency and pagination**

- [ ] Every response carries a state token (ZBP-7-R39) that the client retains and echoes
      (ZBP-7-R40).
- [ ] The server serves a view at least as fresh, or fails explicitly (ZBP-7-R41).
- [ ] The token is opaque and never parsed by clients (ZBP-7-R42), and is rejected outside its region
      (ZBP-7-R43).
- [ ] Collections paginate by opaque cursor, never by offset (ZBP-7-R44), encoding position and
      snapshot (ZBP-7-R45), in one dialect across the surface (ZBP-7-R46).

**Identity, delegation, authorization**

- [ ] Credentials are sender-constrained (ZBP-7-R47), and more than one may be valid at once so
      rotation can overlap (ZBP-7-R48).
- [ ] Identity expresses a principal chain (ZBP-7-R49); delegation is the default and impersonation is
      marked and audited (ZBP-7-R50); authorization evaluates the whole chain and never exceeds its
      intersection (ZBP-7-R51).
- [ ] Forwarding hops preserve the original credential (ZBP-7-R52), add their own assertion
      (ZBP-7-R53), and receivers verify both (ZBP-7-R54).
- [ ] Scopes are declared in a registry (ZBP-7-R55), required scopes are declared per operation with
      generated enforcement (ZBP-7-R56), and absent authority denies (ZBP-7-R57).
- [ ] Field visibility is declared and applied once at serialisation (ZBP-7-R58), and introspection and
      documentation honour the same policy (ZBP-7-R59).
- [ ] The request names its tenant, which is verified rather than trusted (ZBP-7-R60).

**Quota and cost**

- [ ] Quota is enforced in both requests and work (ZBP-7-R61), with a declared stable work unit
      (ZBP-7-R62).
- [ ] Every operation declares a work bound (ZBP-7-R63), a maximum page and batch size (ZBP-7-R64), and
      a complexity limit where it accepts caller-supplied shaping (ZBP-7-R65).
- [ ] Admission uses a pre-execution estimate (ZBP-7-R66), reconciled against measurement with
      systematic underestimation actionable (ZBP-7-R67).
- [ ] Every response reports measured cost (ZBP-7-R68) and quota state (ZBP-7-R69).
- [ ] Quota exhaustion is distinguishable from overload (ZBP-7-R70), and cost is charged to an
      unambiguous, observable party (ZBP-7-R71).

**Caching**

- [ ] Cacheability is declared per operation (ZBP-7-R72), correctness never depends on a hit
      (ZBP-7-R73), the cold path is exercised deliberately (ZBP-7-R74), and cache keys include every
      input affecting visible content (ZBP-7-R75).

**Regions and failover**

- [ ] Each region has its own hostname (ZBP-7-R76) resolving to exactly one region, never silently
      served elsewhere (ZBP-7-R77).
- [ ] A discovery operation returns the regional endpoint (ZBP-7-R78) and may accompany authentication,
      with the client honouring what it returns (ZBP-7-R79).
- [ ] Responses name the serving region (ZBP-7-R80).
- [ ] Failover discards all session state (ZBP-7-R81), is never transparent (ZBP-7-R82), and residency
      is enforced server-side (ZBP-7-R83).

**Degradation**

- [ ] Operations can be marked unavailable or restricted from the control plane (ZBP-7-R84), returning
      an enumerated classified error rather than a timeout (ZBP-7-R85), as configuration rather than a
      deployment (ZBP-7-R86), exercised on a schedule (ZBP-7-R87).

**Archive and audit**

- [ ] Every request is recorded with the required fields (ZBP-7-R88), off the critical path
      (ZBP-7-R89), in batched appends (ZBP-7-R90), outside the transactional store (ZBP-7-R91).
- [ ] Retention is declared and automatically enforced (ZBP-7-R92); sensitive fields are redacted at
      write time (ZBP-7-R93); erasure reaches the archive (ZBP-7-R94).
- [ ] Archived requests are retrievable by identifier under the same authorization as the original data
      (ZBP-7-R95).
- [ ] The audit log is a distinct artifact (ZBP-7-R96) with the required contents, retention, access
      control, and no sampling (ZBP-7-R97).

**Schema, evolution, clients**

- [ ] The schema is published machine-readably and completely (ZBP-7-R98).
- [ ] Identifiers are opaque and type-prefixed (ZBP-7-R99), non-enumerable and distinct from storage
      keys (ZBP-7-R100), and their format is treated as permanent (ZBP-7-R101).
- [ ] Clients tolerate unknown fields and enum members (ZBP-7-R102), and shape changes follow
      expand/migrate/contract (ZBP-7-R103).
- [ ] Deprecation is signalled in responses (ZBP-7-R104), recorded in schema and documentation
      (ZBP-7-R105), and supported by usage telemetry before removal (ZBP-7-R106).
- [ ] Per-tenant schema extension is declarable, validated, introspectable, and versioned
      (ZBP-7-R107), and is not merely an untyped blob (ZBP-7-R108).
- [ ] Clients are generated (ZBP-7-R109) and implement every listed caller-side behaviour
      (ZBP-7-R110).
- [ ] A credential-selected test mode exists (ZBP-7-R111), every reachable side effect honours it
      (ZBP-7-R112), and declared failure conditions can be provoked (ZBP-7-R113).

## Rationale

The arguments are not repeated here; each lives in a Field Note.

The organising claim — that these concerns share a cost curve and therefore must be decided together,
before the surface has callers — is [ZFN-67](/zfn/67-the-api-id-build-today/). The idea that this
material belongs in a defined envelope rather than accreting into payloads is
[ZFN-51](/zfn/51-design-the-request-envelope-first/), and the reason the contract is a schema that
generates clients is [ZFN-14](/zfn/14-schema-first-apis-generate-clients/) — without which every
caller-side requirement here becomes a suggestion.

The state token comes from [ZFN-25](/zfn/25-read-your-writes-version-token/); the deadline as an
inherited budget from [ZFN-61](/zfn/61-propagate-the-deadline/); the enumerated, classified error
surface from [ZFN-58](/zfn/58-errors-are-part-of-the-contract/); the marking of read-only and
idempotent operations from [ZFN-19](/zfn/19-annotate-readonly-idempotent-endpoints/). Quota at the
edge is [ZFN-18](/zfn/18-enforce-quotas-at-ingress/), what it degrades into is
[ZFN-13](/zfn/13-load-shedding-and-flow-control/), and the reasoning behind a second currency is
[ZFN-53](/zfn/53-attack-the-unit-economics-of-abuse/). Cache discipline is
[ZFN-21](/zfn/21-caches-sparingly-immutable-only/). Identifiers are
[ZFN-56](/zfn/56-typed-prefixed-ids/). The archive's write shape is
[ZFN-65](/zfn/65-journal-writes-micro-batches/) and its relationship to the surrounding messaging
substrate is [ZFN-12](/zfn/12-queues-topics-journals/); the obligation it creates is
[ZFN-57](/zfn/57-deletion-is-a-feature/). Schema change is
[ZFN-62](/zfn/62-expand-migrate-contract/). The preference throughout for registered header fields and
existing token formats over private ones is [ZFN-30](/zfn/30-use-standards-dont-reinvent/).

## References

- [ZBP-1](/zbp/1-dpop-bound-requests/) — proof of possession, assumed by ZBP-7-R47.
- [ZBP-2](/zbp/2-workload-security-token-service/) — the workload identity a forwarding hop asserts
  with, assumed by ZBP-7-R53.
- [ZBP-3](/zbp/3-tenant-isolation/) — the tenant context ZBP-7-R60 verifies against.
- [RFC 8174](ref:rfc8174) — the interpretation of the normative keywords used throughout.
- [RFC 9110](ref:rfc9110) — safe and idempotent method semantics underlying the operation taxonomy.
- [RFC 9449](ref:dpop) — DPoP.
- [RFC 8693](ref:rfc8693) — token exchange, and the delegation-versus-impersonation distinction.
- [RFC 9457](ref:rfc9457) — problem details, the error wire shape.
- [`Idempotency-Key`](ref:idempotency) and [RateLimit fields](ref:ratelimit) — the IETF drafts adopted
  for idempotency and quota signalling.
- [`Server-Timing`](ref:servertiming) — the cost-reporting field.
- [RFC 9745](ref:deprecation) and [RFC 8594](ref:sunset) — deprecation and sunset signalling.
- [AIP-151](ref:aip151) — the long-running operation pattern.

## Changelog

- **2026-08-30** (1.0.0): First published as a draft, deriving the normative specification from
  ZFN-67.
