Launch Sale: Creator and Business plans are discounted for a limited time. View pricing

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, 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 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
  }
})