pdfs-build Templates API

Create, inspect, publish and rename templates. Only published templates can be rendered over the API.

Operations 6

GET /v2/organizations/{organizationId}/templates List templates #
POST /v2/organizations/{organizationId}/templates Create template #
GET /v2/organizations/{organizationId}/templates/{externalId} Get template #
PATCH /v2/organizations/{organizationId}/templates/{externalId} Rename external id or set tenant scope #
POST /v2/organizations/{organizationId}/templates/{externalId}/publish Publish template #
POST /v2/organizations/{organizationId}/templates/{externalId}/unpublish Unpublish template #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • 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.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/pdfs-build:pdfs-build-templates-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

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 Specification

pdfs-build-templates-api-openapi.yml Raw ↑
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/