Webhooks
Receive real-time notifications for campaign lifecycle events.
Webhooks
Webhooks let you receive real-time HTTP notifications when campaign events occur, instead of polling the API.
Event Types
| Event Type | Description | Status |
|---|---|---|
campaign.created | Fires when a campaign is submitted via POST /campaigns | Active |
campaign.updated | Fires when a campaign is modified (budget, end date, or pause state) | Active |
campaign.processing | Fires when the ad platform is processing the campaign | Defined (not yet wired) |
campaign.completed | Fires when a campaign finishes its run | Defined (not yet wired) |
campaign.failed | Fires when the ad platform rejects the campaign | Defined (not yet wired) |
Note:
campaign.processing,campaign.completed, andcampaign.failedare defined in the API and available for webhook registration, but notifications for these lifecycle events are still being connected to the campaign creation pipeline. You can register webhooks for them now so they'll fire automatically once wired.
Payload Format
Every webhook delivery is an HTTP POST to your registered URL with this body:
{
"eventType": "campaign.created",
"data": {
"id": 98765,
"name": "Summer Release Promo",
"platform": "meta",
"status": "TO_REVIEW",
"startDate": "2026-04-01T00:00:00.000Z",
"endDate": "2026-04-14T00:00:00.000Z",
"budget": 300
},
"timestamp": "2026-04-01T00:01:23.456Z"
}| Field | Type | Description |
|---|---|---|
eventType | string | One of the event types listed above |
data | CampaignData | The campaign data at the time of the event |
timestamp | string (ISO 8601) | When the event occurred |
Delivery Headers
Each webhook request includes these headers:
Content-Type: application/json
X-Symphony-Event: campaign.created
Use the X-Symphony-Event header to route events in your handler without parsing the body.
Webhook Status Lifecycle
Each registered webhook has a status that tracks its delivery health:
| Status | Meaning |
|---|---|
pending | Webhook is registered and ready to receive events |
processing | Event was delivered successfully (last delivery succeeded) |
failed | Last delivery attempt failed (e.g., your server returned an error or was unreachable) |
finished | Webhook has been fully processed |
When you update a webhook's URL, its status resets to pending.
Managing Webhooks
List all webhooks
curl https://api.symphonyos.co/api/sym/v2/webhooks \
-H "x-api-key: YOUR_API_KEY"Response:
{
"success": true,
"data": [
{
"id": 1,
"url": "https://your-app.com/webhooks/symphony",
"status": "pending",
"eventType": "campaign.created",
"createdAt": "2026-03-15T10:30:00.000Z",
"updatedAt": "2026-03-15T10:30:00.000Z"
},
{
"id": 2,
"url": "https://your-app.com/webhooks/symphony",
"status": "processing",
"eventType": "campaign.updated",
"createdAt": "2026-03-15T10:31:00.000Z",
"updatedAt": "2026-03-20T14:00:00.000Z"
}
]
}Create a webhook
curl -X POST https://api.symphonyos.co/api/sym/v2/webhooks \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/symphony",
"eventType": "campaign.created"
}'Response (201):
{
"success": true,
"data": {
"id": 3,
"url": "https://your-app.com/webhooks/symphony",
"status": "pending",
"eventType": "campaign.created",
"createdAt": "2026-03-25T09:00:00.000Z",
"updatedAt": "2026-03-25T09:00:00.000Z"
}
}Update a webhook
Change the URL for an existing webhook. Status resets to pending.
curl -X PUT https://api.symphonyos.co/api/sym/v2/webhooks/3 \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/v2/symphony"
}'Response:
{
"success": true,
"data": {
"id": 3,
"url": "https://your-app.com/webhooks/v2/symphony",
"status": "pending",
"eventType": "campaign.created",
"createdAt": "2026-03-25T09:00:00.000Z",
"updatedAt": "2026-03-25T12:00:00.000Z"
}
}Delete a webhook
curl -X DELETE https://api.symphonyos.co/api/sym/v2/webhooks/3 \
-H "x-api-key: YOUR_API_KEY"Response:
{
"success": true,
"message": "Webhook 3 deleted successfully"
}Best Practices
- Respond quickly — return a
200status within 5 seconds. Process the event asynchronously if needed. - Handle duplicates — in rare cases, the same event may be delivered more than once. Use the
timestampand campaignidto deduplicate. - Register for specific events — create separate webhooks for each event type you care about, rather than trying to catch everything with one.
- Monitor webhook status — periodically check
GET /webhooksto ensure none havefailedstatus. Update the URL if your endpoint has moved. - Use HTTPS — always register
https://URLs to protect webhook payloads in transit.
Updated about 1 month ago
