202 Accepted is the beginning: designing APIs for long-running jobs
Give asynchronous work an operation handle, explicit states and a documented recovery path. Acceptance is different from completion.

A client submits an export request and receives HTTP 202 Accepted. The export is still queued. It may finish, fail during processing, or outlive the client's connection. The response needs to tell the client how to find out which happened.
Return an operation handle and document how clients check progress, read failures, request cancellation and retrieve results. Include what happens when the client stops waiting or reconnects later.
Return a durable handle
One REST design returns an operation identifier and a supported status URL. Document how the caller authenticates subsequent reads and how the operation is associated with its owner. Never treat an unguessable identifier as a substitute for authorization.
Google AIP-151 describes a long-running-operation model with a handle, completion state, metadata and either a final response or execution error. It is a useful design reference, not a requirement that every API adopt Google's exact resource shape or HTTP mapping.
Use states with clear transitions. An illustrative lifecycle is queued, running, succeeded, failed or cancelled. Decide which states are terminal and what progress values mean. A percentage that restarts after each internal phase should not be presented as a monotonic measure of total completion.
Separate rejection from execution failure
If the initial request is malformed or unauthorized, return the appropriate request error rather than creating a doomed job unnecessarily. If accepted work later fails, represent that failure on the operation resource using the documented error model.
Keep partial output explicit. A completed archive and a partially generated archive are different results. If an operation can succeed with per-item failures, return enough structure for callers to identify and handle those items.
Document result lifetime separately from operation lifetime. The status record may remain available after a signed download URL expires. Describe whether a caller can request a fresh URL and what happens when the underlying result is deleted.
Bound polling and waiting
Publish the recommended polling cadence and any applicable backoff or server guidance. A client should stop waiting when its local deadline expires, while preserving the handle so the caller can check later. A local timeout does not prove that server work stopped.
SDK helpers can make this convenient, but document their behavior: total waiting deadline, cancellation signal, transient polling failures and the distinction between “still running” and “failed.” A helper should not hide the operation handle inside an endless loop.
For many callers watching the same job, consider request coalescing or an appropriate notification mechanism. Either way, status reads still need authorization and the lifecycle remains the source of truth.
Cancellation is its own operation
A cancellation request can race with successful completion. Define whether it is best effort, how the caller sees the final state, and which resources may already have been created. Acknowledging a cancel request is not necessarily a guarantee that the underlying work has ended.
Google's operation management documentation illustrates this distinction for its APIs. Apply only the semantics your own implementation can deliver.
Document the whole path
Test initial rejection, queued work, execution failure, caller timeout, repeated status reads, cancellation races and expired results. Give readers one guide that connects submission, observation and retrieval.



