HTTP methods for an order API: what GET, POST, PUT and DELETE promise
Choose HTTP methods for an order API by their defined effects, then document replacement, retries and deletion with concrete requests.

HTTP method semantics give a client information before it knows your application’s domain. GET requests a representation. POST asks a resource to process the supplied content. PUT creates or replaces the state of the target resource. DELETE asks to remove the association between the target URI and its current functionality. For a developer building an order API, these distinctions help decide which operations clients can repeat and which operations need an additional application contract.
The method does not describe every internal action. A GET may produce a log entry, and a DELETE may preserve a record for an audit. RFC 9110 defines the semantics in terms of the requested effect, including the distinction between safe and idempotent methods. That is the useful starting point for endpoint design.
Give the operation a target
Consider a hypothetical shop with orders and delivery preferences. The following requests express different operations:
GET /orders/418 HTTP/1.1
Host: shop.example
Accept: application/json
POST /orders HTTP/1.1
Host: shop.example
Content-Type: application/json
{"items":[{"sku":"pen-blue","quantity":2}]}
PUT /customers/73/delivery-preferences HTTP/1.1
Host: shop.example
Content-Type: application/json
{"leaveAtDoor":false,"instructions":"Ring the bell"}
The GET retrieves an order representation. The POST submits a request to the order collection, allowing the server to choose a new order identifier. The PUT names the exact preferences resource whose state the client wants to replace. If clients supply their own stable order identifiers, PUT can also be a reasonable creation method, provided the server genuinely implements its replacement semantics.
Resource names alone do not make these contracts sound. A route called /orders can still be confusing if its GET handler charges a card. A payment is an intended state change, so placing it behind GET conflicts with the method’s safe semantics. Browsers, crawlers and other clients may issue safe requests without seeking separate user approval for a state change.
Safe and idempotent answer different questions
A safe method has essentially read-only requested semantics. An idempotent method has the same intended effect after multiple identical requests as after one. GET is both safe and idempotent. PUT and DELETE are idempotent but are intended to change state. POST has no general idempotency guarantee.
Suppose a client repeats the preferences PUT because its connection closed before the response arrived. Setting the same preferences again should leave the resource in the same intended state. The server may log both attempts. It may also return a different status because the first request created the resource and the second updated it. Those differences do not, by themselves, violate idempotency.
A repeated POST creating an order can create two orders unless the API defines a deduplication mechanism. The method name cannot establish whether the first request committed. If the client needs to recover from an unknown outcome, the API can accept a stable application-level command identifier and return the recorded result for later repetitions. The identifier needs durable handling; an in-memory map disappears when the process restarts.
The repeated PUT in the diagram has one target and one replacement value. The POST box represents a new submission unless the application explicitly connects repeated attempts using a command identity.
Replacement requires a complete write contract
A PUT body need not contain every field returned by GET. The server may generate an order ID, creation time or computed total. The contract should define which client-controlled state the representation replaces and which fields the server manages.
For delivery preferences, decide what happens when instructions is absent. If omission clears the instructions, say so. If omission retains the old instructions, the operation behaves like a partial update and needs a carefully documented justification or a different method. Clients cannot infer this behavior from a TypeScript interface that merely marks the field optional.
Validation also belongs in the contract. A server should reject invalid input rather than silently make a materially different change. A value such as leaveAtDoor: "sometimes" is not the boolean representation shown above. Respond with a documented client error, keep the previous valid state, and explain which field failed.
OpenAPI 3.1.1 can describe the operation, request schema and response shapes. The document helps clients discover the contract, but an implementation test must still verify the effect on stored state.
Deletion needs a domain decision
For DELETE /orders/418, decide whether deleting an order is even permitted. A paid order may require a cancellation workflow that preserves financial history and emits a refund command. Calling that operation DELETE does not make the workflow simple. A separate cancellation resource can represent its progress when the business action has several stages.
A successful DELETE also does not necessarily mean immediate physical erasure of every record. RFC 9110 describes removing the association at the target URI. Your retention policy, audit trail and privacy obligations need their own specification. Do not promise irreversible data erasure through a route whose implementation only changes a flag.
If the first DELETE succeeds and a repeated request returns 404, the final intended state can still be the same: the target is unavailable. Decide whether your client library treats that second result as a completed deletion or as an error requiring attention.
Design the retry around the lost response
A useful failure trace separates the server’s commit from the client’s observation. Suppose POST /orders creates order 418, but the connection closes before its 201 response reaches the caller. From the client’s side, that can look the same as a request that never reached the server. Sending a second ordinary POST may create order 419. The server behaved according to its creation contract both times; the missing piece was a way to identify one command across attempts.
Stripe’s engineering article on idempotency develops this failure scenario and explains why retry safety must be designed into an API. Its approach is an application contract layered on top of HTTP. A command key connects attempts, while the service records enough of the original result to answer a replay consistently.
For an order service, decide what makes two requests the same command. Scope the key to the authenticated account or tenant, bind it to the accepted request parameters and reject reuse with incompatible content. If the key is remembered only after the order commits in a separate write, a crash between those writes still permits duplication. The order effect and replay record need a shared atomic boundary, or a recovery design that can reconstruct the missing relationship.
A PUT also needs a concurrent-edit policy. Imagine tab A sets delivery instructions to “Ring the bell,” loses the response, and waits before retrying. Tab B then changes the instructions to “Leave with reception.” A repeated unconditional PUT from tab A can overwrite tab B’s newer choice. Idempotency describes repetition of the operation’s intended effect; it does not freeze the resource against other writers.
Use a representation precondition when the caller intends to modify only the version it read. An If-Match failure tells the client to inspect current state before submitting a replacement based on new information. Do not attach a fresh validator automatically to the old body and call that conflict resolution. The user has not approved overwriting the change made in the meantime.
Test the property the method promises
A useful PUT test starts with existing preferences, sends the same valid replacement twice, and compares the final client-controlled state after each request. A useful GET test checks that retrieving an order does not trigger a new order transition. These tests examine behavior that response snapshots alone cannot establish.
Keep retry policy separate from method selection. A method’s idempotency helps a client reason about repeated requests, but credentials, conditional headers and resource changes can still affect the outcome. Document the allowed preconditions and error responses beside the method, so the client can tell when a retry repeats the same operation and when it starts a different one.