Skip to content

Webhooks

Webhooks push conversation events from Lane.Chat to a URL on your server, so you can sync conversations, trigger downstream automation, or feed analytics. You subscribe per application and choose which events you want.

Configuration

Set a webhook URL, enable webhooks, and select events for an application (from the dashboard). Optionally set a webhook secret — when present, every delivery is HMAC-signed so you can verify authenticity.

Delivery format

Deliveries are POST requests with a JSON body. Every payload has the same envelope:

json
{
  "event": "message.received",
  "app_id": 123,
  "app_pub": "app_1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d",
  "timestamp": 1753180800,
  "data": { }
}
  • timestamp is a Unix epoch (seconds).
  • data is event-specific.
  • Deliveries are queued and retried asynchronously.

Headers

HeaderValue
Content-Typeapplication/json
User-AgentLaneChat-Webhook/1.0
X-Webhook-SignatureHMAC-SHA256(secret, raw_body) (only when a secret is configured)

Verifying the signature

The signature is the hex HMAC-SHA256 of the raw request body using your webhook secret. Compute it over the exact bytes you received and compare in constant time.

Node.js

js
import crypto from 'node:crypto'

function verify(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('hex')
  const a = Buffer.from(expected)
  const b = Buffer.from(signatureHeader || '')
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

// Express (make sure you have the RAW body, not the parsed object)
app.post('/lanechat/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const raw = req.body.toString('utf8')
  if (!verify(raw, req.get('X-Webhook-Signature'), process.env.LANECHAT_WEBHOOK_SECRET)) {
    return res.status(401).end()
  }
  const event = JSON.parse(raw)
  // handle event...
  res.status(200).end()
})

PHP

php
function verify(string $rawBody, ?string $signature, string $secret): bool {
    $expected = hash_hmac('sha256', $rawBody, $secret);
    return is_string($signature) && hash_equals($expected, $signature);
}

$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? null;
if (!verify($raw, $sig, getenv('LANECHAT_WEBHOOK_SECRET'))) {
    http_response_code(401);
    exit;
}
$event = json_decode($raw, true);

Python

python
import hashlib
import hmac

def verify(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

# Flask
@app.post("/lanechat/webhook")
def hook():
    raw = request.get_data()
    if not verify(raw, request.headers.get("X-Webhook-Signature", ""), WEBHOOK_SECRET):
        return "", 401
    event = request.get_json()
    return "", 200

Sign over the raw body

Recompute the HMAC over the exact received bytes. If your framework re-serializes the JSON before you hash it, key ordering or whitespace changes will break the comparison.

Event catalog

The events you can subscribe to:

Message events

EventFires when
message.receivedA message is received from a customer
message.sentA message is sent to a customer
message.ai_responseThe AI replies

Conversation events

EventFires when
chat.startedA conversation starts
chat.endedA conversation ends
chat.assignedA conversation is assigned to an agent
chat.transferredA conversation is transferred
chat.queuedA conversation enters the queue

Customer events

EventFires when
client.onlineA customer comes online
client.offlineA customer goes offline
client.bannedA customer is banned
client.unbannedA customer is unbanned
client.bindingA customer binds a notification channel

Agent events

EventFires when
agent.onlineAn agent comes online
agent.offlineAn agent goes offline
agent.busyAn agent becomes busy
agent.availableAn agent becomes available

System events

EventFires when
system.errorA system error occurs
system.notificationA system notification is emitted

Responding

Return a 2xx status quickly to acknowledge receipt. Do heavy work asynchronously — deliveries are retried on failure, so make your handler idempotent.

Lane.Chat — AI customer support, multichannel CRM, and roleplay AI.