Help us improve
Share bugs, ideas, or general feedback.
From kernel
Enforces REST API design patterns: noun-based resource naming, HTTP methods/status codes, pagination, error responses, validation, and versioning.
npx claudepluginhub ariaxhan/kernel-claude --plugin kernelHow this skill is triggered — by the user, by Claude, or both
Slash command
/kernel:apiThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
<skill id="api">
Guides REST API design patterns for production-grade endpoints including resource naming, status codes, pagination, filtering, error responses, versioning, and rate limiting. Use when designing new APIs or reviewing contracts.
Guides REST API design with standards for resource naming, versioning, and RFC 7807 error responses. Use when designing endpoints, pagination, or API structure.
Guides REST API design with best practices for HTTP methods, status codes, structured errors, pagination, versioning, and OpenAPI documentation.
Share bugs, ideas, or general feedback.
<core_principles>
<url_structure>
# Resource-based URLs
GET /api/v1/users # List
GET /api/v1/users/:id # Get one
POST /api/v1/users # Create
PUT /api/v1/users/:id # Replace
PATCH /api/v1/users/:id # Update
DELETE /api/v1/users/:id # Delete
# Sub-resources for relationships
GET /api/v1/users/:id/orders # User's orders
POST /api/v1/users/:id/orders # Create order for user
# Actions (use sparingly)
POST /api/v1/orders/:id/cancel # Verb for non-CRUD action
POST /api/v1/auth/login # Auth endpoints
# GOOD: kebab-case, plural, no verbs
/api/v1/team-members
/api/v1/orders?status=active
# BAD: verbs, singular, snake_case in URL
/api/v1/getUser
/api/v1/user
/api/v1/team_members
</url_structure>
<status_codes>
# Success
200 OK - GET, PUT, PATCH with response body
201 Created - POST (include Location header)
204 No Content - DELETE, PUT without response body
# Client Errors
400 Bad Request - Malformed JSON, validation failure
401 Unauthorized - Missing or invalid auth token
403 Forbidden - Authenticated but not authorized
404 Not Found - Resource doesn't exist
409 Conflict - Duplicate entry, state conflict
422 Unprocessable Entity - Valid JSON but semantically invalid
429 Too Many Requests - Rate limit exceeded
# Server Errors
500 Internal Server Error - Unexpected failure (never expose details)
502 Bad Gateway - Upstream service failed
503 Service Unavailable - Overloaded, include Retry-After
</status_codes>
<response_format>
{
"data": {
"id": "abc-123",
"email": "user@example.com",
"name": "Alice"
}
}
{
"data": [
{ "id": "abc-123", "name": "Alice" },
{ "id": "def-456", "name": "Bob" }
],
"meta": {
"total": 142,
"page": 1,
"per_page": 20,
"total_pages": 8
},
"links": {
"self": "/api/v1/users?page=1",
"next": "/api/v1/users?page=2",
"last": "/api/v1/users?page=8"
}
}
{
"error": {
"code": "validation_error",
"message": "Request validation failed",
"details": [
{ "field": "email", "message": "Must be valid email", "code": "invalid_format" }
]
}
}
</response_format>
``` GET /api/v1/users?page=2&per_page=20SELECT * FROM users ORDER BY created_at DESC LIMIT 20 OFFSET 20;
<!-- Cursor-based (scalable, large datasets) -->
GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20
SELECT * FROM users WHERE id > :cursor_id ORDER BY id LIMIT 21;
Use cursor for: infinite scroll, feeds, >10K rows.
Use offset for: admin dashboards, search results with page numbers.
</pagination>
<implementation>
```typescript
import { z } from "zod"
import { NextRequest, NextResponse } from "next/server"
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
})
export async function POST(req: NextRequest) {
const body = await req.json()
const parsed = createUserSchema.safeParse(body)
if (!parsed.success) {
return NextResponse.json({
error: {
code: "validation_error",
message: "Request validation failed",
details: parsed.error.issues.map(i => ({
field: i.path.join("."),
message: i.message,
code: i.code,
})),
},
}, { status: 422 })
}
const user = await createUser(parsed.data)
return NextResponse.json(
{ data: user },
{
status: 201,
headers: { Location: `/api/v1/users/${user.id}` },
},
)
}
```
# URL path versioning (recommended)
/api/v1/users
/api/v2/users
</versioning>
<rate_limiting>
HTTP/1.1 200 OK X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1640000000
HTTP/1.1 429 Too Many Requests Retry-After: 60 { "error": { "code": "rate_limit_exceeded", "message": "Rate limit exceeded. Try again in 60 seconds." } }
</rate_limiting>
<anti_patterns>
<block id="verbs_in_urls">/getUsers, /createOrder. Use HTTP methods for actions.</block>
<block id="200_for_errors">{ "status": 200, "success": false }. Use HTTP status codes.</block>
<block id="no_validation">Trusting user input. Validate everything with schemas.</block>
<block id="leaking_details">Stack traces in error responses. Generic messages for users.</block>
<block id="no_pagination">Returning all records. Pagination is mandatory for lists.</block>
</anti_patterns>
<!-- Updated 2026-03-30: Claude Code best practices, REST API 2026 patterns -->
<idempotency>
Idempotency is mandatory for state-changing operations in distributed and agentic contexts:
```typescript
// POST with idempotency key prevents duplicate operations
export async function POST(req: NextRequest) {
const idempotencyKey = req.headers.get('Idempotency-Key')
if (idempotencyKey) {
const existing = await cache.get(`idem:${idempotencyKey}`)
if (existing) return NextResponse.json(existing, { status: 200 })
}
const result = await processRequest(req)
if (idempotencyKey) {
await cache.set(`idem:${idempotencyKey}`, result, { ttl: 86400 })
}
return NextResponse.json(result, { status: 201 })
}
Idempotency keys are especially important for:
<health_and_observability> Every API should expose health and observability endpoints:
GET /health # Liveness: returns 200 if process is alive
GET /health/ready # Readiness: returns 200 if all dependencies are up
GET /health/startup # Startup probe: returns 200 once initialization complete
Health response format:
{
"status": "ok",
"version": "1.2.3",
"dependencies": {
"database": "ok",
"cache": "ok",
"queue": "degraded"
}
}
503 if any critical dependency is down. 200 with "degraded" if non-critical. </health_and_observability>
<agentic_client_design> When your API will be called by AI agents (not just browsers/mobile), additional design rules apply:
Retry-safe by default: Agents retry on timeout and network error. Every state-changing endpoint must be idempotent OR require an Idempotency-Key header. Document which one. Agents that can't safely retry will corrupt state or lose operations.
Explicit error taxonomy: Agents parse error responses to decide retry vs. abort. Use distinct error codes for distinct failure modes. "error: something went wrong" forces agents to guess.
{ "error": { "code": "rate_limit_exceeded", "retry_after": 60 } } // agent knows: wait 60s, retry
{ "error": { "code": "validation_error", "field": "email" } } // agent knows: fix input, don't retry
{ "error": { "code": "insufficient_credits" } } // agent knows: abort, escalate to human
Batch endpoints: Agents calling N endpoints in a loop amplify latency. Offer bulk variants for high-frequency reads (GET /api/v1/users/batch?ids=a,b,c) for lists an agent might traverse.
Machine-readable pagination: Always return next_cursor as a direct string, never compute
it from page + per_page. Agents parsing offset arithmetic introduce off-by-one bugs.
Cursor approach: return cursor string, agent passes it back verbatim — no math required.
</agentic_client_design>
<on_complete> agentdb write-end '{"skill":"api","endpoints_created":,"validation":"zod|pydantic|none","pagination":"cursor|offset|none","versioning":"v1|none"}'
Record endpoints added and patterns used. </on_complete>