How it works
- A unique operation ID is generated per task invocation
- A webhook URL is built by substituting that ID (and other routing params) into a URL template
push_notification_configis injected into the task request body with that URL and HMAC credentials- The publisher POSTs webhook notifications to your URL as the task status changes
- Each notification echoes
operation_idback in the payload so you can correlate it without parsing the URL
@adcp/client library, this entire flow is handled automatically — configure webhookUrlTemplate and webhookSecret once on the client and push_notification_config is injected into every outgoing task call.
Naming: snake_case vs camelCase
This trips people up. There are two naming conventions in play:
The AdCP field name is always
push_notification_config (snake_case). It goes in the task request body alongside your other task parameters.
For A2A, the A2A protocol wraps it in a configuration envelope using camelCase — but the object’s contents are identical.
Adding push_notification_config to a request
MCP
Includepush_notification_config as a task argument, merged with the rest of your task parameters:
A2A
For A2A, skill parameters stay inmessage.parts[].data.parameters. The push notification config goes in the top-level configuration object:
Operation IDs and URL templates
Operation IDs let you route incoming webhooks to the right handler. The typical pattern:- Generate a unique ID per task call
- Embed it in the webhook URL path
- The publisher echoes
operation_idin the payload — no URL parsing needed
"operation_id": "cd51e063-2b79-4a6d-afac-ed7789c3a443", so your handler can route to the right pending operation without parsing the URL.
When webhooks fire
Webhooks are sent for each status change after the initial response, as long aspush_notification_config is in the request.
If the task completes synchronously (initial response is already completed or failed), no webhook is sent — you already have the result.
Status changes that trigger webhooks:
Webhook payload formats
MCP
A2A
A2A sends aTask object (for final states) or TaskStatusUpdateEvent (for progress). The AdCP result data is in status.message.parts[].data:
Protocol comparison
Status-specific result data
Authentication
Bearer token
Simple token auth — the publisher sends your token in theAuthorization header.
Configuration:
HMAC-SHA256 (recommended for production)
The publisher signs each request with a shared secret and includes a timestamp for replay protection. You verify both the signature and the timestamp on receipt. Configuration:{unix_timestamp}.{raw_json_body} — the Unix timestamp (in seconds), a dot, then the exact JSON bytes being sent in the HTTP body.
rawBody must be the exact bytes sent on the wire. Implementations must sign the same serialization that goes in the HTTP body — signing a re-serialized or reformatted version of the payload will cause verification failures.
Publisher implementation (signing):
verify callback on express.json() to capture the raw body:
X-ADCP-Timestamp header. Receivers should reject requests where |current_time - timestamp| > 300 seconds.
:::
Reliability
Webhooks use at-least-once delivery — you may receive the same event more than once, and events may arrive out of order. Usetask_id + status + timestamp to handle this:
Best practices
- Always implement polling as backup — webhooks can fail; poll at a reduced interval (e.g. every 2 minutes) when webhooks are configured, and stop once you receive a terminal status
- Handle duplicates — use
task_id+timestampto skip already-processed or out-of-order events - Verify signatures — always validate
X-ADCP-Signaturebefore processing - Acknowledge immediately — return
200before doing any heavy processing to avoid publisher timeouts and unnecessary retries - Don’t rely on URL structure — use
operation_idfrom the payload for routing, not URL parsing - Use HMAC-SHA256 in production — Bearer tokens are simpler but don’t protect against payload tampering
Reporting webhooks
Reporting webhooks are separate from task status webhooks. They deliver periodic performance data for active media buys and are configured viareporting_webhook in create_media_buy, not via push_notification_config.
See Task Reference for details on reporting_webhook.
Next steps
- Task Lifecycle — status values and transitions
- Async Operations — handling long-running tasks
- Error Handling — webhook error patterns