Automation & workflow

AI Agents and n8n

The option where the transcript never leaves your infrastructure - which, for recorded conversations under GDPR, is often the deciding factor.

Via webhookn8n · bitpull AI agents

Where this stands today

n8n does the same job as Zapier and Make: a webhook node receives the finished conversation and the workflow decides what happens next. The difference is where the data sits. Self-hosted n8n means the transcript of every call goes from bitpull to a server you run, and no further.

For a call centre handling health, legal or financial conversations in the EU, that is not a preference. It is usually the difference between an integration that passes review and one that does not.

The technical detail that catches people out is the response mode. n8n's Webhook node defaults to responding when the workflow finishes. If the workflow then makes four API calls, the sender waits for all four. Set the node to respond immediately and do the work afterwards.

What the agent can do

Webhook node, respond immediately

Acknowledge the delivery in milliseconds, then run the rest of the workflow. The sender should never wait for your CRM.

Full data residency

Transcripts, summaries and caller numbers stay on your host. Nothing is stored by a third-party automation vendor.

Code node for the payload

JavaScript in the workflow means normalising numbers and reshaping the payload without an extra service.

Persist first, process second

Write the raw body to a database as step one. Everything after that becomes replayable, which no other option on this page gives you.

How it is wired

  1. Conversation ends
  2. n8n webhookresponds immediately
  3. Store rawPostgres - replayable
  4. ProcessCRM, ticket, notification

Code

n8n Code node - normalise and classify the delivery
// Code node - "Normalise conversation"
const body = $input.first().json.body ?? $input.first().json

const summary = body.summary ?? body.session?.summary ?? {}
const messages = body.messages ?? body.transcript ?? []

const e164 = (raw) => {
  if (!raw) return null
  // Callers arrive as +43…, 0043…, or as a full sip:…@host URI.
  const inner = String(raw).match(/<([^>]+)>/)?.[1] ?? String(raw)
  const user = inner.replace(/^(sips?|tel):/i, '').split(';')[0].split('@')[0]
  const d = user.replace(/[^\d+]/g, '')
  if (d.startsWith('+')) return d
  if (d.startsWith('00')) return '+' + d.slice(2)
  if (d.startsWith('0')) return '+43' + d.slice(1)
  return d ? '+' + d : null
}

return [{
  json: {
    received_at: new Date().toISOString(),
    caller: e164(body.sipFrom ?? body.from),
    channel: body.channel ?? 'unknown',
    direction: body.direction ?? null,
    outcome: summary.outcome ?? 'UNKNOWN',
    sentiment: summary.sentiment ?? 'UNKNOWN',
    needs_followup: Boolean(summary.followUp?.required),
    title: summary.title ?? null,
    key_points: summary.keyPoints ?? [],
    turns: messages.length,
    transcript: messages
      .map((m) => `${m.role === 'USER' ? 'Caller' : 'Agent'}: ${m.content}`)
      .join('\n'),
    raw: body                      // keep the original - mappings will change
  }
}]
Runs after the webhook node has already answered. Keep the mapping tolerant: field names are not a published contract.
Workflow outline
Webhook  (POST, Respond: Immediately)
   ↓
Postgres - insert raw payload into conversations_raw
   ↓
Code - "Normalise conversation"
   ↓
Switch on outcome
   ├─ UNRESOLVED        → HTTP Request: create helpdesk ticket
   ├─ needs_followup    → HTTP Request: create CRM task
   └─ else              → no-op (the row is already stored)

Error Trigger workflow → alert the on-call channel

Limits worth knowing before you start

  • No bitpull node in the n8n library. This is the generic Webhook node.
  • The default response mode makes the sender wait for the whole workflow. Set it to respond immediately.
  • A self-hosted n8n that is down loses the delivery. That is an argument for storing the raw body as the very first node, before any processing.
  • Expose the webhook over HTTPS with a hard-to-guess path. The URL is the only thing standing between the internet and your conversation data.
  • n8n version differences matter more than with hosted tools. Pin a version and test upgrades against a saved payload.

Questions

Why n8n rather than Zapier?

Data residency, mostly. Self-hosted n8n keeps transcripts and caller numbers on your own infrastructure. It also has no per-task bill, which changes the calculus at volume.

How do I replay a failed delivery?

Only if you stored it. Insert the raw body as the first node, then replay from your own table - the sending side does not offer a replay API.

Can n8n call back into bitpull?

It can call the public REST API - creating a session, for example. Account-level endpoints need a user token, which is not what a workflow should be holding.

Ready to run this?

Agents are created and operated on bitpull.ai - this site documents how to connect one. If the outbound webhook is not visible in your dashboard yet, that is the account capability flag, and support can enable it.