Goal
Keep API credentials on the server while moving file bytes from the browser to the Assets upload origin.Prerequisites
- A backend route that authenticates the user before creating a session
- A browser
FileorBlob - CORS configured on the bucket when the browser origin differs from your app
Workflow
1
Authenticate the user on your server and choose a safe bucket/key. Never accept an unrestricted key or visibility from an untrusted browser without applying your own policy.
2
Create a single session for small files or a chunked session with
file_size, file_name, mime_type, and an optional whole-file checksum.3
Return only the signed upload response to the browser. The token is time-limited and scoped to the chosen upload policy.
4
PUT the file or each part. Chunked parts include
X-Content-SHA256; the SDK can report progress, retry with backoff, and resume missing parts.5
Complete the session and persist the returned asset. Poll its state before exposing a public URL.
Server creates the upload URL
The SDK mapsexpiresIn, maxBytes, and fileSize to the API’s expires_in, max_bytes, and file_size fields. A session defaults to a short expiry; set a longer value only when the user experience requires it.
Browser uploads the file
Chunked uploads for large files
The JavaScript SDK’suploadWithProgress uses a 5 MiB part size by default for files above the single-upload threshold. It caps concurrency and retry attempts at eight, reports aggregate and per-part progress, and cancels the server session when an AbortSignal aborts the operation.
Use the lower-level session methods when you need to persist an upload screen’s state across page reloads. resumeUploadSession returns the received part list; upload only missing parts, then call completeUploadSession.
Checksums, limits, and idempotency
- A chunked part must be exactly the expected size (the final part may be smaller) and its SHA-256 must match
X-Content-SHA256. A mismatch removes the part and returns a checksum error. - The account content policy and bucket policy are checked when a session is created and again when the completed asset is committed. MIME allowlists, family-specific byte limits, and
max_object_bytescan reject an otherwise valid browser request. - Send an
idempotencyKeywhen creating a session from a retrying backend. The same key and same canonical request reuse the session; a changed key, size, MIME type, or policy returns a conflict. - Upload tokens expire and are single-use for single uploads. Treat an expired or used token as a new-session condition, not as a reason to retry the same PUT indefinitely.
CORS and browser security
- Configure the bucket’s
cors_originsfor browser origins that need to PUT directly.allowed_originsis the delivery/origin allowlist used by the Assets policy model. - Use a server-generated key or a server-approved prefix. Do not let a browser overwrite another user’s key merely because it can request a signed URL.
- The upload URL is not a download URL. After completion, use the asset’s public URL or mint a private signed URL according to the final visibility.
Expected result
The browser sends bytes directly to StackShift Assets, and your backend retains control over identity, key, limits, visibility, and the resulting asset record.
Related guides
Assets SDK quick start
Install the official SDKs, upload a first asset, and handle the readiness state before you publish its URL.
Private assets and signed URLs
Keep files private, mint version-bound download tokens, and avoid accidentally caching or logging protected media.
Upload UX and DAM
Build a durable upload and digital-asset-management workflow with resumable sessions, revision-safe mutations, search, collections, webhooks, and usage summaries.
Embeddable Assets upload widget
Install
@stackshift-cloud/assets-widget for React or vanilla JavaScript with constrained backend-issued capabilities, resumable chunks, camera and URL input, cropping, previews, progress, retry, and cancellation.