
Your order system knows when an order is placed. Your CRM needs to know when a customer writes on WhatsApp. Without a connection, someone checks one screen and types into another, and things are missed. Polling an API every minute is slow and wastes requests.
A webhook is an HTTP request that one system sends to another the moment something happens. SaleMag sends webhooks to tell your system about new messages, new contacts and finished campaigns. To act back in SaleMag, your system calls the REST API.
What kinds of webhooks does SaleMag have?
SaleMag has one kind of webhook you set up, plus the platform webhooks it sets up for you:
| Kind | Direction | What it is for | Where you set it up |
|---|---|---|---|
| Outgoing event webhooks | SaleMag to your URL | New messages, new contacts, finished campaigns | Webhooks page, or /api/v1/webhooks |
| Platform webhooks | Meta, stores and Telegram to SaleMag | Receive chats, products and orders | Registered for you when you connect a channel or store |
Which events do SaleMag outgoing webhooks send?
SaleMag outgoing webhooks send three events. You choose events per endpoint; an endpoint with no events chosen receives all of them.
| Event | When it fires | Main object in the payload |
|---|---|---|
message.received |
A customer message arrives on WhatsApp, Messenger, Instagram, Telegram or WeChat | message: id, conversation_id, direction, channel, body, sent_at |
contact.created |
A new contact is added in the app, or by a customer's first message | contact: id, phone_e164, email, first_name, last_name |
campaign.completed |
A broadcast finishes, with the status completed or failed |
campaign: id, name, channel, status, totals |
test.ping |
Only when you press Send test | message: a test text |
Contacts brought in by a CSV import, a store sync or POST /api/v1/contacts do not fire contact.created.
A message.received delivery looks like this:
{
"event": "message.received",
"message": {
"id": 9812,
"conversation_id": 311,
"direction": "in",
"channel": "whatsapp",
"body": "Ei dress ta koto?",
"sent_at": "2026-10-24T09:15:02+00:00"
},
"timestamp": "2026-10-24T09:15:03+00:00"
}
For campaign.completed, totals holds the counts total, queued, sent, delivered, read, failed, clicked and unsubscribed.
What headers does each delivery have?
Every delivery is an HTTP POST with a JSON body and these headers:
| Header | Value |
|---|---|
Content-Type |
application/json |
X-Webhook-Event |
The event name, such as message.received |
X-Webhook-Signature |
t={unix seconds},v1={HMAC-SHA256 hex} |
User-Agent |
Ends in Webhooks/1.0 |
How do I verify the webhook signature?
Compute an HMAC-SHA256 of {t}.{raw body} with your signing secret, as a hex string, and compare it with v1. The secret starts with whsec_; use the whole string, prefix included. Always hash the raw body as received, not a re-encoded copy.
import crypto from "node:crypto"
// rawBody: the request body as a string, exactly as received
export function verifySaleMagWebhook(rawBody, signatureHeader, secret, toleranceSec = 300) {
const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=", 2)))
if (!parts.t || !parts.v1) return false
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSec) return false
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex")
const a = Buffer.from(expected)
const b = Buffer.from(parts.v1)
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
The same check in PHP:
$raw = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? ''), $sig);
$expected = hash_hmac('sha256', ($sig['t'] ?? '') . '.' . $raw, $secret);
$valid = isset($sig['v1']) && hash_equals($expected, $sig['v1']);
Every attempt, including a retry, is signed again with a new timestamp, so you can safely reject old timestamps.
What happens when my endpoint is down?
SaleMag counts a delivery as successful when your endpoint returns any 2xx status within 10 seconds. Anything else is retried on this schedule:
| Attempt | When |
|---|---|
| 1 | Right away |
| 2 | 1 minute after attempt 1 fails |
| 3 | 5 minutes after attempt 2 fails |
| 4 | 1 hour after attempt 3 fails |
| 5 | 1 day after attempt 4 fails, then no more |
Each attempt is saved in the delivery log with the event, HTTP status, attempt number and time. The Webhooks page shows the last five per endpoint, and View deliveries shows the full list. A URL that points to a private or local network address is blocked and not retried.
Because a retry can arrive after a slow success, make your receiver safe to run twice: store the object id and skip one you have already handled.
How do I manage webhook endpoints?
Manage endpoints on the Webhooks page in the app, or from code:
- Create, edit, turn off or delete an endpoint. Team members need the developer permission to open the page.
- Rotate the secret. The old secret stops working at once, so update your receiver right away.
- Send test. Queues a
test.pingto that endpoint. - From the API.
GET,POSTandDELETEon/api/v1/webhookswith a token that has thewebhooks:writescope. The create response includes the secret once. See the REST API.
Can my system send data into SaleMag?
Yes, through the REST API, not through a webhook. SaleMag has no incoming webhook URL. Your system calls the API with a bearer token instead:
| Goal | API call |
|---|---|
| Create or update a contact | POST /api/v1/contacts, PATCH /api/v1/contacts/{id} |
| Send a WhatsApp template or text, or an SMS | POST /api/v1/messages/send |
| Create and launch a campaign | The /api/v1/campaigns endpoints |
| Ask an AI agent | POST /api/v1/ai/agents/{id}/chat |
A common pattern is to receive message.received on your server, decide what to do, and answer through POST /api/v1/messages/send. Outside the 24-hour window, only an approved template is delivered.
Does SaleMag send order data to my system?
No. Order and cart events are not sent as outgoing webhooks, so a new chat order or a store order does not reach your URL. Chat orders are handled on the Orders page in SaleMag. For orders from a connected Shopify, WooCommerce or BigCommerce store, use that store's own webhooks.
What do SaleMag webhooks not do?
- No order, cart, lead or message status events as outgoing webhooks. Only the three events above leave SaleMag.
- No incoming webhook URL. To send data into SaleMag, use the REST API.
- No native Zapier, Make or n8n app. They work through plain webhooks and the REST API.
- No manual resend of an outgoing delivery. Retries are automatic.
Who are SaleMag webhooks for?
Developers and agencies connecting SaleMag to a website, ERP, POS, CRM or no-code tool. Webhooks tell your system what happened in SaleMag; to send messages or change data from code, the REST API covers that side.
How do I start?
Create a SaleMag account, add your endpoint on the Webhooks page, and press Send test. Plan prices are on the pricing page.
Step by step
- 1
Add an endpoint
Open Webhooks, add your HTTPS URL and pick the events, or leave all unchecked to receive every event.
- 2
Save the secret
Copy the whsec_ signing secret when it is shown. It is shown only once; rotate it if it is lost.
- 3
Verify the signature
Check X-Webhook-Signature against an HMAC-SHA256 of the timestamp and the raw body, then return a 2xx response quickly.
- 4
Send a test
Press Send test to receive a test.ping, then check the delivery log for the HTTP status.
Frequently asked questions
Which events can SaleMag send to my webhook URL?
SaleMag sends three events: message.received, contact.created and campaign.completed. The Send test button also sends a test.ping, which you cannot subscribe to.
How do I verify a SaleMag webhook signature?
Read the X-Webhook-Signature header, which looks like t={timestamp},v1={signature}. Compute HMAC-SHA256 of '{timestamp}.{raw body}' with your whsec_ secret as a hex string, and compare it with v1 in constant time.
Does SaleMag retry failed webhook deliveries?
Yes. A delivery that does not get a 2xx response within 10 seconds is tried again after 1 minute, 5 minutes, 1 hour and 1 day, so 5 attempts in total. Every attempt appears in the delivery log.
Does SaleMag send a webhook when an order is placed?
No. SaleMag's outgoing webhooks carry only message.received, contact.created and campaign.completed. There are no order or cart events.
Can my website or ERP send a WhatsApp message through SaleMag?
Yes, through the REST API rather than a webhook. Call POST /api/v1/messages/send with a token that has the messages:write scope; outside the 24-hour window, use an approved template. See the REST API.
Do I need to set up Meta's WhatsApp webhook myself?
No. When you connect a number through SaleMag's WhatsApp signup, SaleMag subscribes it to Meta's webhooks. The channel settings have a webhook health check and a button to register the webhook with Meta again.
Does SaleMag work with Zapier, Make or n8n?
Yes, through webhooks and the API. SaleMag has no native Zapier app, but these tools can receive SaleMag's outgoing webhooks and call the REST API.
Are delivery status webhooks, like sent or read, available?
No. SaleMag tracks sent, delivered and read ticks in the inbox, but it does not forward message status changes to your URL.
About the author
WhatsApp & F-commerce automation, Dhaka
The Dhaka team that builds SaleMag, the AI sales inbox for WhatsApp, Messenger and Instagram used by online sellers in Bangladesh.