Automette docs
Generating

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 every recipient — a certificate per student, an invoice per customer — and 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

In the dashboard, Dynamic URLs → New URL. Or:

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...", "name": "Cohort 2026 certificates" }'

The response carries the signing secret and a sample_url that works as-is. Keep the secret somewhere your server can read it.

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:

  1. Add a secret.
  2. Migrate whatever signs URLs to the new one, at your own pace.
  3. 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_atAfter this, the URL returns 410. The cache lifetime is capped so expiry is exact.
max_rendersCounts renders, not views. A cached document keeps serving after the cap.
cache_ttlHow long the CDN keeps a copy. Defaults to a day; set it low for something you edit often.
formatsWhich extensions this URL will serve.
statusdisabled 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, returned

A 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

On this page