pdfs-build Templates API
Create, inspect, publish and rename templates. Only published templates can be rendered over the API.
Create, inspect, publish and rename templates. Only published templates can be rendered over the API.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/pdfs-build:pdfs-build-templates-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: pdfs.build Templates API
version: '2.0'
summary: Render PDFs from templates, manage templates, webhooks, embedded editor sessions and delegated agent access.
description: 'All requests are authenticated with an organization-scoped API key (`prs_...`) sent as a Bearer token. Create keys under **Settings → API Keys** in the dashboard or with the MCP `create_api_key` tool. Keys are shown once.
The organization in every `/v2` URL must match the key''s organization, otherwise the request fails with `403 organization_scope_mismatch`.
**Errors** are JSON with a top-level `error` string — a machine-readable code for most failures, a phrase for a few legacy ones — usually alongside a human-readable `message`. Data validation and template compilation failures carry `details` / `diagnostics` instead of a `message`.
**Versions.** `/v2` addresses templates by an external id you control. The legacy `/v1` routes identify templates by their generated internal id and remain supported for existing integrations.
**Timestamps.** Objects served by the render service (templates, render jobs, logs, webhook endpoints) carry UTC timestamps in ISO 8601 form without a timezone suffix, e.g. `2026-08-30T12:00:00.123456`. Embedded-editor sessions and delegated access return RFC 3339 with a trailing `Z`.
The narrative guide — authentication, webhook signatures, the MCP server, migrating from v1 — lives at https://pdfs.build/docs/ (Markdown mirror: https://pdfs.build/docs.md).'
contact:
name: pdfs.build
url: https://pdfs.build
email: hello@pdfs.build
termsOfService: https://pdfs.build/terms/
license:
name: Proprietary
url: https://pdfs.build/terms/
servers:
- url: https://api.pdfs.build
description: Production
security:
- apiKey: []
tags:
- name: Templates
description: Create, inspect, publish and rename templates. Only published templates can be rendered over the API.
paths:
/v2/organizations/{organizationId}/templates:
parameters:
- $ref: '#/components/parameters/organizationId'
get:
tags:
- Templates
summary: List templates
operationId: listTemplates
description: Returns the published templates in the organization. Drafts created by the key's owner are not listed but remain reachable through the single-template endpoint.
responses:
'200':
description: Published templates.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/TemplateSummary'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/OrganizationScopeMismatch'
post:
tags:
- Templates
summary: Create template
operationId: createTemplate
description: 'Creates a draft template owned by the user that minted the API key. Drafts are not renderable until published. The source is normalized through the same pipeline the app uses and should define a top-level `#let render(...)` entry function whose named parameters match the schema fields.
External ids are case-sensitive and stay reserved after a template is deleted.'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTemplateRequest'
responses:
'201':
description: The created draft, including both the generated `internalId` and your `externalId`.
content:
application/json:
schema:
$ref: '#/components/schemas/Template'
'400':
description: '`invalid_external_id`, `invalid_scope_path`, a missing `name` or `code`, or `schemaLocked` set while `sampleData` contains fields the schema does not declare.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: '`organization_scope_mismatch`, or `scope_path_admin_required` when `scopePath` is set with a key that does not belong to an organization owner or admin.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: '`external_id_conflict` — the external id is already reserved in this organization.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/v2/organizations/{organizationId}/templates/{externalId}:
parameters:
- $ref: '#/components/parameters/organizationId'
- $ref: '#/components/parameters/externalId'
get:
tags:
- Templates
summary: Get template
operationId: getTemplate
description: Returns a template's metadata, JSON Schema, sample data, `schemaLocked` state and tenant `scopePath`. Use `schema` to know which keys your render `data` must include. Published templates are visible to the whole organization; drafts only to their owner. Pass `version` to get the `schema` and `sampleData` of the version a render with that selector compiles, instead of the working copy.
parameters:
- $ref: '#/components/parameters/resolveImages'
- $ref: '#/components/parameters/templateVersion'
responses:
'200':
description: The template.
content:
application/json:
schema:
$ref: '#/components/schemas/Template'
'400':
$ref: '#/components/responses/InvalidExternalId'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/OrganizationScopeMismatch'
'404':
$ref: '#/components/responses/TemplateNotFound'
patch:
tags:
- Templates
summary: Rename external id or set tenant scope
operationId: updateTemplate
description: Renames the template's external id and/or sets its delegated-access `scopePath`. At least one field is required. After a rename the old v2 URL stops resolving immediately; the internal id, the v1 URL and historical render-log references do not change. Requires template ownership or organization admin access; `scopePath` additionally requires a key owned by an organization owner or admin.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateTemplateRequest'
responses:
'200':
description: The updated template.
content:
application/json:
schema:
$ref: '#/components/schemas/Template'
'400':
description: Neither field provided, `invalid_external_id`, or `invalid_scope_path`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: '`organization_scope_mismatch`, `forbidden` (no edit access), or `scope_path_admin_required`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
$ref: '#/components/responses/TemplateNotFound'
'409':
description: '`external_id_conflict` — the new external id is already reserved.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/v2/organizations/{organizationId}/templates/{externalId}/publish:
parameters:
- $ref: '#/components/parameters/organizationId'
- $ref: '#/components/parameters/externalId'
post:
tags:
- Templates
summary: Publish template
operationId: publishTemplate
description: 'Promotes a draft to `published`: the source is normalized, compiled against the default fixture suite, and frozen as a new version that the `latest` channel points at. Calling it again on a published template ships the current draft the same way, so edits made since the last publish (in the embedded editor, for example) reach renders; with no edits since, `latest` stays on the newest version and no duplicate is frozen. Published templates are visible to the whole organization and renderable over the API. The caller must own the template or be an organization admin/owner. Enforces the plan''s published-template limit.'
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/PublishTemplateRequest'
responses:
'200':
description: The published template, with the version `latest` now serves.
content:
application/json:
schema:
$ref: '#/components/schemas/PublishedTemplate'
'400':
description: '`invalid_external_id`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
description: '`template_limit_reached` — unpublish a template or upgrade the plan.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: '`organization_scope_mismatch` or `forbidden`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
$ref: '#/components/responses/TemplateNotFound'
'422':
description: The template does not compile against its sample data.
content:
application/json:
schema:
$ref: '#/components/schemas/CompilationError'
/v2/organizations/{organizationId}/templates/{externalId}/unpublish:
parameters:
- $ref: '#/components/parameters/organizationId'
- $ref: '#/components/parameters/externalId'
post:
tags:
- Templates
summary: Unpublish template
operationId: unpublishTemplate
description: Moves a published template back to `draft`. It can no longer be rendered over the API and disappears from organization listings. Version history and render logs are preserved; republishing promotes a new version.
responses:
'200':
description: The template, now a draft.
content:
application/json:
schema:
$ref: '#/components/schemas/Template'
'400':
description: '`already_draft`, or `invalid_external_id`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: '`organization_scope_mismatch` or `forbidden`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
$ref: '#/components/responses/TemplateNotFound'
components:
schemas:
Timestamp:
type: string
description: UTC, ISO 8601 without a timezone suffix.
example: '2026-08-30T12:00:00.123456'
PublishTemplateRequest:
type: object
properties:
name:
type: string
description: Rename on publish.
description:
type: string
description: Update the description on publish.
TemplateSummary:
type: object
required:
- internalId
- externalId
- name
- status
- scopePath
- createdAt
- updatedAt
properties:
internalId:
type: string
description: Generated, immutable. What v1 URLs and the embedded SDK address.
externalId:
$ref: '#/components/schemas/ExternalId'
name:
type: string
description:
type:
- string
- 'null'
status:
$ref: '#/components/schemas/TemplateStatus'
scopePath:
$ref: '#/components/schemas/ScopePath'
createdAt:
$ref: '#/components/schemas/Timestamp'
updatedAt:
$ref: '#/components/schemas/Timestamp'
ExternalId:
type: string
pattern: ^[A-Za-z0-9._~-]{1,128}$
description: Organization-scoped template id you control. 1–128 ASCII letters, digits, `.`, `_`, `~` or `-`; `.` and `..` are reserved. Case-sensitive.
Template:
allOf:
- $ref: '#/components/schemas/TemplateSummary'
- type: object
required:
- schemaLocked
properties:
schema:
type:
- object
- 'null'
description: JSON Schema of the render `data` payload.
additionalProperties: true
schemaLocked:
type: boolean
description: 'When true the data contract is frozen: embedded editing can restyle the document but cannot add, remove or retype a field.'
sampleData:
type:
- object
- 'null'
description: Example data satisfying `schema`.
additionalProperties: true
baseTemplateId:
type:
- string
- 'null'
description: Internal id of the base template this one layers on, if any.
version:
type:
- integer
- 'null'
description: 'Present when `version` was given: the frozen version `schema` and `sampleData` come from, or null for the working copy.'
channel:
type:
- string
- 'null'
description: 'Present when `version` was given: the channel the selector resolved through, if any.'
TypstDiagnostic:
type: object
required:
- severity
- message
properties:
severity:
type: string
enum:
- error
- warning
message:
type: string
line:
type: integer
column:
type: integer
snippet:
type: string
source_context:
type: string
hint:
type: string
ScopePath:
type: string
maxLength: 512
description: 'Tenant scope for delegated agent access: zero or more segments, each 1–128 chars from the external-id charset and each ending in `/`, e.g. `acct_9f3/entity_dubai/`. Empty means unscoped — invisible to every delegated grant.'
example: acct_9f3/entity_dubai/
PublishedTemplate:
allOf:
- $ref: '#/components/schemas/Template'
- type: object
required:
- publishedVersion
properties:
publishedVersion:
type: integer
minimum: 1
description: 'The version `latest` now points at: the one this publish froze, or the newest existing one when nothing changed since it. Pin renders to it with `version`.'
CreateTemplateRequest:
type: object
required:
- externalId
- name
- code
properties:
externalId:
$ref: '#/components/schemas/ExternalId'
name:
type: string
description: Display name.
description:
type: string
code:
type: string
description: Template source. Must define a top-level `#let render(...)` entry function. (`typstCode` is accepted as a legacy alias.)
schema:
type: object
description: JSON Schema describing the render `data` payload.
additionalProperties: true
sampleData:
type: object
description: Example data that satisfies `schema`.
additionalProperties: true
pageSettings:
type: object
description: Page size, margins and other layout settings.
additionalProperties: true
schemaLocked:
type: boolean
default: false
description: Freeze the data contract at creation. Cannot be changed later. Rejected if `sampleData` contains fields the schema does not declare.
scopePath:
$ref: '#/components/schemas/ScopePath'
TemplateStatus:
type: string
enum:
- draft
- published
UpdateTemplateRequest:
type: object
minProperties: 1
properties:
externalId:
$ref: '#/components/schemas/ExternalId'
scopePath:
$ref: '#/components/schemas/ScopePath'
CompilationError:
type: object
required:
- error
- diagnostics
properties:
error:
type: string
const: Compilation failed
diagnostics:
type: array
items:
$ref: '#/components/schemas/TypstDiagnostic'
Error:
type: object
required:
- error
properties:
error:
type: string
description: Machine-readable code (e.g. `draft_not_renderable`) or, for a few legacy failures, a short phrase.
message:
type: string
description: Human-readable detail. Present on most coded errors.
example:
error: draft_not_renderable
message: Only published templates can be rendered via the API. Publish this template first.
parameters:
templateVersion:
name: version
in: query
schema:
type: string
examples:
- latest
- '4'
- staging
- draft
description: 'Answer with the `schema` and `sampleData` of the content a render with this selector compiles: a version number, a channel name such as `latest` (the promoted version), or `draft` (the working copy). Omitted returns the working copy. An unknown version or channel fails with `400`.'
externalId:
name: externalId
in: path
required: true
schema:
$ref: '#/components/schemas/ExternalId'
description: The template's organization-scoped external id.
example: invoice-primary
organizationId:
name: organizationId
in: path
required: true
schema:
type: string
description: Your organization id. Must match the API key's organization.
example: org_abc
resolveImages:
name: resolve_images
in: query
schema:
type: string
enum:
- 'true'
description: When `true`, image references in `sampleData` are resolved to fetchable URLs. Always on for templates built on a base template.
responses:
Unauthorized:
description: Missing, invalid or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Unauthorized
OrganizationScopeMismatch:
description: The organization in the URL does not match the API key's organization.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: organization_scope_mismatch
message: The organization in the URL does not match the API key organization
InvalidExternalId:
description: '`invalid_external_id`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: invalid_external_id
message: externalId must be 1-128 characters using only ASCII letters, digits, '.', '_', '~', or '-', and cannot be '.' or '..'
TemplateNotFound:
description: No template with that id is accessible to this key. Another user's draft is indistinguishable from a missing template.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Template not found
securitySchemes:
apiKey:
type: http
scheme: bearer
bearerFormat: prs_...
description: Organization-scoped API key, created under Settings → API Keys or with the MCP `create_api_key` tool.
externalDocs:
description: 'API guide: authentication, errors, webhooks, MCP, migrating from v1'
url: https://pdfs.build/docs/