---
title: "Publish template"
method: POST
path: "/v2/organizations/{organizationId}/templates/{externalId}/publish"
tags: ["Templates"]
---

# Publish template

`POST /v2/organizations/{organizationId}/templates/{externalId}/publish`

## Request

- Base URL: `https://api.pdfs.build`
- URL: `https://api.pdfs.build/v2/organizations/{organizationId}/templates/{externalId}/publish`
- Auth: HTTP bearer

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.

## Request body

- PublishTemplateRequest
  - `name` string: Rename on publish.
  - `description` string: Update the description on publish.

## Response `200`

The published template, with the version `latest` now serves.

- PublishedTemplate
  - `internalId` string, required: Generated, immutable. What v1 URLs and the embedded SDK address.
  - `externalId` string, required: Organization-scoped template id you control. 1–128 ASCII letters, digits, `.`, `_`, `~` or `-`; `.` and `..` are reserved. Case-sensitive.
  - `name` string, required
  - `description` string, nullable
  - `status` 'draft' | 'published', required
  - `scopePath` string, required: 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.
  - `createdAt` string, required: UTC, ISO 8601 without a timezone suffix.
  - `updatedAt` string, required: UTC, ISO 8601 without a timezone suffix.
  - `schema` object, nullable: JSON Schema of the render `data` payload.
  - `schemaLocked` boolean, required: When true the data contract is frozen: embedded editing can restyle the document but cannot add, remove or retype a field.
  - `sampleData` object, nullable: Example data satisfying `schema`.
  - `baseTemplateId` string, nullable: Internal id of the base template this one layers on, if any.
  - `version` integer, nullable: Present when `version` was given: the frozen version `schema` and `sampleData` come from, or null for the working copy.
  - `channel` string, nullable: Present when `version` was given: the channel the selector resolved through, if any.
  - `publishedVersion` integer, required: 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`.

## Other responses

- `400`: `invalid_external_id`.
- `401`: Missing, invalid or expired API key.
- `402`: `template_limit_reached` — unpublish a template or upgrade the plan.
- `403`: `organization_scope_mismatch` or `forbidden`.
- `404`: No template with that id is accessible to this key. Another user's draft is indistinguishable from a missing template.
- `422`: The template does not compile against its sample data.

---

[API](https://skmtc.dev/pdfs/apis/pdfs-build-api.md) · [All operations](https://skmtc.dev/pdfs/apis/pdfs-build-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pdfs/apis/pdfs-build-api/revisions/1c7340b9521d?raw)
