API Reference · Public v1 surface
User Guide
Developer
/v1: the surface built for agents
The public developer surface authenticates with an API key instead of a user session. It currently exposes the template catalog — the same payloads as the unauthenticated /templates routes, gated by key so usage is attributable and revocable.
Authentication: x-api-key
Send any active key as the x-api-key header. Missing, invalid, or revoked keys all fail with 401 before the route runs — a revoked key is indistinguishable from an invalid one.
| Field | Type | Description |
|---|---|---|
| x-api-key* | header | A key issued by POST /api-keys: pf_ followed by 48 hex characters. |
| Field | Type | Description |
|---|---|---|
| 401 | ApiError | “Missing x-api-key header.” — the header was absent or empty. |
| 401 | ApiError | “Invalid API key.” — unknown or revoked key. |
/v1/templatesx-api-keyIdentical payload to /templates, including the category and q query parameters and the full-catalog category counts.
curl http://localhost/api/v1/templates \
-H "x-api-key: pf_9f2c4e6a8b0d1f3e5a7c9b8d7e6f5a4b3c2d1e0f9a8b7c6d"curl "http://localhost/api/v1/templates?category=mailer-boxes&q=mailer" \
-H "x-api-key: pf_9f2c4e6a8b0d1f3e5a7c9b8d7e6f5a4b3c2d1e0f9a8b7c6d"{
"templates": [
{
"id": "mailer-box",
"name": "Mailer Box",
"category": "mailer-boxes",
"kind": "box",
"defaultDimensions": { "widthMm": 230, "heightMm": 160, "depthMm": 70 },
"materials": ["cardboard", "kraft", "corrugated"],
"finishes": ["matte", "gloss", "soft-touch", "foil"],
"dielineAvailable": true,
"description": "Self-locking e-commerce mailer with tuck-in flaps. The workhorse of subscription boxes and DTC shipping."
}
],
"total": 1,
"categories": [
{ "id": "folding-cartons", "label": "Folding Cartons", "count": 1 },
{ "id": "mailer-boxes", "label": "Mailer Boxes", "count": 1 },
{ "id": "rigid-boxes", "label": "Rigid Boxes", "count": 1 },
{ "id": "shipping-boxes", "label": "Shipping Boxes", "count": 1 },
{ "id": "product-boxes", "label": "Product Boxes", "count": 2 },
{ "id": "bottles-jars", "label": "Bottles & Jars", "count": 2 },
{ "id": "cans-tins", "label": "Cans & Tins", "count": 1 },
{ "id": "pouches-bags", "label": "Pouches & Bags", "count": 1 },
{ "id": "tubes", "label": "Tubes", "count": 1 },
{ "id": "labels-sleeves", "label": "Labels & Sleeves", "count": 0 }
]
}| Field | Type | Description |
|---|---|---|
| 400 | ApiError | “Unknown template category '<id>'.” |
/v1/templates/:idx-api-keycurl http://localhost/api/v1/templates/rsc-box \
-H "x-api-key: pf_9f2c4e6a8b0d1f3e5a7c9b8d7e6f5a4b3c2d1e0f9a8b7c6d"{ "template": { "id": "rsc-box", "name": "RSC Shipping Box", … } }| Field | Type | Description |
|---|---|---|
| 404 | ApiError | “Template '<id>' was not found.” |
Why two surfaces?
The JWT surface is for the product itself — short-lived user sessions driving the web app. The v1 surface is for anything you build: scripts, integrations, AI agents. Keys are attributable (every key belongs to an account), revocable without touching the user credential, and safe to hand to an agent because they only unlock the public surface.
Handing this page to an AI agent
http://localhost/api for the Docker deployment — substitute your domain — or http://localhost:4000 for a bare-metal dev api), a key, and this page. Every request and response on it is real — the agent can verify each curl against a running stack and get the same bytes back.