API reference / Generate a document
Generate a document
One endpoint turns a published template plus JSON into a PDF.
POST /v1/generateRequest
Send a JSON body with two fields, and your API key in the
Authorization header.
| Field | Type | Description |
|---|---|---|
| templateId | stringrequired | The code of a published template you own (looks like tpl_invoice_8x2k). The API renders its currently published version. |
| data | objectrequired | A JSON object whose keys fill the template's variables. It must be an object, not an array or null. |
import { writeFile } from "node:fs/promises";
import { PdfGlyph } from "pdfglyph";
const client = new PdfGlyph({
apiKey: "YOUR_API_KEY",
baseUrl: "https://api.pdfglyph.dev",
});
const { pdf } = await client.generate({
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"
},
});
// pdf is a Uint8Array — write it to a file, return it, or stream it.
await writeFile("document.pdf", pdf);Response
On success the API returns:
201withContent-Type: application/pdf. The response body is the raw PDF bytes (sent inline asdocument.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.X-Request-Id: a correlation id present on every response, success or error. Quote it to support to have a request found in the platform logs.
There is no JSON envelope around a success: the bytes are the document. Write them straight to a
file or a response stream. The SDK surfaces all three headers as generationId,
durationMs, and requestId on the result.
Idempotency
Pass an optional Idempotency-Key header to make a generation safe to retry. Any opaque
string unique to the call works, and a UUID is ideal. 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 addsX-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's re-rendered. - Conflict. Reusing a key with a different body returns
idempotency_conflict(409): a key maps 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) with aRetry-Afterheader sized to a render. Wait that long, then retry. - 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) just re-attempts. The SDK sends this header when you pass idempotencyKey
to generate(...), flags a replay as replayed: true on the result, and uses the key to retry
network failures automatically without risking a double bill.
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). datamust satisfy the template's variables, or you getinvalid_data(422) listing what's wrong.- An unknown or unpublished template returns
template_not_found(404) ortemplate_not_published(422).
A failed call never consumes quota, so you can retry safely once you've 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.