In-App Chat SDK
Beta
Webhook

Webhooks

Subscribe to server-side chat events for moderation, analytics, CRM sync, and offline push notifications. Events are delivered as signed HTTPS POSTs to your endpoint.

Real-time push

HTTP callbacks within hundreds of milliseconds of chat activity — no polling required.

Message events

chat.message.created for every user and system message across your channels.

HMAC signatures

Verify X-Sendexa-Signature with your webhook secret before trusting any payload.

Automatic retries

Failed deliveries retry up to 5 times with exponential backoff.

Delivery latency

<500ms

event → POST

Max retries

5

exponential backoff

Event types

3

message · member · channel

Signature

SHA256

HMAC header

Event Types
chat.message.created

A message was posted to a channel (client SDK or server REST).

chat.member.joined

A user was added to a channel’s membership list.

chat.channel.created

A new channel was created for your business.

Delivery Envelope

Every webhook POST uses the same envelope. Event-specific data lives under data. Respond with 2xx within 10 seconds or the attempt is marked failed and retried.

FieldTypeRequiredDescription
idstring
required
Unique event delivery id (evt_...). Use for idempotency.
typestring
required
One of chat.message.created | chat.member.joined | chat.channel.created
createdAtstring
required
ISO 8601 timestamp when the event occurred.
businessIdstring
required
Your Sendexa business id.
dataobject
required
Event payload — shape depends on type.
Common envelope
{
"id": "evt_01HZYC1K",
"type": "chat.message.created",
"createdAt": "2026-07-12T10:05:12.500Z",
"businessId": "BU-VOIF86X7",
"apiVersion": "v1",
"data": { }
}
chat.message.created
New message in a channel

Fired for every message accepted into history — including posts from @sendexa/chat-sdk and POST .../messages. Ideal for push notifications to offline members and abuse scanning.

FieldTypeRequiredDescription
data.message.idstring
required
Message id (msg_...).
data.message.channelIdstring
required
Channel the message belongs to.
data.message.userIdstringoptionalAuthor user id; null/omitted for pure system posts.
data.message.bodystring
required
Message text.
data.message.typestring
required
text or system.
data.message.seqnumber
required
Per-channel sequence number.
data.channel.memberIdsstring[]
required
Current members — useful for fan-out push.
JSON
{
"id": "evt_01HZYC1K",
"type": "chat.message.created",
"createdAt": "2026-07-12T10:05:12.500Z",
"businessId": "BU-VOIF86X7",
"apiVersion": "v1",
"data": {
"message": {
"id": "msg_01HZYB8P",
"channelId": "ch_01HZYA3N",
"userId": "user_01HZX9K2M",
"body": "Hi — I need help with my order.",
"type": "text",
"metadata": {
"clientMessageId": "local-uuid-1"
},
"createdAt": "2026-07-12T10:05:12.441Z",
"seq": 1
},
"channel": {
"id": "ch_01HZYA3N",
"type": "dm",
"name": "Support · Ama",
"memberIds": ["user_01HZX9K2M", "agent_support_01"]
}
}
}
chat.member.joined
Member added to a channel

Emitted when a user id is added to a channel — at creation time for initial members and later when membership expands. Use this to grant access in your product UI or invite agents.

JSON
{
"id": "evt_01HZYC2M",
"type": "chat.member.joined",
"createdAt": "2026-07-12T10:01:00.120Z",
"businessId": "BU-VOIF86X7",
"apiVersion": "v1",
"data": {
"channelId": "ch_01HZYA3N",
"userId": "agent_support_01",
"displayName": "Support Agent",
"joinedAt": "2026-07-12T10:01:00.100Z",
"addedBy": "system",
"channel": {
"id": "ch_01HZYA3N",
"type": "dm",
"name": "Support · Ama",
"memberIds": ["user_01HZX9K2M", "agent_support_01"]
}
}
}
chat.channel.created
New channel created

Fired once when POST /v1/chat-sdk/channels succeeds. Pair with member joined events if you need per-user side effects for the initial roster.

JSON
{
"id": "evt_01HZYC0A",
"type": "chat.channel.created",
"createdAt": "2026-07-12T10:01:00.050Z",
"businessId": "BU-VOIF86X7",
"apiVersion": "v1",
"data": {
"channel": {
"id": "ch_01HZYA3N",
"type": "dm",
"name": "Support · Ama",
"memberIds": ["user_01HZX9K2M", "agent_support_01"],
"metadata": {
"ticketId": "TCK-1042"
},
"createdAt": "2026-07-12T10:01:00.000Z"
}
}
}

Request Headers

HeaderDescription
Content-Typeapplication/json
X-Sendexa-Signaturehex HMAC-SHA256 of the raw body using your webhook secret
X-Sendexa-EventSame as body.type (e.g. chat.message.created)
X-Sendexa-DeliveryUnique delivery attempt id
User-AgentSendexa-Webhooks/1.0

Verify Signatures

webhooks/chat-sdk.js
import crypto from 'crypto';
import express from 'express';
const app = express();
// IMPORTANT: use raw body for HMAC — not a re-serialized object
app.post(
'/webhooks/sendexa/chat',
express.raw({ type: 'application/json' }),
(req, res) => {
const secret = process.env.SENDEXA_WEBHOOK_SECRET;
const signature = req.get('X-Sendexa-Signature') || '';
const expected = crypto
.createHmac('sha256', secret)
.update(req.body) // Buffer
.digest('hex');
const valid =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body.toString('utf8'));
// Idempotency: skip if event.id already processed
switch (event.type) {
case 'chat.message.created':
// e.g. send FCM/APNs to offline members except author
handleMessageCreated(event.data);
break;
case 'chat.member.joined':
handleMemberJoined(event.data);
break;
case 'chat.channel.created':
handleChannelCreated(event.data);
break;
default:
// Ignore unknown types for forward compatibility
break;
}
res.status(200).json({ received: true });
}
);
Retries & Idempotency

If your endpoint returns a non-2xx status or times out (10s), Sendexa retries the same event with a new X-Sendexa-Delivery id. The event id stays stable — store processed ids to ignore duplicates.

AttemptDelay after previous failure
1Immediate
230 seconds
32 minutes
410 minutes
530 minutes

Return 200 quickly after durable enqueue (queue worker, DB write). Do heavy work asynchronously so retries are not caused by slow push providers.

Common Use Cases
Push

Offline notifications

On chat.message.created, notify members who are not currently online via FCM / APNs / SMS.

Ops

Agent routing

On chat.channel.created, open a ticket and assign an agent; listen for chat.member.joined when they accept.

Safety

Moderation

Scan message bodies server-side and flag or soft-block abusive content without depending on the client.