← Blog
Networking & ISP Systems

Choose HTTP status codes for update and delete

Make status codes describe whether a mutation completed, created a resource, returned a representation, or only entered an asynchronous workflow.

By Emmanuel Corels

An update or delete does not have one universal success code. Choose the response that states what happened at the HTTP boundary: whether the action completed now, whether PUT created the target, whether a representation is returned, or whether work was merely accepted for later execution. That distinction is part of the API contract, not decoration.

A compact decision model

  • 200 OK: the action completed and the response carries a useful representation or status document.
  • 204 No Content: the action completed and there is no response content. Headers can still carry metadata.
  • 201 Created: a PUT to a previously nonexistent target successfully created its current representation. Send a Location header when the created resource's URI is available.
  • 202 Accepted: processing has not completed. The operation might still fail; provide a way to monitor its outcome.

RFC 9110 is more precise for PUT than common summaries: when PUT creates a representation at a target that had none, the origin server must send 201. When it successfully modifies an existing representation, it must send either 200 or 204.

Design the representation before picking 200 or 204

Use 200 when clients benefit from the canonical updated resource, computed fields, warnings, or a deletion receipt:

HTTP/1.1 200 OK
Content-Type: application/json
ETag: "rev-18"

{"id":42,"name":"Revised product","revision":18}

Use 204 when success and response metadata are enough:

HTTP/1.1 204 No Content
ETag: "rev-18"

A 204 response is terminated after its header section and cannot contain content. Do not serialize an empty JSON object, a success message, or stray whitespace while claiming 204. Framework middleware can accidentally append a body, so verify the wire response.

For DELETE, RFC 9110 recommends 204 when enacted with no representation, 200 when a representation describes status, and 202 when it has not yet been enacted. A tombstone or audit receipt may justify 200; a synchronous deletion needing no payload fits 204.

Do not use 202 as a faster spelling of success

HTTP/1.1 202 Accepted
Location: /operations/7
Retry-After: 3
Content-Type: application/json

{"operation_id":"7","status":"pending"}

The server needs an operation resource, webhook, event, or equivalent documented completion channel. Define terminal states, retention, authorization, retry guidance, and failure details. A worker enqueue followed by 202 is not sufficient if clients can never learn that the job failed.

Make submission idempotent where retries are expected. A client can lose the 202 response and submit again; an idempotency key or natural operation key can prevent duplicate side effects. The final operation result should identify the affected resource and its current validator.

Protect updates from lost writes

Status codes alone do not prevent two clients from overwriting each other. Return an ETag with a representation, and require a conditional mutation:

PUT /products/42 HTTP/1.1
If-Match: "rev-17"
Content-Type: application/json

{"name":"Revised product"}

If the validator no longer matches, reject the mutation rather than silently overwriting a newer revision. RFC 9110 defines 412 Precondition Failed for a false request precondition. An API that requires a precondition can use 428 Precondition Required as defined by RFC 6585. Document which validator is strong enough for mutation safety.

RFC 9110 also limits validators on successful PUT: the origin server must not send an ETag or Last-Modified validator unless it saved the received representation without transformation and the validator reflects that new representation. If the server normalizes the document, return the canonical representation and its validator through a design consistent with those semantics rather than echoing an invalid validator.

Separate method semantics from database verbs

PUT means replacing the state of the target resource with the enclosed representation; it is idempotent. It does not simply mean “run an SQL UPDATE.” PATCH applies partial modifications under the selected patch media type. POST has application-defined processing semantics. Choose the method from the resource contract, then choose the status from the outcome.

DELETE removes the association between the target URI and its current functionality; storage reclamation can happen later. That is why an enacted DELETE can succeed even if archival cleanup continues internally. Conversely, if the externally visible deletion itself is pending, 202 is honest.

Retries, repeated deletes, and absence

Idempotent means repeating the same request has the same intended effect, not that every response must have the same status. A first DELETE can return 204 and a later one 404 because the resource is now absent. Some APIs intentionally return 204 for an already-absent target to simplify retry convergence. Either can work if authorization does not leak existence and the behavior is documented and tested.

Distinguish 404 from 410 only when the server intentionally knows and communicates that the resource is permanently gone. Decide what GET returns after deletion, how caches are invalidated, and whether restoration changes identity or creates a new resource.

Pre-deployment verification

  • Test PUT against both an existing and nonexistent target; assert 200/204 versus 201 and verify persisted state.
  • Assert that every 204 response has zero content bytes and no contradictory content metadata.
  • For 202, force worker success, rejection, timeout, and dead-letter outcomes; ensure the operation resource reaches a truthful terminal state.
  • Race two conditional updates and confirm one cannot silently overwrite the other.
  • Retry after simulated connection loss and verify idempotency, audit records, cache behavior, and client SDK handling.
  • Inspect the actual gateway response. Proxies and middleware can rewrite statuses, strip validators, or manufacture bodies.

Rollback and incident recovery

Changing 200 to 204 can break clients that always parse JSON. Adding 201 can break clients that hard-code 200. Publish the behavior in the API schema, run contract tests against supported clients, and roll out behind a compatible version or capability boundary where necessary.

If an API has returned 202 for jobs that were lost, reconcile the queue, operation store, and side-effect system before replay. Mark irrecoverable operations failed instead of leaving them pending forever. If unconditional PUT caused lost updates, freeze conflicting writers, reconstruct revisions from audit history, notify owners of ambiguous records, and introduce validators before reopening writes. Rolling back code does not restore overwritten data.

Attribution and verification

This guide was prompted by xpepermint's Stack Overflow question and Daniel Vassallo's accepted answer, used under CC BY-SA. Its status-code mapping remains current. It was independently verified against the primary specification: RFC 9110 sections on PUT, DELETE, conditional requests, and 2xx responses, plus RFC 6585's 428 Precondition Required.

Primary source: Review the official reference ↗