Source: https://developers.tokportal.com/videos/
Markdown: https://developers.tokportal.com/videos.md

# Videos

Videos are the content units within a [bundle](https://developers.tokportal.com/bundles). Each video occupies a specific **position** (1-indexed) in the bundle and represents a single piece of content to be published on a creator's social media account.

## Video Types

Every video has a `video_type` that determines what content it contains:

| Type       | Description                                                                                                                                                                                                                                                                      |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `video`    | A single video file                                                                                                                                                                                                                                                              |
| `carousel` | Multiple images displayed as a slideshow                                                                                                                                                                                                                                         |
| `story`    | A TikTok/Instagram Story — one video **or** one image, no description. Counts as one video slot and is verified by an account-manager screenshot (`evidence_screenshot`). See [Configure Videos → Story Fields](https://developers.tokportal.com/configure-videos#story-fields). |

## Platform-Specific Behavior

Each platform supports different combinations of video types and has its own requirements.

### TikTok

| Type       | Requirements                                                                                                                    |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `video`    | Video file required. Sound URL optional.                                                                                        |
| `carousel` | Carousel images required. Sound URL **required**.                                                                               |
| `story`    | Exactly one media: `video_url` **or** `story_image_url`. No description. Optional same-platform `story_repost_url` (+1 credit). |

### Instagram

Instagram requires the `instagram_content_type` field to distinguish between reels and feed posts:

| 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                            |

> **INFO: Reel Fixed Photos vs Post Carousel**
> On Instagram, `video_type: "carousel"` behaves differently depending on `instagram_content_type`:
>
> - With `"reel"`: Instagram creates a **video** from your images (Fixed Photos — not swipeable). This is a Reel, not a carousel.
> - With `"post"`: Instagram creates a **swipeable photo carousel** in the feed.

Instagram also supports `video_type: "story"` (one video or one image, no `instagram_content_type`). See [Configure Videos → Story Fields](https://developers.tokportal.com/configure-videos#story-fields).

### YouTube

YouTube accounts cannot be ordered through the API. Public bundle creation supports **TikTok and Instagram only**, and a YouTube bundle is rejected with `YOUTUBE_DELAYED` — even though `GET /credit-costs` lists a YouTube price, the price is real but the order path is not open. YouTube accounts are delivered through a separate, non-API flow; talk to the team. You may still see reserved YouTube fields in templates and generated schemas.

## Video Status Lifecycle

Each video progresses through the following statuses:

```
pending → configured → published → accepted → in_review → finalized
```

| Status       | Description                                                                                                                                                    |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pending`    | Position exists but no content has been configured yet.                                                                                                        |
| `configured` | Content has been uploaded and metadata set. Ready for review.                                                                                                  |
| `published`  | The video has been published to the platform by the creator.                                                                                                   |
| `accepted`   | The brand has reviewed and accepted the published video.                                                                                                       |
| `in_review`  | The video is awaiting user review. If the user takes no review action for about 72 hours, TokPortal automatically moves the video listing to `finalized`.      |
| `finalized`  | The video lifecycle is complete, either after user approval or automatic finalization at the end of the 72-hour review window. No further changes are allowed. |

The 72-hour window starts when the video listing enters `in_review`. Finalize it to approve it immediately, or request corrections before the window expires. If neither action is taken, finalization is automatic — this applies **regardless of `auto_finalize_videos`**, which does not switch automatic finalization off.

## Platform Signals

Once a video is live, TokPortal checks what the platform did with it: about **48 hours** after posting and again after **7 days**, plus whenever the post is scraped for analytics. The result is returned on every video as `platform_signals`, also on the [order progress page](https://app.tokportal.com/dashboard) and in [Analytics](https://developers.tokportal.com/analytics#platform-signals). The object is always present; every field is `null` until the first check — `null` means *not checked yet*, never *no*.

```json
"platform_signals": {
  "checked_at": "2026-09-22T05:47:52Z",
  "not_recommended": true,
  "not_recommended_since": "2026-09-19T05:28:00Z",
  "ai_label": null,
  "sound": "non_commercial",
  "sound_reason": "Can't promote due to audio copyright issue"
}
```

| Field                   | Type                                                     | Description                                                                                                                                                                                                                                                                                                              |
| ----------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `checked_at`            | string \| null                                           | Last time the live post was observed.                                                                                                                                                                                                                                                                                    |
| `not_recommended`       | boolean \| null                                          | **TikTok Not For Feed / Not Recommended.** `true` = the post stays online but is excluded from the For You feed and typically sits at 0 views. Usually a sign that TikTok treats the content as reused across accounts or low-effort. Re-posting the same media does not help; new original content on the account does. |
| `not_recommended_since` | string \| null                                           | First observation of the flag.                                                                                                                                                                                                                                                                                           |
| `ai_label`              | `creator` \| `platform` \| null                          | An "AI-generated" label is shown on the post: `creator` = declared at posting time, `platform` = applied by TikTok's moderation on its own.                                                                                                                                                                              |
| `sound`                 | `ok` \| `non_commercial` \| `removed` \| `muted` \| null | `non_commercial`: the sound is outside TikTok's commercial music library — the post cannot be boosted with Promote / Spark Ads. `removed`: TikTok pulled the sound for copyright, the video plays muted. `muted`: Instagram muted the audio.                                                                             |
| `sound_reason`          | string \| null                                           | Platform wording when the sound is restricted.                                                                                                                                                                                                                                                                           |

The same observations are pushed as webhooks the moment they are detected: [`video.not_recommended`](https://developers.tokportal.com/webhooks#platform-signal-events), [`video.ai_labeled`](https://developers.tokportal.com/webhooks#platform-signal-events) and [`video.sound_restricted`](https://developers.tokportal.com/webhooks#platform-signal-events). Portfolio-wide, [`GET /analytics/signals`](https://developers.tokportal.com/analytics#flagged-posts) lists every flagged post.

## Publish Date Rules

The `target_publish_date` field controls when the video should be published.

- **Account still being created** (not yet delivered): minimum **3 days** from today.
- **Account already delivered**, or a bundle running on an **existing account**: minimum **1 day** from today.

A bundle can also carry at most **3 videos targeting the same day**. Dates earlier than the allowed minimum are rejected with a `VALIDATION_ERROR`.

## List All Videos in a Bundle

Retrieve every video configured for a bundle.

```
GET /bundles/:id/videos
```

```bash
curl -X GET https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos \
  -H "X-API-Key: sk_xxx"
```

**Response:**

```json
{
  "data": [
    {
      "position": 1,
      "video_type": "video",
      "status": "configured",
      "description": "Unboxing the new product line",
      "target_publish_date": "2026-03-15",
      "video_url": "https://pub-xxx.r2.dev/videos/abc123.mp4",
      "external_ref": "campaign-42-v1",
      "download_issue": false,
      "download_issue_comment": null
    },
    {
      "position": 2,
      "video_type": "carousel",
      "status": "pending",
      "description": null,
      "target_publish_date": null,
      "video_url": null,
      "external_ref": null,
      "download_issue": false,
      "download_issue_comment": null
    }
  ]
}
```

## Get a Single Video

```
GET /bundles/:id/videos/:position
```

```bash
curl -X GET https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/1 \
  -H "X-API-Key: sk_xxx"
```

**Response:**

```json
{
  "data": {
    "position": 1,
    "video_type": "video",
    "status": "configured",
    "description": "Unboxing the new product line",
    "target_publish_date": "2026-03-15",
    "video_url": "https://pub-xxx.r2.dev/videos/abc123.mp4",
    "tiktok_sound_url": "https://www.tiktok.com/music/original-sound-123",
    "editing_instructions": "Add brand logo at the end",
    "external_ref": "campaign-42-v1",
    "download_issue": false,
    "download_issue_comment": null,
    "platform_signals": {
      "checked_at": null,
      "not_recommended": null,
      "not_recommended_since": null,
      "ai_label": null,
      "sound": null,
      "sound_reason": null
    }
  }
}
```

## What's Next

| Topic                                                                                                 | Description                                                              |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| [Configure Videos](https://developers.tokportal.com/videos/configure-videos)                          | Set content, metadata, and platform-specific fields                      |
| [Patch Video Metadata](https://developers.tokportal.com/videos/configure-videos#patch-video-metadata) | Update `external_ref` or `name` at any time without re-uploading content |
| [CSV Import](https://developers.tokportal.com/videos/csv-import)                                      | Bulk-import videos from a CSV file                                       |
| [Video Actions](https://developers.tokportal.com/videos/video-actions)                                | Finalize, request corrections, or unschedule videos                      |
| [Fix Download Link](https://developers.tokportal.com/videos/fix-download-link)                        | Detect and fix broken download links flagged by managers                 |
