Downloads & Storage
A rendered PDF lives in a private bucket. download_url is not a permanent address — it is a signed, time-limited link, minted fresh every time you read the job. Store the job id; resolve the link when you actually need the bytes.
How download URLs work
Section titled “How download URLs work”- The bucket is private. There is no public URL for a rendered document.
download_urlis a presignedGETlink, signed for exactly one object.- It is generated on read and never persisted. Two reads of the same job return two different links to the same file.
- It expires — one hour by default.
Getting a fresh link
Section titled “Getting a fresh link”Read the job whenever you need a working link:
curl https://api.tupsara.com/v1/pdf/a3f1c2e4-9b70-4c1e-8f2a-6d5b0e7c1a34 \ -H "Authorization: Bearer pdf_live_..."{ "id": "a3f1c2e4-9b70-4c1e-8f2a-6d5b0e7c1a34", "status": "completed", "download_url": "https://.../a3f1c2e4.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&X-Amz-Signature=...", "file_size_bytes": 18420, "render_duration_ms": 812, "created_at": "2026-08-31T19:58:51Z"}The signature is computed locally, so asking for a link costs a database read — not a round trip to storage.
Before the render completes
Section titled “Before the render completes”download_url, file_size_bytes and render_duration_ms are null until status is completed. A job that ends as failed keeps them null — there is no partial file to fetch. Branch on status, not on the presence of a URL.
Response fields
Section titled “Response fields”| Field | Meaning |
|---|---|
id |
UUID of the job. The only durable handle — this is what you store. |
status |
pending, rendering, completed or failed. |
download_url |
Signed link to the PDF. null until the render completes. |
file_size_bytes |
Size of the rendered file. |
render_duration_ms |
Time the browser spent producing this document. |
created_at |
When the job was accepted, in UTC. |
Full request/response reference: PDF.
When a link expires
Section titled “When a link expires”An expired link is rejected by the object store, not by Tupsara — so you get its error, not the error envelope the API uses everywhere else. There is nothing to retry at that URL. Read the job again and use the new link.
Never hand a download_url to something that will use it later: a queued email, a stored bookmark, a cached client response. Hand over the job id and resolve the link at the moment of the click.
Listing past renders
Section titled “Listing past renders”curl "https://api.tupsara.com/v1/pdf?limit=20&offset=0" \ -H "Authorization: Bearer pdf_live_..."Every item in the page carries its own freshly signed link, on the same clock as the rest. Listing is a way to find jobs, not a way to build a permanent table of URLs.