# PATCH /v1/videos/by-external-id/{external_video_id}

Update one or more editable metadata fields (`notes`, `budget`, `campaign`, `video_status`, `status`, `ecpm`, `tags`) on an already-tracked video without re-issuing a `POST /v1/videos/track` call.

This page covers **PATCH only**. To look up a video's current stats and metadata, see [`GET /v1/videos/by-external-id/{external_video_id}`](./by-external-id).

The `external_video_id` is path-encoded; URLs that contain `/` or other reserved characters must be percent-encoded.

## 📋 Overview

| Property | Value |
|----------|-------|
| **Method** | `PATCH` |
| **Endpoint** | `https://api.influtics.com/v1/videos/by-external-id/{external_video_id}` |
| **Auth Required** | ✅ |

---

## 🔐 Headers

```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

## 📝 Path Parameter

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `external_video_id` | string | ✅ | The platform-specific video ID of the video to update. |

## 📝 Request Body

At least one editable field must be present in the body — empty bodies return `400 VALIDATION_ERROR`.

| Field | Type | Required | Description | Allowed values |
|-------|------|----------|-------------|----------------|
| `notes` | string | ❌ | Free-form notes (any plain text). | ≤ 2000 chars. Empty string clears the column. |
| `budget` | integer \| null | ❌ | Total per-video budget. | >0 integer. `null` / empty string clears. |
| `campaign` | string \| null | ❌ | Campaign name. Empty string or `null` = clears. | ≤ 200 chars, trimmed. Allowed: letters, digits, spaces, and `-_.()|:%&`. Forbidden: commas and any other character (e.g. `<`, `>`, `?`, `/`, `[`, `]`, `{`, `}`, `@`, `#`, `;`). |
| `video_status` | string | ❌ | Tracking-state. | One of `tracking`, `paused`, `deleted`. |
| `status` | string | ❌ | Workflow-state. | One of `to do`, `running`, `ended`. |
| `ecpm` | number \| null | ❌ | Effective cost per mille. | float ≥ 0 (e.g. `2.50` ⇒ $2.50 per 1000 views). `null` or empty string clears the field. `0` is a valid value. |
| `tags` | string[] | ❌ | Tag names. Names are matched case-insensitively; new names auto-create the tag. | 1–50 entries, each ≤ 80 chars. ADD-only — existing tags are preserved; omitted names are not removed. |

> **Field-name note** — PATCH returns the list of fields that were updated as `data.updated_fields` (the equivalent of `data.results[].applied_fields` in the batch endpoint).

## Example Request — update notes + campaign + ecpm

```bash
curl -X PATCH https://api.influtics.com/v1/videos/by-external-id/7489443966908189959 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notes": "Post-mortem: best-performing creator of the campaign",
    "campaign": "summer_launch_2026",
    "ecpm": 3.25
  }'
```

## Example Request — clear ecpm with `null`

```bash
curl -X PATCH https://api.influtics.com/v1/videos/by-external-id/7489443966908189959 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ecpm": null }'
```

## Example Request — replace tag set

```bash
curl -X PATCH https://api.influtics.com/v1/videos/by-external-id/7489443966908189959 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tags": ["fashion", "lifestyle", "summer_launch_2026"] }'
```

## ✅ Response (200 OK)

```json
{
  "success": true,
  "data": {
    "video_id": "77168374-dd2c-496a-b1ae-3d4944f48ccb",
    "external_video_id": "7489443966908189959",
    "updated_fields": ["notes", "campaign_tag", "ecpm"]
  },
  "meta": {
    "request_id": "ab12cd34-...",
    "processing_time_ms": 87
  }
}
```

When `tags` is supplied, an additional `tags_result` object is included:

```json
{
  "success": true,
  "data": {
    "video_id": "77168374-dd2c-496a-b1ae-3d4944f48ccb",
    "external_video_id": "7489443966908189959",
    "updated_fields": ["tags"],
    "tags_result": {
      "attached": ["fashion", "lifestyle", "summer_launch_2026"],
      "created": ["summer_launch_2026"],
      "total": 3
    }
  },
  "meta": {
    "request_id": "ab12cd34-...",
    "processing_time_ms": 91
  }
}
```

## 📊 PATCH Response Fields

| Field | Type | Description |
|-------|------|-------------|
| `data.video_id` | string | Internal video UUID |
| `data.external_video_id` | string | Platform-specific video ID |
| `data.updated_fields` | string[] | The columns actually written to the database. |
| `data.tags_result` | object \| omitted | Present only when `tags` was sent. `attached` lists tag names that ended up linked to the video; `created` lists names auto-created on the fly; `total` is the resulting tag count. |
| `meta.request_id` | string | Unique request identifier |
| `meta.processing_time_ms` | number | Time taken to process the request (ms) |

---

## ⚠️ Error Responses

All errors share the standard envelope `{ success: false, error: { code, message, request_id, ... } }`. The table below lists every documented `code` for PATCH.

| Status | `code` | Trigger | Additional `error` fields |
|--------|--------|---------|---------------------------|
| `400` | `VALIDATION_ERROR` | Empty body (no editable fields); or any supplied field fails type/range checks (`ecpm < 0`, `notes > 2000` chars, `campaign` contains `,`, `tags` entry > 80 chars, `video_status` not in `tracking`/`paused`/`deleted`, `status` not in `to do`/`running`/`ended`, …) | `field` |
| `400` | `VALIDATION_ERROR` | Malformed JSON body | — |
| `401` | `INVALID_API_KEY` / `MISSING_AUTH` | Missing or invalid `Authorization` header | — |
| `402` | `PAID_PLAN_REQUIRED` | Free-tier API key attempted the call | `upgrade_url` |
| `404` | `VIDEO_NOT_FOUND` | PATCH-targeted video does not exist or belongs to another org | — |
| `429` | `RATE_LIMIT_EXCEEDED` | PATCH rate limit exceeded | — |
| `500` | `INTERNAL_ERROR` | Server-side error | — |

## 🎯 Use Cases

- **Single-video updates** — retroactively tag a video that you forgot to label during ingest, or move it to `ended` when the campaign wraps.
- **Bulk workflow automation** — script nightly jobs that flip videos to `to do → running → ended` based on your CRM's stage.
- **Pricing adjustments** — apply updated `ecpm` or `budget` figures after a rate renegotiation.
- **Tag hygiene** — replace the tag set on a video (e.g. drop old campaign tags, add new ones) in a single call.

---

**Related**: [POST /v1/videos/track →](./track) · [GET /v1/videos/by-external-id/{id} →](./by-external-id) · [Get Video Stats →](./stats) · [View Usage →](../account/usage)
