How-to
Set up a webhook
Create a webhook endpoint, choose its events, add payload filters, send a test event, and enable, disable or rotate the secret.
This guide creates a webhook, narrows it to the events you need, and checks that your endpoint receives signed requests. You can do everything in the dashboard or with the Webhooks API.
Before you begin
- A public URL that accepts
POSTrequests. HTTPS is strongly recommended. URLs onlocalhostor private IP ranges are rejected; for local development, use a tunnel such as ngrok or Cloudflare Tunnel. - An endpoint that keeps the raw request body so it can verify the signature.
- For the API, a key with Full Access.
- A free webhook slot. Pay as you go includes 3 endpoints, Pro 10, and Business and Custom 100.
Create the webhook
-
Open Webhooks. Go to Email APIWebhooks and select Add webhook.
-
Enter a name and URL. The name must be unique in the workspace, for example
Production events. The URL is your endpoint, for examplehttps://acme.com/webhooks/emailit. Select Create. -
Copy the secret. The dialog shows the webhook secret, starting with
whsec_, together with the warning “You can see the webhook secret only once. Store it safely.” Copy it into your app’s environment, for example asEMAILIT_WEBHOOK_SECRET, and select Done.
A webhook created in the dashboard is subscribed to every event type. The webhook’s Settings tab opens so you can narrow it.
Call Create a webhook. List the event types in events, or set all_events to true. The 201 response includes the secret.
curl https://api.emailit.com/v2/webhooks \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production events",
"url": "https://acme.com/webhooks/emailit",
"events": ["email.delivered", "email.bounced", "email.complained"]
}'Unlike the dashboard, the API defaults to all_events: false and an empty events list, so a webhook created without either receives nothing. A duplicate name returns 409; reaching your plan’s endpoint limit returns 422 with usage.used and usage.limit.
Choose events
A webhook receives either every event type or only the types you select.
On the webhook’s Settings tab, turn off All events, then select types in the Events card. Events are grouped by resource (Emails, Domains, Audiences, Subscribers, Contacts, Templates, Suppressions, Email Verifications, Email Verification Lists), and each group has a Select all box. Select Save.
The picker doesn’t list every type Emailit sends. email.canceled, email.held, email.unsubscribed, email.resubscribed, subscriber.resubscribed and the campaign.* events reach webhooks that have All events on, or you can add them to the list with the API. The Deprecated group holds old event names that are no longer sent. See Event types.
Call Update a webhook. events replaces the whole list. Setting all_events to true clears the list.
curl -X POST https://api.emailit.com/v2/webhooks/wh_2xGk8Hd3RvN6qT1mWsB9cL4pZ7e \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"all_events": false, "events": ["email.received", "email.held", "email.canceled"]}'Filter events by payload
Pay as you goProBusinessCustomA payload filter delivers an event only when its data matches your rules. Filters apply on top of the event selection: an event must be subscribed and match the filter.
Use it to split traffic between endpoints, for example one webhook per product line, or to drop events you’d otherwise ignore in code.
- Match mode: All rules match (
all) or Any rule matches (any). - Rules: up to 25. Each rule has a field, an operator and a value.
- Field: a dotted path into the event’s
data.object, for exampleto,status,meta.planor, for click events,link.url. Apayload.prefix is optional, sopayload.fromandfromare the same. - Comparisons are case-sensitive and compare values as text, except
greater_thanandless_than, which compare numbers.
| Operator | Matches when the field |
|---|---|
equals / not_equals |
Is / isn’t exactly the value. |
contains / not_contains |
Contains / doesn’t contain the value. |
starts_with / ends_with |
Starts / ends with the value. |
greater_than / less_than |
Is a number greater / less than the value. |
is_set / is_not_set |
Has a non-empty value / is missing or empty. No value needed. |
in / not_in |
Equals / doesn’t equal one of the values in an array. Send the array with the API, as in the example below. |
On the webhook’s Settings tab, find the Filter card. Choose the match mode, add rules with a field, operator and value, and select Save. Leave the rules empty to deliver every subscribed event. On Pay as you go, the card is locked and shows Upgrade.
Send filter with Create a webhook or Update a webhook. Set it to null to remove it. On Pay as you go, a filter returns 403 with "error": "plan_required".
curl -X POST https://api.emailit.com/v2/webhooks/wh_2xGk8Hd3RvN6qT1mWsB9cL4pZ7e \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"match": "all",
"rules": [
{ "field": "to", "operator": "ends_with", "value": "@acme.com" },
{ "field": "meta.plan", "operator": "in", "value": ["pro", "business"] }
]
}
}'More examples:
| Goal | Rule |
|---|---|
| Only mail received on one address | to equals support@inbound.acme.com |
| Only one sender domain | from ends_with @billing.acme.com |
| Only emails you labeled with metadata | meta.source equals checkout |
| Only clicks on your pricing page | link.url starts_with https://acme.com/pricing |
| Only emails that carry a customer ID | meta.customer_id is_set |
If a workspace moves to Pay as you go, existing filters stay saved but are ignored, and every subscribed event is delivered.
Send a test event
A test posts one sample event to your URL immediately, signed with the webhook’s current secret. Subscribed events and filters are ignored, the request isn’t retried, and it doesn’t appear on the Requests tab. Each webhook allows 5 tests per minute.
On the webhook page, open the actions menu (…) and select Send test. Pick an event type and select Send test. The dialog shows “Your endpoint returned 200” (or the status your endpoint returned) and the response body. “Could not reach the endpoint” means the request didn’t get an HTTP response, for example because of a DNS error, a timeout or a redirect.
Call Send a test event with any event type.
curl https://api.emailit.com/v2/webhooks/wh_2xGk8Hd3RvN6qT1mWsB9cL4pZ7e/test \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "email.delivered"}'The response has ok, status_code, body (your endpoint’s response, up to 2,000 characters), type and the payload that was sent.
Test events use sample data, with an event_id that starts with evt_test_, and their shape can differ slightly from live events. Build your handler against the event reference and confirm with a real send.
Enable or disable a webhook
Disable a webhook to stop deliveries without losing its settings, for example during maintenance.
- Dashboard: open the actions menu and select Disable webhook or Enable webhook. The status shows Enabled or Disabled on the webhook page.
- API: call Update a webhook with
{"enabled": false}or{"enabled": true}.
While a webhook is disabled, new events aren’t queued for it, and requests already waiting for a retry are paused. Events that happen while it’s disabled aren’t delivered later; read them with the Events API if you need them. Emailit also disables webhooks automatically after 3 days of failures; see Retries and failures.
Deleting a webhook drops all its pending events.
Rotate the secret
Rotate the secret if it may have leaked, or as routine hygiene.
- Dashboard: open the actions menu, select Webhook secret, then Reset. The new secret is shown once.
- API: call Rotate the signing secret. The response contains the new
secret. Retrieve a webhook also returns the current secret.
The old secret stops working immediately, and every request from then on, including retries of older events, is signed with the new one. To rotate without rejecting requests, make your endpoint accept either secret for a few minutes, reset the secret, deploy the new value, then remove the old one.
Verify it worked
- Send a test event and confirm your endpoint returns
2xx. - Send a real email, or trigger the event you subscribed to.
- On the webhook’s Requests tab, the request shows Delivered. The webhook’s Last used time updates.