Five minutes end to end — from zero to a rendered PDF. Every call on this page is one you can paste in order: the template you create in step 2 is the template you render in step 3, with the same field keys.
A real POST /v1/render against a published template with a public, rate-capped key. The bytes you get back are the bytes a paying call returns.
curl -X POST https://api.pixldocs.com/v1/render \
-H "Authorization: Bearer pxd_YOUR_KEY" \
-H "Content-Type: application/json" \
--output invoice.pdf \
-d '{"template_id":"TEMPLATE_ID","format":"pdf","use_sample_data":true,"data":{}}'That key is public and capped on the server — it renders published gallery templates only. Sign up for a key of your own, your own templates, and webhooks.
1. Get an API key
Sign up, then open Account settings → API keys. There is one key type, pxd_, on every plan, and its renders are never watermarked. A key is scoped to one workspace and renders only the templates in that workspace.
GET /v1/templates returns {"templates":[],"total":0} until you put a document in it. That is correct, not broken — step 2 is what fills it.2. Put a document in your workspace
POST the markup and you own it outright. The @field block at the top declares the contract: those keys, and no others, are what the template will accept.
curl -X POST https://api.pixldocs.com/v1/templates \
-H "Authorization: Bearer pxd_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"kind": "html",
"name": "My first invoice",
"html_source": "{% comment %}\n@field customer_name text \"Asha Patel\"\n@field invoice_no text \"INV-2026-0001\"\n@field amount text \"1,240.00\"\n{% endcomment %}\n<h1>Invoice {{ invoice_no }}</h1>\n<p>Billed to {{ customer_name }}</p>\n<p>Amount due {{ amount }}</p>"
}'That returns 201 with the template_id and accepted_keys already listed, so there is no second call to discover the contract:
{
"template_id": "5c28bb6b-0ef5-435e-8429-d92551b14897",
"kind": "html",
"status": "draft",
"default_format": "pdf",
"accepted_keys": ["customer_name", "invoice_no", "amount"]
}For a template you did not write — one a teammate added, or one you started from in the gallery — ask for its contract rather than guessing field names:
curl https://api.pixldocs.com/v1/templates \
-H "Authorization: Bearer pxd_YOUR_KEY"
curl https://api.pixldocs.com/v1/templates/TEMPLATE_ID/fields \
-H "Authorization: Bearer pxd_YOUR_KEY"3. Render it
Same template_id, and the three keys the @field block declared:
curl -X POST https://api.pixldocs.com/v1/render \
-H "Authorization: Bearer pxd_YOUR_KEY" \
-H "Content-Type: application/json" \
--dump-header - \
--output invoice.pdf \
-d '{
"template_id": "TEMPLATE_ID",
"format": "pdf",
"data": {
"customer_name": "Asha Patel",
"invoice_no": "INV-2026-0001",
"amount": "1,240.00"
}
}'The body is the PDF itself. The headers tell you what the call cost and what, if anything, you left blank:
Content-Type: application/pdf
X-Pixldocs-Page-Count: 1
X-Pixldocs-Credits-Charged: 1
X-Pixldocs-Request-Id: f30fdf70-716e-4de2-840c-6f947930a1b5
X-Pixldocs-Unsupplied-Fields: invoice_no,amountX-Pixldocs-Unsupplied-Fields appears only when a declared field was missing from data. It renders blank rather than falling back to the sample value, so a billable document never carries someone else's placeholder.
400 unknown_fields — nothing renders and no credit is used. The response names the offending keys and lists accepted_keys, so a typo never becomes a document carrying the template's placeholder value. See Templates & fields.4. No template? Render your own HTML
If you already generate markup, you can skip templates entirely and POST it directly. Nothing is saved and there is no template_id to manage.
curl -X POST https://api.pixldocs.com/v1/render/html \
-H "Authorization: Bearer pxd_YOUR_KEY" \
-H "Content-Type: application/json" \
--output invoice.pdf \
-d '{ "html": "<h1>Invoice INV-2026-0001</h1>", "format": "pdf" }'5. Publish, or keep it private
draft— only your workspace can see it.pending_review— submitted to the public gallery, awaiting approval.published— listed in the public gallery. It does not make the template renderable by other people's API keys: a key only ever renders templates in its own workspace, and using a gallerytemplate_idfrom outside returns403 template_forbidden.
Those three strings are the whole set. Pass any of them as ?status= on the list call above; anything else is refused with 400 invalid_query_param rather than answered with an empty list.