Outbound webhooks
Subscribe an HTTPS endpoint to Kustomer events. Create webhooks with the API, in Settings or from an app definition, then receive signed JSON for every customer, conversation, message, user, team and tag change.
An outbound webhook is a URL plus a list of events. When one of those events happens in your org, Kustomer sends the event envelope to the URL as a signed POST.
Expose an HTTPS endpoint
It must accept
POSTwith a JSON body and answer with a2xxwithin a few seconds. Verify the signature before trusting the body.Create the webhook
Use the API, Settings › Platform › Outbound webhooks, or the
outboundWebhookssection of an app definition.Store the token
The response includes a
token. It's the HMAC key forX-Kustomer-Webhook-Signature. Keep it in your secret store.Watch deliveries
List recent delivery attempts per webhook to debug failures.
Create a webhook with the API
post/v1/outbound-webhooks
curl -X POST "https://acme.api.kustomerapp.com/v1/outbound-webhooks" \
-H "Authorization: Bearer $KUSTOMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Conversation sync",
"url": "https://hooks.example.com/kustomer",
"events": ["kustomer.conversation.create", "kustomer.conversation.update", "kustomer.message.create"],
"headers": [{ "name": "X-Source", "value": "kustomer" }]
}'namestringrequired- Display name, up to 255 characters.
urlstringrequired- Destination. Must be an absolute
https://URL; anything else fails withNot a valid absolute HTTPS URL. eventsstring[]required- Up to 12 unique event names from the event catalog.
headers{ name, value }[]- Extra headers sent with every delivery. Names up to 100 characters, values up to 255. Use one for a shared secret if your gateway needs it. They're applied after Kustomer's own headers, so don't reuse
Content-Type,X-Kustomer-Webhook-EventorX-Kustomer-Webhook-Signature: yours would replace them. enabledboolean- Defaults to
true.
{
"data": {
"type": "outbound-webhook",
"id": "5f3e8f9b2b3fc60013f6b836",
"attributes": {
"name": "Conversation sync",
"enabled": true,
"url": "https://hooks.example.com/kustomer",
"isError": false,
"events": ["kustomer.conversation.create", "kustomer.conversation.update", "kustomer.message.create"],
"headers": [{ "name": "X-Source", "value": "kustomer" }],
"token": "f1253ac0dacd0a8fdbc5b8caac814865b53b164acb83f9163d5adad0195a6771…",
"createdAt": "2026-10-21T14:58:35.499Z",
"updatedAt": "2026-10-21T14:58:35.499Z"
}
}
}isError is true while Kustomer has automatically disabled the webhook.
Receive events
A minimal receiver verifies the signature against the raw body, acknowledges immediately, then processes the event asynchronously.
import crypto from 'node:crypto'
import express from 'express'
const app = express()
const TOKEN = process.env.KUSTOMER_WEBHOOK_TOKEN!
app.post('/kustomer', express.raw({ type: 'application/json' }), (req, res) => {
const expected = crypto.createHmac('sha256', TOKEN).update(req.body).digest('hex')
const received = req.get('X-Kustomer-Webhook-Signature') ?? ''
if (received.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) {
return res.sendStatus(401)
}
res.sendStatus(200)
queue.publish(JSON.parse(req.body.toString('utf8')))
})Read Delivery and security before going to production. It covers retries, duplicates, ordering and automatic disabling.
Manage webhooks
Pause a webhook during maintenance with PATCH { "enabled": false }. Events that happen while it's disabled are not queued for later, so reconcile afterwards with incremental sync.
Define webhooks in an app
Apps can create webhooks when they're installed. Add an outboundWebhooks array to the app definition with the same fields:
{
"app": "ecommstore",
"version": "1.2.0",
"title": "E-comm Store",
"outboundWebhooks": [
{
"name": "Send conversation events to E-comm Store",
"url": "https://api.ecommstore.example/kustomer/events",
"headers": [{ "name": "X-Source", "value": "kustomer" }],
"events": ["kustomer.conversation.create", "kustomer.conversation.update"]
}
]
}Webhooks or workflows?
Outbound webhooks send every event of the subscribed types, with a fixed payload. If you only care about a subset ("conversations tagged vip that just closed") or need a different payload shape, use a workflow with a condition and a REST API step instead. Workflows can also react to events webhooks don't offer, such as custom objects and notes.