API reference / Generate a document

Generate a document

One endpoint turns a published template plus JSON into a PDF.

POST /v1/generate

Request

Send a JSON body with two fields, and your API key in the Authorization header.

FieldTypeDescription
templateIdstringrequiredThe code of a published template you own (looks like tpl_invoice_8x2k). The API renders its currently published version.
dataobjectrequiredA JSON object whose keys fill the template's variables. It must be an object, not an array or null.
curl -X POST https://api.pdfglyph.dev/v1/generate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "tpl_invoice_8x2k",
    "data": {
      "customer": "Northwind Ltd",
      "number": "INV-2048",
      "items": [
        {
          "description": "Consulting",
          "amount": "€900.00"
        },
        {
          "description": "Support",
          "amount": "€340.00"
        }
      ],
      "total": "€1,240.00"
    }
  }' \
  --output document.pdf

Response

On success the API returns:

  • 201 with Content-Type: application/pdf. The response body is the raw PDF bytes (sent inline as document.pdf), ready to save, email, or stream on to your user.
  • X-Generation-Id — the id of the logged generation, useful for correlating with your history.
  • X-Render-Duration-Ms — how long the render took, in milliseconds.

There is no JSON envelope around a success: the bytes are the document. Write them straight to a file or a response stream.

Idempotency

Pass an optional Idempotency-Key header — any opaque string unique to the call (a UUID is ideal) — to make a generation safe to retry. If a request is lost on the wire and you retry with the same key, you get the first outcome back instead of a second charge.

Idempotency-Key: 1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed
  • Replay. Within a 24-hour window, a repeat with the same key and the same body returns the original generation — the same X-Generation-Id, billed exactly once — and adds X-Idempotent-Replayed: true. Because PDFs are never stored, the bytes are re-rendered from the exact (immutable) template version the first call used, so the document is identical even if you have published a new version since. Send the same body on the retry, since it is re-rendered.
  • Conflict. Reusing a key with a different body returns idempotency_conflict (409) — a key must map to one request. Use a fresh key for a new request.
  • Still in flight. If the first request is still rendering when the retry arrives, you get a transient service_unavailable (503); retry shortly with backoff.
  • Scope & expiry. Keys are scoped to your account and expire after 24 hours, after which the same string is free to start a new generation.

A failed call stores no key — it consumed no quota, so retrying it (with or without the same key) simply re-attempts. The Node SDK sends this header for you when you pass idempotencyKey to generate(...).

Errors

Failures return JSON instead of a PDF, with this shape and the matching HTTP status:

{
  "error": {
    "code": "invalid_data",
    "message": "Variable 'items' must be a list, but got a string."
  }
}

The set of codes is closed and documented in Errors. A few worth knowing here:

  • The body must be valid JSON, or you get invalid_json (400).
  • data must satisfy the template's variables, or you get invalid_data (422) listing what is wrong.
  • An unknown or unpublished template returns template_not_found (404) or template_not_published (422).

A failed call never consumes quota, so you can retry safely once you have fixed the cause.

Notes

  • The endpoint is server-to-server. Call it from your backend with a key kept secret.
  • The render has a 30-second budget and external assets a 10-second timeout each. See Fonts & images.
  • Throughput and monthly volume are bounded per plan. See Rate limits & quotas.