Appearance
Template slugs
A slug is a name you give a template — quarterly-report rather than the id it was created with. It works anywhere you render or read a template by template_id, including in front of an @ suffix: quarterly-report@3 and quarterly-report@production both render what the id form would. The one place it is not accepted is the template_id below, which assigns a name and so needs the id itself.
A slug is unique within your workspace, and a template holds at most one. Assigning a second releases the first, so a template's name can be changed without ever naming two things at once.
A slug is 3–64 characters of lowercase letters, digits and single hyphens. It cannot begin with papermill-, which is reserved for public templates, and it cannot have the shape of a template id — twenty lowercase letters and digits, which is what an id looks like. The last two rules are Papermill's own, and breaking one is rejected with message Invalid slug '<slug>': <the rule it broke> carrying ERR_SLUG_INVALID. A name that breaks the length or character rules is rejected as a malformed request, naming the field and the rule but carrying no code.
Assign a slug to a template
POST
/v2/slugs
Gives a template a human-readable name that can be used in place of its id. A slug is unique within your workspace, and a template holds at most one: assigning a second one releases the first. Re-assigning a slug the template already holds succeeds unchanged.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Request Body
application/json
JSON "slug": "string", "template_id": "string"
{
}
Responses
Slug assigned
application/json
JSON "slug": "string", "template_id": "string"
{
}
List assigned slugs
GET
/v2/slugs
Every slug assigned in your workspace, with the template each one names. A workspace holds at most one slug per template, so the listing is capped at 1000 and there is no cursor: a workspace with more templates than that would need one added before the listing could be read as complete.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Responses
Assigned slugs
application/json
JSON "slugs": [ { "slug": "string", "template_id": "string" } ]
{
}
Check whether a slug is available
GET
/v2/slugs/{slug}
Reports whether a slug is free to assign in your workspace, and which template it names when it is already taken. A slug that breaks the naming rules is reported as invalid rather than as available.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Parameters
Path Parameters
slug*
Type
Requiredstring
Min Length
3Max Length
64Pattern
"^[a-z0-9]+(?:-[a-z0-9]+)*$"Responses
Slug status
application/json
JSON "slug": "string", "available": true, "template_id": "string"
{
}
Release a slug
DELETE
/v2/slugs/{slug}
Releases a slug so it can be assigned again. A slug can be released even when the template it named is no longer available.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Parameters
Path Parameters
slug*
Type
Requiredstring
Min Length
3Max Length
64Pattern
"^[a-z0-9]+(?:-[a-z0-9]+)*$"Responses
Slug released
Example
Name a template, then render through the name. Nothing else about the request changes — the slug simply takes the place of the id.
shell
set -e
curl -fsS -X POST "https://api.papermill.io/v2/slugs" \
-H "Authorization: Bearer $PAPERMILL_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"slug\": \"quarterly-report\", \"template_id\": \"$TEMPLATE_ID\"}"
curl -X POST "https://api.papermill.io/v2/pdf?template_id=quarterly-report" \
-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}"}
requests.post(
"https://api.papermill.io/v2/slugs",
headers=auth,
json={"slug": "quarterly-report", "template_id": template_id},
).raise_for_status()
pdf = requests.post(
"https://api.papermill.io/v2/pdf",
params={"template_id": "quarterly-report"},
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 assigned = await fetch('https://api.papermill.io/v2/slugs', {
method: 'POST',
headers: { ...auth, 'Content-Type': 'application/json' },
body: JSON.stringify({ slug: 'quarterly-report', template_id: templateId }),
})
if (!assigned.ok) throw new Error(`${assigned.status} ${assigned.statusText}`)
const pdf = await fetch('https://api.papermill.io/v2/pdf?template_id=quarterly-report', {
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()))Assigning a slug the template already holds succeeds and changes nothing, so this is safe to repeat from a setup script. When another template holds it, Papermill rejects the request with message Slug '<slug>' is already in use — names are taken, never stolen, so pointing one at a different template means releasing it first.
Checking a name before you take it
GET …/slugs/{slug} answers whether a name is free, and names the template holding it when it is not. A name that breaks the rules is reported as invalid rather than as available, so a setup script can tell "nobody has this yet" apart from "nobody can ever have this".
Releasing a slug
Releasing frees the name for reuse and leaves the template untouched — it keeps rendering under its id, and under any version or tag. Anything rendering by the slug stops, so release a name only when you mean to retire it. When the name is not assigned, Papermill rejects the request with message Slug '<slug>' is not assigned carrying ERR_SLUG_NOT_ASSIGNED.
A slug can be released even when the template it named is gone, so a name is never stranded by the template outliving its usefulness.