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)
previewCsv application/json POST /render/preview/csv post /render/preview/docx
Preview template DOCX with an inline definition (editor use — no DB save required)
previewDocx application/json POST /render/preview/docx post /render/preview/html
Preview template HTML with an inline definition (editor use — no DB save required)
previewHtml application/json POST /render/preview/html post /render/preview/pdf
Preview template PDF with an inline definition (editor use — no DB save required)
previewPdf | 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. |
application/json POST /render/preview/pdf post /render/preview/pptx
Preview template PPTX with an inline definition (editor use — no DB save required)
previewPptx application/json 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.
getPreviewStatus GET /render/preview/status post /render/preview/xlsx
Preview template XLSX with an inline definition (editor use — no DB save required)
previewXlsx application/json 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.
submitReadBatch application/json 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.
insertPdfPages application/json 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.
mergePdf application/json 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.
watermarkPdf application/json 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.
listTemplates | 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. |
GET /templates/ post /templates/
Create a new template
createTemplate application/json POST /templates/ delete /templates/{key}
Soft-delete a template (admin only; requires If-Match header)
deleteTemplate | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ | |
| if-match | header | Current version ETag, e.g. "1.0.3" |
DELETE /templates/{key} get /templates/{key}
Get a template with its current definition
getTemplate | 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 | ✓ |
GET /templates/{key} put /templates/{key}
Update a template definition (requires If-Match header; auto-bumps version)
updateTemplate | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ | |
| if-match | header | Current version ETag, e.g. "1.0.3" |
application/json 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.
diffTemplateVersions | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ |
application/json 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.
getTemplateFormMetadata | 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 | ✓ |
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.
listTemplateLabels | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ |
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.
deleteTemplateLabel | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ | |
| label | path | ✓ |
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.
getTemplateAtLabel | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ | |
| label | path | ✓ |
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.
setTemplateLabel | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ | |
| label | path | ✓ |
application/json 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.
deleteTemplateSample | Name | In | Required | Description |
|---|---|---|---|
| version | query | Target template version. Defaults to currentVersion. | |
| key | path | ✓ |
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.
getTemplateSample | Name | In | Required | Description |
|---|---|---|---|
| version | query | Target template version. Defaults to currentVersion. | |
| key | path | ✓ |
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`.
putTemplateSample | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ |
application/json PUT /templates/{key}/sample get /templates/{key}/schema
Get the JSON Schema input definition for a template
getTemplateSchema | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ |
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.
validateTemplateData | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ |
application/json POST /templates/{key}/validate get /templates/{key}/versions
List all saved versions for a template (newest first, paginated)
listTemplateVersions | 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 | ✓ |
GET /templates/{key}/versions get /templates/{key}/versions/{version}
Get a template's definition at a specific version
getTemplateVersion | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ | |
| version | path | ✓ |
GET /templates/{key}/versions/{version} post /templates/{key}/versions/{version}/restore
Promote a historical version to current (admin only; requires If-Match header)
restoreTemplateVersion | Name | In | Required | Description |
|---|---|---|---|
| key | path | ✓ | |
| version | path | ✓ | |
| if-match | header | Current version ETag, e.g. "1.0.3" |
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.
generateTemplate application/json 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)
listAssets | 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 |
GET /assets/ delete /assets/{id}
Delete an asset by ID
deleteAsset | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
DELETE /assets/{id} post /assets/upload
Upload an image asset (multipart/form-data, max 10 MB)
uploadAsset POST /assets/upload Schedules
9 endpoints
get /schedules/
List schedules
Returns a paginated list of schedule definitions. Admin scope required.
listSchedules | 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) |
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.
createSchedule application/json POST /schedules/ delete /schedules/{id}
Delete schedule
Deletes a schedule and all its execution history. Admin scope required.
deleteSchedule | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
DELETE /schedules/{id} get /schedules/{id}
Get schedule by ID
Returns a single schedule definition with secrets masked. Admin scope required.
getSchedule | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
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.
updateSchedule | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
application/json PATCH /schedules/{id} put /schedules/{id}
Update schedule (full replace)
Replaces the entire schedule definition. Recomputes next fire time. Admin scope required.
replaceSchedule | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
application/json PUT /schedules/{id} get /schedules/{id}/executions
List execution history
Returns a paginated list of executions for a schedule. Admin scope required.
listScheduleExecutions | 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 | ✓ |
GET /schedules/{id}/executions get /schedules/{id}/executions/{execId}
Get execution detail
Returns a single execution record. Admin scope required.
getScheduleExecution | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ | |
| execId | path | ✓ |
GET /schedules/{id}/executions/{execId} post /schedules/{id}/trigger
Manually trigger schedule
Creates an immediate execution for the schedule. Rate-limited. Admin scope required.
triggerSchedule | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
POST /schedules/{id}/trigger Auth
2 endpoints
post /auth/editor-token
Exchange an API key for a short-lived editor session token (8 hours)
createEditorToken application/json POST /auth/editor-token get /auth/status
Check whether authentication is required and if editor login is available
getAuthStatus 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).
listBatchDlq | 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 |
GET /admin/batch-dlq/ get /admin/batch-dlq/{id}
Get a batch webhook DLQ entry
getBatchDlqEntry | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
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.
abandonBatchDlqEntry | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
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.
replayBatchDlqEntry | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
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).
listScheduleDlq | 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 |
GET /admin/schedule-dlq/ get /admin/schedule-dlq/{id}
Get a schedule delivery DLQ entry
getScheduleDlqEntry | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
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.
abandonScheduleDlqEntry | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
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.
replayScheduleDlqEntry | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
POST /admin/schedule-dlq/{id}/replay get /admin/users/
List all named editor users (key redacted)
listUsers GET /admin/users/ post /admin/users/
Add a new named editor user
createUser application/json POST /admin/users/ delete /admin/users/{id}
Remove a named editor user (existing tokens rejected on next request)
deleteUser | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
DELETE /admin/users/{id} put /admin/users/{id}
Update an existing named editor user
updateUser | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
application/json PUT /admin/users/{id} post /admin/users/reload
Re-read EDITOR_USERS_FILE and replace the in-memory registry
reloadUsers 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.
purgeAuditEvents | 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. |
DELETE /audit-events/ get /audit-events/
List audit events
Returns a paginated, filterable list of audit events. Admin scope required.
listAuditEvents | 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) |
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).
getUsageRollup | 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 |
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).
exportUsageCsv | Name | In | Required | Description |
|---|---|---|---|
| from | query | ✓ | |
| to | query | ✓ | |
| source | query | ||
| templateKey | query | ||
| mode | query |
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.
getCapabilities GET /capabilities get /health
Liveness probe — always 200 if the process is up
getHealth GET /health get /health/ready
Readiness probe — 503 if storage is unreachable
getHealthReady 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.
rewriteNode application/json POST /nodes/rewrite post /nodes/rewrite/outcome
Record the user's apply/dismiss choice for an AI node rewrite
recordRewriteOutcome application/json POST /nodes/rewrite/outcome Render
13 endpoints
post /render/
Render a template to PDF
renderPdf | 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. |
application/json 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.
renderBatchPdf application/json 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.
submitAsyncBatchPdf application/json 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.
submitAsyncBatchDocx application/json 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.
renderBatchDocx application/json 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.
getAsyncBatchJob | Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ |
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.
renderBatchPptx application/json 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.
renderCsv application/json 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.
renderDocx application/json POST /render/docx post /render/html
Render a template to HTML
renderHtml application/json 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.
renderPptx application/json 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.
validateTemplateDefinition application/json 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`).
renderXlsx application/json POST /render/xlsx