Webhooks
Getting conversations out of the platform
When a conversation ends, bitpull posts it to one HTTPS URL you control. That single hop is what every CRM, helpdesk and automation recipe on this site is built on.
What actually exists
One outbound webhook per agent. You set an https URL and an active flag in the agent's Actions tab; after every completed conversation bitpull posts the conversation data there as JSON - transcript, summary, outcome, sentiment, channel and timings. A Test button sends a sample delivery immediately, and the active toggle pauses delivery without losing the URL.
That is the whole mechanism. It is not an event bus, there is no subscription model and there are no topics. Understanding that is worth more than any amount of clever consumer code.
- Conversation endsphone · web · chat
- bitpull posts JSONone URL, per agent
- Your endpointanswer 200 fast
- Your systemsCRM · ticket · Slack · sheet
The payload
The exact envelope - key names, nesting, which fields are present on a web chat versus a phone call - is not part of any published contract, and this site will not invent one. Point the webhook at the tester on this site, press Test in the dashboard, and read the delivery you actually get. Then write the mapping against that.
What the platform holds about a conversation is knowable, because the same data drives the dashboard's conversation view. Expect the delivery to carry some arrangement of these:
| Concept | Values it takes | What it is good for |
|---|---|---|
| Session identity | sessionId, roomName | Idempotency. Store it and drop duplicates. |
| Channel & direction | channel, INBOUND / OUTBOUND | Routing. A cold outbound call rarely deserves the same treatment as an inbound one. |
| Timing | startedAt, answeredAt, endedAt | Duration, and whether the call was ever picked up. |
| Telephony | sipFrom, sipTo, sipStatusCode | The caller number - in provider-specific formats. Normalise before matching. |
| Outcome | RESOLVED · PARTIALLY_RESOLVED · UNRESOLVED · INFORMATIONAL · UNKNOWN | The single most useful field. Gate ticket and task creation on it. |
| Sentiment | POSITIVE · NEUTRAL · NEGATIVE · MIXED · UNKNOWN | Escalation and priority. |
| Summary | title, key points, follow-up required + reason | What a human reads instead of the transcript. |
| Transcript | turns of USER / ASSISTANT with timestamps | The record. Usually belongs behind access control, not in a chat channel. |
| Language & voice | language, voiceId | Routing to a language-specific team. |
| Failure | status, failureReason | Distinguishing a bad conversation from a call that never connected. |
Consuming it without losing data
Four rules, in the order they will bite you.
1. Answer immediately, work afterwards
Return 200 before you touch a CRM. A webhook consumer that waits for three third-party APIs is a webhook consumer that times out.
export default async function handler(req, res) {
// 1. Persist the raw body first. Everything after this is replayable.
const id = await store.insertRaw({
receivedAt: new Date().toISOString(),
headers: req.headers,
body: req.body
})
// 2. Acknowledge. Nothing downstream is allowed to delay this.
res.status(200).json({ ok: true })
// 3. Do the real work out of band, where a failure is retryable.
await queue.publish('conversation.received', { id })
}2. Assume there is no retry
No retry schedule, replay API or delivery log is documented on the sending side. Treat a delivery as fire-and-forget: persist the raw body as the very first thing your handler does, and drive everything else from your own store. That one habit turns a lost integration into a replayable one.
3. The URL is the credential
No request signature is documented, so there is nothing to verify a delivery against. In practice that means the path itself has to be unguessable, and your endpoint has to be prepared for anyone who finds it.
# Good: the path itself is the secret, and it is long.
https://hooks.example.com/bitpull/9f3c1a7b4e2d8065
# Also fine: a token your handler checks before doing anything.
https://hooks.example.com/bitpull?t=9f3c1a7b4e2d8065
# Bad: guessable, and now anyone can inject conversations into your CRM.
https://example.com/webhook4. Be idempotent
Key on the session id. Even without documented retries, a proxy, a redeploy or a replay of your own will eventually deliver the same conversation twice - and a duplicate CRM task is how people lose trust in an integration.
The event model this does not have
It is worth being explicit, because the names below are what people search for and expect. bitpull has one outbound trigger: the conversation finished. It does not emit call.started, call.answered, appointment.created or anything else, and it has no subscription model in which you could select them.
| Event people expect | Reality today | What to do instead |
|---|---|---|
conversation.completed | Beta · account flag | This is the one that exists. Everything below is a variation you have to build. |
call.started | Planned | Not emitted. If you need live call state, poll the conversation list with a user token - or reconsider whether you do. |
call.completed | Beta · account flag | Covered by the completion delivery: channel and the SIP fields tell you it was a call. |
appointment.created | Planned | Calendly is the system of record for bookings - subscribe to its webhooks, not to bitpull. |
lead.created | Via webhook | Derive it: outcome plus the follow-up flag in the summary is your lead signal. |
callback.requested | Via webhook | Same derivation. Write the prompt so the summary states it plainly. |
escalation.created | Via webhook | Derive from sentiment NEGATIVE together with an UNRESOLVED outcome. |
If you need something during the conversation rather than after it, the mechanism is not a webhook at all - it is a declared agent tool, which the model calls mid-conversation and waits for.
The lower-effort alternative
Every agent can also send a conversation summary to an email address after each conversation, configured in its Notifications tab. No endpoint, no deployment, no maintenance. For a small team that just wants to see what the agent handled, it is often the right answer, and the webhook can come later.