Source: https://developers.tokportal.com/fix-download-link/
Markdown: https://developers.tokportal.com/fix-download-link.md

# Fix Download Link

When an account manager encounters a broken, expired, or inaccessible download link for a video, they flag it. The video's `download_issue` field becomes `true` and a `download_issue_comment` describes the problem reported by the manager.

## How It Works

1. **Manager flags a broken link** — the video gets `download_issue: true` with a comment describing the issue
2. **You detect the flag** — poll your videos via `GET /bundles/:id/videos` and check for `download_issue: true`
3. **You provide a new URL** — call `POST /bundles/:id/videos/:position/fix-download` with a working link
4. **Flag is cleared** — the manager can proceed with the video

## Detecting Broken Links

Every video response includes two fields related to download issues:

| Field                    | Type           | Description                                 |
| ------------------------ | -------------- | ------------------------------------------- |
| `download_issue`         | boolean        | `true` if the manager flagged a broken link |
| `download_issue_comment` | string \| null | The manager's description of the problem    |

These fields are returned by:

- `GET /bundles/:id/videos` (list all videos)
- `GET /bundles/:id/videos/:position` (get single video)

### Example: video with a broken link

```json
{
  "data": {
    "position": 2,
    "status": "configured",
    "video_url": "https://expired-cdn.example.com/old-video.mp4",
    "download_issue": true,
    "download_issue_comment": "Link returns 403 Forbidden. Please provide a new URL."
  }
}
```

## Fix a Broken Link

```
POST /bundles/:id/videos/:position/fix-download
```

Provide a new working URL to replace the broken one. The video file is downloaded and re-uploaded to TokPortal's storage, then the `download_issue` flag is cleared and the **target publish date is automatically reset to tomorrow** (today + 1 day) so the account manager is not penalized for the delay.

> **INFO: Date reset**
> When you fix a broken link, the video's `target_publish_date` is automatically moved to tomorrow and the `target_publish_end_date` to the day after. This ensures the manager has a fresh deadline and isn't marked as late because of a broken link issue.

### Request Body

| Field             | Required    | Description                                                                     |
| ----------------- | ----------- | ------------------------------------------------------------------------------- |
| `video_url`       | Conditional | New video URL (for `video` type). Provide this **or** `carousel_images`.        |
| `carousel_images` | Conditional | New carousel image URLs (for `carousel` type). Provide this **or** `video_url`. |

**For a regular video:**

```bash
curl -X POST https://app.tokportal.com/api/ext/bundles/{bundle_id}/videos/2/fix-download \
  -H "X-API-Key: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://cdn.example.com/fixed-video.mp4"
  }'
```

**For a carousel:**

```bash
curl -X POST https://app.tokportal.com/api/ext/bundles/{bundle_id}/videos/2/fix-download \
  -H "X-API-Key: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "carousel_images": [
      "https://cdn.example.com/slide1.jpg",
      "https://cdn.example.com/slide2.jpg"
    ]
  }'
```

### Response (200)

```json
{
  "data": {
    "id": "uuid",
    "position": 2,
    "status": "configured",
    "video_type": "video",
    "name": "Video 2",
    "video_url": "https://pub-xxx.r2.dev/videos/bundle_id/new-file.mp4",
    "download_issue": false,
    "download_issue_comment": null,
    "target_publish_date": "2026-02-22T00:00:00Z",
    "target_publish_end_date": "2026-02-23T00:00:00Z",
    "updated_at": "2026-02-21T14:00:00Z"
  }
}
```

### Errors

| Code                          | Status | When                                                              |
| ----------------------------- | ------ | ----------------------------------------------------------------- |
| `BUNDLE_NOT_FOUND`            | 404    | Bundle ID not found                                               |
| `BUNDLE_NOT_OWNED`            | 403    | Bundle belongs to another user                                    |
| `VIDEO_NOT_FOUND`             | 404    | No video at this position                                         |
| `VIDEO_POSITION_OUT_OF_RANGE` | 400    | Position exceeds `videos_quantity`                                |
| `VIDEO_NO_DOWNLOAD_ISSUE`     | 409    | The video does not have a download issue flagged — nothing to fix |
| `UPLOAD_FAILED`               | 500    | Failed to download/upload the replacement file                    |

## Uploading a File Instead of a URL

If you want to upload a video file directly instead of providing a URL, use the standard upload endpoints first:

1. **Presigned upload:** `POST /upload/video` → get `upload_url` and `public_url` → PUT the file → use `public_url` in fix-download
2. **Direct upload:** `POST /upload/video/direct` (multipart) → get `public_url` → use it in fix-download

See [Media Upload](https://developers.tokportal.com/media-upload) for details.

> **TIP: Automation tip**
> Set up a periodic check (e.g. every 30 minutes) that calls `GET /bundles/:id/videos` on your active bundles and filters for `download_issue: true`. This lets you detect and fix broken links quickly, avoiding delays in your campaign.
