Webhooks
Webhooks allow you to receive real-time notifications when consent events occur in Redacto CMP. This enables you to sync consent status with your systems, trigger workflows, and maintain audit trails.
Overview
When a consent event occurs (e.g., user grants consent), Redacto sends an HTTP POST request to your configured webhook endpoint with event details.
┌─────────────┐ ┌─────────────────┐ ┌──────────────┐
│ User │────────▶│ Redacto CMP │────────▶│ Your Webhook │
│ Action │ │ (Event Occurs) │ │ Endpoint │
└─────────────┘ └─────────────────┘ └──────────────┘
Event Types
Redacto supports the following webhook event types:
Consent Events
| Event | Description |
|---|---|
consent.event.granted | User granted consent for all purposes |
consent.event.denied | User explicitly denied consent |
consent.event.partially_granted | User granted consent for some purposes |
consent.event.withdrawn | User withdrew previously given consent |
consent.event.renew | User renewed their consent |
consent.event.expired | Consent expired based on validity period |
Purpose Events
| Event | Description |
|---|---|
consent.purpose.withdrawn | Consent withdrawn for a specific purpose |
consent.purpose.expired | Consent expired for a specific purpose |
Payload Structure
Consent Event Payload
When a consent event occurs, you receive the following payload:
{
"event_type": "consent.event.granted",
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"consent_event_uuid": "550e8400-e29b-41d4-a716-446655440000",
"organisation_uuid": "org-uuid-here",
"workspace_uuid": "ws-uuid-here",
"notice_uuid": "notice-uuid-here",
"notice_version": 1,
"source": "web_sdk",
"status": "granted",
"chain_position": 1,
"meta_data": {
"user_agent": "Mozilla/5.0...",
"ip_address": "192.168.1.1"
},
"org_user_id": "user123",
"primary_email": "[email protected]",
"primary_mobile": "+1234567890",
"created_at": "2024-01-15T10:30:00Z",
"campaign_uuid": null
}
}Payload Fields:
| Field | Type | Description |
|---|---|---|
event_type | string | The type of event that occurred |
event_id | string | Unique identifier for this event (for deduplication) |
timestamp | string | ISO 8601 timestamp of when the event occurred |
data.consent_event_uuid | string | UUID of the consent event record |
data.organisation_uuid | string | Your organisation UUID |
data.workspace_uuid | string | The workspace UUID |
data.notice_uuid | string | The consent notice UUID |
data.notice_version | number | Version of the consent notice |
data.source | string | Where the consent was collected |
data.status | string | Consent status (granted, denied, etc.) |
data.chain_position | number | Position in the consent chain |
data.meta_data | object | Additional context about the consent |
data.org_user_id | string | Your user identifier (if provided) |
data.primary_email | string | User's email (if provided) |
data.primary_mobile | string | User's phone (if provided) |
data.created_at | string | When the consent was recorded |
data.campaign_uuid | string | Associated campaign (if any) |
Purpose Event Payload
When a purpose-level event occurs:
{
"event_type": "consent.purpose.withdrawn",
"event_id": "660e8400-e29b-41d4-a716-446655440001",
"timestamp": "2024-01-15T11:00:00Z",
"data": {
"purpose_uuid": "purpose-uuid-here",
"name": "Marketing Communications",
"status": "withdrawn",
"validity": "1 year",
"expiry_datetime": "2025-01-15T10:30:00Z",
"org_user_id": "user123",
"primary_email": "[email protected]",
"primary_mobile": "+1234567890",
"created_at": "2024-01-15T11:00:00Z"
}
}Purpose Payload Fields:
| Field | Type | Description |
|---|---|---|
data.purpose_uuid | string | UUID of the purpose |
data.name | string | Name of the purpose |
data.status | string | Current status (withdrawn, expired) |
data.validity | string | Validity period of the consent |
data.expiry_datetime | string | When the consent expires |
Setup
1. Create a Webhook Endpoint
Create an endpoint in your application to receive webhook events:
// Express.js example
app.post("/webhooks/redacto", express.json(), (req, res) => {
const event = req.body;
console.log(`Received event: ${event.event_type}`);
console.log(`Event ID: ${event.event_id}`);
// Process the event
switch (event.event_type) {
case "consent.event.granted":
handleConsentGranted(event.data);
break;
case "consent.event.withdrawn":
handleConsentWithdrawn(event.data);
break;
case "consent.purpose.withdrawn":
handlePurposeWithdrawn(event.data);
break;
// ... handle other events
}
// Acknowledge receipt
res.status(200).json({ received: true });
});2. Configure in Redacto Console
- Log in to the Redacto Console
- Navigate to Integrations > Webhooks
- Click Add Webhook
- Enter your endpoint URL
- Select the events you want to receive
- Save the configuration
3. Verify Your Endpoint
Redacto will send a test event to verify your endpoint is working. Ensure your endpoint:
- Responds with a 2xx status code
- Responds within 30 seconds
- Returns a JSON response
Handling Webhooks
Idempotency
Each event includes a unique event_id. Use this for deduplication:
async function handleWebhook(event) {
// Check if we've already processed this event
const processed = await db.webhookEvents.findOne({
event_id: event.event_id,
});
if (processed) {
console.log(`Event ${event.event_id} already processed`);
return;
}
// Process the event
await processEvent(event);
// Mark as processed
await db.webhookEvents.create({
event_id: event.event_id,
processed_at: new Date(),
});
}Error Handling
If your endpoint fails to respond with a 2xx status code, Redacto will retry the webhook:
- Retries are attempted with exponential backoff
- Maximum of 3 retry attempts
- After all retries fail, the event is logged for manual review
app.post("/webhooks/redacto", async (req, res) => {
try {
await processEvent(req.body);
res.status(200).json({ success: true });
} catch (error) {
console.error("Webhook processing failed:", error);
// Return 500 to trigger retry
res.status(500).json({ error: "Processing failed" });
}
});Async Processing
For long-running operations, acknowledge the webhook immediately and process asynchronously:
app.post("/webhooks/redacto", async (req, res) => {
// Immediately acknowledge receipt
res.status(200).json({ received: true });
// Queue for async processing
await messageQueue.publish("consent-events", req.body);
});
// Separate worker processes the queue
messageQueue.subscribe("consent-events", async (event) => {
await processEvent(event);
});Use Cases
Sync Consent to CRM
async function handleConsentGranted(data) {
const { org_user_id, primary_email, status } = data;
await crm.updateContact(org_user_id, {
consent_status: status,
consent_date: data.created_at,
marketing_opt_in: true,
});
}Update Email Preferences
async function handlePurposeWithdrawn(data) {
if (data.name === "Marketing Communications") {
await emailService.unsubscribe(data.primary_email);
}
}Audit Logging
async function logConsentEvent(event) {
await auditLog.create({
event_type: event.event_type,
user_id: event.data.org_user_id,
email: event.data.primary_email,
consent_status: event.data.status,
notice_uuid: event.data.notice_uuid,
timestamp: event.timestamp,
source: event.data.source,
});
}Trigger Workflows
async function handleConsentGranted(data) {
// Trigger welcome email workflow
if (data.source === "signup_form") {
await workflows.trigger("welcome-sequence", {
email: data.primary_email,
user_id: data.org_user_id,
});
}
}Security Considerations
Verify Webhook Origin
Ensure webhooks come from Redacto by checking the source IP or implementing signature verification if available.
Use HTTPS
Always use HTTPS for your webhook endpoint to ensure data is encrypted in transit.
Validate Payload
Validate the webhook payload before processing:
function validatePayload(event) {
if (!event.event_type || !event.event_id || !event.data) {
throw new Error("Invalid webhook payload");
}
// Verify organisation_uuid matches your account
if (event.data.organisation_uuid !== YOUR_ORG_UUID) {
throw new Error("Organisation mismatch");
}
}Updated 9 days ago