Summary: Look at Stripe, Shopify, GitHub or Amazon and you find
POST /orders/123/cancelandPOST /payments/456/capturewhere the tutorials promisedPUTandDELETE. This is not REST being abandoned. It is the recognition that CRUD is not expressive enough to describe every business operation — some operations are commands and state transitions, not resource manipulations. The discipline lies in knowing which is which, because action endpoints are as easy to overuse as they are to justify.
This article merges two 2026 treatments of the same question: Cloud With Azeem's semantic analysis and Umesh Kumar Yadav's catalogue of the enterprise pressures that produce the pattern in practice.
The tutorial model, and where it stops working
Every REST introduction teaches the same mapping: GET retrieves, POST creates, PUT replaces, DELETE removes. For a genuine resource — a user that can be created, fetched, replaced and removed — this communicates intent perfectly well, and Best Practices for Better RESTful API documents that baseline in detail.
The trouble starts when business behaviour is forced through the same four verbs. Consider an order that moves through Pending → Paid → Packed → Shipped → Delivered → Cancelled → Refunded. A customer clicks Cancel Order. Is that a deletion?
The order must usually remain permanently available for customer support, accounting, fraud investigation, reporting, regulatory obligations, refunds and shipment reconciliation. So DELETE /orders/123 communicates the wrong thing. The operation does not mean "make this resource cease to exist." It means "transition this order to cancelled and execute everything that transition implies."
HTTP semantics are not your database
This is the load-bearing correction, and it cuts against the most common explanation for the pattern.
The popular version of the argument runs: production systems rarely hard-delete anything — they set is_deleted, deleted_at, deleted_by for audit trails, compliance, recovery, financial reconciliation and fraud investigation — and therefore the API should not expose DELETE. That inference does not hold. HTTP semantics and database implementation are different layers. An API may perfectly well expose:
DELETE /users/123
while internally executing:
UPDATE users SET deleted_at = CURRENT_TIMESTAMP WHERE id = 123;
The HTTP operation describes what happens to the resource from the client's perspective. The storage strategy is an implementation detail — the soft-delete mechanics themselves belong to the data layer covered in Database Concepts Every Backend Developer Must Understand. A company does not need to erase a row because its API says DELETE, and it does not need to avoid DELETE because it soft-deletes internally. Soft deletion is therefore a widespread enterprise habit that correlates with avoiding DELETE — not a technical reason for it.
The real reason: commands don't fit CRUD
Cancelling an order may trigger a status change, inventory release, refund initiation, a halted warehouse workflow, notifications, loyalty recalculation, an audit record and downstream events. Calling that "delete order 123" hides nearly all of the interesting meaning.
POST /orders/123/cancel
says "execute the cancellation operation for order 123." This is the action-oriented or command-style endpoint, and it is the same command-versus-state distinction that Async APIs - don't confuse your events, commands and state draws for asynchronous interfaces: a command names an intent to change something, and conflating it with state representation loses information at the boundary. When such a command spans services, it becomes the compensating-transaction problem that What Senior Architects Know About SAGA That Juniors Don't addresses.
Business domains are full of verbs that CRUD has no slot for — approve, cancel, refund, capture, publish, archive, freeze, activate, verify, resend, retry, complete, reject. Forcing PATCH /payments/123 with {"status": "captured"} technically works, but capturing a payment is not a column update: it involves payment processors, authorization rules, accounting records, fraud checks, events and reconciliation.
POST accommodates this because POST was never only "create." HTTP defines it as submitting a representation for processing by the target resource according to that resource's own semantics — deliberately broad.
Why PUT lost ground to PATCH
PUT is not "update." It asks the server to make the resource's state correspond to the representation supplied. That is replacement, and it has a sharp edge: fields omitted from the request are, under a strict implementation, treated as removed. A client intending to change only an email can silently destroy the name and age.
Most user interfaces never replace a whole resource — a settings page changes one phone number. PATCH /users/42 with just the changed field leaves everything else alone, which is why PATCH fits modern application APIs so well.
But PATCH is not merely "partial PUT." It has its own semantics and multiple wire formats — JSON Merge Patch and JSON Patch among them — so an API still has to define precisely what its PATCH means.
Idempotency is the hard part
If one concept should precede any HTTP-method decision, it is idempotency. PUT is idempotent: resending the same representation leaves the same final state. DELETE is defined as idempotent too — once the resource is gone, repeating the request does not change the intended end state.
POST is not inherently idempotent, and that is exactly the problem in a distributed system. A client sends POST /orders/123/cancel and the connection fails. Did it happen? Should it retry? If the operation triggers a refund, a notification and an inventory update, blind retry is dangerous.
This is why serious APIs introduce idempotency keys:
Idempotency-Key: 7f2c9a...
letting the server recognize a retry as the same logical operation. The API Design Patterns Nobody Teaches You treats idempotency keys as a first-class pattern, and the point generalizes: choosing POST is easy; designing a POST operation that survives retries is the hard part. An action endpoint without a retry story is an outage waiting for a network blip.
The enterprise pressures that reinforce the pattern
Beyond semantics, several practical forces push organizations toward POST:
- Batch operations. Deleting a thousand users as a thousand
DELETEcalls is wasteful.POST /users/batch-deletewith a body of IDs gives better network efficiency, transaction management, bulk validation, per-item success/failure reporting and simpler authorization. Best Practices for Better RESTful API specifies a Bulk Delete convention for exactly this need. - Historical compatibility. HTML forms support only
GETandPOST. Older browsers, API gateways, firewalls, reverse proxies and load balancers have variously mishandledPUTandDELETE. Modern infrastructure is fine, but the standardized-on-POST habit persists in many enterprises. - Clarity for consumers.
POST /orders/123/canceltells a consumer exactly what will happen.DELETE /orders/123leaves the business semantics open to interpretation.
Where action endpoints go wrong
Restraint matters, because the pattern is seductive. Once a team discovers action endpoints, it starts producing:
POST /users/123/update-name
POST /users/123/change-email
POST /users/123/change-phone
POST /users/123/update-address
Now the API has more verbs than the business does. That is not progress. If the operation genuinely is a straightforward resource modification, PATCH /users/123 is cleaner and carries better-understood semantics. The test is whether the operation is a domain command with consequences or a field change — and a name like update-name answers that question against itself.
The mature hybrid
Large organizations do not choose between REST and actions. They layer them:
| Layer | Shape | Use for |
|---|---|---|
| Resource operations | GET/POST/PUT/PATCH/DELETE /users/{id} |
genuine CRUD on a resource |
| Partial updates | PATCH /users/{id} |
field-level edits from a UI |
| Business actions | POST /orders/{id}/cancel |
domain commands with side effects |
| Batch operations | POST /users/batch-delete |
bulk work with per-item outcomes |
This is the same posture Framework vs Methodology vs Best Practice Key Differences in Architecture argues for generally: REST is a guideline that earns deviation, not a rule that forbids it. PUT and DELETE have not disappeared — they are used more selectively, for the operations whose semantics they actually match.
Related
- Best Practices for Better RESTful API — the prescriptive resource-CRUD baseline this article nuances; its PUT/PATCH/DELETE/Bulk Delete conventions remain the default for genuine resource operations.
- The API Design Patterns Nobody Teaches You — idempotency keys, error contracts and versioning; the retry-safety discipline action endpoints depend on.
- Async APIs - don't confuse your events, commands and state — the same command-versus-state distinction, applied to asynchronous interfaces.
- What Senior Architects Know About SAGA That Juniors Don't — what happens when a business command spans services and needs compensating transactions.
- Database Concepts Every Backend Developer Must Understand — the storage layer that HTTP semantics are deliberately decoupled from.
- 10 Things You Should Avoid in Your ASP.NET Core Controllers — controller-level consequences of HTTP-method and endpoint-shape choices.
- Framework vs Methodology vs Best Practice Key Differences in Architecture — why REST is a guideline rather than a standard to be obeyed literally.
- Idempotency — the property that decides whether an action endpoint is safe to retry, collected in one place.