curl -X POST "http://localhost:7100/v1/upload/sign" \
-H "x-api-key: sk_live_master_secret_key" \
-H "Content-Type: application/json" \
-d '{
"folder": "avatars",
"maxBytes": 5242880,
"expiresInSeconds": 300,
"access": "public"
}'When building modern web and mobile applications, users often upload images, avatars, or documents.
If your frontend sends 20MB files through your backend application server (Node.js, Python, or Ruby), your backend will spend critical CPU cycles and RAM buffering files instead of handling API requests.
Direct Client Uploads solve this problem completely:
text┌─────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │ Browser / App │ │ Backend Server │ │ Storage Server │ └────────┬────────┘ └──────────┬──────────┘ └──────────┬──────────┘ │ │ │ │ 1. "Request upload token" │ │ │────────────────────────────────>│ │ │ │ 2. POST /v1/upload/sign │ │ │ (Authenticated with API key) │ │ │──────────────────────────────────>│ │ │ │ │ │ 3. Returns signed HMAC token │ │ │<──────────────────────────────────│ │ 4. Passes token to frontend │ │ │<────────────────────────────────│ │ │ │ │ 5. POST /v1/upload with X-Upload-Token header (direct upload) │ │────────────────────────────────────────────────────────────────────>│ │ │ │ 6. Upload succeeds! Returns asset metadata & CDN URL │ │<────────────────────────────────────────────────────────────────────│
Step 1: Generate Signed Token (Backend)#
On your backend server, make a POST /v1/upload/sign request authenticated with your write/secret API key:
Request:
bashcurl -X POST "http://localhost:7100/v1/upload/sign" \ -H "x-api-key: YOUR_SECRET_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "folder": "avatars", "maxBytes": 5242880, "expiresInSeconds": 300, "access": "public" }'
Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
folder | string | No | Root | Restricts upload destination to a specific folder. |
maxBytes | number | No | Server max | Maximum file size allowed in bytes (e.g. 5242880 = 5MB). |
expiresInSeconds | number | No | 300 (5 min) | Time until token expires. |
access | string | No | 'public' | Access permission: 'public' or 'private'. |
webhookUrl | string | No | None | Optional webhook URL triggered upon successful upload. |
Response:
json{ "success": true, "token": "dcd_sig_eyJpZCI6ImsxMjMiLCJleHAiOjE3NjE1Nzg0MDB9...", "uploadUrl": "http://localhost:7100/v1/upload", "expiresInSeconds": 300, "folder": "avatars", "maxBytes": 5242880, "access": "public" }
Step 2: Upload Directly from Frontend#
Your frontend browser or mobile app takes the token and uploads the file directly to the storage server using standard multipart/form-data:
javascriptconst formData = new FormData(); formData.append('file', fileInput.files[0]); const response = await fetch(tokenData.uploadUrl, { method: 'POST', headers: { 'X-Upload-Token': tokenData.token, }, body: formData, }); const result = await response.json(); console.log('File uploaded directly:', result.data.publicId, result.data.url);
Security Guarantees#
When an upload arrives with an X-Upload-Token:
- Tamper-Proof HMAC Signature: The server cryptographically validates the signature. Any tampering with the folder, expiration, or byte limit invalidates the token.
- Expiration Enforcement: Tokens expire automatically after the specified time. Expired tokens are rejected with HTTP 401.
- Strict Size Limits: If the incoming file stream exceeds
maxBytes, the stream is terminated immediately with HTTP 413 Payload Too Large. - Folder Isolation: Clients cannot write outside the permitted folder scope.
Next Steps#
- React & Next.js Upload Recipe →: Complete frontend code example.
- Private Assets & Signed URLs →: Restrict access to confidential files.
- REST API Reference →: Full API specification.