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.