Codewright Public API (v1)
Read-only, public REST API over the recipe library.
https://codewright.tools/api/v1Auth
Rate limits
Requests are limited per client IP using a sliding window: 60 requests per 60 seconds by default. When you exceed it, the response is 429 with a Retry-After header (seconds until the window resets):
{ "error": { "code": "rate_limited", "message": "Too many requests. Please retry later." } }
Logging & usage metrics
Every /api/v1/* route logs one line of structured JSON to stdout with method, path, status, and latency_ms only — no query strings, headers, or other user-identifying data. Usage counters are tracked in-memory for the lifetime of the process and reset on every deploy/restart.
Internal routes
Not part of the public v1 surface — for the operator only, gated by a shared-secret header.
GET /api/internal/usage
Returns the current usage snapshot as JSON: { "usage": { "<routeLabel>": { "count": number, "statusCounts": { "<status>": number } } } }
Set INTERNAL_API_TOKEN in the deployment environment, then send it back on each request. If unset or wrong, the route always returns 401 unauthorized — it fails closed rather than falling back to open access.
curl https://codewright.tools/api/internal/usage \
-H "x-internal-token: $INTERNAL_API_TOKEN"
Errors
All error responses share one shape: { "error": { "code": "string", "message": "human-readable", "details": "optional" } }
| Code | Status | Meaning |
|---|---|---|
| invalid_request | 400 | Failed validation — details is a list of per-field problems. |
| invalid_params | 400 | Render request had an unknown or out-of-range parameter. |
| not_found | 404 | No resource with that id. |
| rate_limited | 429 | Too many requests from this client IP; see Retry-After header. |
| upstream_error | 502 | The recipe store failed. |
| service_unavailable | 503 | The recipe store isn't configured in this environment. |
Endpoints
| Method | Path | Summary |
|---|---|---|
| GET | /recipes | List and filter recipes. |
| GET | /recipes/search | Full-text search over recipes. |
| GET | /recipes/{id} | Fetch one recipe's full document. |
| POST | /recipes/{id}/render | Render a recipe's commands for a platform with parameter values substituted in. Never executes anything server-side — it only returns text. |
| GET | /workflows | List and filter workflows. |
| GET | /workflows/search | Full-text search over workflows. |
| GET | /workflows/{id} | Fetch one workflow's full document (entry, nodes), for a client that wants to do its own node-walking. |
GET/recipes
List and filter recipes.
| Param | Type | Notes |
|---|---|---|
| category | string | one of git, docker, files, network, node, db |
| platform | string | one of macos, linux, windows |
| risk | string | one of read_only, low, medium, destructive |
| limit | integer | 1–100, default 20 |
| offset | integer | default 0 |
curl "https://codewright.tools/api/v1/recipes?category=git&risk=read_only&limit=5"
{ recipes: [...], pagination: { limit, offset, total } } — each item is a summary (id, slug, category, title, summary, risk, requires_confirmation, platforms, tags, version), not the full document.
GET/recipes/search
Full-text search over recipes.
| Param | Type | Notes |
|---|---|---|
| q | string | required, non-empty |
| limit | integer | 1–100, default 20 |
curl "https://codewright.tools/api/v1/recipes/search?q=undo%20last%20commit"
{ query, recipes: [...] } with the same summary shape as /recipes.
GET/recipes/{id}
Fetch one recipe's full document.
curl "https://codewright.tools/api/v1/recipes/git.undo_last_commit"
{ recipe: {...} }, or 404 not_found if no recipe has that id.
POST/recipes/{id}/render
Render a recipe's commands for a platform with parameter values substituted in. Never executes anything server-side — it only returns text.
| Param | Type | Notes |
|---|---|---|
| platform | string | required, one of macos, linux, windows |
| params | object | optional map of parameter name → string/number/boolean. Missing parameters fall back to the recipe's declared default. |
curl -X POST "https://codewright.tools/api/v1/recipes/network.find_process_on_port/render" \
-H "Content-Type: application/json" \
-d '{"platform": "macos", "params": {"port": 3000}}'
{ id, version, risk, requires_confirmation, commands: [{ command, purpose, risk }], verification: [{ command, expect }], undo: { possible, note? } }. Errors: invalid_platform / platform_unavailable (400), invalid_params (400), invalid_recipe_data (500).
GET/workflows
List and filter workflows.
| Param | Type | Notes |
|---|---|---|
| category | string | one of git, docker, files, network, node, db |
| limit | integer | 1–100, default 20 |
| offset | integer | default 0 |
curl "https://codewright.tools/api/v1/workflows?category=git&limit=5"
{ workflows: [...], pagination: { limit, offset, total } } — each item is a summary (id, category, title, description, tags, version), not the full node graph.
GET/workflows/search
Full-text search over workflows.
| Param | Type | Notes |
|---|---|---|
| q | string | required, non-empty |
| limit | integer | 1–100, default 20 |
curl "https://codewright.tools/api/v1/workflows/search?q=cant%20push"
{ query, workflows: [...] } with the same summary shape as /workflows.
GET/workflows/{id}
Fetch one workflow's full document (entry, nodes), for a client that wants to do its own node-walking.
curl "https://codewright.tools/api/v1/workflows/git.cant_push_branch"
{ workflow: {...} }, or 404 not_found if no workflow has that id. The stateful POST /workflows/{id}/advance route is out of scope until the runtime-interpreter design lands.
This page and the OpenAPI spec are maintained by hand against the live route handlers — when a v1 route's request/response shape changes, both are updated in the same PR.