Overview

Webhooks allow you to receive HTTP POST requests to your server when events occur in Ozi, such as when an email is delivered, bounced, or clicked. This enables you to build real-time integrations and automate workflows.

Setting up webhooks

1

Create a webhook endpoint

Create an endpoint on your server that can receive POST requests. The endpoint must:
  • Accept POST requests with JSON body
  • Return a 2xx status code to acknowledge receipt
  • Respond within 10 seconds
  • Be reachable at an address Ozi will call - see below
2

Add webhook in dashboard

Go to Webhooks in your Ozi dashboard and create a new webhook:
  • Enter your endpoint URL
  • Select which events you want to receive
  • Copy the signing secret for verification
3

Verify webhook signatures

Always verify webhook signatures to ensure requests are from Ozi. See the Signature Verification section below.

Addresses Ozi will not call

The server makes these requests, so a URL that points back at the server itself is refused - both when the webhook is saved and again on every call. The check on each call is made on the connection itself: Ozi connects to exactly the address it checked, so a name that is repointed between the check and the call cannot slip through. Redirects are never followed; a 3xx answer counts as a failed delivery, so give Ozi the final address. Always refused, wherever Ozi runs:
  • localhost, 127.0.0.0/8 and ::1
  • the link-local range 169.254.0.0/16, which is where cloud providers keep their metadata service
  • 0.0.0.0 and anything that is not http or https
On Ozi’s cloud, private network addresses such as 10.0.0.5, 172.16.0.0/12 or 192.168.1.20, and names like crm.internal, are refused as well: webhooks must point to a public address on the internet. On a self-hosted deployment, private addresses are allowed: calling another service on your own network is a normal thing to do. The same rules apply to an automation’s Call a webhook step.

Event types

Email events

Contact events

Domain events

Mailbox events

Use these with the Mailbox API to fetch the full conversation when something arrives.

Webhook payload

Each webhook request includes a JSON payload with the following structure. See Event data details for details on the data field for each event type.

Payload fields

Request headers

Each webhook request includes the following headers:
The ozi-js and ozi-python SDKs verify these headers for you.

Signature verification

Always verify webhook signatures to ensure requests are authentic. The signature is computed as:

Next.js App Router

Express

Verification only

If you only need to verify the signature without parsing:

Manual verification

If you prefer to verify manually without the SDK:

Retry behavior

If your endpoint doesn’t return a 2xx response, Ozi will retry delivery with exponential backoff: After 6 failed attempts, the webhook call is marked as failed.
If your webhook endpoint fails 30 consecutive calls, the webhook will be automatically disabled to prevent continued failures. You can re-enable it from the dashboard.

Best practices

Return a 2xx response as soon as possible. Process webhook data asynchronously if needed. Requests timeout after 10 seconds.
Use the id field in the payload to deduplicate events. In rare cases, the same event may be delivered more than once.
Always verify the X-Ozi-Signature header to ensure requests are from Ozi and haven’t been tampered with.
The SDK rejects signatures older than 5 minutes by default. This prevents replay attacks.
Always use HTTPS endpoints in production to encrypt webhook data in transit.

Testing webhooks

You can send a test webhook from the dashboard to verify your endpoint is working correctly:
  1. Go to Webhooks
  2. Click on your webhook
  3. Click “Send Test” to send a test event
The test event will have type webhook.test with the following payload:

Troubleshooting

  • Verify your endpoint URL is correct and publicly accessible - Check that your endpoint returns a 2xx status code - Ensure the webhook is set to ACTIVE status in the dashboard - Check if the webhook was auto-disabled due to consecutive failures
  • Use the raw request body, not parsed JSON - Ensure you’re using the correct webhook secret - Check that the timestamp hasn’t expired (5 minute window) - Verify you’re computing the HMAC correctly: HMAC-SHA256(secret, "${timestamp}.${rawBody}")
After 30 consecutive failed calls, webhooks are automatically disabled. Fix the issue with your endpoint, then re-enable the webhook from the dashboard. The failure counter resets on the next successful delivery.

Event data details

This section documents the data field structure for each event type.

Email events

Most email events share a common base structure:

email.bounced

Includes additional bounce details:

email.failed

Includes failure reason:

email.suppressed

Includes suppression details:

email.opened

Includes open tracking details:

email.clicked

Includes click tracking details:

Contact events

All contact events (contact.created, contact.updated, contact.deleted) include:

Domain events

All domain events (domain.created, domain.verified, domain.updated, domain.deleted) include:

Mailbox events

Both mailbox events (mailbox.message.received, mailbox.message.sent) include: