← Ristmark

Generating with the /v1 API

Generate documents programmatically with a keyed REST API. Full reference: Swagger UI.

1. Create an API key

Settings → API Keys → Create. Copy the plk_live_… key when it’s shown — it isn’t displayed again. Keyed usage is billed to your plan.

What a key can do. A key is scoped to /v1 and nothing else: generate documents, list your approved templates, and export an approved template’s definition together with the images that template uses. It cannot write anything — no editing, creating or deleting templates, assets, usage or billing — and it cannot create or revoke keys; those need a signed-in session. So a key sitting in a CI secret or a Zapier connection is a generation-and-read credential, not a workspace login. A leaked one can spend your generation allowance and read your approved templates and their images until you revoke it.

2. Authenticate

Send the key as a Bearer token or the x-api-key header:

Authorization: Bearer plk_live_xxx
# or
x-api-key: plk_live_xxx

3. Generate a document

POST /v1/documents with a template id and the data to bind:

curl -X POST https://api.ristmark.app/v1/documents \
  -H "Authorization: Bearer plk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "templateId": "<id>", "format": "pdf", "data": { "invoiceNo": "NW-2026-0184" } }'

Returns the rendered document bytes. format is pdf (default), html or docx. Omit data (or pass null) to render the template’s sample data — the response then carries a sample-data-used warning (see below) so you can tell a real render from a demo one. Only approved templates are generatable via the API.

4. Discover templates

GET /v1/templates lists the caller’s approved templates only — in_review (freshly migrated, unconfirmed) templates never appear here, matching the trust fence on generation itself:

curl https://api.ristmark.app/v1/templates \
  -H "Authorization: Bearer plk_live_xxx"

Each item is { id, name, latestVersion? }. This route is read-only discovery — it isn’t metered.

5. Export a template

GET /v1/templates/{id}/export returns the template itself — the reusable artifact, not a rendered output of it:

curl -OJ https://api.ristmark.app/v1/templates/<id>/export \
  -H "Authorization: Bearer plk_live_xxx"

The response is a ristmark.template.v1 envelope: the template’s canonical JSON (blocks, field definitions, $data bindings, $t translations, width tokens, locale and currency) plus the images and letterheads it references, base64-embedded, so the bundle stands on its own. Digests are computed over the bytes the response carries, so it verifies itself.

Two lists tell you where a bundle is not complete, rather than leaving you to find out: unresolvedAssetIds names an asset this workspace could not produce bytes for, and dataBoundImageSrc names any image whose src is a {{ }} binding — that picture is chosen by the data you generate with, so no export can enumerate it. The bundle carries whichever of those the template’s own sample data resolves to; bring the rest yourself. Both are empty for the ordinary template.

?version=<n> exports a published revision; omit it for the current working copy — the same meaning version has on POST /v1/documents. Approved templates only, same trust fence as generation.

If a template’s assets are too large to embed in one response, the route refuses with 413 and names ?assets=omit, which returns the same template JSON plus an asset manifest (ids, MIME types, sizes, SHA-256 digests) without the bytes.

This route is not metered, so an export does not consume your generation allowance.

There is no import endpoint yet: re-creating an exported template in a Ristmark workspace today means posting its template object to the signed-in workspace API and re-uploading its assets.

6. Generate in bulk

POST /v1/documents/batch with up to 25 items, each { templateId, data, format } — each item is metered as one generation, minimum (a very long generated item — over 20 rendered pages — counts one extra generation per additional 20 pages; see Pricing & limits). A response looks like:

{
  "requested": 2,
  "succeeded": 2,
  "results": [
    { "index": 0, "status": "ok", "format": "pdf", "document": "<base64>" },
    { "index": 1, "status": "ok", "format": "pdf", "document": "<base64>" }
  ]
}

Partial success is the contract: one bad item never fails the others — a failed item comes back as { "status": "error", "error": { "code", "message" } } instead of stopping the batch. Batch warnings ride per-item JSON (each result’s warnings array), not the X-Ristmark-Render-Warnings header — a single HTTP response covers up to 25 items, so there’s no one header to carry a per-item signal on.

Have many rows of tabular data instead of a hand-built JSON array? Generate from a spreadsheet covers the CSV/Excel upload path (up to 200 rows) instead.

7. Limits & errors

  • 402 — plan inactive or over cap. 429 — rate limited. 404 — template not found / not approved. 422 — the template produced no visible pages for the given data (every section hidden by its visibility condition). 400 — invalid request (bad UUID, bad format, empty/over-cap batch, unrecognised assets value). 413 — an export’s assets are too large to embed; retry with ?assets=omit.
  • A degraded-but-still-usable render (e.g. a DOCX that dropped its letterhead) is reported in the X-Ristmark-Render-Warnings response header — the document still returns; it’s a signal to look closer, not a failure.
  • sample-data-used rides the same warning channel when data was omitted/null and the render fell back to the template’s sample data — worth checking for if you expected a real render.
  • See Pricing & limits for per-plan caps.