Pulp Engine Document Rendering
Get started

Everything in Pulp Engine

Built for developers and engineering teams. Grouped by who tends to care. Every item ships under the same licence — no Pro tier, no feature gates.

For developers

Ship document features without inventing infrastructure.

REST + OpenAPI 3.0

Full spec at /docs/json. Swagger UI at /docs. Generate your own client in any language.

Typed SDKs

TypeScript and Python (Pydantic v2) — version-locked to the server release, shipped in-repo today (npm/PyPI publish pending). Generate clients for other languages from the OpenAPI spec.

CLI tooling

@pulp-engine/cli: push/pull for content sync, render/dev for local iteration, validate/test/diff/merge, promote, codegen, and backup.

<code>dryRun: true</code>

Validate templates and exercise expressions without producing output — typically 5–50 ms, ~80× faster than a full render.

Plugin system

Add custom node types, renderers, storage backends, auth providers, render hooks, or new API routes.

Async batch rendering

POST /render/batch/async (PDF) and …/batch/async/docx return a job id, deliver results via webhook callback, and survive restarts with a durable DLQ.

Scheduling & delivery

Cron-scheduled renders with pluggable delivery: email (SMTP), S3/object storage, or webhook. Retry policy, DLQ inspection, and per-schedule audit trail.

PDF post-processing

POST /render/pdf/merge, /render/pdf/watermark, and /render/pdf/insert for combining, stamping, or splicing PDFs without re-rendering from a template.

Observability built in

Prometheus metrics, structured logs, OpenTelemetry, X-Request-ID propagation, render accounting headers.

Bill your customers per render

Metered usage out of the box: GET /usage + /usage.csv admin endpoints (per-tenant counts, durations, bytes, pages), render:completed plugin event for real-time billing pipelines, and X-Render-* response headers on every render. Pipes cleanly to Stripe Metering, Orb, Metronome, or your own invoice job. Rollup endpoints require a database-backed storage mode (Postgres or SQL Server); headers and plugin events work everywhere.

For engineering teams

Authoring, versioning, and delivery surfaces built for developer authors — not business users.

Visual template editor

Rich text, tables, 15 chart types, images, conditionals, repeaters, pivot tables, barcode/QR, signature fields, and table-of-contents. Lives at /editor/.

Six output formats

PDF (Chromium), DOCX, PPTX (beta), XLSX, CSV, HTML — from one template definition.

AI template scaffolding

Prompt → draft TemplateDefinition, schema-valid by construction. A shortcut for devs starting a new template, not end-user magic.

Version history + diff

Every save is a version. Restore, promote labels (e.g. stable) without redeploying, and POST /templates/:key/diff for structural diffs between any two versions — also callable from CI.

Embeddable editor + form

<pulp-engine-editor> drops the full editor into a customer app via iframe. <pulp-engine-form> exposes just the data-entry surface for end users.

Starter templates

28 built-in starting points in the new-template dialog, from Blank and invoices to pivot examples, component showcases, page chrome, and stock reports.

For ops & security

Run it like any other production service.

Three storage backends

Postgres (default), SQL Server, file-backed. Swap with one env var.

S3 asset binaries

Object-storage-backed asset binaries with health probes. Multi-instance without shared volumes.

Multi-tenant mode

MULTI_TENANT_ENABLED isolates templates, assets, credentials, audit events, and schedules per tenant.

OIDC / SSO

PKCE auth-code flow with auto-provisioning and silent refresh. Okta, Azure AD, Keycloak, Auth0.

Scoped API credentials

Separate keys for admin, render, preview, and editor. Verify-only previous-key rotation slot.

Runtime user management

/admin/users CRUD plus /admin/users/reload — provision editors, rotate attribution, and re-read the EDITOR_USERS_JSON registry without restarting the API.

Evaluation & licensed builds

Same binary, one env var: unlicensed deploys stamp a small watermark on output (fine for development, staging, and internal PoCs). A PULP_LICENCE_KEY from your commercial agreement removes it — no separate "enterprise" build. The gate is an Ed25519-signed token verified offline against a public key baked into the build. See Licence key format for the wire format and operator runbook.

Render isolation modes

Hardened production posture

Docs disabled by default, CSP, HTTPS enforcement, magic-byte upload validation, DNS-rebinding SSRF guard.

Audit trails

Per-actor audit events, named-user attribution, retention-aware purge with erasure-by-actor.

HA + backup

Reference compose file, nginx upstream config, backup CLI with verify, restore runbook.
Honest heads-up Known limits · being actively addressed

Where Pulp Engine doesn't pretend yet

A new commercial product deserves honest limits. These are the questions larger-team buyers ask before rolling out production deployments; none are blockers for most teams today, and Early Adopters get direct input as we close them.

Per-template RBAC

Not yet

Access control today is by scope (admin · editor · render · preview) and, in multi-tenant mode, by tenant. There is no per-template permission layer — any editor in a tenant can edit any template in that tenant. Fine for most teams; worth knowing if your compliance model requires "template owner" / "read-only reviewer" roles. On the roadmap.

Load benchmarks

Published reference-rig numbers + a reproducible harness

A six-cell render benchmark — warm PDF at three page-count sizes (1 / 10 / 100) plus DOCX / PPTX / XLSX at the 10-page template — with a reproducible compose stack, load script, and three template packs. Render-mode comparison (child-process · container · socket) and HA scaling (1 / 2 / 4 instances) are covered by re-running the harness against the matching stack configuration. The harness ships in the repo so you can run it on your own hardware, and the reference-rig numbers are published in the benchmark pack. Still pending: a native-Linux rig (to remove the WSL2 variable) and container-mode numbers. See the benchmark pack.

Render process isolation

Opt-in, not default

RENDER_MODE=child-process (default) shares a Chromium pool across renders. For stricter per-render isolation, RENDER_MODE=container sandboxes each render in an ephemeral container, and socket additionally moves Docker authority off the API process. Default is fine for most; regulated deployments should pick a stronger mode. Read the threat model →

Ready to try it?

Free evaluation runs in under a minute. No database, no signup, no credit card.