Dynamická konfigurácia pre každý hovor
Predvolene má každé telefónne číslo a verejný kľúč priradeného statického agenta. Keď potrebujete prispôsobenie pre každého volajúceho alebo pre každého návštevníka — smerovanie VIP, kontext prihláseného používateľa, A/B testy promptov — prepnite do režimu webhooku a nechajte rozhodnúť svoj server.
Ako to funguje
- Prihlásite sa na odber udalosti
telephony.incoming(telefón) aleboweb.incoming(widget). Obe sú blokujúce webhooky: ThunderPhone čaká na vašu odpoveď až 10 sekúnd pred pokračovaním hovoru. - ThunderPhone vám odošle
{call_id, from_number, to_number}(relácie widgetu obsahujú polia špecifické pre widget namiesto čísel — pozrite si schému požiadavky). - Váš server odpovie konfiguráciou agenta (prompt, hlas, produkt, nástroje). ThunderPhone túto konfiguráciu použije pre hovor.
- Ak vrátite
{}, prekročíte časový limit alebo dôjde k chybe, ako záloha sa použije staticky priradený agent. Bezpečné predvolené správanie.
1. Nakonfigurujte cieľ webhooku
Telefonické hovory
Pre telefónne čísla prihláste svoj endpoint na odber telephony.incoming:
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Prod call-incoming",
"url": "https://example.com/thunderphone/incoming",
"events": ["telephony.incoming"]
}'
Odpoveď obsahuje jednorazový secret — uložte si ho; použijete ho
na overenie podpisu.
Webový widget
Pre relácie widgetu vytvorte verejný kľúč v mode="webhook"
so zabudovanou URL vášho endpointu:
curl -X POST https://api.thunderphone.com/v1/publishable-key \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Dynamic widget",
"mode": "webhook",
"webhook_url": "https://example.com/thunderphone/widget-incoming",
"allowed_domains": ["example.com"]
}'
Widget bude na túto URL odosielať požiadavku POST pri každom začatí relácie.
2. Implementujte obslužnú funkciu
Tri základné pravidlá:
- Overte podpis pri každej požiadavke (pozrite si Overenie podpisov webhookov). Nevynechávajte to ani vo vývoji — nastavte to správne raz a potom to znovu používajte.
- Odpovedajte rýchlo. Desať sekúnd je pevný limit a každá sekunda je pre volajúceho ticho. Ak potrebujete, vykonajte vyhľadávanie v databáze, ale synchrónne nevolajte nadväzujúce LLM — ak chcete dynamické generovanie promptov, vopred ich vypočítajte a ukladajte do vyrovnávacej pamäte.
- Zabezpečte čistý fallback. Každý neočakávaný stav by mal vrátiť
{}, aby hovor spracoval staticky priradený agent.
import hashlib
import hmac
import json
import os
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]
def verify(body: bytes, sig: str) -> bool:
expected = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig or "")
@app.post("/thunderphone/incoming")
async def incoming(request: Request):
body = await request.body()
if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
raise HTTPException(401)
event = json.loads(body)
if event["type"] not in ("telephony.incoming", "web.incoming"):
return {} # fall back to default
caller = event["data"]["from_number"]
# Cheap DB lookup: is this a known VIP?
customer = lookup_customer(caller)
if customer and customer.tier == "vip":
return {
"prompt": f"You are a VIP concierge for {customer.name}. Be proactive…",
"voice": "john",
"product": "storm-base",
}
return {} # default agent handles non-VIPs
def lookup_customer(phone: str):
# ... your CRM integration ...
pass
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;
function verify(body, sig) {
const expected = crypto.createHmac("sha256", SECRET).update(body).digest("hex");
return sig &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}
app.post(
"/thunderphone/incoming",
express.raw({ type: "application/json" }),
async (req, res) => {
if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
const IMPORTANT_TYPES = new Set([
"telephony.incoming",
"web.incoming",
]);
if (!IMPORTANT_TYPES.has(event.type)) return res.json({});
const customer = await lookupCustomer(event.data.from_number);
if (customer?.tier === "vip") {
return res.json({
prompt: `You are a VIP concierge for ${customer.name}. Be proactive…`,
voice: "john",
product: "storm-base",
});
}
res.json({}); // fall back to default agent
},
);
3. Schéma odpovede
Telo odpovede presne zodpovedá schéme odpovede na prichádzajúci hovor. Bežne používané polia:
| Pole | Typ | Popis |
|---|---|---|
prompt | reťazec (povinné) | Systémový prompt pre agenta |
voice | reťazec (povinné) | ID hlasu z GET /v1/voices |
product | reťazec | Predvolene spark |
background_track | reťazec | null | ID zvuku na pozadí |
acknowledgement_prompt_mode | reťazec | auto alebo manual (iba Storm s potvrdením) |
acknowledgement_prompt | reťazec | Povinné, keď je režim manual |
tools | pole | Vložené schémy funkčných nástrojov — pozrite si Funkčné nástroje |
Vzory
Kontext prihláseného používateľa
Vo widgetoch v režime webhooku stránka návštevníka už vie, kto
je. Zavolajte svoj webhook s parametrom query stringu, ktorý SDK
widgetu prepošle (?customer_id=123), a vyhľadajte zákazníka na strane servera.
Zavádzanie výziev A/B
Skôr než to vytvoríte ručne, všimnite si, že ThunderPhone má natívnu funkciu
Experimenty
(/dashboard/experiments a kartu A/B v nástroji na vytváranie agentov), ktorá
definuje varianty, rozdeľuje návštevnosť a porovnáva výsledky jednotlivých variantov —
webhook nie je potrebný.
Ak napriek tomu potrebujete riadenie na strane webhooku: hašujte call_id → segment;
poskytnite výzvu A pre 0..49 a výzvu B pre 50..99. Zaznamenajte, ktorý
segment ste vybrali, do vlastnej databázy a neskôr ho porovnajte so
známkou dokončeného hovoru.
Smerovanie podľa času
Počas pracovných hodín → agent „živá podpora“; mimo pracovných hodín → agent „zaznamenať správu“.
Vo vašom obslužnom programe stačí jednoduché prepínanie podľa new Date().getUTCHours().
Ďalšie kroky
Presné schémy požiadaviek a odpovedí vrátane každého konfiguračného kľúča.
Správne nastavte HMAC raz a používajte ho všade.
Kombinujte dynamické smerovanie s nástrojmi pre jednotlivých agentov.
Opakovania, poradie, časové limity.