spb/ultra-sharp-agent-skills Public
Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.
Python 100%
1<!--2Author: Simon-Pierre Boucher3Contact: contact@spboucher.ai4-->56# Patterns — Designing REST APIs78## Contents9- URL and method matrix10- Pagination envelope (cursor)11- problem+json error schemas12- Idempotency-Key handling13- OpenAPI 3.1 skeleton14- Gotchas1516## URL and method matrix1718```19GET /v1/orders list (paginated) 20020POST /v1/orders create 201 + Location21GET /v1/orders/{id} read 200 / 40422PUT /v1/orders/{id} full replace (idempotent) 200 / 40423PATCH /v1/orders/{id} partial update 200 / 404 / 40924DELETE /v1/orders/{id} delete (idempotent) 204 / 40425POST /v1/orders/{id}/refund non-CRUD action 201 / 40926GET /v1/customers/{id}/orders nested read-only view 20027```28Nest at most one level; deeper relations get top-level resources with filters29(`/v1/orders?customer_id=7`), not `/customers/7/orders/3/items/9`.3031## Pagination envelope (cursor)3233```json34{35 "data": [{ "id": "ord_01J8", "status": "active" }],36 "pagination": {37 "next_cursor": "eyJpZCI6Im9yZF8wMUo4In0",38 "has_more": true,39 "limit": 2040 }41}42```43Request: `GET /v1/orders?limit=20&cursor=eyJpZCI6...`. The cursor is opaque44(base64 of the last sort key) — clients must not parse it. `limit` above the45max of 100 → clamp to 100, do not error.4647## problem+json error schemas4849Header: `Content-Type: application/problem+json`5051```json52{53 "type": "https://api.example.com/errors/validation",54 "title": "Validation failed",55 "status": 422,56 "detail": "2 fields are invalid.",57 "instance": "/v1/orders",58 "errors": [59 { "field": "email", "message": "must be a valid email address" },60 { "field": "quantity", "message": "must be between 1 and 99" }61 ]62}63```6465Conflict example:6667```json68{69 "type": "https://api.example.com/errors/state-conflict",70 "title": "Order already shipped",71 "status": 409,72 "detail": "Order ord_01J8 cannot be cancelled after shipment.",73 "instance": "/v1/orders/ord_01J8/cancellation"74}75```7677Define the schema once under `components/schemas/Problem` and `$ref` it from78every 4xx/5xx response.7980## Idempotency-Key handling8182```python83# Pseudocode for POST endpoints that create resources.84# Keys expire after 24h — long enough for client retry storms, short enough85# to bound storage.86def create_order(request):87 key = request.headers.get("Idempotency-Key")88 if key:89 cached = idempotency_store.get(key)90 if cached:91 if cached.request_hash != hash(request.body):92 return problem(422, "Idempotency-Key reused with different body")93 return cached.response # replay, no side effect94 response = do_create(request.body) # single side effect95 if key:96 idempotency_store.put(key, hash(request.body), response, ttl_hours=24)97 return response98```99100## OpenAPI 3.1 skeleton101102```yaml103openapi: 3.1.0104info: { title: Orders API, version: 1.0.0 }105servers: [{ url: https://api.example.com/v1 }]106paths:107 /orders:108 get:109 parameters:110 - { name: limit, in: query, schema: { type: integer, maximum: 100, default: 20 } }111 - { name: cursor, in: query, schema: { type: string } }112 responses:113 "200": { $ref: "#/components/responses/OrderList" }114 "429": { $ref: "#/components/responses/Problem" }115 post:116 parameters:117 - { name: Idempotency-Key, in: header, schema: { type: string } }118 responses:119 "201": { $ref: "#/components/responses/Order" }120 "422": { $ref: "#/components/responses/Problem" }121components:122 schemas:123 Problem:124 type: object125 properties:126 type: { type: string }127 title: { type: string }128 status: { type: integer }129 detail: { type: string }130 instance: { type: string }131```132133## Gotchas134135- **404 vs 403 leaks existence.** If callers must not learn a resource exists,136 return 404 for both missing and forbidden.137- **PUT with partial body silently nulls fields** — that is correct replace138 semantics; if clients expect merge, they need PATCH. Document which you offer.139- **Offset pagination drifts** when rows are inserted mid-scan; deep offsets140 also get slow. Cursors fix both — default to them.141- **Trailing slashes**: `/orders` and `/orders/` must not be two resources.142 Redirect or normalize one to the other.143- **Enum widening is breaking for clients that switch exhaustively.** Adding144 an enum value is only additive if the spec documents "unknown values may145 appear" from v1.146- **Date-times**: always RFC 3339 UTC (`2026-08-05T14:30:00Z`); epoch147 integers and local times cause silent client bugs.148