Skip to main content
Live. This area is documented as current, user-reliable behavior.

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 sends expires_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.
  • expiresIn keeps a leaked link short-lived. It is a duration such as 10m, not a timestamp.
  • maxDownloads creates an authoritative download allowance. A GET consumes an allowance; a HEAD validates access without consuming it.
  • Private responses use Cache-Control: private, no-store and 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.url only 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 signedUrl method 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.

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.