POST to that endpoint whenever one of them fires.
This is the outward direction. For events arriving into Senderz from your store or your email provider, see Inbound webhooks.
What works today
Endpoint management, signed delivery, retries and pause/resume are all live. Two things are worth knowing before you build against this:
- The signing secret is only returned to the API caller. Neither in-app screen shows it. If you need to verify signatures, create the endpoint through the Public API.
- There is no delivery log. Senderz records the outcome of every attempt internally, but nothing in the app or the API lets you read it back. Log deliveries on your side.
Create an endpoint
There are three ways in. They write to the same place, but they are not equivalent.- Integrations page
- Settings
- Public API
This is the best in-app option.The signing secret is not displayed. See The signing secret.
1
Open Integrations
Integrations is a top-level item in the sidebar. It is not inside Settings.
2
Click API · Webhook
The button sits in the page header, on the right. It opens the New webhook panel.
3
Fill in the panel
Give it a Display name, paste the Endpoint URL, and tick the events you want. All event types are listed here.
4
Create
Once you have at least one endpoint, a Custom webhooks section appears further down the page with Pause, Resume, Copy URL and Delete on each card.
Creating, editing, pausing and deleting an endpoint requires the owner or admin role. Any workspace member can view the list.
The signing secret
The secret is 32 random bytes rendered as a 64-character hex string. It is generated when the endpoint is created and returned in that one response. It is never returned again, and there is no rotate or reveal action. Neither in-app screen renders it. If you create an endpoint from the Integrations page or from Settings, the secret exists on the server and signs every delivery, but you never see it, so you cannot verify the signature. That is the practical reason to create endpoints through the API.Endpoint URL rules
The URL is checked when you save it, and again immediately before every single delivery. An endpoint must be:- HTTPS. Plain
httpis rejected. - On the default port. Any explicit port, including
:443, is rejected. - A hostname with a dot in it. Bare names like
localhostare rejected. - Publicly resolvable. The hostname is resolved and every address it returns must be public.
webhook_url_blocked and the message “A webhook endpoint must be a public HTTPS address on the default port.” The message is deliberately the same for every reason, so it will not tell you which rule you tripped.
What a delivery looks like
Every delivery is aPOST with Content-Type: application/json and these headers:
The body always has the same three top-level keys:
data differs per event family. deliveryId matches the X-Sender-Delivery header.
Order, checkout and shipment events
Order, checkout and shipment events
amount is in the store’s own currency units, not minor units, and it is whatever the store platform reported. Any of these four can be null depending on the platform and the event. externalId is the customer or order identifier from the store.Contact events
Contact events
Email and SMS engagement events
Email and SMS engagement events
messageId, contactId, channel, reason and occurredAt instead.List events
List events
Review and loyalty events
Review and loyalty events
vendor, points, tier and rewardCode instead.Verify the signature
Compute the HMAC over the raw request body bytes, exactly as received. Do not parse the JSON and re-serialise it first: key order and whitespace will differ and the signature will not match.1
Capture the raw body
Most frameworks parse JSON before your handler runs. Configure a raw or buffered body for this route.
2
Compute and compare
Build
sha256= plus the hex HMAC-SHA256 of the raw body using your signing secret, and compare it against X-Sender-Signature in constant time.3
Respond 2xx fast
Acknowledge first, process afterwards. Anything slower than 10 seconds is treated as a failure and retried.
4
Deduplicate
Delivery is at-least-once. Record
deliveryId and ignore one you have already handled.Event vocabulary
There are 29 subscribable event names. Subscribe to"*" instead of a list to receive all of them.
Commerce
All ten require a connected store. Which of them you actually receive depends on the platform: not every store sends every lifecycle event. See Integrations overview.
Contacts and lists
contact.unsubscribed covers the email channel only. An SMS-only opt-out arrives as contact.updated.
Messaging
Reviews
Requires a connected reviews app.Loyalty
Requires a connected loyalty app.All 29 event names, flat
All 29 event names, flat
order.completed, order.refunded, order.fulfilled, order.partially_fulfilled, order.cancelled, shipment.confirmed, shipment.out_for_delivery, shipment.delivered, checkout.started, cart.abandoned, contact.created, contact.updated, contact.unsubscribed, list.subscribed, list.unsubscribed, campaign.sent, email.opened, email.clicked, sms.delivered, sms.failed, review.request_ready, review.submitted, review.question, review.answer, loyalty.points_earned, loyalty.points_expiring, loyalty.tier_changed, loyalty.redemption, loyalty.referralRetries, timeouts and failure
Each delivery gets up to 5 attempts. Waits between attempts double, starting at 5 seconds: roughly 5s, 10s, 20s, then 40s. After the fifth failed attempt the delivery is dead-lettered and dropped. There is no replay action and no automatic catch-up. Retried:- Any response outside the 2xx range, including 4xx.
- Connection errors.
- Anything that takes longer than 10 seconds. The request is aborted at that point.
- A redirect. A 3xx response is never followed. The delivery fails immediately, and it is not resent to the redirect target. Register the final URL, not one that redirects to it.
- A URL that no longer passes the URL rules. The hostname is re-resolved before every delivery, so an endpoint whose DNS later points at a private address silently stops delivering. Nothing in the app tells you this happened.
- A paused or deleted endpoint. A delivery already queued when you pause an endpoint is dropped, not held. Pausing loses events for the duration; it does not buffer them.
Delivery visibility
Senderz records the outcome of every delivery internally, but there is no delivery log you can read. No screen lists deliveries, no API returns them, and no email tells you an endpoint has started failing. Practically, that means:- Log received deliveries on your side, including
deliveryIdandX-Sender-Event. - Alert yourself on a gap in traffic rather than waiting for Senderz to tell you.
- When you change an endpoint URL, confirm the first delivery arrives before removing the old receiver.
Edit, pause and delete
In the app
Both screens give you the same two actions per endpoint:- Pause and Resume. Pausing stops deliveries immediately, including ones already queued for it. Nothing that happened while it was paused is resent when you resume.
- Delete. Permanent, with no undo.
Over the API
The Public API covers list, create and delete. It has no edit route, so an API-key client changes an endpoint by deleting it and creating a replacement. Create the replacement first, so you do not drop events in the gap.A signed-in session can edit an endpoint in place with
PATCH /api/v1/integrations/webhooks/:id, sending only name, url, events or active. That route needs a logged-in session rather than an API key, so it is not available to integrations authenticating with a workspace key. Changing url re-runs the URL rules, and a rejected URL leaves the old one in place. It will not accept "*" in events, even though create does: a wildcard endpoint can have its name, URL and paused state changed, but not its event list.Troubleshooting
I saved the endpoint but nothing ever arrives
I saved the endpoint but nothing ever arrives
Work through these in order.
- Is the endpoint paused? Check the badge on the card.
- Did you subscribe to an event that has no producer?
campaign.sentnever fires, andprofile.createdfrom the Settings dialog is not a real event name. - Does the event need a connected integration? Commerce, review and loyalty events all do.
- Are you expecting
list.subscribedorcontact.createdfrom a path that does not raise them? See the warnings in Contacts and lists. - Does your URL redirect? A 3xx is never followed. Register the final URL.
- Has your DNS changed? The hostname is re-checked before every delivery, and an address that is no longer public blocks delivery silently.
My signature check always fails
My signature check always fails
Almost always one of three things.
- You are hashing a re-serialised body. Hash the raw bytes.
- You are comparing only the digest. The header value includes the
sha256=prefix. - You created the endpoint in the app, so you never received the secret. Recreate it through the API.
I get the same event twice
I get the same event twice
Expected. Delivery is at-least-once, and a slow response that is retried is the usual cause. Deduplicate on
deliveryId and make your handler idempotent.Creating the endpoint returns webhook_url_blocked
Creating the endpoint returns webhook_url_blocked
Your URL failed one of the URL rules. The error message is intentionally identical for every reason, so check them all: HTTPS, no explicit port, a dotted public hostname, and a hostname that resolves only to public addresses.
I cannot see the Webhooks screen at all
I cannot see the Webhooks screen at all
Webhook management sits behind the store integrations feature, which can be switched off for a workspace. If the screen is unavailable, contact support. Viewing the list is open to every member, but creating, editing and deleting need the owner or admin role.
If you would rather not run a receiver at all, Make can consume these events for you and hand them to hundreds of downstream apps with no code.

