Currently Available: Need a skilled Software Developer for your next project?
Categories
APIs

Documenting Pagination and Webhook Contracts

API publishers need to tell API consumers how to request the next part of a long list and how the provider sends event notifications. An API contract lists the rules for requests and responses and explains what each outcome means. Pagination splits a long result list across multiple responses. A webhook is an HTTP request a provider sends to a consumer’s endpoint when an event occurs.

A clear contract tells consumers what to send next, how to know when the list ends, what causes the provider to retry a delivery, and what to do when retries stop. Document response formats alongside failures and recovery for the full exchange.

Show the pagination loop

Document pagination as a loop. The consumer requests a page, the server returns items and a value for the next request, and the consumer sends that value to get the next page. State the page-size parameter’s default and maximum. Explain whether the requested size sets a limit or guarantees that many items, and which request parameters the consumer must keep unchanged across pages.

A cursor tells the server where to continue a list, and its meaning depends on the API. Google’s AIP-158 pagination guidance recommends opaque page tokens, which consumers return unchanged without reading or constructing. Stripe’s credit-note line-item endpoint instead documents cursors such as starting_after as object IDs. Explain which model the endpoint uses, which direction the cursor moves, and whether consumers must keep filters or sorting unchanged.

For an opaque-token API, the exchange could look like this:

GET /records?page_size=2
{
  "items": [
    { "id": "rec_101" },
    { "id": "rec_102" }
  ],
  "next_page_token": "opaque-value"
}

The consumer uses the returned token in the next request:

GET /records?page_size=2&page_token=opaque-value

The API must give consumers one clear signal that the list has ended. In this example, an empty next_page_token means no page remains:

{
  "items": [
    { "id": "rec_103" }
  ],
  "next_page_token": ""
}

A short or empty page does not necessarily mean the list has ended. AIP-158 explicitly allows a response to contain fewer items than requested, including zero, while more results remain. Consumers should follow the token signal rather than infer completion from page length. If the API returns a total count, say whether that count is exact or an estimate and whether consumers should use it to decide when to stop.

These field names and values illustrate one contract pattern; an API should document its actual names and values. For example, AIP-158 permits a page-size default and maximum to be specified, but its example values are not universal defaults.

Describe pagination failures and recovery

Tell consumers what the server does when they send a malformed, expired, or incompatible cursor. Give the HTTP status and error body, say whether the server returns any items, and explain whether the consumer must start again from the first page. If a changed filter or sort order invalidates a cursor, name the parameters that must stay the same and explain how the server reports the mismatch.

AIP-158 recommends an invalid-argument error when a request changes parameters that must stay the same between pages. It allows the consumer to change the page size while keeping other parameters fixed. Each API sets its own cursor expiration and error responses. Document the behavior consumers will encounter; expiration periods and recovery rules vary by API. If a cursor expires, tell consumers whether they can restart the listing and whether that risks missing or repeating records.

Show what a webhook acknowledgment means

For each webhook event, document its HTTP method and content type. List the relevant headers and payload fields, and include the event type and an example request. Explain what each identifier refers to. An event ID identifies an event. A delivery ID may identify one delivery or stay the same when the provider sends it again. The provider defines these roles. Stripe recommends using event IDs to detect duplicate events, while GitHub documents that its X-GitHub-Delivery value stays the same when a delivery is sent again. Stripe's and GitHub's conventions are provider-specific and do not define shared header rules.

State the expected response and deadline, then explain what a successful response tells the provider. For GitHub webhooks, GitHub recommends a 2xx response within 10 seconds; it records 4xx and 5xx responses as failures. GitHub also recommends queuing longer work so the endpoint can respond promptly, as described in its webhook best practices.

The API owner must say whether a 2xx response means the receiver safely stored the event or finished the related business operation. These are different outcomes: the receiver can acknowledge an event after placing it in a queue that stores it reliably, then process it later. If that later work fails, document how the consumer detects and recovers from the failure. The OpenAPI Specification provides a webhooks field for describing incoming webhook requests and expected responses. Document operational details such as retry timing and replay windows separately.

Specify retries, duplicates, and terminal outcomes

Publish the conditions that trigger another attempt, the schedule or maximum retry period, whether attempts happen automatically, and how consumers recover after automatic retries stop. Do not assume that webhook providers share a retry policy:

Provider and scope Documented retry and recovery behavior
Stripe live mode Automatic delivery attempts continue for up to three days with exponential backoff.
Stripe sandbox Events receive three retry attempts over a few hours. Stripe also documents manual resend windows of up to 15 days through the Dashboard and up to 30 days through the CLI.
GitHub GitHub does not automatically redeliver failed deliveries. Consumers can redeliver them manually or build a recovery script; GitHub’s redelivery documentation covers deliveries from the past three days.

These policies belong to the named providers and environments. They do not establish a general webhook standard. Stripe’s webhook documentation also says that a manual resend does not by itself cancel automatic retries.

Describe the delivery lifecycle in terms consumers can observe: the provider attempts delivery, the receiver acknowledges or fails to acknowledge it, the provider retries if its policy permits, and automatic retries eventually end. Then say whether a manual resend or reconciliation remains available. A replay resends a delivery; it does not by itself prove that the consumer’s business state is correct.

State whether the provider guarantees delivery order or allows duplicate deliveries. An idempotent consumer can handle the same event more than once without applying the event’s effect more than once. Stripe warns that deliveries can be duplicated and can arrive out of order. It recommends using event IDs to identify duplicates and notes that separate event objects can describe the same underlying change. Consumers should not assume timestamps provide a complete ordering. If a consumer misses an event, explain whether it should retrieve current state through the API or use another reconciliation path.

What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *