Events and webhooks
How data moves in and out of Kustomer as events. Outbound webhooks push changes to your systems, inbound hooks accept data from anywhere, and every change carries a before-and-after diff.
Every create and update in Kustomer produces an event: a JSON envelope with the full resource and a diff of what changed. Workflows react to events internally. Outbound webhooks deliver a subset of them to your servers. Inbound hooks turn data you send into events that start workflows.
Diagram source
flowchart LR
subgraph In["Into Kustomer"]
API[REST API]
Hooks[Inbound hooks<br/>form · web · email]
end
subgraph K["Kustomer"]
Store[(Records)]
Bus{{Events}}
WF[Workflows]
end
subgraph Out["Out of Kustomer"]
OWH[Outbound webhooks]
REST[Workflow REST API steps]
end
API --> Store --> Bus
Hooks --> Bus
Bus --> WF
WF --> Store
Bus --> OWH --> You[(Your service)]
WF --> REST --> YouPOST /kustomer HTTP/1.1 Content-Type: application/json X-Kustomer-Webhook-Event: kustomer.conversation.update X-Kustomer-Webhook-Signature: … X-Source: kustomer // a custom header you configured // signature = hex(HMAC-SHA256(token, rawBody)) // body: 638 bytes
Choose a mechanism
The event envelope
Outbound webhooks and workflows see the same envelope. data is the full resource as of the change, in the same JSON:API shape the REST API returns. changes lists only the attributes and relationships that changed.
{
"id": "6717c2a4e5b1f00012ab34cd",
"name": "kustomer.conversation.update",
"org": "5f8481c6a22ab5b9d5f2dd8a",
"partition": "5f8481c6a22ab5b9d5f2dd8a",
"orgName": "acme",
"data": {
"type": "conversation",
"id": "6717c29fe5b1f00012ab34c1",
"attributes": {
"name": "Where is my order?",
"status": "done",
"priority": 3,
"channels": ["email"],
"tags": ["5f9a1c2e8b3d4e0012345678"],
"updatedAt": "2026-10-21T15:02:11.482Z"
},
"relationships": {
"customer": { "data": { "type": "customer", "id": "6717c29de5b1f00012ab34b9" } }
},
"links": { "self": "/v1/conversations/6717c29fe5b1f00012ab34c1" }
},
"createdAt": "2026-10-21T15:02:11.511Z",
"changes": {
"attributes": {
"status": { "op": "replace", "before": "open", "after": "done" }
},
"relationships": {}
}
}idstringrequired- Unique event ID. Use it to deduplicate: delivery is at least once.
namestringrequiredkustomer.<resource>.<create|update>. Also sent in theX-Kustomer-Webhook-Eventheader.orgstringrequired- Your org ID.
partitioncarries the same value. orgNamestring- Your org's subdomain name.
dataobjectrequired- The resource after the change:
type,id,attributes,relationships,links. changesobject- For updates,
attributesandrelationshipsmaps of{ op, before, after }.opisreplace, oraddwhen the field had no value (thenbeforeis absent). Custom attributes change as onecustomentry whosebeforeandafterare the whole custom objects, so comparechanges.attributes.custom.before.<field>with.after.<field>. createdAtdate-timerequired- When the event was produced. Order events by this, not by arrival time.
clientstring- Which kind of client made the change, for example
api. Useful when you need to avoid loops.
What's in this section
Subscribe an HTTPS endpoint to Kustomer events through the API, the UI or an app definition.
Every event you can subscribe to, with payloads, plus the extra events available to workflows.
Verify signatures, handle retries and duplicates, and keep your webhook from being disabled.
Form, web and email hooks that bring outside data into workflows.