Dinamička konfiguracija po pozivu
Prema zadanim postavkama svakom telefonskom broju i javnom ključu dodijeljen je statički agent. Kada trebate prilagodbu po pozivatelju ili po posjetitelju — VIP usmjeravanje, kontekst prijavljenog korisnika, A/B testove upita — prijeđite na način rada webhooka i prepustite odluku svom poslužitelju.
Kako funkcionira
- Pretplatite se na događaj
telephony.incoming(telefon) iliweb.incoming(widget). Oba su blokirajući webhookovi: ThunderPhone čeka do 10 sekundi na vaš odgovor prije nastavka poziva. - ThunderPhone vam šalje
{call_id, from_number, to_number}(sesije widgeta sadrže polja specifična za widget umjesto brojeva — pogledajte shemu zahtjeva). - Vaš poslužitelj odgovara konfiguracijom agenta (upit, glas, proizvod, alati). ThunderPhone tu konfiguraciju upotrebljava za poziv.
- Ako vratite
{}, istekne vrijeme ili dođe do pogreške, statički dodijeljeni agent upotrebljava se kao zamjenska opcija. Sigurna zadana postavka.
1. Konfigurirajte odredište webhooka
Telefonski pozivi
Za telefonske brojeve pretplatite svoju krajnju točku na 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"]
}'
Odgovor uključuje jednokratni secret — spremite ga; upotrebljavat ćete ga
za provjeru potpisa.
Web widget
Za sesije widgeta izradite javni ključ u mode="webhook"
s ugrađenim URL-om vaše krajnje točke:
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 će poslati POST zahtjev na ovaj URL pri svakom početku sesije.
2. Implementirajte obrađivač
Tri osnovna pravila:
- Provjerite potpis za svaki zahtjev (pogledajte Provjera potpisa webhooka). Nemojte to preskočiti u razvojnom okruženju — ispravno implementirajte jednom i ponovno upotrebljavajte.
- Odgovorite brzo. Deset sekundi strogo je ograničenje, a svaka sekunda tišina je za pozivatelja. Po potrebi obavite pretrage baze podataka, ali nemojte sinkrono pozivati nizvodne LLM-ove — ako želite dinamičko generiranje uputa, unaprijed ih izračunajte i predmemorirajte.
- Pouzdano primijenite rezervnu opciju. Svako neočekivano stanje treba vratiti
{}kako bi statički dodijeljeni agent obradio poziv.
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. Shema odgovora
Tijelo odgovora u potpunosti odgovara shemi odgovora za dolazni poziv. Najčešće korištena polja:
| Polje | Vrsta | Opis |
|---|---|---|
prompt | niz znakova (obavezno) | Sistemska uputa za agenta |
voice | niz znakova (obavezno) | ID glasa iz GET /v1/voices |
product | niz znakova | Zadano je spark |
background_track | niz znakova | null | ID ambijentalnog zvuka |
acknowledgement_prompt_mode | niz znakova | auto ili manual (samo Storm s potvrdom) |
acknowledgement_prompt | niz znakova | Obavezno kada je način rada manual |
tools | polje | Ugrađene sheme funkcionalnih alata — pogledajte Funkcionalni alati |
Obrasci
Kontekst prijavljenog korisnika
U widgetima u načinu rada web-dojavnika stranica posjetitelja već zna tko je
on. Pozovite svoj web-dojavnik s parametrom niza upita koji SDK widgeta
prosljeđuje (?customer_id=123) i potražite korisnika na poslužitelju.
Uvođenje A/B upita
Prije nego što ovo izradite ručno, imajte na umu da ThunderPhone ima ugrađenu
značajku Eksperimenti
(/dashboard/experiments i karticu A/B u alatu za izradu agenta) koja
definira varijante, dijeli promet i uspoređuje rezultate po varijanti —
web-dojavnik nije potreban.
Ako vam ipak treba upravljanje na strani web-dojavnika: raspršite call_id → spremnik;
poslužite upit A za 0..49 i upit B za 50..99. Zabilježite koji ste
spremnik odabrali u vlastitoj bazi podataka i kasnije ga povežite s ocjenom
dovršenog poziva.
Usmjeravanje prema vremenu
Radno vrijeme → agent za „podršku uživo”; izvan radnog vremena → agent za
„primanje poruke”. Čisto grananje prema new Date().getUTCHours() u vašem rukovatelju.
Sljedeći koraci
Točne sheme zahtjeva i odgovora, uključujući svaki konfiguracijski ključ.
Jednom ispravno postavite HMAC; ponovno ga upotrebljavajte posvuda.
Kombinirajte dinamičko usmjeravanje s alatima po agentu.
Ponovni pokušaji, redoslijed, vremenska ograničenja.