REST API v1

Chatlane Webhooks

Outbound webhook reference — subscribe an HTTPS endpoint to inbox events and receive signed JSON payloads for messages and conversation activity.

Base URL
https://chatlane.io/api/v1/
⌘K
Try in Postman

Overview

Outbound webhooks let Chatlane notify your systems the moment something happens in an inbox. Each webhook is an HTTPS URL plus a set of subscribed events; whenever a subscribed event occurs, Chatlane sends an HTTP POST with a JSON payload describing what happened.

Webhooks are configured per inbox by team admins under Inbox Settings → Webhooks. Each webhook has:

  • URL — the HTTPS endpoint Chatlane will POST to.
  • Secret (optional) — used to sign each delivery so you can verify it came from Chatlane. Stored encrypted.
  • Subscribed events — the events this endpoint should receive (see Events below).
  • Active — inactive webhooks receive nothing.

Delivery

  • Deliveries are POST requests with Content-Type: application/json and the event payload as the raw body.
  • Your endpoint should respond with a 2xx status within 10 seconds. Slow endpoints should acknowledge immediately and process asynchronously.
  • If the request cannot be completed (connection failure, timeout), delivery is retried up to 3 times, backing off 30 seconds, 2 minutes, then 5 minutes. Non-2xx responses are logged but not retried.
  • Deliveries may occasionally arrive out of order or more than once — use the IDs in the payload to deduplicate if needed.

Verifying signatures

When a webhook has a secret, every delivery includes an X-Chatlane-Signature header: the HMAC-SHA256 hex digest of the raw request body, keyed with your secret. Compute the same digest and compare using a constant-time comparison.

PHP

$signature = hash_hmac('sha256', $request->getContent(), $secret);

if (! hash_equals($signature, $request->header('X-Chatlane-Signature'))) {
    abort(401);
}

Node.js

const crypto = require('crypto');

const signature = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');

if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(req.headers['x-chatlane-signature'] ?? ''))) {
    return res.status(401).end();
}

Common payload fields

Every event payload includes:

Field Type Description
event string The event name, e.g. message.created.
inbox_id integer The inbox the event occurred in.
conversation object { "id": 123, "status": "active" } — the conversation involved.
contact object | null { "id": 5, "email": "[email protected]" } — the conversation's contact, or null for entity ("order") conversations.
order_external_id string | null The external ID of the entity record (e.g. an order number) for entity-inbox conversations; null for regular contact conversations.

Events

message.created

Fires when a new message is added to a conversation. Activity entries never fire this event; internal notes only fire it when recorded via the API with record_only, and record_only transactional emails are excluded.

{
    "event": "message.created",
    "from_contact": true,
    "inbox_id": 12,
    "conversation": { "id": 345, "status": "active" },
    "contact": { "id": 5, "email": "[email protected]" },
    "recipient": null,
    "order_external_id": null,
    "message": {
        "id": 6789,
        "type": "web",
        "created_at": "2026-07-22T13:45:00+00:00"
    }
}
Field Type Description
from_contact boolean true when the message was sent by the contact (inbound); false for outbound/team messages.
contact object | null The counterpart contact: the sender for inbound messages, the addressed recipient for outbound ones.
recipient object | null The explicitly addressed peer contact for direct replies; null when the message is addressed to the support team.
message.type string Channel type, e.g. web, email, sms, whatsapp, note.

conversation.read

Fires when a conversation that had unread activity is marked as read by a team member.

{
    "event": "conversation.read",
    "inbox_id": 12,
    "conversation": { "id": 345, "status": "active" },
    "contact": null,
    "order_external_id": "ORD-1042"
}

conversation.created

Fires when a new conversation is started in the inbox — by an inbound message, the widget, or a team member.

{
    "event": "conversation.created",
    "inbox_id": 12,
    "conversation": { "id": 346, "status": "active" },
    "contact": { "id": 5, "email": "[email protected]" },
    "order_external_id": null
}

conversation.status.updated

Fires when a conversation's status changes (e.g. it is closed, reopened, or marked pending).

{
    "event": "conversation.status.updated",
    "inbox_id": 12,
    "conversation": { "id": 345, "status": "closed" },
    "contact": { "id": 5, "email": "[email protected]" },
    "order_external_id": null,
    "old_status": "active",
    "new_status": "closed"
}
Field Type Description
old_status string Status before the change: active, pending, or closed.
new_status string Status after the change. Matches conversation.status.

conversation.assigned

Fires when a conversation's assignee changes, including when it becomes unassigned.

{
    "event": "conversation.assigned",
    "inbox_id": 12,
    "conversation": { "id": 345, "status": "active" },
    "contact": { "id": 5, "email": "[email protected]" },
    "order_external_id": null,
    "assignee": {
        "id": 3,
        "name": "Alex Doe",
        "email": "[email protected]"
    }
}
Field Type Description
assignee object | null The team member now assigned, or null when the conversation was unassigned.

message.delivery_status.updated

Fires when a message's delivery status changes — typically as outbound messages progress through provider callbacks. This event can be high-volume; subscribe only if you need per-message delivery tracking.

{
    "event": "message.delivery_status.updated",
    "inbox_id": 12,
    "conversation": { "id": 345, "status": "active" },
    "contact": { "id": 5, "email": "[email protected]" },
    "order_external_id": null,
    "message": {
        "id": 6789,
        "type": "whatsapp",
        "delivery_status": "delivered"
    }
}
Field Type Description
message.delivery_status string The new status, e.g. sent, delivered, read, failed.

Backward compatibility

Webhooks created before event subscriptions existed are pinned to message.created and conversation.read. Payloads are additive: new fields may be added over time, so parse leniently and ignore unknown fields and unknown event names.