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
When it fires
A webhook is delivered whenever a collection changes state:
- Sent as an HTTP
POSTwithContent-Type: application/json. - Includes an
X-ADN-Eventheader set to the event name (for examplecollection.updated). - Delivery is fire-and-forget / at-least-once — design your handler to be idempotent.
- Event ordering is not guaranteed; use the
eventand the collection'supdated_atfield rather than assuming arrival order. - Requests are currently unsigned. Treat the webhook URL as a secret and, if needed, verify the
collection_idagainst the API. - Respond promptly with a
2xxstatus 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
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
}
})