WebhooksWebhooks

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

Events

incoming_chat

A thread starts: new chat, visitor resume, agent resume chat, agent start_chat from Traffic.

Specifics

Actionincoming_chat
Push equivalentRealtime, incoming_chat

Payload

{chat_id, seq, chat: ChatSummary} (visitor: CustomerChat)

Sample webhookJSON
{
  "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

Actionincoming_event
Push equivalentRealtime, incoming_event

Payload

{chat_id, seq, thread_id, event: ChatEvent}

Sample webhookJSON
{
  "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

Actionchat_deactivated
Push equivalentRealtime, chat_deactivated

Payload

{chat_id, seq, thread: Thread, reason} reason ∈ agent, customer, inactivity, banned

Sample webhookJSON
{
  "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

Actionchat_transferred
Push equivalentRealtime, chat_transferred

Payload

{chat_id, seq, thread: Thread, transferred_to: {type, id}, queue: QueueInfo|null}

Sample webhookJSON
{
  "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

Actionthread_tagged
Push equivalentRealtime, thread_tagged

Payload

{chat_id, seq, thread: Thread, tag_id}

Sample webhookJSON
{
  "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

Actionrouting_status_set
Push equivalentRealtime, routing_status_set

Payload

{agent_id, status}

Sample webhookJSON
{
  "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

Actioncustomer_visit_started
Push equivalentRealtime, customer_visit_started

Payload

{visitor: Visitor}

Sample webhookJSON
{
  "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.

FieldDescription

webhook_idstring (uuid)required

The webhook that sent this delivery.

secret_keystringrequired

The webhook's signing secret; checking the X-Mlc-Signature header instead is safer.

actionstringrequired

One of incoming_chat, incoming_event, chat_deactivated, chat_transferred, thread_tagged, routing_status_set, customer_visit_started.

organization_idstring (uuid)required

Your organization ID.

payloadobjectrequired

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.

GoGo
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)
}
Shell, for debuggingShell
# 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 ok

Manage 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.

MethodWhat it does
Register a webhookRegister a webhook.
List webhooksList webhooks.
Unregister a webhookUnregister a webhook.
Turn a webhook on or offTurn a webhook on or off.