Skip to content

API contract from the client's side

Agree what the screen can render, recover, continue and reconcile before choosing its endpoint shape.

Predict first

Before you read: predict

Three console reports arrive in order. Their type URIs end in /held, /stock, /held; their titles are “Wait”, “Wait”, “Later”. All have HTTP status 409 and different instance URIs.

Which records belong together in a report grouped by RFC 9457 problem category?

Pick one

The 30-second version

Design the screen's data, failure, continuation and write receipts together. A successful request can still contain a failed region. Stable machine fields and human explanations serve different consumers. Check what an adapter preserves when responses are partial, extended or replayed. The contract includes those boundary cases, not just a screenshot of the happy path.

Agree what the screen can render, recover, continue and reconcile before choosing its endpoint shape.

How it works

Step 1 of 7

Identify independently useful screen regions. Specify whether one failed dependency rejects the whole representation or occupies its own envelope.

Analogy: A keyed plug meeting its socket

A plug and socket meet through a defined boundary: matching outside dimensions alone does not guarantee that their contacts line up. A connector drawing gives both sides the same positions and labels. Changing the casing while preserving those contact positions differs from moving a contact without changing its label. The receiver needs the agreed interface, not a reconstruction of everything behind it.

Connector relation Screen contract
Contact positions shared by both drawings Agreed machine fields and their meaning
Distinct contacts within one housing Independently interpreted parts of an envelope
Label on a contact versus a descriptive note Machine identity versus human explanation
An adapter that leaves a contact disconnected A wrapper that drops a required response field

In real terms: inspect what actually crosses the boundary. Limits: plugs do not paginate, replay messages or evolve at runtime; those mechanisms require the explicit HTTP examples. Contrasting case: two matching outer shapes can conceal incompatible contact assignments. API styles, cursor construction and boundary validation own their respective implementation details.

Examples, run for real

EXH-1940 → MR !2140: inspect the representation itself

It contains an identity region and a history region. Below is its exact parsed envelope, followed by the nested problem object. The server and consumer assert these fields; they are not abbreviated counts. RFC 9457 §1

E1 envelope {"regions":{"identity":{"data":{"displayName":"DEMO reviewer"}},"history":{"problem":{"type":"https://contracts.example/problems/history-unavailable","title":"history-unavailable","status":503,"detail":"The history service could not answer.","instance":"/incidents/demo-8","retryable":true}}}}
E1 problem {"type":"https://contracts.example/problems/history-unavailable","title":"history-unavailable","status":503,"detail":"The history service could not answer.","instance":"/incidents/demo-8","retryable":true}
Step Observable field Its documented purpose
1 identity.data, history.problem Distinguish region representations
2 type, title, instance Separate category, summary and occurrence
3 retryable An application extension, not an RFC-defined guarantee
4 next:null below The fixture's explicit terminal marker

A terminal audit-history response

The second request returns one audit row and a terminal marker. This example inspects the boundary object; it does not run a traversal algorithm or a checkout repair. The row count and the marker are different fields with separately documented meanings. RFC 9110 §3.2

E2 terminal page {"items":[{"id":"audit-9"}],"next":null}

All examples close their local HTTP server. They prove the declared payloads and consumer observations, not backend authorization, persistent deduplication or a production schema migration. A contract review pairs these observed objects with examples of each failure and replay shape the screen must understand.

Try it

Answer first, then check. Nothing is saved.

Question 1 of 5

A checkout page was built for endpoint X, which answers 503 with one problem object whenever any of its lookups fails. Its renderer:

Endpoint Y replaces X, and the renderer is unchanged. Only the shipping lookup fails, and Y answers 200 with {regions:{totals:{data:{label:'€42'}},shipping:{problem:{type:'https://contracts.example/problems/address-check',title:'address-check'}},loyalty:{data:{label:'14 pts'}}}}.

What does the page show?

Code for this question
const reply = await fetch(route);if (!reply.ok) return show('error');const { regions } = await reply.json();for (const name of ['totals', 'shipping', 'loyalty']) show(`${name}: ${regions[name].data.label}`);
Pick one

Question 2 of 5

A checkout list receives {items:[a], next:'/pages?cursor=opaque-k7'}. The server also has a terminal page [b,c] at that URI.

The client runs the following code with limit=2; what are its final row and HTTP-request counts?

Code for this question
do {  page = await get(path);  rows.push(...page.items);  path = page.next;} while (page.items.length === limit && path);
Pick one

Question 3 of 5

After reload, a checkout has pending operation IDs checkout-a and checkout-b. Their identical POST bodies already created separate orders, but neither response reached the screen. Each retry sends its row's operation ID in the Idempotency-Key request header. Every reply carries an X-Request-Id header and a body { id, replayed }. A wrapper turns each retry's request and its reply's headers and body into a receipt; the screen clears the pending row whose ID equals the receipt's operationId and links /orders/<orderId>.

Which wrapper clears both rows and links the orders the server created?

Pick one

Question 4 of 5

A form binds its two controls to the submitted body's members shipping.postcode and shipping.street. Its error adapter reads each errors[].pointer as an RFC 6901 JSON Pointer into that body and highlights the control bound to the member the pointer resolves to. Both values failed validation.

Which server projection highlights both controls?

Pick one

Question 5 of 5

Three deployed clients validate problem responses with Ajv 6 and render pointers only from a body that validates:

The server's next release keeps errors:[{pointer:'/billing/postcode'}] and adds supportTrace:'trace-demo'.

How many postcode highlights appear with X, Y and Z?

Code for this question
const problem = { type: 'object', required: ['type'], properties: { type: { type: 'string' }, title: {}, status: {}, detail: {}, instance: {}, errors: { type: 'array' } } };const X = new Ajv().compile({ ...problem, additionalProperties: false });const Y = new Ajv().compile(problem);const Z = new Ajv({ removeAdditional: true }).compile({ ...problem, additionalProperties: false });const highlights = (body, check) => (check(body) ? body.errors.length : 0);
Pick one

Go deeper

Pro tip

Review one success payload and one failure payload together at the API boundary. Record the documented fields the screen uses, including optional extensions. DOC tip supported by RFC 9457 §3; a contract review itself is not runtime evidence.

Sources
  • RFC 9457: problem identity, title, instance, structured extensions, evolution and human detail.
  • RFC 9110: resources, representations and methods; it does not prescribe our screen envelope or continuation format.
  • RFC 6901: JSON Pointer syntax and evaluation, read 2026-09-30 by direct fetch: “a sequence of zero or more reference tokens, each prefixed by a '/' (%x2F) character.”
  • Idempotency-Key draft-07: expired work in progress, pinned to the listed curriculum draft's archived revision. Datatracker confirms its expired status. No adopted-standard claim is made.

Checked against the tools and docs as of 2026-09-30.