Dynamisk konfigurasjon per samtale
Velg stemmeagenten som svarer – eller skriv om prompten og innstillingene – separat for hvert innkommende anrop, styrt av tilpasset logikk i en webhook du kontrollerer.
Som standard har hvert telefonnummer og hver publiserbare nøkkel en statisk tildelt stemmeagent. Når du trenger tilpasning per innringer eller per besøkende — VIP-ruting, kontekst for innloggede brukere, A/B-tester av ledetekster — bytter du til webhook-modus og lar serveren din avgjøre.
Slik fungerer det
- Du abonnerer på hendelsen
telephony.incoming(telefon) ellerweb.incoming(widget). Begge er blokkerende webhooks: ThunderPhone venter i opptil 10 sekunder på svaret ditt før samtalen fortsetter. - ThunderPhone sender deg
{call_id, from_number, to_number}(widgetøkter inneholder widgetspesifikke felt i stedet for numre — se forespørselsskjemaet). - Serveren din svarer med en stemmeagentkonfigurasjon (ledetekst, stemme, produkt, verktøy). ThunderPhone bruker den konfigurasjonen for samtalen.
- Hvis du returnerer
{}, får tidsavbrudd eller en feil, brukes den statisk tildelte stemmeagenten som reserve. En sikker standard.
1. Konfigurer webhook-destinasjonen
For telefonnumre abonnerer du endepunktet ditt på 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"]
}'Svaret inneholder en secret som bare vises én gang — lagre den; du bruker den
til signaturverifisering.
For widgetøkter oppretter du en publiserbar nøkkel i mode="webhook"
med endepunktets URL innebygd:
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"]
}'Widgeten sender en POST-forespørsel til denne URL-en ved starten av hver økt.
2. Implementer behandleren
Tre tommelfingerregler:
- Verifiser signaturen på hver forespørsel (se Verifiser webhook-signaturer). Ikke hopp over dette i utvikling — gjør det riktig én gang og gjenbruk det.
- Svar raskt. Ti sekunder er den absolutte grensen, og hvert sekund er stillhet for innringeren. Gjør databaseoppslag hvis du trenger det, men ikke kall nedstrøms LLM-er synkront — hvis du vil ha dynamisk promptgenerering, forhåndsberegn og bufre.
- Fall tilbake på en ryddig måte. Enhver uventet tilstand skal returnere
{}slik at den statisk tilordnede agenten håndterer samtalen.
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 ...
passimport 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. Svarskjema
Svarteksten samsvarer nøyaktig med svarskjemaet for innkommende samtaler. Vanlig brukte felt:
| Felt | Type | Beskrivelse |
|---|---|---|
prompt | string (påkrevd) | Systemprompt for agenten |
voice | string (påkrevd) | Stemme-ID fra GET /v1/voices |
product | string | Standard er spark |
background_track | string | null | ID for bakgrunnslyd |
acknowledgement_prompt_mode | string | auto eller manual (kun Storm med bekreftelse) |
acknowledgement_prompt | string | Påkrevd når modus er manual |
tools | array | Innebygde skjemaer for funksjonsverktøy — se Funksjonsverktøy |
Mønstre
Kontekst for innlogget bruker
I widgeter i webhook-modus vet den besøkendes side allerede hvem de
er. Kall webhooken din med en spørringsparameter som widget-SDK-en
videresender (?customer_id=123), og slå opp kunden på serversiden.
A/B-utrulling av prompt
Før du bygger dette selv, merk at ThunderPhone har en innebygd
eksperimenter-funksjon
(/dashboard/experiments og A/B-fanen i agentbyggeren) som
definerer varianter, fordeler trafikk og sammenligner resultater per variant —
ingen webhook kreves.
Hvis du likevel trenger kontroll på webhook-siden: hash call_id → bøtte;
server prompt A for 0..49 og prompt B for 50..99. Registrer hvilken
bøtte du valgte i din egen database, og korreler senere mot vurderingen av
det fullførte anropet.
Tidsbasert ruting
Åpningstid → stemmeagent for «direkte kundestøtte»; utenfor åpningstid → stemmeagent for «ta imot en melding».
Ren svitsjing på new Date().getUTCHours() i handleren din.
Neste steg
Nøyaktige skjemaer for forespørsler og svar, inkludert alle konfigurasjonsnøkler.
Få HMAC riktig én gang, og gjenbruk det overalt.
Kombiner dynamisk ruting med verktøy per agent.
Nye forsøk, rekkefølge, tidsavbrudd.