> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mosaic.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Events

> Receive real-time notifications for agent run lifecycle.

Provide a `callback_url` when calling [Run Agent](/api/agent-runs/post-agent-run) to receive webhook POSTs for run lifecycle events.

## Authentication

If you have a webhook secret configured, every webhook includes an `X-Mosaic-Signature` header containing the secret as a plaintext string. Validate with a direct comparison:

```python theme={null}
if request.headers.get('X-Mosaic-Signature') != os.environ['MOSAIC_WEBHOOK_SECRET']:
    return {"error": "Unauthorized"}, 401
```

***

## Common Fields

Every webhook payload contains these fields:

| Field                | Type             | Description                                            |
| -------------------- | ---------------- | ------------------------------------------------------ |
| `flag`               | `string`         | `"RUN_STARTED"`, `"RUN_PROGRESS"`, or `"RUN_FINISHED"` |
| `agent_id`           | `string`         | Agent UUID                                             |
| `run_id`             | `string`         | Run UUID                                               |
| `status`             | `string`         | Current run status                                     |
| `node_status_counts` | `object`         | `{ completed, in_progress, failed }`                   |
| `triggered_by`       | `object \| null` | What initiated the run (see below)                     |

`needs_credits` and `errors` are included on `RUN_FINISHED` payloads when available.

***

## RUN\_STARTED

Sent when the run begins.

```json theme={null}
{
  "flag": "RUN_STARTED",
  "agent_id": "123e4567-e89b-12d3-a456-789012345678",
  "run_id": "7f8d9c2b-4a6e-8b3f-1d5c-9e2f3a4b5c6d",
  "status": "running",
  "inputs": [
    {
      "video_url": "https://storage.googleapis.com/.../input.mp4",
      "thumbnail_url": "https://storage.googleapis.com/.../thumb.jpg"
    }
  ],
  "node_status_counts": { "completed": 1, "in_progress": 5, "failed": 0 },
  "triggered_by": null
}
```

**Additional fields:** `inputs` — array of `{ video_url, thumbnail_url }` for the input videos.

***

## RUN\_PROGRESS

Sent when one or more nodes change status. Useful for progress tracking without polling.

```json theme={null}
{
  "flag": "RUN_PROGRESS",
  "agent_id": "123e4567-e89b-12d3-a456-789012345678",
  "run_id": "7f8d9c2b-4a6e-8b3f-1d5c-9e2f3a4b5c6d",
  "status": "running",
  "node_status_counts": { "completed": 4, "in_progress": 3, "failed": 0 },
  "updated_nodes": [
    {
      "original_node_id": "2ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "status": "completed",
      "status_message": null,
      "errors": [],
      "outputs": [
        {
          "id": "7ba7b810-9dad-11d1-80b4-00c04fd430c8",
          "video_url": "https://storage.googleapis.com/...",
          "thumbnail_url": "https://storage.googleapis.com/...",
          "premiere_prproj_url": "https://storage.googleapis.com/.../timeline.zip?...",
          "sources": [
            {
              "provider": "getty",
              "source_kind": "licensed_library",
              "media_type": "image",
              "provider_asset_id": "1234567890",
              "render_asset_id": "getty_image_000",
              "query": "mayor press conference",
              "start_ms": 1200,
              "end_ms": 3400,
              "placement": "partial_screen",
              "layout": "bottom",
              "getty_asset_family": "editorial"
            }
          ],
          "completed_at": "2024-01-15T10:35:00Z",
          "original_node_id": "2ba7b810-9dad-11d1-80b4-00c04fd430c8"
        }
      ]
    }
  ],
  "triggered_by": null
}
```

**Additional fields:**

| Field                              | Type             | Description                                                                             |
| ---------------------------------- | ---------------- | --------------------------------------------------------------------------------------- |
| `updated_nodes[].original_node_id` | `string \| null` | Template `agent_node_id` — use to map back to your agent                                |
| `updated_nodes[].status`           | `string`         | `completed`, `running`, `queued`, `failed`, `blocked`, `partial_complete`               |
| `updated_nodes[].status_message`   | `string \| null` | Human-readable message                                                                  |
| `updated_nodes[].errors`           | `array`          | Error messages from failed tasks in this node. Empty array when the node has no errors. |
| `updated_nodes[].outputs`          | `array`          | Outputs produced by this node (if any)                                                  |

***

## RUN\_FINISHED

Sent when the run reaches a terminal state. Contains all final outputs.

```json theme={null}
{
  "flag": "RUN_FINISHED",
  "agent_id": "123e4567-e89b-12d3-a456-789012345678",
  "run_id": "7f8d9c2b-4a6e-8b3f-1d5c-9e2f3a4b5c6d",
  "status": "completed",
  "status_message": null,
  "needs_credits": false,
  "errors": [],
  "inputs": [
    {
      "video_url": "https://storage.googleapis.com/.../input.mp4",
      "thumbnail_url": "https://storage.googleapis.com/.../thumb.jpg"
    }
  ],
  "outputs": [
    {
      "id": "7ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "video_url": "https://storage.googleapis.com/.../output.mp4",
      "thumbnail_url": "https://storage.googleapis.com/.../thumb.jpg",
      "premiere_prproj_url": "https://storage.googleapis.com/.../timeline.zip?...",
      "sources": [
        {
          "provider": "getty",
          "source_kind": "licensed_library",
          "media_type": "image",
          "provider_asset_id": "1234567890",
          "render_asset_id": "getty_image_000",
          "query": "mayor press conference",
          "start_ms": 1200,
          "end_ms": 3400,
          "placement": "partial_screen",
          "layout": "bottom",
          "getty_asset_family": "editorial"
        }
      ],
      "completed_at": "2024-01-15T10:35:00Z",
      "original_node_id": "2ba7b810-9dad-11d1-80b4-00c04fd430c8"
    }
  ],
  "node_status_counts": { "completed": 10, "in_progress": 0, "failed": 0 },
  "triggered_by": null
}
```

When a run fails, `errors` contains error details grouped by template node:

```json theme={null}
{
  "flag": "RUN_FINISHED",
  "status": "failed",
  "status_message": "Processing failed for one or more tiles.",
  "errors": [
    {
      "original_node_id": "2ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "messages": ["FFmpeg render failed: output format not supported"]
    },
    {
      "original_node_id": "3ca7b810-9dad-11d1-80b4-00c04fd430c8",
      "messages": [
        "Audio transcription timed out after 300s",
        "Retry failed: service unavailable"
      ]
    }
  ],
  "node_status_counts": { "completed": 3, "in_progress": 0, "failed": 2 },
  "..."
}
```

**Additional fields:**

| Field                       | Type             | Description                                                                                              |
| --------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------- |
| `status`                    | `string`         | `completed`, `partial_complete`, or `failed`                                                             |
| `status_message`            | `string \| null` | Human-readable detail on failure                                                                         |
| `needs_credits`             | `boolean`        | `true` when one or more runtime tasks in the run are marked `needs_credits=true` (insufficient credits). |
| `errors`                    | `array`          | Error messages grouped by template node. Empty array `[]` when the run has no errors.                    |
| `errors[].original_node_id` | `string \| null` | Template `agent_node_id` the errors originated from                                                      |
| `errors[].messages`         | `array`          | List of error messages from failed tasks in this node                                                    |
| `inputs`                    | `array`          | `{ video_url, thumbnail_url }` for each input                                                            |
| `outputs`                   | `array`          | Final rendered outputs (see below)                                                                       |

When `needs_credits` is `true`, check `GET /plan` first. If no paid plan is active, list plans with `GET /plan/list`, upgrade with `POST /plan/upgrade`, and complete checkout if `requires_checkout: true`. After plan activation, optionally enable auto top-ups via `POST /credits/settings` if your active plan is `creator`, `creator_annual`, `professional`, or `professional_annual`, then call `POST /agent_run/{run_id}/resume` to continue the run.

**Output fields:**

| Field                 | Type             | Description                                                                                                                                                                 |
| --------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  | `string`         | Render ID — use for chaining runs via `node_render_ids`                                                                                                                     |
| `video_url`           | `string`         | Signed download URL (valid 7 days)                                                                                                                                          |
| `thumbnail_url`       | `string \| null` | Signed thumbnail URL                                                                                                                                                        |
| `premiere_prproj_url` | `string \| null` | Signed URL for the Premiere Pro timeline zip attached to this render (when available)                                                                                       |
| `sources`             | `array`          | Source citations for licensed or external media used in this output. Getty b-roll citations include timing, query, asset IDs when resolved, placement, and rights metadata. |
| `completed_at`        | `string`         | ISO 8601 timestamp                                                                                                                                                          |
| `original_node_id`    | `string \| null` | Template node that produced this output                                                                                                                                     |

***

## `triggered_by`

Present when the run was started by a trigger or YouTube URL. `null` for standard API runs.

```json theme={null}
{
  "type": "youtube",
  "channel_id": "UCxxxxxxxxxxxxxx",
  "video_id": "dQw4w9WgXcQ",
  "video_title": "Never Gonna Give You Up",
  "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "triggered_at": "2024-01-15T10:30:00Z",
  "trigger_id": "8f7d6c5b-4a3e-2b1f-9d8c-1a2b3c4d5e6f"
}
```

`type` is one of `"youtube"`, `"schedule"`, or `"manual"`.

***

## Run Status Values

| Status             | Meaning                               |
| ------------------ | ------------------------------------- |
| `running`          | Processing in progress                |
| `completed`        | All outputs generated successfully    |
| `partial_complete` | Some outputs succeeded, others failed |
| `failed`           | No outputs generated                  |
| `cancelled`        | Cancelled by user                     |
