Long-Running Operations
Work that outlives a request: 202 Accepted with an operation resource to poll, Retry-After pacing, webhooks on completion, cancellation, and result retention.
Some work takes longer than a client should wait on an open connection: exports, imports, bulk jobs, anything that calls slow third parties. Don't hold the request open and hope. Accept the work, give it an address, and let the client check back. The model follows Google AIP-151, which treats anything over about 10 seconds as long-running.
Accepting the Work
The request returns 202 Accepted right away, with a Location pointing at an operation resource:
POST /exports
Idempotency-Key: f47ac10b-58cc-4372-a567-0e02b2c3d479
Content-Type: application/json
{ "resource": "payments", "created_after": "2026-10-01T00:00:00Z" }
HTTP/1.1 202 Accepted
Location: /operations/019a0b70-1e2f-7a3b-8c4d-5e6f7a8b9c0d
Retry-After: 5
The body is the operation itself, so the client doesn't need a second request to see it. An Idempotency-Key makes the POST safe to retry without starting the job twice.
The Operation Resource
{
"id": "019a0b70-1e2f-7a3b-8c4d-5e6f7a8b9c0d",
"type": "export",
"status": "running",
"progress": 0.42,
"created_at": "2026-10-22T14:23:45Z",
"completed_at": null,
"result_url": null,
"error": null
}
status |
Meaning |
|---|---|
queued |
Accepted, not started |
running |
In progress |
succeeded |
Done; result_url points at the result |
failed |
Done; error uses the standard error format |
canceled |
Stopped at the client's request |
The terminal states are terminal, the same rule as in Payment States & Ledgers. Errors that stop the work from being accepted at all (validation, auth) are ordinary 4xx responses to the original request, never a failed operation.
Polling
GET /operations/019a0b70-1e2f-7a3b-8c4d-5e6f7a8b9c0d
- While the operation is running, the response includes
Retry-Afterwith the suggested seconds until the next poll. Clients honor it. - Polls support
If-None-Matchand return304when nothing changed (see Caching), so polling is cheap. - On success, the response is
200with the finished operation. The client followsresult_urlto fetch the result, which is a normal resource (an export file, a created object).
Webhooks on Completion
Polling is the fallback. Clients that have a webhook endpoint receive operation.succeeded or operation.failed instead, with the operation in the payload. Good clients use both: the webhook for speed, an occasional poll for anything the webhook missed.
Cancellation
POST /operations/019a0b70-1e2f-7a3b-8c4d-5e6f7a8b9c0d/cancel
- Cancellation is a request, not a guarantee. The response is
202and the operation moves tocanceledonce the worker stops. - Canceling a finished operation returns
409with the current state. - Work that has side effects (charges, emails) documents what's undone and what isn't.
Retention
Completed operations and their results are kept for 30 days, the window AIP-151 suggests, then expire. A GET on an expired operation returns 404. Results that need to live longer belong in a real resource, not an operation.
When Not to Use This
If the work reliably finishes in a second or two, keep it synchronous. An async API is more code for every client. Reach for operations when the work is slow, variable, or depends on systems you don't control.
References
Specifications
- RFC 9110: HTTP Semantics,
202 Accepted,LocationandRetry-After
Reference
- MDN: 202 Accepted, accepting work that isn't done yet
- MDN: Retry-After, pacing polls
- MDN: Location, pointing at the operation
In practice
- Google AIP-151: Long-running operations, the operation resource and its retention
- Microsoft: Azure REST API Guidelines, long-running operations and jobs