Webhooks
Webhooks push consent events to your server in real time. ConsentForge sends an HTTP POST to your endpoint when consent is created, updated, expired, or reset.
Managing webhooks via API
Create a webhook endpoint
POST /api/v1/webhooks
Authorization: Bearer cf_api_live_YOUR_TOKEN
Content-Type: application/json
{
"property_id": "prop_abc",
"url": "https://your-server.com/webhooks/consentforge",
"events": ["consent.created", "consent.updated", "consent.expired"],
"active": true
}
Response:
{
"id": "wh_xyz789",
"property_id": "prop_abc",
"url": "https://your-server.com/webhooks/consentforge",
"events": ["consent.created", "consent.updated", "consent.expired"],
"active": true,
"secret": "whsec_live_xxxxxxxxxxxxxxxx",
"created_at": "2026-03-09T12:00:00Z"
}
The secret field is only returned on creation. Store it securely — it is never returned again.
List webhooks
GET /api/v1/webhooks?property_id=prop_abc
Authorization: Bearer cf_api_live_YOUR_TOKEN
Update a webhook
PATCH /api/v1/webhooks/wh_xyz789
Authorization: Bearer cf_api_live_YOUR_TOKEN
Content-Type: application/json
{
"events": ["consent.created", "consent.updated"],
"active": false
}
Delete a webhook
DELETE /api/v1/webhooks/wh_xyz789
Authorization: Bearer cf_api_live_YOUR_TOKEN
Events
| Event | When it fires |
|---|---|
consent.created | First consent decision recorded for a user on this property |
consent.updated | User revisits and changes their choices |
consent.expired | Consent reaches its configured expiry date |
consent.reset | Property-level consent reset applied (all users re-asked) |
scan.completed | A property scan finishes |
scan.finding_added | A new script or cookie was found in a scan |
Payload envelope
All webhook requests share the same outer envelope:
{
"event": "consent.created",
"delivery_id": "del_01HXX",
"timestamp": "2026-03-09T12:00:00Z",
"property_id": "prop_abc",
"data": { ... }
}
| Field | Description |
|---|---|
event | The event type |
delivery_id | Unique ID for this delivery — use for idempotency |
timestamp | ISO 8601 UTC timestamp when the event was generated |
property_id | The property the event relates to |
data | Event-specific payload (see below) |
Event payloads
consent.created / consent.updated
{
"event": "consent.created",
"delivery_id": "del_01HXX",
"timestamp": "2026-03-09T12:00:00Z",
"property_id": "prop_abc",
"data": {
"receipt_id": "rec_def456",
"policy_id": "pol_ghi789",
"policy_version": 3,
"banner_version": 7,
"choices": {
"necessary": true,
"analytics": true,
"marketing": false,
"functional": true
},
"region": "DE",
"user_agent_hash": "sha256:3a4f...",
"ip_hash": "sha256:8b2c..."
}
}
consent.expired
{
"event": "consent.expired",
"delivery_id": "del_01HYY",
"timestamp": "2026-03-09T12:00:00Z",
"property_id": "prop_abc",
"data": {
"receipt_id": "rec_def456",
"expired_at": "2026-03-09T12:00:00Z",
"reason": "policy_version_change"
}
}
scan.completed
{
"event": "scan.completed",
"delivery_id": "del_01HZZ",
"timestamp": "2026-03-09T12:00:00Z",
"property_id": "prop_abc",
"data": {
"scan_id": "scan_abc123",
"url": "https://example.com",
"findings_count": 14,
"new_findings_count": 2,
"duration_ms": 8420
}
}
Delivery and retries
ConsentForge expects a 2xx HTTP response within 10 seconds. If the response times out or returns a non-2xx status, delivery is retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 (initial) | immediately |
| 2 | 30 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the delivery is marked as failed and no further retries occur. You can manually retry from Dashboard → Property → Webhooks → Delivery Log.
Idempotency
Each delivery has a unique delivery_id. Retried deliveries reuse the same delivery_id. Store processed delivery IDs to avoid double-processing:
const processedIds = new Set(); // use a database in production
app.post('/webhooks/consentforge', (req, res) => {
const { delivery_id, event, data } = req.body;
if (processedIds.has(delivery_id)) {
return res.json({ status: 'duplicate' });
}
processedIds.add(delivery_id);
// process event...
res.json({ status: 'ok' });
});
Signature verification
See Webhook Security for how to verify the X-ConsentForge-Signature header.
Testing
Use the Send Test Event button in Dashboard → Property → Webhooks to send a sample payload to your endpoint. You can also use webhook.site to inspect requests during development.