# Collection Sync Webhook | AudioDN Docs

> The Collection Sync webhook notifies your application whenever a collection is created, updated, or deleted.

Source: https://audiodeliverynetwork.com/docs/webhooks/collection-sync/

---

# Collection Sync Webhook

The Collection Sync webhook lets your application stay in sync with your AudioDN collections. Every time a collection is created, updated, or deleted, AudioDN sends an HTTP `POST` request to the collection webhook URL configured for your organization. This is separate from the [Track Processing webhook](/docs/webhooks/track-processing), which covers track status changes.

#### Configured in the dashboard

The collection webhook URL is set in your **Settings → Webhook** panel in the dashboard, not via the API. Once a URL is saved, every collection create/update/delete for your organization is delivered to it. Leave it blank to disable collection webhooks.

## When it fires

A webhook is delivered whenever a collection changes state:

-   Sent as an HTTP `POST` with `Content-Type: application/json`.
-   Includes an `X-ADN-Event` header set to the event name (for example `collection.updated`).
-   Delivery is fire-and-forget / at-least-once — design your handler to be idempotent.
-   Event ordering is not guaranteed; use the `event` and the collection's `updated_at` field rather than assuming arrival order.
-   Requests are currently unsigned. Treat the webhook URL as a secret and, if needed, verify the `collection_id` against the API.
-   Respond promptly with a `2xx` status code to acknowledge receipt.

## Events

| Event | Meaning |
| --- | --- |
| collection.created | A new collection was created. Sent once, when the collection row is first inserted. |
| collection.updated | An existing collection changed — for example its title, metadata, cover, or player settings. |
| collection.deleted | A collection was deleted. Sent when the collection is removed (its tracks and files are removed with it). |

## Payload schema

Every event delivers the same envelope. The `collection` object contains the collection's current fields; on a `collection.deleted` event it reflects the deleted row (with `deleted_at` set).

```
{
  "event": "collection.created | collection.updated | collection.deleted",
  "organization_id": "uuid",
  "creator_id": "uuid | null",
  "collection_id": "uuid",
  "collection": {
    "id": "uuid",
    "organization_id": "uuid",
    "creator_id": "uuid | null",
    "title": "string | null",
    "organization_index": "string | null",
    "metadata": "object | null",
    "theme": "array | null",
    "player_color": "string | null",
    "player_color_light": "string | null",
    "player_color_dark": "string | null",
    "player_subtitle": "string | null",
    "is_cover_overridable": "boolean",
    "is_theme_overridable": "boolean",
    "cloudflare_image_id": "uuid | null",
    "cover_image": {
      "icon": { "type": "icon", "width": 80, "height": 80, "url": "string" },
      "small": { "type": "small", "width": 200, "height": 200, "url": "string" },
      "regular": { "type": "regular", "width": 400, "height": 400, "url": "string" },
      "large": { "type": "large", "width": 800, "height": 800, "url": "string" }
    },
    "created_at": "string",
    "updated_at": "string",
    "deleted_at": "string | null"
  }
}
```

## Cover images

When a collection has cover art, `collection.cloudflare_image_id` holds the image ID and `collection.cover_image` provides ready-to-use URLs at each available size (both are `null` when there is no cover). If you only have the `cloudflare_image_id`, build any size yourself using the pattern below, where `<variant>` is `icon` (80x80), `small` (200x200), `regular` (400x400), or `large` (800x800):

```
https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/{cloudflare_image_id}/{variant}
```

See [Track Processing → Cover images](/docs/webhooks/track-processing#cover-images) for the full variant reference. The same scheme is used across the API's `cover_image` responses.

## Example payloads

### Created

Sent when a new collection is created.

```
{
  "event": "collection.created",
  "organization_id": "11111111-1111-1111-1111-111111111111",
  "creator_id": null,
  "collection_id": "22222222-2222-2222-2222-222222222222",
  "collection": {
    "id": "22222222-2222-2222-2222-222222222222",
    "organization_id": "11111111-1111-1111-1111-111111111111",
    "creator_id": null,
    "title": "Season 1",
    "organization_index": "your-collection-id-001",
    "metadata": null,
    "theme": null,
    "player_color": "#1DB954",
    "player_color_light": "#1BAA4D",
    "player_color_dark": "#1DB954",
    "player_subtitle": "Acme Media",
    "is_cover_overridable": true,
    "is_theme_overridable": true,
    "cloudflare_image_id": null,
    "cover_image": null,
    "created_at": "2026-07-02T10:00:00.000Z",
    "updated_at": "2026-07-02T10:00:00.000Z",
    "deleted_at": null
  }
}
```

### Updated

Sent when a collection's fields change.

```
{
  "event": "collection.updated",
  "organization_id": "11111111-1111-1111-1111-111111111111",
  "creator_id": null,
  "collection_id": "22222222-2222-2222-2222-222222222222",
  "collection": {
    "id": "22222222-2222-2222-2222-222222222222",
    "organization_id": "11111111-1111-1111-1111-111111111111",
    "creator_id": null,
    "title": "Season 1 (Remastered)",
    "organization_index": "your-collection-id-001",
    "metadata": { "genre": "podcast" },
    "theme": null,
    "player_color": "#1DB954",
    "player_color_light": "#1BAA4D",
    "player_color_dark": "#1DB954",
    "player_subtitle": "Acme Media",
    "is_cover_overridable": true,
    "is_theme_overridable": true,
    "cloudflare_image_id": "99999999-9999-9999-9999-999999999999",
    "cover_image": {
      "icon": { "type": "icon", "width": 80, "height": 80, "url": "https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/99999999-9999-9999-9999-999999999999/icon" },
      "small": { "type": "small", "width": 200, "height": 200, "url": "https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/99999999-9999-9999-9999-999999999999/small" },
      "regular": { "type": "regular", "width": 400, "height": 400, "url": "https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/99999999-9999-9999-9999-999999999999/regular" },
      "large": { "type": "large", "width": 800, "height": 800, "url": "https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/99999999-9999-9999-9999-999999999999/large" }
    },
    "created_at": "2026-07-02T10:00:00.000Z",
    "updated_at": "2026-07-02T12:30:00.000Z",
    "deleted_at": null
  }
}
```

### Deleted

Sent when a collection is deleted. The `deleted_at` field is populated.

```
{
  "event": "collection.deleted",
  "organization_id": "11111111-1111-1111-1111-111111111111",
  "creator_id": null,
  "collection_id": "22222222-2222-2222-2222-222222222222",
  "collection": {
    "id": "22222222-2222-2222-2222-222222222222",
    "organization_id": "11111111-1111-1111-1111-111111111111",
    "creator_id": null,
    "title": "Season 1 (Remastered)",
    "organization_index": "your-collection-id-001",
    "metadata": { "genre": "podcast" },
    "theme": null,
    "player_color": "#1DB954",
    "player_color_light": "#1BAA4D",
    "player_color_dark": "#1DB954",
    "player_subtitle": "Acme Media",
    "is_cover_overridable": true,
    "is_theme_overridable": true,
    "cloudflare_image_id": "99999999-9999-9999-9999-999999999999",
    "cover_image": {
      "icon": { "type": "icon", "width": 80, "height": 80, "url": "https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/99999999-9999-9999-9999-999999999999/icon" },
      "small": { "type": "small", "width": 200, "height": 200, "url": "https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/99999999-9999-9999-9999-999999999999/small" },
      "regular": { "type": "regular", "width": 400, "height": 400, "url": "https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/99999999-9999-9999-9999-999999999999/regular" },
      "large": { "type": "large", "width": 800, "height": 800, "url": "https://imagedelivery.net/iVHDyjXAr_lt5UUJaLbk1Q/99999999-9999-9999-9999-999999999999/large" }
    },
    "created_at": "2026-07-02T10:00:00.000Z",
    "updated_at": "2026-07-02T13:00:00.000Z",
    "deleted_at": "2026-07-02T13:00:00.000Z"
  }
}
```

#### Verifying delivery

Each delivery attempt is logged on AudioDN's side, including the HTTP status code returned by your endpoint. If you stop receiving events, confirm your endpoint returns a `2xx` response quickly and that the collection webhook URL in Settings is correct.

## Handling webhooks

A minimal handler reads the `event` field and keeps your local copy of the collection in sync. Because delivery is at-least-once and unordered, key your logic on `collection_id` and the collection's `updated_at` rather than on receipt order.

```
app.post('/webhooks/audiodn/collections', (req, res) => {
  // Acknowledge quickly, then process asynchronously.
  res.sendStatus(200)

  const { event, collection_id, collection } = req.body

  switch (event) {
    case 'collection.created':
    case 'collection.updated':
      // Upsert your local copy of the collection.
      upsertCollection(collection_id, collection)
      break
    case 'collection.deleted':
      // Remove your local copy.
      removeCollection(collection_id)
      break
  }
})
```
