Files
claudekit/skills/openapi/references/http-status-codes.md
T
2026-04-19 14:10:38 +07:00

192 lines
6.9 KiB
Markdown

# HTTP Status Codes for REST APIs
Quick reference for selecting the correct HTTP status code in REST API responses.
---
## 2xx Success
| Code | Name | When to Use |
|------|------|-------------|
| `200` | OK | General success. GET returns data, PUT/PATCH returns updated resource. |
| `201` | Created | POST successfully created a resource. Include `Location` header. |
| `202` | Accepted | Request accepted for async processing. Return a job/task ID. |
| `204` | No Content | DELETE success or PUT/PATCH with no response body needed. |
**Guidelines:**
- `200` is the default success response for GET, PUT, PATCH
- `201` must be used when a new resource is created (POST)
- `204` is preferred for DELETE (no body to return)
- `202` signals "we got it, processing later" -- return a status URL
```json
// 201 Created response
{
"id": "usr_abc123",
"name": "Jane Doe",
"createdAt": "2026-01-15T10:30:00Z"
}
// Header: Location: /v1/users/usr_abc123
```
---
## 3xx Redirection
| Code | Name | When to Use |
|------|------|-------------|
| `301` | Moved Permanently | Resource URL changed permanently. Clients should update bookmarks. |
| `302` | Found | Temporary redirect. Original URL still valid. |
| `304` | Not Modified | Conditional GET -- resource unchanged since `If-None-Match`/`If-Modified-Since`. |
| `307` | Temporary Redirect | Like 302 but preserves HTTP method. Use for API redirects. |
| `308` | Permanent Redirect | Like 301 but preserves HTTP method. |
**Guidelines:**
- Prefer `307`/`308` over `302`/`301` in APIs (method preservation)
- `304` reduces bandwidth when clients cache responses
- Always include `Location` header with redirect responses
---
## 4xx Client Errors
| Code | Name | When to Use |
|------|------|-------------|
| `400` | Bad Request | Malformed syntax, invalid JSON, failed validation. |
| `401` | Unauthorized | Missing or invalid authentication credentials. |
| `403` | Forbidden | Authenticated but lacks permission for this resource. |
| `404` | Not Found | Resource does not exist at this URL. |
| `405` | Method Not Allowed | HTTP method not supported on this endpoint. |
| `409` | Conflict | Request conflicts with current state (duplicate, version mismatch). |
| `410` | Gone | Resource existed but has been permanently deleted. |
| `415` | Unsupported Media Type | Content-Type header not supported. |
| `422` | Unprocessable Entity | Valid JSON but semantically invalid (business rule violation). |
| `429` | Too Many Requests | Rate limit exceeded. Include `Retry-After` header. |
**Guidelines:**
- `400` for **structural** issues (bad JSON, missing required field, wrong type) — caught by the parser/validator.
- `422` for **semantic** failures (email already taken, invalid state transition, quota exceeded) — caught by application logic.
- `401` means "who are you?" — `403` means "I know who you are, but no".
- `409` for optimistic-locking failures and unique-constraint violations.
- `412` for `If-Match` / `If-Unmodified-Since` precondition failures (ETag concurrency).
- `429` must include `Retry-After` with seconds until retry.
**400 vs 422 decision rule:** If the request body failed to parse or a required field is missing, return `400`. If the body parsed fine and every field has the right type but the combination violates a business rule, return `422`.
All error bodies use `application/problem+json` per **RFC 9457**.
```json
// 422 Unprocessable Entity
// Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation failed",
"status": 422,
"detail": "Request validation failed.",
"errors": [
{ "field": "email", "message": "Email already registered", "code": "conflict" },
{ "field": "age", "message": "Must be 18 or older", "code": "outOfRange" }
]
}
```
```json
// 429 Too Many Requests
// Headers: Retry-After: 60
// Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "Rate limit exceeded. Retry after 60s."
}
```
---
## 5xx Server Errors
| Code | Name | When to Use |
|------|------|-------------|
| `500` | Internal Server Error | Unhandled exception. Generic server failure. |
| `501` | Not Implemented | Endpoint exists but functionality not built yet. |
| `502` | Bad Gateway | Upstream service returned invalid response. |
| `503` | Service Unavailable | Server overloaded or in maintenance. Include `Retry-After`. |
| `504` | Gateway Timeout | Upstream service did not respond in time. |
**Guidelines:**
- `500` should never expose stack traces in production
- `503` should include `Retry-After` header and a maintenance message
- Log all 5xx errors with request context for debugging
- Return a consistent error body format for all 5xx responses
```json
// 500 Internal Server Error (production)
// Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/internal-error",
"title": "Internal server error",
"status": 500,
"detail": "An unexpected error occurred. Please try again.",
"instance": "/v1/users/usr_abc123",
"requestId": "req_7f3a9b2c"
}
```
Extension members (`requestId`, `traceId`, etc.) are encouraged by RFC 9457 — include anything that helps the caller report the bug.
---
## Decision Flowchart
```
Request received
|
+-- Is it valid syntax? -- NO --> 400 Bad Request
|
+-- Is caller authenticated? -- NO --> 401 Unauthorized
|
+-- Is caller authorized? -- NO --> 403 Forbidden
|
+-- Does resource exist? -- NO --> 404 Not Found
|
+-- Is it rate-limited? -- YES --> 429 Too Many Requests
|
+-- If-Match / If-Unmodified-Since fails? -- YES --> 412 Precondition Failed
|
+-- Does it pass business rules? -- NO --> 422 Unprocessable Entity
|
+-- Any conflicts? -- YES --> 409 Conflict
|
+-- Server error? -- YES --> 500 Internal Server Error
|
+-- Success!
GET --> 200 OK
POST --> 201 Created
PUT --> 200 OK
PATCH --> 200 OK
DELETE --> 204 No Content
```
---
## Standard Error Response Format — RFC 9457 Problem Details
Every error response uses the `application/problem+json` media type with this shape:
```json
{
"type": "https://api.example.com/problems/<problem-slug>",
"title": "Short human-readable summary",
"status": 422,
"detail": "Human-readable explanation for this occurrence.",
"instance": "/v1/users/usr_abc123",
"errors": [ /* optional: field-level validation breakdown */ ],
"requestId": "req_..."
}
```
Required fields: `type`, `title`, `status`. Everything else is optional but strongly recommended. The `type` URI should resolve to a real documentation page — that is the core benefit of RFC 9457 over ad-hoc envelopes.
*Reference: [RFC 9110 — HTTP Semantics](https://httpwg.org/specs/rfc9110.html), [RFC 9457 — Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457)*