Goal
Authorize a private download at request time without giving a browser your StackShift API key or a permanent public URL.Prerequisites
- An uploaded asset with
visibility=private - A server route that checks your application’s user/session authorization
Workflow
1
Upload the file with
visibility=private or patch an existing asset with the current revision.2
Store only the asset ID and application authorization metadata in your database; do not store signed URLs as durable credentials.
3
When an authorized user requests the file, call
assets.signedUrl on your server with the shortest practical expiry.4
Return the signed URL to the browser and let it download directly. Do not proxy the bytes through your application unless you need custom response handling.
5
For a one-time link, set
maxDownloads to a positive integer and treat HEAD as a validation request that does not consume the allowance.Create a signed download URL
The SDK sendsexpires_in and optional max_downloads to POST /api/v1/assets/{assetID}/signed-url. The server checks that the asset is deliverable before issuing a token.
How signing protects the file
- Private assets have no unauthenticated public URL. The token is bound to the current asset version and expires at the time returned in
expires_at. expiresInkeeps a leaked link short-lived. It is a duration such as10m, not a timestamp.maxDownloadscreates an authoritative download allowance. AGETconsumes an allowance; aHEADvalidates access without consuming it.- Private responses use
Cache-Control: private, no-storeand the equivalent Cloudflare cache directive. Do not add a public CDN cache rule in front of a private path. - A pending, quarantined, failed, deleting, or deleted asset cannot receive a new signed URL. Existing links are revoked when delivery is being revoked.
Where signing belongs
Always mint signed URLs on the server, where the API key lives, and hand only the resulting URL to the browser. Never expose the API key to the client just to generate a link. Treat the full URL, including its query token, as a secret: do not put it in analytics events, permanent logs, or HTML that outlives the authorization decision.Changing visibility safely
- Visibility changes are revision-checked mutations. Read the latest asset, send
If-Match: "<revision>", and refresh after a conflict. - Private-to-public and public-to-private transitions update the current generation and delivery policy. Use the new
asset.urlonly after the response reports a ready, deliverable asset. - For downloads that must remain tied to a historical version, use the version URL helpers only with an authorization design that can mint a token for that version; the ordinary
signedUrlmethod signs the current version.
Expected result
A private asset is reachable only through a valid, expiring token and is delivered with private no-store cache headers.
Related guides
StackShift Assets
A grounded guide to Assets storage, uploads, delivery, imports, BYOB and S2, transformations, OCR, media processing, AI jobs, and DAM controls.
Direct browser uploads
Upload browser files directly with short-lived sessions, per-part checksums, progress callbacks, retries, resume, and cancellation.
Video, scanning, and governance
Process video asynchronously, deliver HLS and posters, expose scan gates, quarantine unsafe assets, and enforce account policies.