Skip to content
v2

API Reference

Render templates, manage assets, and integrate PDF generation into any stack.

Overview

The pdfs.build API lets you render templates to PDFs, manage your template library, and retrieve generation logs — all over HTTPS with JSON request bodies and standard HTTP status codes.

Base URLhttps://api.pdfs.build
AuthBearer token — see Authentication
FormatJSON request body. PDF render returns application/pdf

Authentication

All API requests require a bearer token. Create and manage API keys from Settings → API Keys in your dashboard. Keys are shown once on creation — store them securely.

The primary public REST API is versioned under /v2/organizations/:organizationId/* and accepts API keys only. API keys are organization-scoped, and the organization in the URL must match the key. The legacy /v1/* API remains supported.

Key format

Keys are prefixed with prs_ and scoped to your organization.

cURL
curl https://api.pdfs.build/v2/organizations/org_abc/templates \
-H "Authorization: Bearer prs_your_api_key"
POST/v2/organizations/:organizationId/templates

Create template

Creates a draft template owned by the user that minted the API key. Drafts are not visible to the render endpoint until they are published. Template source is normalized through the same publishing pipeline used by the app and should define a top-level #let render(...) = { ... } function whose named parameters match the schema fields.

Request body

FieldTypeDescription
externalIdstringYour organization-scoped template ID. Required and unique.
namestringDisplay name. Required.
descriptionstringOptional one-line summary.
codestringTemplate source. Must define a top-level #let render(...) entry function. Required.
schemaobjectJSON Schema describing the data payload accepted by the render endpoint.
sampleDataobjectExample data that satisfies the schema.
schemaLockedbooleanOptional. Set to true at creation to prevent embedded editing from adding, removing, or retyping schema fields. Defaults to false and cannot be changed later.
pageSettingsobjectPage size, margins, and other layout settings.

Returns 201 with both the immutable generated internalId and your externalId. V2 URLs use the external ID; v1 continues to use the internal ID.

External IDs are case-sensitive, 1–128 characters, and may contain ASCII letters, digits, ., _, ~, and -. The values . and .. are reserved. IDs remain reserved after a template is deleted.

POST/v2/organizations/:organizationId/templates/:externalId/publish

Publish template

Promotes a draft template to published so it becomes visible to your organization and callable via the render endpoint. The caller must own the template or be an organization admin/owner. Enforces your plan's published-template limit.

Request body (optional)

FieldTypeDescription
namestringRename the template on publish.
descriptionstringUpdate the description on publish.

Returns the updated template detail. Returns 402 template_limit_reached if the org's plan does not allow another published template.

POST/v2/organizations/:organizationId/templates/:externalId/unpublish

Unpublish template

Moves a published template back to draft. Once unpublished, the template can no longer be rendered by the public API and is removed from organization template listings. Existing generation log entries are preserved.

Returns the updated template detail.

POST/v2/organizations/:organizationId/templates/:externalId/render

Render PDF

Compiles the specified template with the provided data payload and returns a binary PDF. Data is validated against the template's schema before rendering.

Request body

FieldTypeDescription
dataobjectKey-value pairs matching the template's schema.

Response

Returns Content-Type: application/pdf on success. On error, returns JSON with an error object.

const response = await fetch(
'https://api.pdfs.build/v2/organizations/org_abc/templates/invoice-primary/render',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: {
company: 'Acme Corp',
items: [
{ name: 'Consulting', qty: 3, price: 150 },
],
due_date: '2025-06-01',
}
}),
}
);

const pdf = await response.arrayBuffer();
GET/v2/organizations/:organizationId/templates

List templates

Returns published templates in the API key's organization. Drafts created by the key owner remain available through the single-template endpoint.

Response
[
{
"internalId": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "invoice-primary",
"name": "Invoice v3",
"description": "Standard invoice with line items",
"status": "published",
"createdAt": "2025-03-12T10:24:00Z",
"updatedAt": "2025-04-01T08:15:00Z"
}
]
GET/v2/organizations/:organizationId/templates/:externalId

Get template

Returns a single template's metadata, schema definition, sample data, and schemaLocked state. Use the schema field to know which keys your data payload must include when rendering.

Response
{
"internalId": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "invoice-primary",
"name": "Invoice v3",
"status": "published",
"schemaLocked": true,
"schema": {
"type": "object",
"properties": {
"company": { "type": "string" },
"due_date": { "type": "string", "format": "date" },
"items": { "type": "array" }
}
},
"sampleData": { /* ... */ }
}
PATCH/v2/organizations/:organizationId/templates/:externalId

Rename external ID

Send {"externalId":"invoice-primary"}. The old v2 URL stops resolving immediately; the internal ID, v1 URL, and historical generation-log foreign keys do not change. Requires template ownership or organization admin access.

GET/v2/organizations/:organizationId/logs

Generation logs

Returns a paginated list of PDF generation events for your organization. Each log exposes templateInternalId and the templateExternalId captured when that generation occurred, so later renames do not rewrite audit history. Use GET /v2/organizations/:organizationId/logs/:logId for detail or GET /v2/organizations/:organizationId/templates/:externalId/logs for one template.

Query parameters

ParameterTypeDescription
limitintegerMax results to return (default: 50)
offsetintegerPagination offset
statusstringFilter by status: success or error
sourcestringFilter by source: api, ui, or mcp
externalIdstringFilter by the external ID captured at generation time. Historical IDs continue to work after a rename.
searchstringFull-text search on template name
fromstring (ISO 8601)Start of time range
tostring (ISO 8601)End of time range

Migrate from v1

V1 remains supported and continues to identify templates by the generated internal ID. V2 adds an organization-scoped external ID that you control. Existing templates initially use their internal ID as the external ID; rename it in the dashboard or with PATCH /v2/organizations/:organizationId/templates/:externalId.

V1V2
POST /v1/renderPOST /v2/organizations/:organizationId/templates/:externalId/render
Send templateId and dataMove the template identifier into the URL and send only data
Generated internal IDClient-managed external ID, unique within the organization

Importing a .docx

An existing Word document can be converted into a template instead of being rebuilt by hand. The import reads the document's structure and produces template code plus a matching JSON Schema, which is then editable like any other template — including through the chat agent.

Available in the app under Templates → Import, and over MCP withimport_docx,process_docx_importandconvert_docx_to_template.

Documents whose structure is expressed with real Word styles — headings, tables, lists — import most cleanly, because those are what map onto template structure. A file formatted by hand, with spacing and alignment instead of styles, carries no structure to read and is usually faster to redraw from one of thestarting designs.

MCP server (for AI agents)

The same authentication layer also serves a Model Context Protocol endpoint. Agents that connect to it can design, publish, and render PDFs inside a single conversation — without API keys or dashboard handoffs.

Endpointhttps://backend.pdfs.build/mcp
TransportStreamable HTTP
AuthOAuth 2.1 with dynamic client registration (RFC 7591). API keys are not accepted on this endpoint — they remain for the REST API only.

Tool surface

The MCP server exposes the entire template lifecycle plus authenticated rendering. MCP renders count toward the same monthly PDF render quota as UI, authenticated form, automation, and REST API renders.

ToolPurpose
list_templates / get_template / browse_templatesDiscover templates and load their code, schema, and sample data.
create_template / duplicate_templateStart a fresh draft or clone an existing template.
write_document / edit_document / write_schema / write_sample_dataAuthor the template code, schema, and data.
compile_documentCompile the active template against default edge fixtures and optional custom fixtures, returning per-fixture diagnostics.
render_templateRender a saved template without an API key. Pass public_share=true to create share/download URLs, and expires_in_days to customize the expiry.
save_templatePersist the active session's changes after the compile gate passes; draft WIP saves can explicitly skip the gate.
publish_template / unpublish_templateMove templates between draft and published. Publishing requires the compile gate to pass.
create_api_key / list_api_keys / delete_api_keyMint, audit, and revoke REST API keys for external integrations.
document_referenceLook up template code reference inline.
list_fonts / upload_font / confirm_font_uploadBrowse org fonts and upload custom TTF/OTF files.
search_google_fonts / install_google_fontBrowse Google Fonts and install a family into the org.

Sessions and parallel agents

Each MCP connection gets its own isolated session on initialize, holding one active template (the one last loaded with get_template or created with create_template). Editing tools like edit_document and save_template operate on that session's active template.

Every editing tool takes a templateId and is refused when it does not match the session's active template, so a write can never land on the wrong document.

To build many templates in parallel, give each agent its own MCP connection — parallel agents then work on separate templates with fully independent state. Do not fan multiple agents out over a single shared connection: their calls run one at a time against one active template, so whichever agent loads a template last makes every other agent's calls fail. If two sessions do end up editing the same template, save_template detects the external modification and refuses to silently overwrite it — reload with get_template and re-apply your changes.

Connect from Claude Desktop, Cursor, or any MCP-compatible client. See theMCP integration guidefor an end-to-end example.

Errors

All errors return a JSON body with a top-level error object.

{
"error": {
"code": "schema_validation_failed",
"message": "Missing required field: company"
}
}
StatusCodeDescription
400invalid_requestMissing or malformed request parameters
400invalid_external_idExternal ID is not URL-safe or exceeds 128 characters
400already_publishedTrying to publish a template that is already published
400already_draftTrying to unpublish a template that is already a draft
401unauthorizedMissing or invalid API key
403forbiddenCaller does not have permission to publish/unpublish this template
403organization_scope_mismatchOrganization URL does not match the API key organization
409external_id_conflictExternal ID is already reserved in the organization
404not_foundTemplate not found or not accessible
402template_limit_reachedOrg plan's published-template limit is reached. Unpublish a template or upgrade the plan.
402api_renders_not_allowed_on_freeThe REST render API is not available on the Free plan. Use the app, authenticated forms, MCP rendering, or upgrade to Starter or higher.
422schema_validation_failedData payload doesn't match the template schema
429rate_limitedRequest rate exceeded — see Rate Limits
500render_failedInternal rendering error

Rate limits

Rate limits apply per API key, per minute. When a limit is exceeded, the API returns a 429 status. Check the response headers to determine when you can retry.

PlanRequests / min
Free10
Pro300
EnterpriseCustom

Rate limit headers

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per minute
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

API keys

API keys are scoped to your organization and grant access to all templates your organization can see. Keys are prefixed with prs_ and displayed only once on creation — copy them immediately and store them in a secret manager or environment variable.

Manage your API keys from Settings → API Keys in the dashboard. You can create multiple keys for different environments and revoke them individually.