Outbound webhooks
Outbound webhooks let your own systems stay in sync with OpenContact in real time, instead of polling the API. Subscribe a URL to the events you care about, and OpenContact POSTs to it as they happen.
Subscribing
Create a subscription on the Developers page (or via the API): enter your URL, pick the events, and copy the signing secret — it’s shown once. You can subscribe to:
contact.createdcontact.status_changedcontact.unsubscribedcontact.bouncedcontact.complained
The payload
Each delivery is a POST with a JSON body:
{
"id": "evt_9f8c…",
"event": "contact.created",
"occurred_at": "2026-07-27T10:00:00+00:00",
"data": { "id": 42, "email": "ana@example.com", "first_name": "Ana", "last_name": "Ng", "status": "subscribed" }
}
The id is stable across retries of the same event — store it and skip anything you’ve already processed (idempotency). It’s also sent in the X-EmailRoute-Delivery header.
Verifying a delivery is genuine
Every request carries:
X-EmailRoute-Event— the event typeX-EmailRoute-Delivery— the delivery id (matchesidin the body)X-EmailRoute-Signature—t=<unix_timestamp>,v1=<hmac>, where the HMAC-SHA256 is keyed with your signing secret over the string"{timestamp}.{raw_body}"
Parse the header, reject anything outside a tolerance window (guards against replay), then compare the signature in constant time:
[$t, $v1] = [null, null];
foreach (explode(',', $request->header('X-EmailRoute-Signature')) as $part) {
[$k, $v] = explode('=', $part, 2);
if ($k === 't') $t = $v;
if ($k === 'v1') $v1 = $v;
}
abort_if(abs(time() - (int) $t) > 300, 401); // 5-minute tolerance
$expected = hash_hmac('sha256', $t.'.'.$rawBody, $secret);
abort_unless(hash_equals($expected, (string) $v1), 401); // true = authentic
Only act on a request whose signature matches.
Reliability
Deliveries retry with exponential backoff if your endpoint returns a non-2xx response, so a brief outage on your side won’t drop events. The dashboard shows each subscription’s recent deliveries (status and response code), and a Send test button posts a sample event so you can validate a new endpoint before real traffic flows.
Common uses
- Mirror suppression into your own database the instant it happens.
- Kick off onboarding in your app when a lead is created.
- Feed a data warehouse or analytics pipeline.
Still stuck?
Send us your project ID and what you saw. Contact support.