Webhooks
MonCha POSTs signed events to your systems the moment they happen.
Available on Scale, or when webhooks are included in an active enterprise contract.
Events
lead.created— a visitor was scored as a lead (see the payload below).conversation.handoff— a conversation requests human help; fields vary between an explicit request and a model-detected handoff.message.unanswered— the bot had no relevant content for a question.ticket.created— a customer opened a support ticket from the widget's Support tab.booking.created— a visitor submitted an appointment request from the widget (see Bookings).
Adding one
Go to API & Agents → Webhooks, enter a public HTTPS endpoint, tick the events you want, and save. Private and internal addresses and redirects are rejected. You'll get a signing secret (whsec_…). The last delivery status is shown next to each webhook so you can spot a broken endpoint.
What you receive
POST https://yourapp.com/hooks/MonCha
X-MonCha-Event: lead.created
X-MonCha-Timestamp: 1783691750
X-MonCha-Signature: 69b291806d1023a89d4e414e…
Content-Type: application/json
{
"event": "lead.created",
"timestamp": 1783691750,
"data": {
"lead_id": "010e19a1-…",
"session_id": "…",
"chatbot_id": "9166406d-…",
"score": 85,
"band": "hot",
"category": "pricing",
"summary": "40-person team evaluating enterprise pricing for Q1",
"sentiment": "positive",
"name": "Priya Sharma",
"email": "[email protected]",
"phone": null,
"country": "SG",
"region": "Singapore",
"external_user_id": "usr_8f21",
"qualification": { "team_size": "40", "timeline": "Q1" },
"captured_via": "inferred"
}
}Verifying the signature
Always verify before trusting a payload. The signature is the hex HMAC-SHA256 of `${timestamp}.${rawBody}`, keyed with your signing secret:
import crypto from 'crypto'
function verify(rawBody, headers, secret) {
const ts = headers['x-moncha-timestamp']
const sig = headers['x-moncha-signature']
if (!ts || !sig || !/^[0-9a-f]{64}$/i.test(sig)) return false
const seconds = Number(ts)
if (!Number.isFinite(seconds) || Math.abs(Date.now() / 1000 - seconds) > 300) return false
const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}.${rawBody}`)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'), Buffer.from(sig, 'hex')
)
}Use the raw request body, not a re-serialized object — key order matters. Reject timestamps more than a few minutes old to prevent replay.
Delivery behavior
- Delivered as a fire-and-forget POST; a slow endpoint never slows your customer's chat.
- 10-second timeout. Respond
2xxquickly and do your work asynchronously. - No automatic retries today — reconcile through the API if you need guarantees.
Scoping
A webhook can listen to all bots or one specific bot. Add several webhooks if different systems care about different events.
Related
Webhooks push events out. To let the bot pull live data during a conversation, see Functions.