api-design-principles/skills/http-methods/SKILL.md
This skill should be used when the user is choosing HTTP methods for API endpoints, designing CRUD operations, implementing idempotent operations, deciding between PUT and PATCH, handling bulk operations, or working with HTTP verb semantics. Covers GET, POST, PUT, PATCH, DELETE with idempotency rules, status code pairings, and real-world patterns.
npx skillsauth add oborchers/fractional-cto http-methodsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
HTTP methods are not suggestions. They are a contract between your API and every client, proxy, cache, browser, and crawler that will ever touch it. When you label an endpoint GET, you promise it is safe to retry, safe to cache, and will never modify state. When you use POST, you acknowledge that duplicates are possible without explicit safeguards. Getting this wrong does not just break conventions — it breaks infrastructure assumptions that the entire web stack relies on.
The five core methods — GET, POST, PUT, PATCH, DELETE — cover virtually every API operation. The rules below are drawn from the HTTP/1.1 specification (RFC 7231), Stripe, GitHub, Google, Zalando, and Microsoft REST API guidelines. Where these sources converge, treat the pattern as settled law.
GET retrieves a resource or collection. It is safe (no side effects), idempotent (same result every call), and cacheable.
Rules:
200 OK with the resource body, or 404 Not Found if the resource does not exist.ETag and If-None-Match headers. Return 304 Not Modified when content has not changed — this is free performance.GET /orders → 200 OK (collection)
GET /orders/789 → 200 OK (single resource)
GET /orders?status=pending&sort=-created_at → 200 OK (filtered, sorted)
GET /orders/789 + If-None-Match: "etag" → 304 Not Modified
BAD:
GET /orders/789/cancel → Modifies state via GET
GET /getOrderById?id=789 → Verb in URL, filter as query param for identity
POST creates a new resource or triggers an action. It is neither safe nor idempotent — two identical POST requests may create two resources.
Rules:
201 Created with a Location header pointing to the new resource for creation operations.200 OK or 202 Accepted for action operations (cancel, refund, send).Idempotency-Key header pattern (see the Idempotency section below).POST /orders → 201 Created + Location: /orders/790
POST /orders/789/cancel → 200 OK (action)
POST /search → 200 OK (complex query body)
POST /orders/batch → 200 OK (batch operation)
BAD:
POST /orders/list → Use GET for retrieval
POST /createOrder → Verb in URL; POST /orders is sufficient
PUT replaces the entire resource at the given URL. Calling PUT twice with the same body produces the same result — this is its defining characteristic.
Rules:
200 OK with the updated resource, or 204 No Content if no body is returned.201 Created if the resource did not exist, 200 OK if it did.PUT /users/42
{
"name": "Oliver Borchers",
"email": "[email protected]",
"role": "admin",
"is_active": true
}
→ 200 OK (all fields replaced)
BAD — partial body with PUT semantics:
PUT /users/42
{ "role": "admin" }
→ name, email, is_active are now wiped — this is correct PUT behavior,
but almost certainly not what the client intended.
Use PATCH for partial updates.
PATCH updates only the fields included in the request body. Everything else stays unchanged. This is the method most modern APIs use for updates.
Rules:
200 OK with the full updated resource so the client sees the current state.application/merge-patch+json) for simplicity. Use JSON Patch (application/json-patch+json) when you need operations like remove, move, or test.PATCH /users/42
{ "role": "admin" }
→ 200 OK (only role changed; name, email, is_active preserved)
Why PATCH over PUT for most updates: Stripe, GitHub, and Twilio all default to partial-update semantics. PUT requires the client to know and send every field, creating risk of accidental data loss when a field is forgotten. PATCH is safer for resources with many fields, for mobile clients with bandwidth constraints, and for concurrent updates where multiple clients modify different fields.
DELETE removes the resource at the given URL. It must be idempotent — deleting an already-deleted resource is not an error.
Rules:
204 No Content with no response body, or 200 OK with the deleted resource as confirmation (Stripe returns { "id": "...", "deleted": true }).204 (idempotent) or 404 — both are acceptable, but 204 is more consistent with idempotency guarantees.PATCH /users/42 { "deleted_at": "2024-..." } or POST /users/42/archive to make the intent explicit.POST /users/batch-delete with a body of IDs is the safer pattern).DELETE /users/42 → 204 No Content
DELETE /users/42 (already deleted) → 204 No Content (idempotent)
DELETE /posts/10/comments/5 → 204 No Content
BAD:
POST /users/42/delete → DELETE verb exists for this purpose
GET /deleteUser?id=42 → GET must never modify state
Use this table as a quick reference for every endpoint you design.
| Method | Idempotent? | Safe? | Cacheable? | Typical Success Codes | Request Body | |--------|-------------|-------|------------|----------------------|--------------| | GET | Yes | Yes | Yes | 200, 304 | No | | POST | No | No | No | 201, 200, 202 | Yes | | PUT | Yes | No | No | 200, 201, 204 | Yes (full) | | PATCH | Usually | No | No | 200, 204 | Yes (partial) | | DELETE | Yes | No | No | 204, 200 | Avoid | | HEAD | Yes | Yes | Yes | 200, 304 | No | | OPTIONS | Yes | Yes | No | 200, 204 | No |
Idempotency means making the same request multiple times produces the same result as making it once. This is critical for reliability — network failures force retries, and retries must not cause duplicate side effects.
Naturally idempotent (safe to retry without extra work):
Not idempotent (requires explicit safeguards):
Idempotency-Key header to make POST safely retryable. The client generates a UUID and includes it in the request header. The server stores the response keyed to this value and returns the cached response on retry.POST /v1/charges
Idempotency-Key: req_unique_abc123
Content-Type: application/json
{ "amount": 2000, "currency": "usd" }
# Client retries with the same key after timeout →
# Server returns cached response, no duplicate charge.
# Response header: Idempotent-Replayed: true
Stripe keys expire after 24 hours. This pattern is now an IETF draft standard adopted by Adyen, PayPal, and others.
GET with side effects. The most dangerous mistake. If a GET handler sends an email, creates a log entry, increments a counter, or modifies a database row, crawlers, prefetchers, and monitoring tools will trigger those side effects silently.
POST for everything. Using POST where GET, PUT, PATCH, or DELETE would be correct destroys cacheability, breaks idempotency assumptions, and forces every client to read documentation instead of relying on HTTP semantics.
PUT with partial bodies. Sending { "email": "[email protected]" } to a PUT endpoint wipes every field not included. If you mean "update one field," use PATCH.
DELETE with complex side effects. If "deleting" an order triggers refunds, emails, and inventory changes, model this as POST /orders/789/cancel instead. DELETE should mean the resource is gone, not that a complex business process kicks off.
Ignoring idempotency on POST. Any POST endpoint that charges money, sends a message, or creates a billable resource must support the Idempotency-Key pattern. Network retries are inevitable; duplicate charges are unacceptable.
Confusing 200 and 201. Return 201 Created when a POST creates a new resource. Return 200 OK when a POST triggers an action or when PUT/PATCH updates an existing resource. The distinction tells clients whether to expect a Location header.
| Scenario | GOOD | BAD | Why |
|----------|------|-----|-----|
| Fetch a user | GET /users/42 | POST /getUser | GET is for retrieval; verbs violate REST |
| Create an order | POST /orders | PUT /orders | PUT requires client to specify ID |
| Update one field | PATCH /users/42 { "role": "admin" } | PUT /users/42 { "role": "admin" } | PUT wipes unspecified fields |
| Delete a resource | DELETE /users/42 | POST /users/42/delete | DELETE verb exists for this |
| Cancel an order | POST /orders/42/cancel | DELETE /orders/42 | DELETE implies removal, not state change |
| Idempotent create | PUT /bookmarks/repo-123 | POST /bookmarks (with dedup) | PUT is naturally idempotent for upserts |
| Simple search | GET /users?q=oliver | POST /users/search | Read-only search should use GET |
| Complex search | POST /search (with body) | GET /search (with body) | GET bodies are unreliable; long queries break URLs |
Working implementations in examples/:
examples/crud-endpoint-patterns.md — Complete CRUD implementation for an /orders resource showing correct method, route, status code, and request/response body for each operation in Node.js/Express and Python/FastAPI.examples/idempotency-key-middleware.md — Idempotency-Key header middleware that prevents duplicate POST operations, with implementations in Node.js/Express and Python/FastAPI.When designing or reviewing API endpoints:
201 Created with a Location header for resource creation204 No Content and is idempotent (succeeds even if resource is already gone)Idempotency-Key headerPOST /resource/{id}/action, not GET or custom verbsPOST /orders, not POST /createOrder)tools
This skill should be used when the user invokes any /plan-* command from the planning-tools plugin (/plan-context, /plan-master, /plan-open-questions, /plan-verify, /plan-tick, /plan-progress, /plan-delete), asks how Claude Code's plan files work, asks where plans are stored, asks to author or audit a multi-phase master planning document, asks how to walk through a plan's Open Questions interactively, asks how to write progress entries, or mentions ~/.claude/plans/ or .claude/planning-tools.local.md. Provides the index of planning-tools commands, the master-plan workflow lifecycle, the v0.3.0+ list-shape mandate (phases and questions as headings + bulleted scope items, never tables), the v0.3.2+ plain-bullet shape (no `- [ ]` checkboxes — heading emoji is the sole tick signal), the progress-entry methodology, and the mechanics of Claude Code's plan-mode file storage.
testing
This skill should be used by the plan-verifier agent and the /plan-verify command to audit a drafted master plan against a fixed checklist. Covers universal-core completeness, the v0.3.0+ no-tables-for-phases-or-questions rule, trigger-based section-coverage gaps, phase actionability (heading + per-phase TL;DR + bulleted scope + exit criteria), the v0.3.1+ per-phase TL;DR requirement, the v0.3.2+ plain-bullet scope shape (legacy `- [ ]`/`- [x]` accepted silently), the v0.3.3+ context-block shape (plan-level `**TL;DR:**` + bulleted metadata, legacy `>` blockquote accepted silently), integer phase numbering enforcement, dependency traceability, citation resolution, callout/evidence convention compliance, Open Questions placement, and the one-PR-per-master-plan rule. Single-owner of the audit checklist.
tools
This skill should be used when authoring, reviewing, or modifying a multi-phase master planning document via the planning-tools plugin (especially the /plan-master and /plan-verify commands). Codifies the universal core sections, trigger-based optional sections, integer-only phase numbering, Open Questions placement, one-PR-per-plan rule, status conventions, evidence attribution, callouts, cross-reference formats, the v0.3.0 list-shape mandate (phases and questions are heading + bulleted list, never markdown tables), the v0.3.1 per-phase TL;DR requirement (1–3 sentence what/why summary under each phase heading for glance-ability), the v0.3.2 plain-bullet scope shape (`- <action>` items, no `- [ ]` checkboxes — the phase status emoji is the sole tick signal), and the v0.3.3 context-block shape (a plan-level `**TL;DR:**` + a bulleted metadata list instead of a `>` blockquote; legacy blockquote blocks accepted silently). Project-agnostic — no ticket-prefix or plan-type taxonomy.
testing
This skill should be used when the user is adjusting spacing, padding, margins, content density, section gaps, vertical rhythm, or separation between elements. Also applies when reviewing whether a design feels cramped or too sparse, choosing between borders and whitespace for separation, or defining a spacing system. Covers the 4px/8px spacing system, macro vs micro whitespace, content density spectrum, separation techniques (whitespace > background shifts > borders), and vertical rhythm.