Configure Videos
Set content and metadata for videos within a bundle. You can configure a single video by position or update multiple videos in one batch request.
Configure a Single Video
PUT /bundles/:id/videos/:position
Sets or updates the content and metadata for the video at the given position (1-indexed).
Configure Multiple Videos (Batch)
PUT /bundles/:id/videos/batch
Updates multiple videos in a single request. The request body must be wrapped in a videos object — it is not a raw array.
{
"videos": [
{ "position": 1, "video_type": "video", "..." : "..." },
{ "position": 2, "video_type": "carousel", "..." : "..." }
]
}
Field Reference
Common Fields
| Field | Type | Required | Description |
|---|---|---|---|
position | integer | Batch only | 1-indexed video position. Required in batch requests. |
video_type | string | Yes | "video" or "carousel". For Instagram: carousel with instagram_content_type: "reel" = Fixed Photos (creates a video from images), carousel with instagram_content_type: "post" = swipeable Carousel. |
description | string | Yes | Caption or description for the post. |
target_publish_date | string | Yes | Desired publish date (YYYY-MM-DD). Min 3 days ahead for new accounts, 1 day for existing. |
video_url | string | Conditional | Video file URL. Required when video_type is "video". You can pass a public_url from the upload endpoint or any external URL (Google Drive, Dropbox, direct link…) — the API will automatically download and re-upload the file to our storage. |
carousel_images | string[] | Conditional | Array of image storage paths. Required when video_type is "carousel". Use the storage_path from the image upload response. |
tiktok_sound_url | string | Conditional | TikTok sound URL. Required for TikTok carousels, optional for TikTok videos. |
editing_instructions | string | No | Free-text instructions for the editor (e.g., "Add logo at end"). |
external_ref | string | No | Your own reference ID for this video (e.g., campaign or CMS identifier). |
profile_picture_url | string | No | Storage path for the account profile picture. Use the storage_path from the image upload response. |
Instagram-Specific Fields
| Field | Type | Required | Description |
|---|---|---|---|
instagram_content_type | string | Yes (Instagram) | "reel" or "post". Required for Instagram bundles. |
instagram_location | string | No | Location tag for the Instagram post. |
instagram_collaborators | string[] | No | Array of Instagram usernames to tag as collaborators. |
instagram_audio_name | string | No | Name of the audio track for Instagram Reels. |
instagram_add_to_story | boolean | No | If true, the creator also shares the post to their Instagram Story. |
Instagram Content Type Combinations
instagram_content_type | video_type | Result |
|---|---|---|
reel | video | Reel Video |
reel | carousel | Reel Fixed Photos — creates a video from images (not swipeable) |
post | video | Post Video |
post | carousel | Post Carousel — swipeable photo carousel |
YouTube Fields (Coming Soon)
YouTube support is not yet available. The following fields are reserved for future use:
| Field | Type | Description |
|---|---|---|
youtube_title | string | Video title. |
youtube_tags | string[] | Array of tags. |
youtube_category | string | Video category. |
youtube_visibility | string | "public", "unlisted", or "private". |
Examples
TikTok Video
curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/1 \
-H "X-API-Key: tok_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"video_type": "video",
"description": "Check out this new product! #ad #sponsored",
"target_publish_date": "2026-03-15",
"video_url": "https://pub-xxx.r2.dev/videos/bundle-id/promo.mp4",
"tiktok_sound_url": "https://www.tiktok.com/music/trending-sound-789",
"editing_instructions": "Add brand watermark in bottom-right corner",
"external_ref": "campaign-42-v1"
}'
Response:
{
"data": {
"position": 1,
"video_type": "video",
"status": "configured",
"description": "Check out this new product! #ad #sponsored",
"target_publish_date": "2026-03-15",
"video_url": "https://pub-xxx.r2.dev/videos/bundle-id/promo.mp4",
"tiktok_sound_url": "https://www.tiktok.com/music/trending-sound-789",
"editing_instructions": "Add brand watermark in bottom-right corner",
"external_ref": "campaign-42-v1"
}
}
TikTok Carousel
curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/2 \
-H "X-API-Key: tok_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"video_type": "carousel",
"description": "Swipe through our latest collection",
"target_publish_date": "2026-03-17",
"carousel_images": [
"carousel-images/org_xxx/bundle_abc123/slide1.jpg",
"carousel-images/org_xxx/bundle_abc123/slide2.jpg",
"carousel-images/org_xxx/bundle_abc123/slide3.jpg"
],
"tiktok_sound_url": "https://www.tiktok.com/music/chill-vibes-456",
"external_ref": "campaign-42-v2"
}'
Response:
{
"data": {
"position": 2,
"video_type": "carousel",
"status": "configured",
"description": "Swipe through our latest collection",
"target_publish_date": "2026-03-17",
"carousel_images": [
"carousel-images/org_xxx/bundle_abc123/slide1.jpg",
"carousel-images/org_xxx/bundle_abc123/slide2.jpg",
"carousel-images/org_xxx/bundle_abc123/slide3.jpg"
],
"tiktok_sound_url": "https://www.tiktok.com/music/chill-vibes-456",
"external_ref": "campaign-42-v2"
}
}
Instagram Reel (Video)
curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_def456/videos/1 \
-H "X-API-Key: tok_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"video_type": "video",
"instagram_content_type": "reel",
"description": "Morning routine with our skincare line",
"target_publish_date": "2026-03-20",
"video_url": "https://pub-xxx.r2.dev/videos/bundle-id/reel-skincare.mp4",
"instagram_location": "Los Angeles, California",
"instagram_collaborators": ["brandofficial"],
"instagram_audio_name": "Original Audio",
"instagram_add_to_story": true,
"external_ref": "ig-reel-001"
}'
Instagram Reel Fixed Photos (creates a video from images, not swipeable)
curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_def456/videos/2 \
-H "X-API-Key: tok_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"video_type": "carousel",
"instagram_content_type": "reel",
"description": "Style inspiration for spring",
"target_publish_date": "2026-03-20",
"carousel_images": [
"carousel-images/org_xxx/bundle_def456/slide1.jpg",
"carousel-images/org_xxx/bundle_def456/slide2.jpg"
],
"instagram_location": "Paris, France",
"instagram_collaborators": ["partner"],
"instagram_add_to_story": true
}'
Instagram Post Carousel (swipeable photo carousel)
curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_def456/videos/3 \
-H "X-API-Key: tok_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"video_type": "carousel",
"instagram_content_type": "post",
"description": "Our latest collection",
"target_publish_date": "2026-03-20",
"carousel_images": [
"carousel-images/org_xxx/bundle_def456/photo1.jpg",
"carousel-images/org_xxx/bundle_def456/photo2.jpg"
],
"instagram_location": "Paris, France"
}'
For Instagram, video_type: "carousel" behaves differently depending on instagram_content_type:
- With
"reel": creates a video from your images (Fixed Photos — not swipeable) - With
"post": creates a swipeable photo carousel
You don't need to upload your video first. If you pass any external URL (Google Drive, Dropbox, any direct link) as video_url, the API will automatically download the file and store it on our servers. The returned video_url in the response will be the final hosted URL. This also works in batch configuration and CSV import.
URLs already hosted on our storage (pub-0d3f...r2.dev) are kept as-is (no redundant re-upload).
Batch Configuration
Configure multiple videos in a single request. The body must be wrapped in { "videos": [...] }.
curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/batch \
-H "X-API-Key: tok_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"videos": [
{
"position": 1,
"video_type": "video",
"description": "Day 1 of the challenge #ad",
"target_publish_date": "2026-03-15",
"video_url": "https://pub-xxx.r2.dev/videos/bundle-id/day1.mp4"
},
{
"position": 2,
"video_type": "video",
"description": "Day 2 — things are getting interesting #ad",
"target_publish_date": "2026-03-18",
"video_url": "https://pub-xxx.r2.dev/videos/bundle-id/day2.mp4",
"tiktok_sound_url": "https://www.tiktok.com/music/hype-beat-101"
},
{
"position": 3,
"video_type": "carousel",
"description": "Final results — swipe to see the transformation",
"target_publish_date": "2026-03-21",
"carousel_images": [
"carousel-images/org_xxx/bundle_abc123/before.jpg",
"carousel-images/org_xxx/bundle_abc123/after.jpg"
],
"tiktok_sound_url": "https://www.tiktok.com/music/reveal-sound-202"
}
]
}'
Response:
{
"data": {
"bundle_id": "bundle_abc123",
"configured": 3,
"errors": [],
"results": [
{
"position": 1,
"video_type": "video",
"status": "configured"
},
{
"position": 2,
"video_type": "video",
"status": "configured"
},
{
"position": 3,
"video_type": "carousel",
"status": "configured"
}
]
}
}
If some videos fail validation, the response includes partial results:
{
"data": {
"bundle_id": "bundle_abc123",
"configured": 2,
"errors": [
{
"position": 3,
"error": "carousel_images is required when video_type is carousel"
}
],
"results": [
{
"position": 1,
"video_type": "video",
"status": "configured"
},
{
"position": 2,
"video_type": "video",
"status": "configured"
}
]
}
}
Date Validation
The target_publish_date must meet the following minimum lead times:
| Account Status | Minimum Lead Time |
|---|---|
| New (not yet delivered) | 3 days from today |
| Existing (already delivered) | 1 day from today |
If the date is too soon, the API returns a VALIDATION_ERROR:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "target_publish_date must be at least 3 days from today for new accounts.",
"details": {
"field": "target_publish_date",
"minimum_date": "2026-02-14"
}
}
}
Important: Media URL Types
When referencing uploaded files in video configuration:
video_url— Use thepublic_urlfrom the video upload response.carousel_images— Use thestorage_pathfrom the image upload response (notpublic_url).profile_picture_url— Use thestorage_pathfrom the image upload response (notpublic_url).
See Media Upload for details on uploading files and obtaining URLs.