{ "info": { "_postman_id": "2e2604e6-0683-47e5-a16f-c2713c2b869e", "name": "Kamy PDF API", "description": "Official Postman collection for the Kamy PDF generation API (kamy.dev/api/v1).\n\n## Quickstart\n\n1. Set the `apiKey` collection variable to your `kamy_pk_...` API key (Dashboard → API Keys).\n2. (Optional) Override `baseUrl` if you're hitting a non-production env. Default is `https://kamy.dev`.\n3. Pick a request from the sidebar and hit **Send**.\n\n## Auth\n\nEvery request authenticates via `Authorization: Bearer {{apiKey}}` — handled automatically through the collection-level Bearer auth setting.\n\n## Conventions\n\n- Path-variable requests (e.g. `:id`, `:slug`) need a value filled in on the **Params → Path Variables** tab.\n- Sample bodies use minimal-but-valid payloads. Swap them for your real data.\n- For long-running renders, `POST /render/async` returns `{ jobId }` and you poll `GET /jobs/:id` until `status === \"completed\"`.\n\n## Source\n\nThis collection is generated from and kept in sync with the OpenAPI spec at https://kamy.dev/api/openapi.json. Updates land in `postman/kamy.json` in the rakanalalami/kamy repo.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "variable": [ { "key": "baseUrl", "value": "https://kamy.dev", "type": "string", "description": "API base URL." }, { "key": "apiKey", "value": "kamy_pk_REPLACE_ME", "type": "string", "description": "Your Kamy API key — keep this server-only." } ], "item": [ { "name": "Health", "description": "Unauthenticated edge-runtime smoke test.", "item": [ { "name": "GET /health", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/health", "host": ["{{baseUrl}}"], "path": ["api", "v1", "health"] }, "description": "Lightweight connectivity check. Does NOT consume rate-limit budget. Good for liveness probes and SDK install verification." }, "response": [] } ] }, { "name": "Renders", "description": "Synchronous and asynchronous PDF generation.", "item": [ { "name": "POST /render", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"template\": \"invoice\",\n \"data\": {\n \"invoiceNumber\": \"INV-001\",\n \"issueDate\": \"2026-01-01\",\n \"dueDate\": \"2026-01-31\",\n \"from\": {\n \"name\": \"Acme Corp\",\n \"address\": [\n \"123 Main St\",\n \"SF, CA\"\n ]\n },\n \"to\": {\n \"name\": \"Client Inc\",\n \"address\": [\n \"456 Oak Ave\",\n \"NY, NY\"\n ]\n },\n \"lineItems\": [\n {\n \"description\": \"Consulting\",\n \"quantity\": 10,\n \"unitPrice\": 150,\n \"amount\": 1500\n }\n ],\n \"subtotal\": 1500,\n \"total\": 1500,\n \"currency\": \"USD\"\n },\n \"idempotencyKey\": \"order-12345\",\n \"options\": {\n \"validateOnly\": false\n }\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/render", "host": ["{{baseUrl}}"], "path": ["api", "v1", "render"] }, "description": "Render a PDF synchronously. Returns `{ id, url, bytes, durationMs, templateId, createdAt }`. The signed URL is valid for 1 hour.\n\nFor templates that take >30s to render, use `/render/async` instead." }, "response": [] }, { "name": "POST /render/async", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"template\": \"report\",\n \"data\": {\n \"title\": \"Q1 Report\",\n \"sections\": []\n }\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/render/async", "host": ["{{baseUrl}}"], "path": ["api", "v1", "render", "async"] }, "description": "Enqueue a render and return `{ jobId, status: 'queued' }` immediately. Poll `GET /jobs/{id}` until `status === 'completed'`, then read `result.url`." }, "response": [] }, { "name": "GET /jobs/:id", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/jobs/:id", "host": ["{{baseUrl}}"], "path": ["api", "v1", "jobs", ":id"], "variable": [ { "key": "id", "value": "job_...", "description": "Job ID from /render/async." } ] }, "description": "Poll job status. Returns `{ jobId, status, result?, error? }`." }, "response": [] }, { "name": "GET /renders", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/renders", "host": ["{{baseUrl}}"], "path": ["api", "v1", "renders"], "query": [ { "key": "limit", "value": "20" }, { "key": "cursor", "value": "", "disabled": true } ] }, "description": "List your recent renders, newest first." }, "response": [] }, { "name": "GET /renders/:id", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/renders/:id", "host": ["{{baseUrl}}"], "path": ["api", "v1", "renders", ":id"], "variable": [ { "key": "id", "value": "render_...", "description": "Render id from /render or /jobs." } ] }, "description": "Re-fetch a render by id — useful after the initial signed URL has expired." }, "response": [] }, { "name": "POST /batch", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"renders\": [\n {\n \"template\": \"invoice\",\n \"data\": {\n \"invoiceNumber\": \"INV-001\",\n \"total\": 100,\n \"currency\": \"USD\"\n }\n },\n {\n \"template\": \"receipt\",\n \"data\": {\n \"receiptNumber\": \"REC-002\",\n \"total\": 50,\n \"currency\": \"USD\"\n }\n }\n ]\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/batch", "host": ["{{baseUrl}}"], "path": ["api", "v1", "batch"] }, "description": "Render up to 100 PDFs in one request. Returns `{ results: [{ id, url, ... } | { error: { code, message } }] }`." }, "response": [] }, { "name": "POST /merge", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"renderIds\": [\n \"render_aaa\",\n \"render_bbb\"\n ]\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/merge", "host": ["{{baseUrl}}"], "path": ["api", "v1", "merge"] }, "description": "Combine 2–20 already-rendered PDFs into one document. Returns `{ id, url, bytes, ... }`." }, "response": [] }, { "name": "POST /sample", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"template\": \"invoice\"\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/sample", "host": ["{{baseUrl}}"], "path": ["api", "v1", "sample"] }, "description": "Render a system-template sample with the built-in fixture data — handy for SDK install verification and previewing the output style without supplying data." }, "response": [] } ] }, { "name": "Templates", "description": "Custom Handlebars templates: list, create, update, version, publish, rollback.", "item": [ { "name": "GET /templates", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/templates", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates"] }, "description": "List your custom templates and the 8 built-in system templates." }, "response": [] }, { "name": "POST /templates", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"slug\": \"invoice-acme\",\n \"name\": \"Acme Invoice\",\n \"html\": \"

{{title}}

\",\n \"css\": \"h1 { color: #1863dc; }\",\n \"schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"title\": {\n \"type\": \"string\"\n }\n }\n }\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/templates", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates"] }, "description": "Create a custom template. The slug must be unique to your account." }, "response": [] }, { "name": "PUT /templates (upsert)", "request": { "method": "PUT", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"slug\": \"invoice-acme\",\n \"name\": \"Acme Invoice\",\n \"html\": \"

{{title}}

\",\n \"css\": \"h1 { color: #1863dc; }\"\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/templates", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates"] }, "description": "Idempotent upsert keyed on `slug` — creates if missing, updates if present. Safe to re-run from CI on every commit. The same operation backing the `kamy push` CLI." }, "response": [] }, { "name": "GET /templates/:id", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/templates/:id", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates", ":id"], "variable": [ { "key": "id", "value": "invoice-acme", "description": "Template id (uuid) or slug." } ] }, "description": "Get a template by id OR slug — the route resolves both." }, "response": [] }, { "name": "PATCH /templates/:id", "request": { "method": "PATCH", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"name\": \"Acme Invoice (Q2 redesign)\",\n \"html\": \"

{{title}}

{{subtitle}}

\"\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/templates/:id", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates", ":id"], "variable": [ { "key": "id", "value": "invoice-acme" } ] }, "description": "Update a template's name, html, css, or schema. Accepts id or slug." }, "response": [] }, { "name": "DELETE /templates/:id", "request": { "method": "DELETE", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/templates/:id", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates", ":id"], "variable": [ { "key": "id", "value": "invoice-acme" } ] }, "description": "Soft-delete a template. Existing rendered PDFs remain accessible." }, "response": [] }, { "name": "GET /templates/:id/versions", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/templates/:id/versions", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates", ":id", "versions"], "variable": [ { "key": "id", "value": "invoice-acme" } ] }, "description": "List all snapshots for a template." }, "response": [] }, { "name": "POST /templates/:id/versions", "request": { "method": "POST", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/templates/:id/versions", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates", ":id", "versions"], "variable": [ { "key": "id", "value": "invoice-acme" } ] }, "description": "Snapshot the current draft as version N+1. Returns `{ id, version, createdAt }`." }, "response": [] }, { "name": "GET /templates/:id/versions/:version", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/templates/:id/versions/:version", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates", ":id", "versions", ":version"], "variable": [ { "key": "id", "value": "invoice-acme" }, { "key": "version", "value": "3" } ] }, "description": "Fetch a specific snapshot." }, "response": [] }, { "name": "POST /templates/:id/publish", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"version\": 3\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/templates/:id/publish", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates", ":id", "publish"], "variable": [ { "key": "id", "value": "invoice-acme" } ] }, "description": "Mark a specific version as the published one. Renders without an explicit `version` use the published version." }, "response": [] }, { "name": "POST /templates/:id/rollback", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"version\": 2\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/templates/:id/rollback", "host": ["{{baseUrl}}"], "path": ["api", "v1", "templates", ":id", "rollback"], "variable": [ { "key": "id", "value": "invoice-acme" } ] }, "description": "Restore the draft to the contents of an earlier version (does not delete intermediate versions)." }, "response": [] } ] }, { "name": "Uploads", "description": "Pre-signed asset uploads for images, fonts, and other large binaries (>6 MB request bodies).", "item": [ { "name": "POST /uploads", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"filename\": \"hero.jpg\",\n \"contentType\": \"image/jpeg\",\n \"sizeBytes\": 4200000\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/uploads", "host": ["{{baseUrl}}"], "path": ["api", "v1", "uploads"] }, "description": "Get a pre-signed PUT URL (15-min single-use). After PUTting your file body to `uploadUrl`, embed `publicUrl` (or the `kamy://asset/` shorthand) in your render data.\n\nHard cap is 100 MB per object." }, "response": [] }, { "name": "GET /uploads/:id", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/uploads/:id", "host": ["{{baseUrl}}"], "path": ["api", "v1", "uploads", ":id"], "variable": [ { "key": "id", "value": "upload_..." } ] }, "description": "Get an asset's metadata and a fresh signed download URL." }, "response": [] } ] }, { "name": "Webhooks", "description": "Subscribe to render events with HMAC-SHA256-signed deliveries.", "item": [ { "name": "GET /webhooks", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/webhooks", "host": ["{{baseUrl}}"], "path": ["api", "v1", "webhooks"] }, "description": "List your webhook subscriptions." }, "response": [] }, { "name": "POST /webhooks", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json", "disabled": false } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "body": { "mode": "raw", "raw": "{\n \"url\": \"https://example.com/hooks/kamy\",\n \"events\": [\n \"render.completed\",\n \"render.failed\"\n ]\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "{{baseUrl}}/api/v1/webhooks", "host": ["{{baseUrl}}"], "path": ["api", "v1", "webhooks"] }, "description": "Create a webhook subscription. The response contains the signing `secret` — saved only on creation. Use it with `verifyWebhook` to validate inbound deliveries." }, "response": [] }, { "name": "GET /webhooks/:id", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/webhooks/:id", "host": ["{{baseUrl}}"], "path": ["api", "v1", "webhooks", ":id"], "variable": [ { "key": "id", "value": "wh_..." } ] } }, "response": [] }, { "name": "DELETE /webhooks/:id", "request": { "method": "DELETE", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/webhooks/:id", "host": ["{{baseUrl}}"], "path": ["api", "v1", "webhooks", ":id"], "variable": [ { "key": "id", "value": "wh_..." } ] }, "description": "Remove a webhook subscription. Existing deliveries already in flight will still complete." }, "response": [] }, { "name": "POST /webhooks/:id/test", "request": { "method": "POST", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/webhooks/:id/test", "host": ["{{baseUrl}}"], "path": ["api", "v1", "webhooks", ":id", "test"], "variable": [ { "key": "id", "value": "wh_..." } ] }, "description": "Fire a synthetic `render.completed` event at the subscribed URL — useful for verifying your handler signature check before going live." }, "response": [] } ] }, { "name": "Account", "description": "Profile, plan, and live usage. All quota-free — safe to wire into hot paths.", "item": [ { "name": "GET /me", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/me", "host": ["{{baseUrl}}"], "path": ["api", "v1", "me"] }, "description": "Authenticated account profile + plan + static plan limits. Cache it — only changes on plan upgrades." }, "response": [] }, { "name": "GET /usage", "request": { "method": "GET", "header": [], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{apiKey}}", "type": "string" } ] }, "url": { "raw": "{{baseUrl}}/api/v1/usage", "host": ["{{baseUrl}}"], "path": ["api", "v1", "usage"] }, "description": "Current UTC-calendar-month render usage and remaining quota. Returns `{ plan, period: { start, end }, renders: { used, quota, remaining } }` where `quota`/`remaining` are `null` on unlimited tiers." }, "response": [] } ] } ] }