Watchlist events, pushed to your endpoint.
Configure a webhook URL on any watchlist and BrandMov POSTs a JSON envelope the moment we detect new ads or removals. Same envelope shape as Stripe or GitHub: an id, a type, an api_version, and a data object.
Envelope
Every event carries the same top-level shape. The event-specific payload lives under data.
{
"id": "evt_5b1c8a3f0e2d4a1b9c7f3d21",
"api_version": "2026-09-01",
"type": "watchlist.new_ads",
"created_at": "2026-09-02T09:00:00+00:00",
"delivery": { "attempt": 1, "test": false },
"data": { ... }
}id: unique event id. Log it and dedupe on your side; a retried delivery reuses the same id.api_version: the payload contract in effect at the time of send. Pin against this if you plan to parse defensively.type: one of the events listed below.delivery.test:truewhen the event was sent by the “Send test event” button on the dashboard,falseon real detections.data.watchlist: always carriesworkspace_idandworkspace_nameso one receiver can route across workspaces.
watchlist.new_ads
Fires when the nightly refresh finds ads on a watched page that weren't there last time. data.new_ads.items lists each new creative, with enough metadata to render a preview or hit the dashboard link.
{
"id": "evt_5b1c8a3f0e2d4a1b9c7f3d21",
"api_version": "2026-09-01",
"type": "watchlist.new_ads",
"created_at": "2026-09-02T09:00:00+00:00",
"delivery": { "attempt": 1, "test": false },
"data": {
"watchlist": {
"id": "wl_abc123",
"brand": "Example Brand",
"page_url": "https://www.facebook.com/examplebrand",
"workspace_id": "1c9d2f4a-7b6e-4a2f-9c3d-8e5f1a2b3c4d",
"workspace_name": "Acme Growth"
},
"new_ads": {
"count": 2,
"items": [
{
"ad_archive_id": "1234567890",
"headline": "New spring drop is here",
"cta_text": "Shop now",
"image_url": "https://cdn.brandmov.com/...jpg",
"page_name": "Example Brand",
"start_date": "2026-09-01",
"is_video": false
}
]
}
}
}watchlist.ad_removed
Fires when ads we'd previously seen on a watched page dropped out of the current pull. data.removed_ads.ad_archive_ids is a flat list of the archive ids that disappeared.
{
"id": "evt_9d4c1b2e0f5a6b3c8d7e0f11",
"api_version": "2026-09-01",
"type": "watchlist.ad_removed",
"created_at": "2026-09-02T21:00:00+00:00",
"delivery": { "attempt": 1, "test": false },
"data": {
"watchlist": {
"id": "wl_abc123",
"brand": "Example Brand",
"page_url": "https://www.facebook.com/examplebrand",
"workspace_id": "1c9d2f4a-7b6e-4a2f-9c3d-8e5f1a2b3c4d",
"workspace_name": "Acme Growth"
},
"removed_ads": {
"count": 2,
"ad_archive_ids": ["1234567890", "1234567891"]
}
}
}Delivery & retries
- POST,
Content-Type: application/json, no auth header. Verify by allow-listing our sender IPs or the target URL secret you bake into the path. - A 2xx is a success. Any other status (or a timeout beyond ~10s) counts as a failure and enters retry.
- Retries back off: a small burst in the first few minutes, then progressively longer gaps for ~24 hours. After that the event is dropped; watchlist state advances regardless.
- Use the “Send test event” button on the Developer page to smoke-check your receiver without waiting for a real detection. It sends a real envelope with
delivery.test = true.