Dynamic URLs
A Dynamic URL is one address you can put in an <img> tag, an email, or behind a
QR code. The data for each document travels in the link itself, signed, so the
same URL serves everyone: a certificate per student, an invoice per
customer. Nothing calls our API until someone actually opens it.
https://dyn.automette.com/d/abc123def456ghij/certificate.pdf?d=eyJuYW1lIjoiQWxpY2UifQ&s=a1b2c3d4
└── token ────┘ └─ filename ─┘ └── payload ──┘ └ signature ┘One URL serves everyone. ?d=<Alice> and ?d=<Bob> are the same Dynamic
URL. Create a second one only when you want separate controls: its own expiry,
or a signing secret you can revoke without touching the first. Never one per
recipient.
Create one
Open Dynamic URLs in the menu and click New URL, or click New URL on a template's page. Choose the template, then the link type:
- Open: public links. Anyone who has a link can change its values and get a different document. Useful where another tool fills the values in, such as merge tags in an email.
- Signed: links secured with an encrypted signature. Only links signed with the signature work, and a changed link is refused. Useful where your own code or website makes the links.
This choice can't be changed later. The URL is named after its template; click the pencil next to the name to change it.
Or create one with the API:
curl -X POST https://automette.com/api/v1/dynamic_urls \
-H "Authorization: Bearer $AUTOMETTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "template_id": "cm4tpl8e2..." }'The response carries the signing secret and a sample_url that works as-is.
Keep the secret somewhere your server can read it.
Make a link
The URL on its own carries no data. Each link to it carries one set of values, and that is what makes each document different.
Open the URL. Its address is shown at the top. On Create a new link, the template's fields are listed as values you can edit, filled with the template's own text. Change them and click Generate link: the link appears on the right, with a preview of the document below it. Leave a value empty to keep the template's own text.
- On an Open URL, a value can be a merge tag, such as
*|FNAME|*in Mailchimp or{{first_name}}in most other email tools. Copy the link, or copy it as an image tag, into your email, and each reader gets their own image. - On a Signed URL, the link is signed for exactly those values. A merge tag can't work here, because the email tool would change the values after they were signed. Use it for one-off links, such as the banner for one blog post.
To make signed links from your own code, open Sign from your code on a Signed URL. It has the signing secrets and code to copy.
A link can carry its values in two ways, and every URL accepts both: readable parameters (?name.text=Alice), or one d= value holding them all, which also holds lists. The link builder uses readable parameters unless the template has a list.
Sign your own URLs
This is the point of the feature. You build addresses locally and we hear nothing until a recipient opens one.
The signed message is:
{token}/{extension}?{query}with the query parameters sorted and s left out. No hostname and no path,
so a signature keeps working even if the URL is served from somewhere else later.
import { createHmac } from "crypto"
const SECRET = process.env.AUTOMETTE_SECRET
const TOKEN = "abc123def456ghij"
function dynamicUrl(data) {
const d = Buffer.from(JSON.stringify(data)).toString("base64url")
const s = createHmac("sha256", SECRET).update(`${TOKEN}/pdf?d=${d}`).digest("hex")
return `https://dyn.automette.com/d/${TOKEN}/certificate.pdf?d=${d}&s=${s}`
}What you can put in the payload
The same field names the render API takes. Canvas templates use
{layerId}.{property}; Typst and Word templates use their field names.
{ "name.text": "Alice Rao", "course.text": "Data Structures", "grade.text": "A" }Which format you get
The extension decides. certificate.pdf renders a PDF, certificate.png an
image. The extension is part of the signature, so a signature made for one
format is refused on another.
The filename before the extension is yours: it is what a browser calls the
download and is not signed. certificate.pdf and cert.pdf are the same
document.
Rotating a secret
A Dynamic URL holds up to five signing secrets, and any unrevoked one works. So rotating is not a cutover:
- Add a secret.
- Migrate whatever signs URLs to the new one, at your own pace.
- Revoke the old one when Last used shows nothing is still on it.
Nothing breaks in between, which matters because a live URL sits in someone else's email template where you cannot reach it.
Revoking is immediate. Anything still signing with that secret starts returning 403 straight away, which is why Last used is worth checking first.
Controls
expires_at | After this, the URL returns 410. The cache lifetime is capped so expiry is exact. |
max_renders | Counts renders, not views. A cached document keeps serving after the cap. |
rate_limit | At most 30 new documents an hour per visitor IP; over that, new data gets a 429. Documents already made are always served. Off by default; keep it off for newsletters, since Gmail and other email services fetch images through shared IPs. Worth turning on for an Open URL on a web page. |
cache_ttl | How long the CDN keeps a copy. Defaults to a day; set it low for something you edit often. |
formats | Which extensions this URL will serve. |
status | disabled returns 410 and clears the CDN copy. |
What happens on a request
1. CDN has it? → bytes, ~15ms
2. No → your document is looked up, the signature and controls checked
3. Rendered before → streamed from storage
4. First time → rendered now, stored, returnedA document is only ever rendered once per distinct payload, however many people open it and from wherever.
Editing the template
Change a template and every Dynamic URL on it re-renders on its next request.
There is nothing to purge and no URL to reissue. The CDN may serve the previous
version until cache_ttl lapses, so lower it on templates you are actively
working on.
Last updated on