Skip to content

Template versions ​

A version is a snapshot of a template's content that never changes again, with an optional message recording what changed. Render one by adding an @ suffix to the template — report@3, or report@latest for the newest. See Versions & Tags for how versions and tags fit together.

Publish a version of a template​

POST
/v2/templates/{templateId}/versions

Stores a read-only snapshot of the template's current content and returns the version number it was given, with 201. Numbers start at 1 and count up. The optional message records what changed. Publishing content identical to the newest version stores nothing and answers 200 with that existing version, under the message it was already published with — a message sent alongside unchanged content is discarded.

Authorizations​

ApiKeyHeader

Your Papermill API key, sent as an x-api-key header.

Type
API Key (header: x-api-key)
or
BearerAuth

Your Papermill API key, sent as Authorization: Bearer <key>. Equivalent to the x-api-key form.

Type
HTTP (bearer)

Parameters​

Path Parameters

templateId*
Type
string
Required
Min Length
1
Max Length
128
Pattern
"^(?!__.*__$)[A-Za-z0-9_-]+$"

Request Body​

application/json
JSON
{
  
"message": "string"
}

Responses​

Content unchanged; the version that already held it

application/json
JSON
{
  
"version": 0,
  
"sha256": "string",
  
"message": "string",
  
"created_at": "string",
  
"created_by": {
  
  
"id": "string",
  
  
"name": "string",
  
  
"email": "string"
  
}
}

Playground​

Authorization
Variables
Key
Value
Body

Samples​

List a template's versions​

GET
/v2/templates/{templateId}/versions

A template's published versions, newest first and without their content. has_more is true when the template holds versions older than the last one returned; those are read one at a time by number, since numbers count up from 1 with no gaps.

Authorizations​

ApiKeyHeader

Your Papermill API key, sent as an x-api-key header.

Type
API Key (header: x-api-key)
or
BearerAuth

Your Papermill API key, sent as Authorization: Bearer <key>. Equivalent to the x-api-key form.

Type
HTTP (bearer)

Parameters​

Path Parameters

templateId*
Type
string
Required
Min Length
1
Max Length
128
Pattern
"^(?!__.*__$)[A-Za-z0-9_-]+$"

Responses​

Published versions

application/json
JSON
{
  
"versions": [
  
  
{
  
  
  
"version": 0,
  
  
  
"sha256": "string",
  
  
  
"message": "string",
  
  
  
"created_at": "string",
  
  
  
"created_by": {
  
  
  
  
"id": "string",
  
  
  
  
"name": "string",
  
  
  
  
"email": "string"
  
  
  
}
  
  
}
  
],
  
"has_more": true
}

Playground​

Authorization
Variables
Key
Value

Samples​

Read one version of a template​

GET
/v2/templates/{templateId}/versions/{version}

Returns a published version and its content. The content carries a header comment naming the version, the time it was published, who published it, and the sha256 of the content below the header — remove the header and hash what remains to check it.

Authorizations​

ApiKeyHeader

Your Papermill API key, sent as an x-api-key header.

Type
API Key (header: x-api-key)
or
BearerAuth

Your Papermill API key, sent as Authorization: Bearer <key>. Equivalent to the x-api-key form.

Type
HTTP (bearer)

Parameters​

Path Parameters

templateId*
Type
string
Required
Min Length
1
Max Length
128
Pattern
"^(?!__.*__$)[A-Za-z0-9_-]+$"
version*
Type
string
Required
Min Length
1
Max Length
16

Responses​

The published version

application/json
JSON
{
  
"version": 0,
  
"sha256": "string",
  
"message": "string",
  
"created_at": "string",
  
"created_by": {
  
  
"id": "string",
  
  
"name": "string",
  
  
"email": "string"
  
},
  
"content": "string"
}

Playground​

Authorization
Variables
Key
Value

Samples​

Example ​

Publish the template's current content with a note on what changed, then render the version you just published. Publishing is safe to repeat: identical content stores nothing and answers 200 with the existing version rather than a new number.

shell
set -e
version=$(curl -fsS -X POST "https://api.papermill.io/v2/templates/$TEMPLATE_ID/versions" \
  -H "Authorization: Bearer $PAPERMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Switch the cover to the new brand marks"}' | jq -er .version)

curl -X POST "https://api.papermill.io/v2/pdf?template=$TEMPLATE_ID@$version" \
  -H "Authorization: Bearer $PAPERMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -o report.pdf \
  -d '{"customer": "Acme Co"}'
python
import os
import requests

key = os.environ["PAPERMILL_API_KEY"]
template_id = os.environ["TEMPLATE_ID"]
auth = {"Authorization": f"Bearer {key}"}

published = requests.post(
    f"https://api.papermill.io/v2/templates/{template_id}/versions",
    headers=auth,
    json={"message": "Switch the cover to the new brand marks"},
)
published.raise_for_status()
version = published.json()["version"]

pdf = requests.post(
    "https://api.papermill.io/v2/pdf",
    params={"template": f"{template_id}@{version}"},
    headers={**auth, "Content-Type": "application/json"},
    json={"customer": "Acme Co"},
)
pdf.raise_for_status()

with open("report.pdf", "wb") as f:
    f.write(pdf.content)
javascript
import fs from 'node:fs/promises'

const key = process.env.PAPERMILL_API_KEY
const templateId = process.env.TEMPLATE_ID
const auth = { Authorization: `Bearer ${key}` }

const published = await fetch(`https://api.papermill.io/v2/templates/${templateId}/versions`, {
  method: 'POST',
  headers: { ...auth, 'Content-Type': 'application/json' },
  body: JSON.stringify({ message: 'Switch the cover to the new brand marks' }),
})
if (!published.ok) throw new Error(`${published.status} ${published.statusText}`)
const { version } = await published.json()

const pdf = await fetch(`https://api.papermill.io/v2/pdf?template=${templateId}@${version}`, {
  method: 'POST',
  headers: { ...auth, 'Content-Type': 'application/json' },
  body: JSON.stringify({ customer: 'Acme Co' }),
})
if (!pdf.ok) throw new Error(`${pdf.status} ${pdf.statusText}`)
await fs.writeFile('report.pdf', Buffer.from(await pdf.arrayBuffer()))

Verifying content you were sent ​

GET …/versions/{version} returns the content with a header comment naming the version and the sha256 of everything below it:

xml
<!-- Papermill template version 3
     sha256 d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
     2026-08-06T09:00:00Z by Ada Lovelace <ada@example.com> -->

The header is a label rather than proof — anyone who edits the content can recompute the digest inside it. To check a file really is the version it claims to be, remove the header, hash what remains, and compare that against the sha256 this endpoint returns. The digest does not cover the header itself, which is what makes the two comparable.