Skip to main content

Self-Build Tutorial — Inbound-to-Outbound Bot

You will build a minimal bot that receives inbound webhooks and replies automatically. This example is an echo bot (it sends back exactly what the end customer wrote), but if you replace the reply step with a call to your AI agent, it becomes a consultation bot as-is.

Prerequisites​

  • API Key (blumnb-..., BrandWrite) — Quick Start
  • Signing secret (whsec_...) and a registered webhook URL — Webhook Setup
  • brandKey

Overall flow​

Code (Node.js)​

const crypto = require('crypto');
const express = require('express');

const BASE = 'https://api.talkbridge.io';
const APIKEY = process.env.API_KEY; // blumnb-...
const WHSEC = process.env.WHSEC; // whsec_...
const BRAND = process.env.BRAND; // mybrand

const app = express();
const seen = new Set(); // Delivery-Id idempotency

app.post('/webhook', express.raw({ type: '*/*' }), async (req, res) => {
// 1) Verify the signature
const ts = req.get('X-Bridge-Timestamp') || '';
const sig = req.get('X-Bridge-Signature') || '';
const mac = crypto.createHmac('sha256', WHSEC)
.update('v0:' + ts + ':').update(req.body).digest('hex');
const expected = 'v0=' + mac;
if (sig.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send('bad signature');
}

// Respond 2xx quickly and do the rest asynchronously (avoids the gateway timeout)
res.sendStatus(200);

// 2) Idempotency & event branching
const deliveryId = req.get('X-Bridge-Delivery-Id');
if (deliveryId && seen.has(deliveryId)) return;
if (deliveryId) seen.add(deliveryId);

const evt = JSON.parse(req.body.toString('utf8')); // { brand, userKey, kind, seq }
if (evt.kind !== 'message') return; // ignore agent echoes, etc.

// 3) Fetch the content
const url = `${BASE}/api/agent/rooms/${encodeURIComponent(evt.userKey)}/messages`
+ `?brand=${encodeURIComponent(evt.brand)}&max=5`;
const r = await fetch(url, { headers: { Authorization: `Bearer ${APIKEY}` } });
const data = await r.json();
const last = (data.messages || []).find(m => m.kind === 'message');
if (!last) return;

// 4) Send the reply (replace this with an AI call to make it a consultation bot)
const reply = `Received: ${last.text}`;
await fetch(`${BASE}/api/agent/send`, {
method: 'POST',
headers: { Authorization: `Bearer ${APIKEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ brandKey: evt.brand, userKey: evt.userKey, text: reply }),
});
});

app.listen(3000, () => console.log('webhook listening :3000'));

Run & expose​

API_KEY=blumnb-... WHSEC=whsec_... BRAND=mybrand node bot.js
# If you need a public address, expose it through a tunnel and register that URL as the webhook in TalkBridge Center

Checkpoints​

  • A signature failure returns 401 — the gateway retries, but keeps being rejected (this is expected).
  • Successful handling returns 200 immediately — the actual work (fetch and send) happens asynchronously after the response.
  • Always filter out kind: "agent" to prevent an infinite loop.
  • Skip any repeated delivery with the same Delivery-Id.

Next​

  • Replace the reply-building step with your AI agent (LLM)
  • Image handling: if the content contains a URL, it is an image message (Fetching Content)
  • Ending a consultation and handoff: end / end-with-bot in the Outbound API
  • Hardening reliability: Retries & Idempotency