Webhooks
Webhooks
Get an HTTPS POST on your server when a chat starts, a message arrives, a chat moves or closes.
How webhooks work
A webhook sends an HTTPS POST to your URL when something happens in your chats. Register one URL per event type with Register a webhook. Bots can have webhooks of their own for the chats they handle (see Build a bot).
Headers: Content-Type: application/json, X-Mlc-Webhook-Id, X-Mlc-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>.
Reply 2xx within 10 s. Otherwise 3 retries within ~1 minute, then dropped and recorded on the webhook (failure_count, last_error). No ordering guarantee; order by payload IDs and seq. Never auto-disabled.
Available webhooks
| Resource | Webhooks |
|---|---|
| Chats | incoming_chat chat_deactivated |
| Events | incoming_event |
| Chat access | chat_transferred |
| Thread tags | thread_tagged |
| Status | routing_status_set |
| Customers | customer_visit_started |
Events
incoming_chat
A thread starts: new chat, visitor resume, agent resume chat, agent start_chat from Traffic.
Specifics
| Action | incoming_chat |
|---|---|
| Push equivalent | Realtime, incoming_chat |
Payload
{chat_id, seq, chat: ChatSummary} (visitor: CustomerChat)
{
"webhook_id": "1af539fa-1ecf-7db0-82a9-01662682e51c",
"secret_key": "whsec_9f2c4e1a7b",
"action": "incoming_chat",
"organization_id": "8cc7b1ee-a0e4-7910-8500-c032c91d4754",
"payload": {
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"seq": 42,
"chat": {
"chat": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"customer_id": "8c60d6b9-574a-79f8-8233-5d37ed1ca076",
"last_seq": 42,
"created_at": "2026-11-13T09:30:00Z",
"updated_at": "2026-11-13T09:30:00Z"
},
"thread": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"status": "active",
"group_id": 0,
"agent_id": "eeddf87a-7eb4-79b0-8e8b-1ae69e61ac1c",
"tag_ids": [
"6134a39f-4a85-7d48-83d5-f6f11d26a09a"
],
"queued_at": null,
"created_at": "2026-11-13T09:30:00Z",
"closed_at": null,
"prechat": []
},
"customer": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"name": "Nadia Rahman",
"email": "nadia@shop.northwind.example",
"session_fields": {
"order_number": "NW-1042"
},
"created_at": "2026-11-13T09:30:00Z",
"last_seen_at": "2026-11-13T09:30:00Z",
"visits": [
{
"url": "https://shop.northwind.example/packs/ridgeline-38",
"title": "Ridgeline 38L pack",
"at": "2026-11-13T09:30:00Z"
}
],
"visit_count": 3,
"banned_until": null,
"display_name": "Nadia Rahman",
"city": "Kuala Lumpur",
"browser": "Firefox",
"os": "Windows",
"online": true,
"chats_count": 3,
"last_rating": "good",
"conversations": [
{
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"status": "queued",
"agent_id": "eeddf87a-7eb4-79b0-8e8b-1ae69e61ac1c",
"group_id": 0,
"tag_ids": [
"6134a39f-4a85-7d48-83d5-f6f11d26a09a"
],
"rating": "good",
"started_at": "2026-11-13T09:30:00Z",
"ended_at": "2026-11-13T09:30:00Z",
"summary": "Asked whether the Ridgeline 38L reaches Kuala Lumpur by Friday"
}
]
},
"last_event": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"thread_id": "855d7d51-73d8-7f38-8253-a11f50ceb306",
"seq": 42,
"type": "message",
"visibility": "all",
"author_type": "customer",
"author_agent_id": null,
"author_customer_id": "801a98db-db8c-7c68-86fd-9ff5926f2382",
"client_message_id": "e2f8677e-0365-7890-83d1-a9a2443e4ab4",
"text": "Hi! Can the Ridgeline 38L reach Kuala Lumpur by Friday?",
"attachment": null,
"created_at": "2026-11-13T09:30:00Z"
}
}
}
}incoming_event
Any event: message, note, file, system message.
Specifics
| Action | incoming_event |
|---|---|
| Push equivalent | Realtime, incoming_event |
Payload
{chat_id, seq, thread_id, event: ChatEvent}
{
"webhook_id": "1af539fa-1ecf-7db0-82a9-01662682e51c",
"secret_key": "whsec_9f2c4e1a7b",
"action": "incoming_event",
"organization_id": "8cc7b1ee-a0e4-7910-8500-c032c91d4754",
"payload": {
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"seq": 42,
"thread_id": "855d7d51-73d8-7f38-8253-a11f50ceb306",
"event": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"thread_id": "855d7d51-73d8-7f38-8253-a11f50ceb306",
"seq": 42,
"type": "message",
"visibility": "all",
"author_type": "customer",
"author_agent_id": null,
"author_customer_id": "801a98db-db8c-7c68-86fd-9ff5926f2382",
"client_message_id": "e2f8677e-0365-7890-83d1-a9a2443e4ab4",
"text": "Hi! Can the Ridgeline 38L reach Kuala Lumpur by Friday?",
"attachment": null,
"created_at": "2026-11-13T09:30:00Z"
}
}
}chat_deactivated
Thread closed.
Specifics
| Action | chat_deactivated |
|---|---|
| Push equivalent | Realtime, chat_deactivated |
Payload
{chat_id, seq, thread: Thread, reason} reason ∈ agent, customer, inactivity, banned
{
"webhook_id": "1af539fa-1ecf-7db0-82a9-01662682e51c",
"secret_key": "whsec_9f2c4e1a7b",
"action": "chat_deactivated",
"organization_id": "8cc7b1ee-a0e4-7910-8500-c032c91d4754",
"payload": {
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"seq": 42,
"thread": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"status": "active",
"group_id": 0,
"agent_id": "eeddf87a-7eb4-79b0-8e8b-1ae69e61ac1c",
"tag_ids": [
"6134a39f-4a85-7d48-83d5-f6f11d26a09a"
],
"queued_at": null,
"created_at": "2026-11-13T09:30:00Z",
"closed_at": null,
"prechat": []
},
"reason": "agent"
}
}chat_transferred
transfer_chat, auto-transfer after 3 min unanswered, 30 s agent-disconnect reassignment.
Specifics
| Action | chat_transferred |
|---|---|
| Push equivalent | Realtime, chat_transferred |
Payload
{chat_id, seq, thread: Thread, transferred_to: {type, id}, queue: QueueInfo|null}
{
"webhook_id": "1af539fa-1ecf-7db0-82a9-01662682e51c",
"secret_key": "whsec_9f2c4e1a7b",
"action": "chat_transferred",
"organization_id": "8cc7b1ee-a0e4-7910-8500-c032c91d4754",
"payload": {
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"seq": 42,
"thread": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"status": "active",
"group_id": 0,
"agent_id": "eeddf87a-7eb4-79b0-8e8b-1ae69e61ac1c",
"tag_ids": [
"6134a39f-4a85-7d48-83d5-f6f11d26a09a"
],
"queued_at": null,
"created_at": "2026-11-13T09:30:00Z",
"closed_at": null,
"prechat": []
},
"transferred_to": {
"type": "agent",
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0"
},
"queue": {
"position": 2,
"wait_time": 120
}
}
}thread_tagged
Tag added.
Specifics
| Action | thread_tagged |
|---|---|
| Push equivalent | Realtime, thread_tagged |
Payload
{chat_id, seq, thread: Thread, tag_id}
{
"webhook_id": "1af539fa-1ecf-7db0-82a9-01662682e51c",
"secret_key": "whsec_9f2c4e1a7b",
"action": "thread_tagged",
"organization_id": "8cc7b1ee-a0e4-7910-8500-c032c91d4754",
"payload": {
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"seq": 42,
"thread": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"status": "active",
"group_id": 0,
"agent_id": "eeddf87a-7eb4-79b0-8e8b-1ae69e61ac1c",
"tag_ids": [
"6134a39f-4a85-7d48-83d5-f6f11d26a09a"
],
"queued_at": null,
"created_at": "2026-11-13T09:30:00Z",
"closed_at": null,
"prechat": []
},
"tag_id": "6134a39f-4a85-7d48-83d5-f6f11d26a09a"
}
}routing_status_set
An agent's status changed, including offline 30 s after their last socket closed.
Specifics
| Action | routing_status_set |
|---|---|
| Push equivalent | Realtime, routing_status_set |
Payload
{agent_id, status}
{
"webhook_id": "1af539fa-1ecf-7db0-82a9-01662682e51c",
"secret_key": "whsec_9f2c4e1a7b",
"action": "routing_status_set",
"organization_id": "8cc7b1ee-a0e4-7910-8500-c032c91d4754",
"payload": {
"agent_id": "eeddf87a-7eb4-79b0-8e8b-1ae69e61ac1c",
"status": "accepting_chats"
}
}customer_visit_started
Visitor socket opened.
Specifics
| Action | customer_visit_started |
|---|---|
| Push equivalent | Realtime, customer_visit_started |
Payload
{visitor: Visitor}
{
"webhook_id": "1af539fa-1ecf-7db0-82a9-01662682e51c",
"secret_key": "whsec_9f2c4e1a7b",
"action": "customer_visit_started",
"organization_id": "8cc7b1ee-a0e4-7910-8500-c032c91d4754",
"payload": {
"visitor": {
"id": "828aec20-b9c3-7700-80fb-c1e028342cc0",
"customer_id": "8c60d6b9-574a-79f8-8233-5d37ed1ca076",
"chat_id": "98c7f777-40e4-7888-8901-1999911daaaa",
"name": "Nadia Rahman",
"email": "nadia@shop.northwind.example",
"status": "chatting",
"agent_id": "eeddf87a-7eb4-79b0-8e8b-1ae69e61ac1c",
"page_title": "Ridgeline 38L pack",
"page_url": "https://shop.northwind.example/packs/ridgeline-38",
"entered_at": "2026-11-13T09:30:00Z",
"visits": 3,
"chats": 3,
"city": "Kuala Lumpur",
"browser": "Firefox",
"os": "Windows",
"referrer": "https://www.google.com/",
"pages": [
{
"url": "https://shop.northwind.example/packs/ridgeline-38",
"title": "Ridgeline 38L pack",
"at": "2026-11-13T09:30:00Z"
}
]
}
}
}The request we send
Every delivery has the same envelope. Answer with any 2xx status within 10 seconds.
| Field | Description |
|---|---|
| The webhook that sent this delivery. |
| The webhook's signing secret; checking the X-Mlc-Signature header instead is safer. |
| One of incoming_chat, incoming_event, chat_deactivated, chat_transferred, thread_tagged, routing_status_set, customer_visit_started. |
| Your organization ID. |
| The same payload as the WS push of that action, notes included for incoming_event. customer_visit_started: {visit_id, visitor: Visitor}, once per visit (a widget visitor's first socket or page with no open visit; reconnects and further pages in the visit send nothing). |
Verify the signature
Check X-Mlc-Signature on every request before you trust it: an HMAC-SHA256 with the webhook’s secret over the timestamp, a dot and the raw body. Reject a timestamp more than 5 minutes old.
func verify(secret string, r *http.Request, body []byte) bool {
var t int64
var sig string
for _, part := range strings.Split(r.Header.Get("X-Mlc-Signature"), ",") {
if v, ok := strings.CutPrefix(part, "t="); ok { t, _ = strconv.ParseInt(v, 10, 64) }
if v, ok := strings.CutPrefix(part, "v1="); ok { sig = v }
}
if d := time.Since(time.Unix(t, 0)); d > 5*time.Minute || d < -5*time.Minute { return false }
mac := hmac.New(sha256.New, []byte(secret))
fmt.Fprintf(mac, "%d.", t)
mac.Write(body)
want, err := hex.DecodeString(sig)
return err == nil && hmac.Equal(mac.Sum(nil), want)
}# The same check in a shell, for debugging (body in body.json, header value in $SIG):
t=${SIG#t=}; t=${t%%,*}; v=${SIG#*v1=}
printf '%s.' "$t" | cat - body.json | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | grep -q "$v" && echo okManage webhooks
Admins of your organization, from a signed-in session in the mlivechat app; a bot token can’t call it. Most teams use Settings, Webhooks instead. The methods are in the Configuration API.
| Method | What it does |
|---|---|
| Register a webhook | Register a webhook. |
| List webhooks | List webhooks. |
| Unregister a webhook | Unregister a webhook. |
| Turn a webhook on or off | Turn a webhook on or off. |