API Reference
Upload
Upload files to your content library using a two-step presigned URL flow. Get a presigned URL, upload directly to storage, then finalize.
How It Works
Uploading follows a three-step pattern that keeps large files off the API server:
- Presign — request a presigned URL from the API
- Upload — PUT your file directly to that URL
- Complete — tell the API the upload is done to create the content record
Carousel uploads
contentItemId returned from the first complete call to attach additional images to the same item.Content Type Rules
| Parameter | Type | Description |
|---|---|---|
shortform | video | mp4, mov — max 300 MB, 3–90s duration, single file |
carousel | image | jpeg, png, webp — max 20 MB per image, 2–10 files |
longform | video | mp4, mov — max 1 GB, single file |
Get Presigned URL
/api/v1/upload/presignRequest a presigned URL for uploading a file directly to storage.
Request body
| Parameter | Type | Description |
|---|---|---|
fileNamerequired | string | Original file name |
contentTyperequired | string | MIME type (e.g. video/mp4, image/jpeg) |
fileSizerequired | number | File size in bytes |
type | string | shortform, carousel, or longform. Auto-detected from MIME type if omitted. |
thumbnail | boolean | Set true for thumbnail uploads. Skips content type validation and storage quota checks. Only image/jpeg, image/png, image/webp accepted. |
Response
{
"presignedUrl": "https://storage.example.com/...",
"key": "userId/fileId/clip.mp4",
"fileId": "550e8400-e29b-41d4-a716-446655440000",
"type": "shortform"
}Example
curl -X POST https://stashkit.net/api/v1/upload/presign \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"fileName": "clip.mp4",
"contentType": "video/mp4",
"fileSize": 12345678,
"type": "shortform"
}'Upload the File
After receiving the presigned URL, upload your file directly to storage with a PUT request. Both the Content-Type and Content-Length headers must match the values you used in the presign step — the presigned URL is signed with these values and mismatched headers will result in a 403 error.
curl -X PUT "<presignedUrl>" \
-H "Content-Type: video/mp4" \
-H "Content-Length: 12345678" \
--data-binary @clip.mp4Presigned URL expiry
Finalize Upload
/api/v1/upload/completeFinalize the upload and create the content item in your library.
Request body
| Parameter | Type | Description |
|---|---|---|
keyrequired | string | The key returned from the presign step |
fileNamerequired | string | Original file name |
contentTyperequired | string | MIME type |
fileSizerequired | number | File size in bytes |
typerequired | string | shortform, carousel, or longform |
durationSeconds | number | Video duration in seconds (shortform / longform) |
contentItemId | string | Existing content item ID — for adding images to a carousel |
Response — new item
{
"item": {
"id": "uuid",
"type": "shortform",
"title": "clip",
"status": "draft",
"fileUrl": "https://...",
"thumbnailUrl": null,
...
},
"contentItemId": "uuid"
}Response — carousel addition
{
"item": { ... },
"file": {
"id": "uuid",
"fileKey": "...",
"sortOrder": 2,
...
},
"contentItemId": "uuid"
}Example
curl -X POST https://stashkit.net/api/v1/upload/complete \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"key": "userId/fileId/clip.mp4",
"fileName": "clip.mp4",
"contentType": "video/mp4",
"fileSize": 12345678,
"type": "shortform",
"durationSeconds": 45
}'Full Carousel Upload Example
Upload a 3-image carousel by repeating the presign → upload → complete cycle. The first call creates the item; subsequent calls attach images using contentItemId.
# Presign
PRESIGN=$(curl -s -X POST https://stashkit.net/api/v1/upload/presign \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"fileName":"slide1.jpg","contentType":"image/jpeg","fileSize":204800,"type":"carousel"}')
# Upload to storage
curl -X PUT "$(echo $PRESIGN | jq -r .presignedUrl)" \
-H "Content-Type: image/jpeg" \
--data-binary @slide1.jpg
# Complete — save the contentItemId
COMPLETE=$(curl -s -X POST https://stashkit.net/api/v1/upload/complete \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"key\": $(echo $PRESIGN | jq .key),
\"fileName\": \"slide1.jpg\",
\"contentType\": \"image/jpeg\",
\"fileSize\": 204800,
\"type\": \"carousel\"
}")
ITEM_ID=$(echo $COMPLETE | jq -r .contentItemId)# Repeat presign + upload for slide2.jpg, then complete with contentItemId:
curl -X POST https://stashkit.net/api/v1/upload/complete \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"key\": \"<key from presign>\",
\"fileName\": \"slide2.jpg\",
\"contentType\": \"image/jpeg\",
\"fileSize\": 180000,
\"type\": \"carousel\",
\"contentItemId\": \"$ITEM_ID\"
}"