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
200immediately — 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-botin the Outbound API - Hardening reliability: Retries & Idempotency