Pulp Engine Document Rendering
Get started
OpenAPI 3.0.3 v0.0.0-extracted · 80 operations across 13 tags

Pulp Engine API

Template management and document rendering API for Pulp Engine. Authenticate with X-Api-Key (scoped API keys) or X-Editor-Token (short-lived session token). See /docs for the interactive UI.

Preview

7 endpoints

post /render/preview/csv

Preview template CSV with an inline definition (editor use — no DB save required)

operationId: previewCsv
Request body
Required · TemplateDefinition object (no DB lookup — editor preview only) · application/json
Responses
200 CSV table data — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
422 Default Response
429 Default Response
POST /render/preview/csv
post /render/preview/docx

Preview template DOCX with an inline definition (editor use — no DB save required)

operationId: previewDocx
Request body
Required · TemplateDefinition object (no DB lookup — editor preview only) · application/json
Responses
200 DOCX document — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
422 Default Response
429 Default Response
POST /render/preview/docx
post /render/preview/html

Preview template HTML with an inline definition (editor use — no DB save required)

operationId: previewHtml
Request body
Required · TemplateDefinition object (no DB lookup — editor preview only) · application/json
Responses
200 Rendered HTML document — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
422 Default Response
429 Default Response
POST /render/preview/html
post /render/preview/pdf

Preview template PDF with an inline definition (editor use — no DB save required)

operationId: previewPdf
Parameters
Name In Required Description
stream query Streaming control. Unset (default) or true → chunked stream. false → force buffered response. Preview has no post-render hooks, so stream=true never errors.
Request body
Required · TemplateDefinition object (no DB lookup — editor preview only) · application/json
Responses
200 PDF document stream — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
422 Default Response
429 Default Response
POST /render/preview/pdf
post /render/preview/pptx

Preview template PPTX with an inline definition (editor use — no DB save required)

operationId: previewPptx
Request body
Required · TemplateDefinition object (no DB lookup — editor preview only) · application/json
Responses
200 PPTX presentation — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
404 Default Response
422 Default Response
429 Default Response
POST /render/preview/pptx
get /render/preview/status

Preview capability status — cached startup snapshot; always returns 200

Returns whether the preview renderer was available when this process started. This is a startup snapshot, not a perpetual guarantee: if preview is available at startup but a later individual render fails, that travels the normal 422/500 path. Raw browser failure details are kept in server logs only.

operationId: getPreviewStatus
Responses
200 Default Response
GET /render/preview/status
post /render/preview/xlsx

Preview template XLSX with an inline definition (editor use — no DB save required)

operationId: previewXlsx
Request body
Required · TemplateDefinition object (no DB lookup — editor preview only) · application/json
Responses
200 XLSX workbook — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
422 Default Response
429 Default Response
POST /render/preview/xlsx

Batch

1 endpoint

post /batch/

Execute multiple GET requests in a single HTTP call

Accepts an array of GET sub-requests targeting template read surfaces (/templates, /templates/:key, /templates/:key/versions, /templates/:key/labels). Returns an array of {status, body} responses. Max 20 items. Auth scope: admin or editor.

operationId: submitReadBatch
Request body
Required · application/json
Responses
200 Default Response
401 Default Response
403 Default Response
POST /batch/

PDF Transform

3 endpoints

post /render/pdf/insert

Insert pages into a PDF

Insert all pages from one PDF into another at a specified position.

operationId: insertPdfPages
Request body
Required · application/json
Responses
200 PDF with inserted pages
400 Default Response
401 Default Response
422 Default Response
429 Default Response
POST /render/pdf/insert
post /render/pdf/merge

Merge multiple PDFs into one

Concatenate pages from multiple PDF documents into a single PDF, in the order provided.

operationId: mergePdf
Request body
Required · application/json
Responses
200 Merged PDF
400 Default Response
401 Default Response
422 Default Response
429 Default Response
POST /render/pdf/merge
post /render/pdf/watermark

Apply a watermark to a PDF

Overlay a text or image watermark on every page (or a page range) of a PDF.

operationId: watermarkPdf
Request body
Required · application/json
Responses
200 Watermarked PDF
400 Default Response
401 Default Response
422 Default Response
429 Default Response
POST /render/pdf/watermark

Templates

20 endpoints

get /templates/

List all templates (paginated)

Returns a paginated list of templates. Use ?expand=versions,labels,lastRenders to include nested relations. When expand is present, limit is capped at 100.

operationId: listTemplates
Parameters
Name In Required Description
limit query Maximum number of items to return (1-1000, default 50)
offset query Number of items to skip
expand query Comma-separated list of relations to include: versions, labels, lastRenders. When present, list limit is capped at 100.
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
GET /templates/
post /templates/

Create a new template

operationId: createTemplate
Request body
Required · application/json
Responses
201 Default Response
400 Default Response
401 Default Response
403 Default Response
409 Default Response
POST /templates/
delete /templates/{key}

Soft-delete a template (admin only; requires If-Match header)

operationId: deleteTemplate
Parameters
Name In Required Description
key path
if-match header Current version ETag, e.g. "1.0.3"
Responses
204 Template deleted
401 Default Response
403 Default Response
404 Default Response
412 Default Response
428 Default Response
DELETE /templates/{key}
get /templates/{key}

Get a template with its current definition

operationId: getTemplate
Parameters
Name In Required Description
expand query Comma-separated list of relations to include: versions, labels, lastRenders. When present, list limit is capped at 100.
key path
Responses
200 Default Response
401 Default Response
403 Default Response
404 Default Response
GET /templates/{key}
put /templates/{key}

Update a template definition (requires If-Match header; auto-bumps version)

operationId: updateTemplate
Parameters
Name In Required Description
key path
if-match header Current version ETag, e.g. "1.0.3"
Request body
Required · application/json
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
404 Default Response
412 Default Response
428 Default Response
PUT /templates/{key}
post /templates/{key}/diff

Diff two stored versions of a template

Computes a structural diff between two stored versions of the same template key. Returns the raw TemplateDiff JSON from @pulp-engine/template-diff — metadata, renderConfig, inputSchema, fieldMapping, document-root, and document-tree changes plus aggregate counts. v1 scope: both versions must exist in the resolved tenant.

operationId: diffTemplateVersions
Parameters
Name In Required Description
key path
Request body
Required · application/json
Responses
200 Default Response
401 Default Response
403 Default Response
404 Default Response
POST /templates/{key}/diff
get /templates/{key}/form-metadata

Get form-rendering metadata for a template

Returns { key, version, title?, description?, inputSchema, hasFieldMappings } for the given template. Used by the <pulp-engine-form> embed to auto-generate a fillable form. hasFieldMappings=true signals that the template transforms raw input via fieldMappings; the v1 form refuses to render such templates because inputSchema describes the POST-MAPPING shape, not the submission shape.

operationId: getTemplateFormMetadata
Parameters
Name In Required Description
version query Exact version to pin; mutually exclusive with label
label query Label name to resolve; mutually exclusive with version
key path
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
404 Default Response
GET /templates/{key}/form-metadata
get /templates/{key}/labels

List all labels on a template

Returns every named label pointer and the version it resolves to. Dual-scoped: admin or editor.

operationId: listTemplateLabels
Parameters
Name In Required Description
key path
Responses
200 Default Response
401 Default Response
403 Default Response
404 Default Response
GET /templates/{key}/labels
delete /templates/{key}/labels/{label}

Delete a label (admin-only)

Removes the label pointer. Idempotent — succeeds silently on unknown labels as long as the template exists. Admin scope required.

operationId: deleteTemplateLabel
Parameters
Name In Required Description
key path
label path
Responses
204 Default Response
401 Default Response
403 Default Response
404 Default Response
DELETE /templates/{key}/labels/{label}
get /templates/{key}/labels/{label}

Get the template definition at a labeled version

Resolves the label to its target version and returns the full template definition. Dual-scoped: admin or editor.

operationId: getTemplateAtLabel
Parameters
Name In Required Description
key path
label path
Responses
200 Default Response
401 Default Response
403 Default Response
404 Default Response
GET /templates/{key}/labels/{label}
put /templates/{key}/labels/{label}

Create or re-point a label (admin-only)

Creates the label if it does not exist, or re-points it if it does. The target version must be a saved version. Supports optimistic concurrency via the If-Match header: when present, the label's current version must match the supplied string or the request fails with 412 Precondition Failed. When absent, the label is upserted unconditionally (for automated promotion flows that trust their own serialization). Admin scope required.

operationId: setTemplateLabel
Parameters
Name In Required Description
key path
label path
Request body
Required · application/json
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
404 Default Response
412 Default Response
PUT /templates/{key}/labels/{label}
delete /templates/{key}/sample

Delete stored sample data for a template version

Admin-only. Idempotent — returns 204 whether or not a row existed. Use ?version= to target a specific version; defaults to the current version. Subsequent GET falls through to SampleService.

operationId: deleteTemplateSample
Parameters
Name In Required Description
version query Target template version. Defaults to currentVersion.
key path
Responses
204 Default Response
401 Default Response
403 Default Response
404 Default Response
DELETE /templates/{key}/sample
get /templates/{key}/sample

Get sample preview data for a template

Returns stored sample data when present and still valid against the template's inputSchema; otherwise synthesises a payload via SampleService. Use ?version= to target a specific historical version; defaults to the current version. The source is reported in the X-Sample-Source response header.

operationId: getTemplateSample
Parameters
Name In Required Description
version query Target template version. Defaults to currentVersion.
key path
Responses
200 Sample data object matching the template's inputSchema.
401 Default Response
403 Default Response
404 Default Response
GET /templates/{key}/sample
put /templates/{key}/sample

Persist sample preview data against the template's current version

Last-write-wins: no If-Match, no ETag response header. The payload is AJV-validated against the current inputSchema before persist; invalid payloads are rejected with 422 and no row is written. Audit: emits a `template_mutation` event with operation `set_sample`.

operationId: putTemplateSample
Parameters
Name In Required Description
key path
Request body
Required · Preview data payload. Validated against the template's current inputSchema by the server; rejected with 422 on AJV failure. · application/json
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
404 Default Response
422 Default Response
PUT /templates/{key}/sample
get /templates/{key}/schema

Get the JSON Schema input definition for a template

operationId: getTemplateSchema
Parameters
Name In Required Description
key path
Responses
200 JSON Schema object describing the expected data payload
401 Default Response
403 Default Response
404 Default Response
GET /templates/{key}/schema
post /templates/{key}/validate

Validate a data payload against a template's input schema

Validates arbitrary input data against the stored template's JSON Schema. Applies field mappings (data adapter) before validation. Use this to pre-check data payloads before calling the render endpoint. This does NOT validate the template structure itself — use POST /render/validate for that.

operationId: validateTemplateData
Parameters
Name In Required Description
key path
Request body
Required · Arbitrary data object to validate against the template's input schema · application/json
Responses
200 Default Response
401 Default Response
403 Default Response
404 Default Response
POST /templates/{key}/validate
get /templates/{key}/versions

List all saved versions for a template (newest first, paginated)

operationId: listTemplateVersions
Parameters
Name In Required Description
limit query Maximum number of items to return (1-1000, default 50)
offset query Number of items to skip
key path
Responses
200 Default Response
401 Default Response
403 Default Response
GET /templates/{key}/versions
get /templates/{key}/versions/{version}

Get a template's definition at a specific version

operationId: getTemplateVersion
Parameters
Name In Required Description
key path
version path
Responses
200 Default Response
401 Default Response
403 Default Response
404 Default Response
GET /templates/{key}/versions/{version}
post /templates/{key}/versions/{version}/restore

Promote a historical version to current (admin only; requires If-Match header)

operationId: restoreTemplateVersion
Parameters
Name In Required Description
key path
version path
if-match header Current version ETag, e.g. "1.0.3"
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
404 Default Response
412 Default Response
428 Default Response
POST /templates/{key}/versions/{version}/restore
post /templates/generate

Generate a draft template from a natural-language prompt (AI)

Schema-constrained Claude tool-use call. Returns an unsaved draft TemplateDefinition validated by the same Zod schema as hand-authored saves. The caller must POST /templates to persist. Per-actor rate limited (default 5/min). Returns 503 when ANTHROPIC_API_KEY is unset. 502 is returned for any upstream failure (network, malformed tool use, or validation exhausted after repair attempts) — query the audit log (event=template_generation) for specifics.

operationId: generateTemplate
Request body
Required · application/json
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
429 Default Response
502 Default Response
503 Default Response
POST /templates/generate

Assets

3 endpoints

get /assets/

List uploaded assets, optionally filtered by name (?q=), MIME type (?mimeType=), or legacy SVG signals (?legacySvg=true)

operationId: listAssets
Parameters
Name In Required Description
q query Case-insensitive substring filter on originalName
mimeType query Exact MIME type filter (e.g. image/png). Case-insensitive.
legacySvg query Return assets matching legacy SVG detection signals: declared mimeType of image/svg+xml OR filename ending with .svg. Use this to enumerate assets that may have been uploaded before v0.27.0 SVG rejection was enforced.
limit query Maximum number of items to return (1-1000, default 50)
offset query Number of items to skip
Responses
200 Default Response
401 Default Response
403 Default Response
GET /assets/
delete /assets/{id}

Delete an asset by ID

operationId: deleteAsset
Parameters
Name In Required Description
id path
Responses
204 Asset deleted
401 Default Response
403 Default Response
404 Default Response
DELETE /assets/{id}
post /assets/upload

Upload an image asset (multipart/form-data, max 10 MB)

operationId: uploadAsset
Responses
201 Default Response
400 Default Response
401 Default Response
403 Default Response
413 Default Response
415 Default Response
POST /assets/upload

Schedules

9 endpoints

get /schedules/

List schedules

Returns a paginated list of schedule definitions. Admin scope required.

operationId: listSchedules
Parameters
Name In Required Description
limit query Maximum number of items to return (1-1000, default 50)
offset query Number of items to skip
enabled query Filter by enabled status (true/false)
Responses
200 Default Response
GET /schedules/
post /schedules/

Create a schedule

Creates a new schedule definition. Computes and stores the next fire time from the cron expression. Admin scope required.

operationId: createSchedule
Request body
Required · application/json
Responses
201 Default Response
400 Default Response
POST /schedules/
delete /schedules/{id}

Delete schedule

Deletes a schedule and all its execution history. Admin scope required.

operationId: deleteSchedule
Parameters
Name In Required Description
id path
Responses
204 Default Response
404 Default Response
DELETE /schedules/{id}
get /schedules/{id}

Get schedule by ID

Returns a single schedule definition with secrets masked. Admin scope required.

operationId: getSchedule
Parameters
Name In Required Description
id path
Responses
200 Default Response
404 Default Response
GET /schedules/{id}
patch /schedules/{id}

Partially update schedule

Updates specified fields only. JSON fields (dataSource, deliveryTargets) are full-replaced, not deep-merged. Recomputes next fire time if cron or enabled changes. Admin scope required.

operationId: updateSchedule
Parameters
Name In Required Description
id path
Request body
Required · application/json
Responses
200 Default Response
400 Default Response
404 Default Response
PATCH /schedules/{id}
put /schedules/{id}

Update schedule (full replace)

Replaces the entire schedule definition. Recomputes next fire time. Admin scope required.

operationId: replaceSchedule
Parameters
Name In Required Description
id path
Request body
Required · application/json
Responses
200 Default Response
400 Default Response
404 Default Response
PUT /schedules/{id}
get /schedules/{id}/executions

List execution history

Returns a paginated list of executions for a schedule. Admin scope required.

operationId: listScheduleExecutions
Parameters
Name In Required Description
limit query Maximum number of items to return (1-1000, default 50)
offset query Number of items to skip
status query Filter by execution status
id path
Responses
200 Default Response
404 Default Response
GET /schedules/{id}/executions
get /schedules/{id}/executions/{execId}

Get execution detail

Returns a single execution record. Admin scope required.

operationId: getScheduleExecution
Parameters
Name In Required Description
id path
execId path
Responses
200 Default Response
404 Default Response
GET /schedules/{id}/executions/{execId}
post /schedules/{id}/trigger

Manually trigger schedule

Creates an immediate execution for the schedule. Rate-limited. Admin scope required.

operationId: triggerSchedule
Parameters
Name In Required Description
id path
Responses
202 Default Response
404 Default Response
503 Default Response
POST /schedules/{id}/trigger

Auth

2 endpoints

post /auth/editor-token

Exchange an API key for a short-lived editor session token (8 hours)

operationId: createEditorToken
Request body
Required · application/json
Responses
200 Default Response
400 Default Response
401 Default Response
429 Default Response
503 Default Response
POST /auth/editor-token
get /auth/status

Check whether authentication is required and if editor login is available

operationId: getAuthStatus
Responses
200 Default Response
GET /auth/status

Admin

13 endpoints

get /admin/batch-dlq/

List batch webhook DLQ entries

Returns a paginated list of dead-lettered batch webhook delivery attempts. Each entry includes a `replayable` flag: on postgres deployments this is a three-part check (the durable batch-job row exists + the result blob is present + the DLQ entry is still `pending`). On file/sqlserver deployments it falls back to the pre-T3 in-memory check. Admin scope required. Returns 503 in file mode (requires STORAGE_MODE=postgres or sqlserver for the DLQ store).

operationId: listBatchDlq
Parameters
Name In Required Description
limit query Maximum number of items to return (1-1000, default 50)
offset query Number of items to skip
status query Filter by status: pending | replayed | abandoned
jobId query Filter by batch job id
Responses
200 Default Response
503 Default Response
GET /admin/batch-dlq/
get /admin/batch-dlq/{id}

Get a batch webhook DLQ entry

operationId: getBatchDlqEntry
Parameters
Name In Required Description
id path
Responses
200 Default Response
404 Default Response
503 Default Response
GET /admin/batch-dlq/{id}
post /admin/batch-dlq/{id}/abandon

Mark a batch webhook DLQ entry as abandoned

Marks a DLQ entry as terminal (abandoned). Does not require active tenant (terminal cleanup). Admin scope required.

operationId: abandonBatchDlqEntry
Parameters
Name In Required Description
id path
Responses
204 Default Response
404 Default Response
409 Default Response
503 Default Response
POST /admin/batch-dlq/{id}/abandon
post /admin/batch-dlq/{id}/replay

Replay a dead-lettered batch webhook delivery

Re-delivers the batch job webhook. On postgres deployments reads the webhook URL, secret, and result payload from the durable batch-job + blob stores, so replay survives API restart. On file/sqlserver deployments falls back to the in-memory job store. Returns 409 `job_expired` if the job has been purged, 409 `job_not_completed` if the job is still running, 409 `already_terminal` if already replayed/abandoned.

operationId: replayBatchDlqEntry
Parameters
Name In Required Description
id path
Responses
200 Default Response
404 Default Response
409 Default Response
503 Default Response
POST /admin/batch-dlq/{id}/replay
get /admin/schedule-dlq/

List schedule delivery DLQ entries

Returns a paginated list of dead-lettered delivery attempts. Response includes references only — the raw target config is never echoed back. Admin scope required. Returns 503 when the DLQ store is not available (file mode — requires STORAGE_MODE=postgres or sqlserver).

operationId: listScheduleDlq
Parameters
Name In Required Description
limit query Maximum number of items to return (1-1000, default 50)
offset query Number of items to skip
status query Filter by status: pending | replayed | abandoned | orphaned
scheduleId query Filter by schedule id
Responses
200 Default Response
503 Default Response
GET /admin/schedule-dlq/
get /admin/schedule-dlq/{id}

Get a schedule delivery DLQ entry

operationId: getScheduleDlqEntry
Parameters
Name In Required Description
id path
Responses
200 Default Response
404 Default Response
503 Default Response
GET /admin/schedule-dlq/{id}
post /admin/schedule-dlq/{id}/abandon

Mark a DLQ entry as abandoned

Marks a DLQ entry as terminal (abandoned). Use when an operator has determined the delivery cannot or should not be replayed. Admin scope required.

operationId: abandonScheduleDlqEntry
Parameters
Name In Required Description
id path
Responses
204 Default Response
404 Default Response
409 Default Response
503 Default Response
POST /admin/schedule-dlq/{id}/abandon
post /admin/schedule-dlq/{id}/replay

Replay a dead-lettered delivery

Re-dispatches a single failed delivery target. Rehydrates the current target config from the live schedule — an operator fix to the schedule (e.g. correcting a webhook URL) is picked up automatically. Returns 409 with a specific `code` when the replay cannot proceed: schedule_gone, schedule_mutated, render_artifact_expired, dispatcher_unavailable, already_terminal.

operationId: replayScheduleDlqEntry
Parameters
Name In Required Description
id path
Responses
200 Default Response
404 Default Response
409 Default Response
503 Default Response
POST /admin/schedule-dlq/{id}/replay
get /admin/users/

List all named editor users (key redacted)

operationId: listUsers
Responses
200 Default Response
409 Default Response
GET /admin/users/
post /admin/users/

Add a new named editor user

operationId: createUser
Request body
Required · application/json
Responses
201 Default Response
409 Default Response
422 Default Response
POST /admin/users/
delete /admin/users/{id}

Remove a named editor user (existing tokens rejected on next request)

operationId: deleteUser
Parameters
Name In Required Description
id path
Responses
200 Default Response
404 Default Response
409 Default Response
DELETE /admin/users/{id}
put /admin/users/{id}

Update an existing named editor user

operationId: updateUser
Parameters
Name In Required Description
id path
Request body
Required · application/json
Responses
200 Default Response
404 Default Response
409 Default Response
422 Default Response
PUT /admin/users/{id}
post /admin/users/reload

Re-read EDITOR_USERS_FILE and replace the in-memory registry

operationId: reloadUsers
Responses
200 Default Response
404 Default Response
409 Default Response
422 Default Response
500 Default Response
POST /admin/users/reload

Audit

2 endpoints

delete /audit-events/

Purge audit events

Deletes audit events. Supply "before" for time-based retention purges, "actor" for GDPR erasure of a specific identity, or both to scope an erasure to events older than a cutoff. At least one of "before" or "actor" is required. Admin scope required.

operationId: purgeAuditEvents
Parameters
Name In Required Description
before query ISO 8601 cutoff — events with timestamp strictly before this value are deleted
actor query Actor identifier — deletes events attributed to this actor (GDPR erasure). May be combined with "before" for scoped erasure.
Responses
200 Default Response
DELETE /audit-events/
get /audit-events/

List audit events

Returns a paginated, filterable list of audit events. Admin scope required.

operationId: listAuditEvents
Parameters
Name In Required Description
limit query Maximum number of items to return (1-1000, default 50)
offset query Number of items to skip
event query Filter by event type
operation query Filter by operation
actor query Filter by actor
resourceType query Filter by resource type
resourceId query Filter by resource ID (template key or asset ID)
since query ISO 8601 lower bound (inclusive)
until query ISO 8601 upper bound (inclusive)
Responses
200 Default Response
GET /audit-events/

Usage

2 endpoints

get /usage

Rollup render usage by time bucket or category

Aggregates render volume for the caller's tenant over a half-open [from, to) window. Temporal buckets (day/week/month) use UTC date_trunc; categorical buckets use direct group-by. Window capped at 92 days. Admin scope required. Returns 503 usage_not_available in file mode (no DB-backed store).

operationId: getUsageRollup
Parameters
Name In Required Description
from query ISO 8601 lower bound (inclusive)
to query ISO 8601 upper bound (exclusive — half-open window)
groupBy query
source query
templateKey query
mode query
Responses
200 Default Response
GET /usage
get /usage.csv

Export render usage rows as CSV for invoicing pipelines

Returns up to USAGE_EXPORT_MAX_ROWS rows as text/csv, ordered by timestamp. Hard cap 500000: if count exceeds cap, returns 413 usage_export_too_large with the count. Narrow the window to fit. Admin scope required. Returns 503 in file mode (no DB-backed store).

operationId: exportUsageCsv
Parameters
Name In Required Description
from query
to query
source query
templateKey query
mode query
Responses
200 Default Response
GET /usage.csv

Health

3 endpoints

get /capabilities

Server feature capabilities — per-format render toggles + future flags

Returns which features are currently enabled at this server. v0.60.0 only carries per-format render route booleans (pdf/html/docx/csv/xlsx/pptx). PPTX is gated by the PPTX_ENABLED env var (default true; beta in v0.60.0). Always returns 200.

operationId: getCapabilities
Responses
200 Default Response
GET /capabilities
get /health

Liveness probe — always 200 if the process is up

operationId: getHealth
Responses
200 Default Response
GET /health
get /health/ready

Readiness probe — 503 if storage is unreachable

operationId: getHealthReady
Responses
200 Default Response
503 Default Response
GET /health/ready

Nodes

2 endpoints

post /nodes/rewrite

AI-rewrite the content of a single text or heading node (SSE stream)

Streams `delta` (append) or `replace` (reset-buffer) events, optional `restart` on repair, then a terminal `done` or `error`. Response Content-Type is text/event-stream. See plan for wire contract. Returns 503 when AI generation is disabled and 409 when block-form Handlebars (`{{#each}}`, `{{#if}}`, `{{else}}`) is present in content.

operationId: rewriteNode
Request body
Required · application/json
Responses
200 Default Response
POST /nodes/rewrite
post /nodes/rewrite/outcome

Record the user's apply/dismiss choice for an AI node rewrite

operationId: recordRewriteOutcome
Request body
Required · application/json
Responses
204 Default Response
POST /nodes/rewrite/outcome

Render

13 endpoints

post /render/

Render a template to PDF

operationId: renderPdf
Parameters
Name In Required Description
stream query Streaming control. Unset (default) or true → chunked stream when no post-render hooks are registered. false → force buffered response. true + post-render hooks → 400 STREAM_INCOMPATIBLE_HOOK.
Request body
Required · application/json
Responses
200 PDF document stream — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
404 Default Response
422 Default Response
429 Default Response
POST /render/
post /render/batch

Render multiple templates to PDF in a single request

Renders up to 50 templates concurrently. Each item is rendered independently; one failure does not abort others. Default response is a single JSON envelope. When the caller sends `Accept: application/x-ndjson`, the route streams one `result` line per item in completion order as each resolves, followed by a single `summary` line with authoritative totals.

operationId: renderBatchPdf
Request body
Required · application/json
Responses
200 Batch results. JSON envelope by default; NDJSON stream when Accept: application/x-ndjson is requested.
400 Default Response
401 Default Response
403 Default Response
429 Default Response
POST /render/batch
post /render/batch/async

Submit an async PDF batch render with webhook callback

Accepts up to 50 templates. Returns 202 immediately with a job ID. Results are rendered in the background and the webhook URL receives a metadata-only notification on completion. Poll GET /render/batch/jobs/:id for full results including rendered PDFs.

operationId: submitAsyncBatchPdf
Request body
Required · application/json
Responses
202 Default Response
400 Default Response
401 Default Response
403 Default Response
429 Default Response
503 Default Response
POST /render/batch/async
post /render/batch/async/docx

Submit an async DOCX batch render with webhook callback

Accepts up to 50 templates. Returns 202 immediately with a job ID. Results are rendered in the background and the webhook URL receives a metadata-only notification on completion. Poll GET /render/batch/jobs/:id for full results including rendered DOCX bytes.

operationId: submitAsyncBatchDocx
Request body
Required · application/json
Responses
202 Default Response
400 Default Response
401 Default Response
403 Default Response
429 Default Response
503 Default Response
POST /render/batch/async/docx
post /render/batch/docx

Render multiple templates to DOCX in a single request

Renders up to 50 templates concurrently. Each item is rendered independently; one failure does not abort others. Results are returned as base64-encoded DOCX bytes in a JSON envelope.

operationId: renderBatchDocx
Request body
Required · application/json
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
429 Default Response
POST /render/batch/docx
get /render/batch/jobs/{id}

Poll async batch job status

Returns the current status and results (when complete) of an async batch render job. Results include base64-encoded rendered output. Jobs are retained after completion for at least WEBHOOK_JOB_RETENTION_SECONDS; on postgres deployments completed jobs survive API restart and remain pollable until that retention window elapses. Pending/processing jobs that survive a restart without their worker are failed at the next start via the orphan-reconciliation step.

operationId: getAsyncBatchJob
Parameters
Name In Required Description
id path
Responses
200 Default Response
401 Default Response
403 Default Response
404 Default Response
GET /render/batch/jobs/{id}
post /render/batch/pptx

Render multiple templates to PPTX in a single request (beta)

Renders up to 50 templates concurrently as PowerPoint presentations. Each item is rendered independently; one failure does not abort others. Each successful result includes a warningCount field counting structured warnings emitted by the renderer. Beta in v0.60.0 — gated by PPTX_ENABLED.

operationId: renderBatchPptx
Request body
Required · application/json
Responses
200 Default Response
400 Default Response
401 Default Response
403 Default Response
404 Default Response
429 Default Response
POST /render/batch/pptx
post /render/csv

Export a table from a template as CSV

Extracts a single table node from a template and renders it as RFC 4180 CSV. If the template contains exactly one table, it is selected automatically. If multiple tables exist, specify `tableId` to select which one.

operationId: renderCsv
Request body
Required · application/json
Responses
200 CSV data — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
404 Default Response
422 Default Response
429 Default Response
POST /render/csv
post /render/docx

Render a template to DOCX

Renders a template to a Word document (.docx). The template AST is walked directly — no browser required.

operationId: renderDocx
Request body
Required · application/json
Responses
200 DOCX document — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
404 Default Response
422 Default Response
429 Default Response
POST /render/docx
post /render/html

Render a template to HTML

operationId: renderHtml
Request body
Required · application/json
Responses
200 Rendered HTML document — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
404 Default Response
422 Default Response
429 Default Response
POST /render/html
post /render/pptx

Render a template to PPTX (beta)

Renders a template to a PowerPoint presentation (.pptx). Beta in v0.60.0 — gated by the PPTX_ENABLED env var (default true). The renderer walks the template AST directly and produces a .pptx binary via pptxgenjs. Structured warnings are surfaced via the x-render-warnings response header.

operationId: renderPptx
Request body
Required · application/json
Responses
200 PPTX presentation — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
404 Default Response
422 Default Response
429 Default Response
POST /render/pptx
post /render/validate

Validate a template for publish readiness — always registered, no HTML/PDF output

Validates a template DEFINITION for structural correctness and renderability. Performs three checks: (1) Zod structural parse, (2) blocked-asset detection, (3) trial HTML render. Use this before publishing a template. This does NOT validate input data — use POST /templates/:key/validate for that.

operationId: validateTemplateDefinition
Request body
Required · TemplateDefinition + sample data for publish-readiness validation · application/json
Responses
200 Default Response
401 Default Response
403 Default Response
429 Default Response
POST /render/validate
post /render/xlsx

Export table data from a template as XLSX

Extracts table nodes from a template and renders them into an Excel workbook. Single-table templates auto-select; multi-table templates produce one sheet per table (or filter to a single table via `tableId`).

operationId: renderXlsx
Request body
Required · application/json
Responses
200 XLSX workbook — or, when dryRun=true, a JSON DryRunResponse
400 Default Response
401 Default Response
403 Default Response
404 Default Response
422 Default Response
429 Default Response
POST /render/xlsx