Download URLs
A download signed URL grants read access to exactly one stored path until a moment you choose. Mint it with a session; use it without one.
POST /api/buckets/documents/sign
Content-Type: application/json
Cookie: <session>
{
"path": "9f3c1e2a-7b44-4d10-9e21-8a6f0c1d2e3b-contract.pdf",
"expiresIn": 3600
}
{
"success": true,
"signedUrl": "https://app.example.com/api/buckets/documents/signed?path=9f3c1e2a-…-contract.pdf&op=download&expires=1775386800000&token=4b1f…",
"expiresAt": "2026-04-05T11:00:00Z",
"operation": "download"
}
operation defaults to download, so the field is omitted above. Hand signedUrl to a browser, an email template, or a PDF renderer — it needs nothing else.
Request Body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
path |
string | Yes | — | The storage key, exactly as the upload returned it. |
expiresIn |
integer | No | 3600 |
Lifetime in seconds. Between 60 and 604800 (7 days). |
operation |
string | No | download |
download or upload. See Upload Signing. |
Outcomes
| Situation | Result |
|---|---|
| Valid request | 200 with signedUrl and expiresAt. |
expiresIn below 60 or above 604800 |
400 |
path missing or empty |
400 |
No file at path |
404 — a download token must reference real bytes |
| No session | 401 |
Session lacking the bucket's sign permission |
404 |
| Unknown bucket | 404 |
Note the existence check. Unlike upload signing, download signing refuses to mint a token for a path that holds nothing, so a signed URL you are holding is a URL that resolved at least once.
Using and Expiring the URL
The URL resolves against GET /api/buckets/{bucket}/signed. Three things can go wrong at that point, and all three answer 403:
| At use time | Result |
|---|---|
| Within the window, signature intact | 200 with the bytes. |
Past expiresAt |
403 |
| Any query parameter altered — path, expiry, op | 403 (signature mismatch) |
An upload token used for a GET |
403 (operation mismatch) |
| Token valid but the file was deleted meanwhile | 404 |
Tokens are compared in constant time, so a malformed or near-miss token leaks nothing through response timing. The 403 for expiry and the 403 for tampering are deliberately the same answer.
Choose the shortest window that works. A signed URL cannot be revoked individually — once minted it is valid until expiresAt, whatever happens to the user's session, role or account in the meantime. Sixty seconds for a redirect, an hour for an email link, seven days only for something genuinely long-lived. The only global revocation is rotating AUTH_SECRET, which invalidates every outstanding URL at once.
Transforms on a Signed URL
An image signed URL accepts transform parameters appended to it. The token covers path, op and expires — not the transform query — so one signed URL serves any size:
<signedUrl>&width=200&fit=cover
Raster images arrive with Content-Disposition: inline on this route, so they render directly in an <img>. SVG does not: it is forced to attachment because an inline SVG on your origin can execute script. See Upload Security.
Related Pages
- Signed URLs — why signing exists and who may sign.
- Upload & Batch Signing — write tokens and bulk minting.
- Attachment Fields & Storage — records that arrive pre-signed.
- Resize & Crop — the parameters you can append.
- File Operations — session-based download.
Last updated July 27, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta — contributions and corrections are welcome.