Quaterio

API Reference

Generate PDFs, manage documents, and configure webhooks via REST API. Available on Business plan and above.

Base URL: https://quaterio.com/api/v1

Authentication

All API requests require a Bearer token in the Authorization header. Tokens use the q_ prefix and are verified via SHA-256 hash lookup.

# Include your API token in every request
curl https://quaterio.com/api/v1/documents \
-H "Authorization: Bearer q_your_api_token"

Generate API tokens in your Dashboard → Settings → API Tokens. API access requires a Business or Enterprise subscription.

PDF Generation

Generate PDFs from templates with variable replacement, raw HTML or Markdown. All endpoints accept page settings (margins, font family, columns, headers, footers) and support returnBlob: true for binary response.

POST/api/v1/generate/template

Generate a PDF from a template with variable replacement. Supports caching, print-ready output (CMYK, trim marks) and debug mode.

Show all 6 parameters
ParameterTypeDescription
templateIdrequiredstringUUID of the template, or the custom ID you set in the dashboard (e.g. "monthly_invoice")
variablesobjectKey-value pairs for template placeholders. Supports text, html, markdown, qrcode, barcode and formfield content types. A value may also be an array of objects, which fills a repeating region in the template (see below)
returnBlobbooleanReturn binary PDF instead of download URL
printReadyobjectPrint-ready output: { cmyk, outlineFonts, trimMarks, bleedMm } (Business+ only)
noCachebooleanSkip cache and regenerate
debugbooleanInclude debug information in response
curl -X POST https://quaterio.com/api/v1/generate/template \ -H "Authorization: Bearer q_..." \ -H "Content-Type: application/json" \ -d '{ "templateId": "monthly_invoice", "variables": { "company": "Acme Inc", "total": "$4,250.00", "lines": [ { "description": "Consulting", "qty": "10", "amount": "$1,500.00" }, { "description": "Design work", "qty": "3", "amount": "$2,750.00" } ] } }' # → { "ok": true, "data": { "downloadUrl": ".../invoice_en_a7f3b2c1.pdf" } } # # "lines" fills a repeating region. In the template, mark the row that should # repeat and address each field with the collection name: # # <tr data-repeat="lines"> # <td><!-- placeholder: lines.description --></td> # <td><!-- placeholder: lines.qty --></td> # <td><!-- placeholder: lines.amount --></td> # </tr> # # The row is rendered once per entry, so the template needs no fixed row count. # An empty or omitted array removes the row entirely.
POST/api/v1/generate/html

Generate a PDF from raw HTML. Accepts page settings for margins, font family, columns, headers and footers.

Show all 12 parameters
ParameterTypeDescription
htmlrequiredstringHTML content to render as PDF
returnBlobbooleanReturn binary PDF instead of download URL
pageSizestringPage size preset: a4, letter, legal, a3, a5 (default: a4). Ignored when pageWidthMm/pageHeightMm are set
pageWidthMmnumberCustom page width in mm (50-1000). Use with pageHeightMm instead of pageSize
pageHeightMmnumberCustom page height in mm (50-2000). Use with pageWidthMm instead of pageSize
marginsobjectPage margins in mm: { top, right, bottom, left } (each 0-100)
stylesobjectTypography: { h1Size, h2Size, h3Size, h4Size, h5Size, h6Size, pSize, lineHeight }
fontFamilystringFont family for the document (any Google Font name)
columnsnumberNumber of text columns, 1-4 (default: 1)
headerobjectHeader config: { content, isVisible, alignment, horizontalAlignment }
footerobjectFooter config: { content, isVisible, alignment, horizontalAlignment }
printReadyobjectPrint-ready output: { cmyk, outlineFonts, trimMarks, bleedMm } (Business+ only)
curl -X POST https://quaterio.com/api/v1/generate/html \ -H "Authorization: Bearer q_..." \ -H "Content-Type: application/json" \ -d '{ "html": "<h1>Hello World</h1><p>Your content here.</p>", "pageSize": "a4", "margins": { "top": 20, "right": 15, "bottom": 20, "left": 15 }, "fontFamily": "Inter", "columns": 1 }'
POST/api/v1/generate/markdown

Generate a PDF from Markdown. Converted to HTML internally, then rendered with the same engine.

Show all 12 parameters
ParameterTypeDescription
markdownrequiredstringMarkdown content to render as PDF
returnBlobbooleanReturn binary PDF instead of download URL
pageSizestringPage size preset: a4, letter, legal, a3, a5 (default: a4). Ignored when pageWidthMm/pageHeightMm are set
pageWidthMmnumberCustom page width in mm (50-1000). Use with pageHeightMm instead of pageSize
pageHeightMmnumberCustom page height in mm (50-2000). Use with pageWidthMm instead of pageSize
marginsobjectPage margins in mm: { top, right, bottom, left } (each 0-100)
stylesobjectTypography: { h1Size, h2Size, h3Size, h4Size, h5Size, h6Size, pSize, lineHeight }
fontFamilystringFont family for the document (any Google Font name)
columnsnumberNumber of text columns, 1-4 (default: 1)
headerobjectHeader config: { content, isVisible, alignment, horizontalAlignment }
footerobjectFooter config: { content, isVisible, alignment, horizontalAlignment }
printReadyobjectPrint-ready output: { cmyk, outlineFonts, trimMarks, bleedMm } (Business+ only)
curl -X POST https://quaterio.com/api/v1/generate/markdown \ -H "Authorization: Bearer q_..." \ -H "Content-Type: application/json" \ -d '{ "markdown": "# Report\n\nQuarterly summary...", "pageSize": "letter", "margins": { "top": 25, "right": 20, "bottom": 25, "left": 20 }, "header": { "content": "Quarterly Report", "isVisible": true } }'
POST/api/v1/generate/batch

Generate multiple PDFs from one template. Each row provides its own set of variables. Returns per-row success/failure status.

ParameterTypeDescription
templateIdrequiredstringUUID of the template, or the custom ID you set in the dashboard (e.g. "monthly_invoice")
rowsrequiredarrayArray of variable objects. Each entry generates ONE SEPARATE PDF with its own variables. Not to be confused with a repeating region inside a single document, which is an array within one entry. Business: max 100 rows. Enterprise: max 1,000
concurrencynumberParallel generation limit (default 5, max 20)
curl -X POST https://quaterio.com/api/v1/generate/batch \ -H "Authorization: Bearer q_..." \ -d '{ "templateId": "monthly_invoice", "rows": [ { "company": "Acme Inc", "total": "$1,000" }, { "company": "Globex Corp", "total": "$2,500" } ], "concurrency": 5 }'
POST/api/v1/generate/facturx

Generate an EN 16931 electronic invoice as a PDF/A-3 with the CII XML embedded (Factur-X / ZUGFeRD). Business plan and up. Totals are derived from the lines, never accepted from the request, and the finished XML is validated against the CEN Schematron before a PDF is assembled: a failing invoice returns findings rather than a file. Takes no page settings, because templateId must be a layout already checked for the placeholders the invoice needs.

ParameterTypeDescription
templateIdrequiredstringA Factur-X template: "system-facturx-simple", "-detailed" or "-modern", each also in French ("-fr") and German ("-de"), or the UUID/alias of your own layout. It must declare a placeholder for all ten required business terms or the request is rejected before rendering
invoicerequiredobjectThe invoice. Required: number (BT-1), issueDate (BT-2, YYYY-MM-DD), typeCode ("380" or "381"), currency (ISO 4217), seller, buyer, and at least one entry in lines. Optional: dueDate, deliveryDate, buyerReference, note, allowances, charges, paymentMeans, paymentTerms, prepaidAmount. All amounts are STRINGS, so no float rounding reaches a total
invoice.targetProfilestringWhich profile to target. Only "EN16931" is generated today. A target, not a declaration: what the file actually is gets read back off BT-24 after the XML is built
returnBlobbooleanDefaults to TRUE here, unlike every other generate endpoint. An invoice carries the buyer identity, the amounts and the seller bank account, so the bytes come back in the response rather than being uploaded to a URL the PDF proxy serves without authentication. Send false to get a URL instead
curl -X POST https://quaterio.com/api/v1/generate/facturx \ -H "Authorization: Bearer q_..." \ -H "Content-Type: application/json" \ --output invoice.pdf \ -d '{ "templateId": "system-facturx-simple", "invoice": { "number": "2026-0042", "issueDate": "2026-08-11", "typeCode": "380", "currency": "EUR", "dueDate": "2026-09-10", "seller": { "name": "Acme AB", "vatId": "SE556677889901", "address": { "line1": "Storgatan 1", "city": "Stockholm", "postcode": "111 22", "country": "SE" } }, "buyer": { "name": "Globex SARL", "vatId": "FR12345678901", "address": { "line1": "10 rue de Rivoli", "city": "Paris", "postcode": "75004", "country": "FR" } }, "lines": [ { "id": "1", "name": "Consulting", "quantity": "10", "unitCode": "HUR", "netPrice": "99.00", "vat": { "code": "S", "rate": "20.00" } } ], "paymentTerms": "30 days net" } }'

Resources

Create, read, update and delete documents. Give a document an alias and every endpoint here accepts that name in place of its ID. All list endpoints support pagination and language filtering.

GET/api/v1/documents

List all documents in your organization. Supports pagination and language filtering.

POST/api/v1/documents

Create a new document.

ParameterTypeDescription
titlerequiredstringDocument title
languagestringLanguage code (defaults to organization default)
GET/api/v1/documents/:id

Get a single document. :id accepts the UUID or the alias.

PATCH/api/v1/documents/:id

Update a document in place. Send only the fields you want to change. Setting an alias is what makes a document callable by name from the rest of the API.

Show all 6 parameters
ParameterTypeDescription
titlestringNew title
slugstringAlias, 1-50 characters of a-z, 0-9, dash and underscore. Send null or "" to remove it. Omit the field to leave the current alias alone: renaming a document never moves its alias.
descriptionstringFree text description
languagestringLanguage code
categoriesarrayCategory names
contentobjectThe serialized layout. The variable schema is recomputed from it, so /schema stays in step.
curl -X PATCH https://quaterio.com/api/v1/documents/<id> \ -H "Authorization: Bearer q_..." \ -H "Content-Type: application/json" \ -d '{"slug": "monthly_invoice"}' # The document is now callable by name: # POST /api/v1/generate/template {"templateId": "monthly_invoice", ...}
DELETE/api/v1/documents/:id

Delete a document. :id accepts the UUID or the alias.

GET/api/v1/documents/:id/schema

Get the variable schema for a document. Returns each placeholder name, its content type (text, html, markdown, qrcode, barcode, formfield), whether a default value is set (required = no default), and the number of occurrences. Use this so agents can bind their own data without reading the document source.

curl https://quaterio.com/api/v1/documents/monthly_invoice/schema \ -H "Authorization: Bearer q_..." # → { # "ok": true, # "data": { # "documentId": "...", # "slug": "monthly_invoice", # "title": "Monthly Invoice", # "language": "en", # "updatedAt": "2026-08-07T10:12:00.000Z", # "variables": [ # { "name": "customer_name", "contentType": "text", "required": true, "defaultValue": null, "occurrences": 2 }, # { "name": "terms_html", "contentType": "html", "required": false, "defaultValue": "<p>Standard terms apply.</p>", "occurrences": 1 }, # { "name": "tracking_qr", "contentType": "qrcode", "required": true, "defaultValue": null, "occurrences": 1, "options": { "size": 150 } } # ] # } # }
POST/api/v1/documents/:id/duplicate

Duplicate a document with optional title and language override. The alias is not copied: it is unique per organization and identifies the original.

ParameterTypeDescription
titlestringTitle for the duplicate
languagestringLanguage for the duplicate

Webhooks

Get notified when events happen. Register a URL and select which events to subscribe to. Every payload is HMAC-SHA256 signed so you can verify authenticity.

GET/api/v1/webhooks

List all webhooks in your organization.

POST/api/v1/webhooks

Create a new webhook. Returns the signing secret once. Payloads include X-Quaterio-Signature (HMAC-SHA256) and X-Quaterio-Event headers.

ParameterTypeDescription
urlrequiredstringHTTPS URL to receive webhook events
eventsrequiredstring[]Events to subscribe to: document.created, document.updated, document.deleted, document.exported, template.created, template.updated, template.deleted, content.created, content.updated, content.deleted
descriptionstringOptional label for this webhook (max 500 chars)
curl -X POST https://quaterio.com/api/v1/webhooks \ -H "Authorization: Bearer q_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/webhook", "events": ["document.exported", "template.created"], "description": "Production notifications" }' # → { "ok": true, "data": { "id": "...", "secret": "shown-only-once" } }
GET/api/v1/webhooks/:id

Get a single webhook by ID.

DELETE/api/v1/webhooks/:id

Delete a webhook.

Verifying webhook signatures

Each delivery includes two headers for verification:

HeaderDescription
X-Quaterio-SignatureHMAC-SHA256 of the raw body, prefixed with sha256=
X-Quaterio-EventThe event type, e.g. document.exported
const crypto = require('crypto'); const signature = req.headers['x-quaterio-signature']; const expected = 'sha256=' + crypto .createHmac('sha256', webhookSecret) .update(rawBody) .digest('hex'); const valid = crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected), );

Errors

All errors return a consistent JSON structure with an error code and human-readable message.

{ "ok": false, "error": { "code": "VALIDATION_ERROR", "message": "templateId is required" } }
HTTP StatusDescription
400Validation error (missing or invalid parameters)
401Missing or invalid API token
403Insufficient tier (API requires Business or Enterprise)
404Resource not found
500Internal server error

Ready to integrate?

Sign up, design a document in the visual editor, generate your first PDF via API in about five minutes.