telephony.incoming / web.incoming
Blockerande webhook som formar konfigurationen för ett inkommande samtal i realtid.
När ett inkommande telefonsamtal når ett nummer utan en tilldelad
röstagent, eller när en webbwidgetsession startar med en publicerbar nyckel i
mode="webhook", skickar ThunderPhone en blockerande
telephony.incoming / web.incoming-begäran till din
äldre webhook-URL
och väntar upp till 10 sekunder på ett konfigurationssvar. Använd detta
utbyte för att dynamiskt välja prompt, röst och verktyg per samtal —
se guiden för dynamisk samtalskonfiguration
för det kompletta mönstret.
Det blockerande utbytet har ingen reservlösning: om din hanterare returnerar en
status som inte är 2xx, får en timeout eller returnerar en konfiguration som
inte klarar valideringen, avvisas samtalet (telefonsamtalet kopplas inte fram;
widgetsessionens begäran misslyckas med 502/422). Svara snabbt — den som
ringer hör en uppringningssignal medan du fattar beslutet.
Begärans nyttolast
För telefonsamtal (telephony.incoming):
{
"type": "telephony.incoming",
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
}
}| Fält | Typ | Beskrivning |
|---|---|---|
call_id | integer | Samtals-ID — stabilt i alla händelser för detta samtal |
from_number | string | E.164-nummer för den som ringer |
to_number | string | E.164-destination (ett av dina ThunderPhone-nummer) |
För webbsessioner i webbwidgeten (web.incoming) identifierar data
inbäddningssidan i stället för telefonnummer:
{
"type": "web.incoming",
"data": {
"call_id": 987654322,
"origin_domain": "https://example.com",
"publishable_key_prefix": "pk_live_a1b2"
}
}| Fält | Typ | Beskrivning |
|---|---|---|
call_id | integer | Samtals-ID |
origin_domain | string | Sidans ursprung som är värd för widgeten |
publishable_key_prefix | string | De första tecknen i den publicerbara nyckel som öppnade sessionen |
language, primary_language | string | Finns när widgetsessionen begärde en språkåsidosättning |
voice | string | Finns när widgetsessionen begärde en röståsidosättning |
website_context | string | Finns när widgeten skickade sidkontext per session |
Svarsschema
Returnera ett JSON-objekt som beskriver agentkonfigurationen för det här samtalet.
prompt och voice krävs; allt annat är valfritt.
{
"prompt": "You are a helpful booking assistant for Acme Restaurant.",
"voice": "john",
"product": "spark",
"background_track": null,
"tools": []
}| Fält | Typ | Krävs | Beskrivning |
|---|---|---|---|
prompt | string | ja | Systemprompt som styr agenten |
voice | string | ja | Röst-id från GET /v1/voices, t.ex. john. voice_name accepteras som ett alias. Okända röster underkänns vid validering och samtalet avvisas |
product | string | nej | Standardvärdet är spark. Tillåtna: spark, bolt, storm-base, storm-base-with-ack, storm-extra, storm-extra-with-ack |
thinking_level | string | nej | minimal, base (standard) eller extra. Åsidosätts för Storm-produkter: storm-extra* tvingar extra, andra storm-* tvingar base |
audio_context_mode | string | nej | full (standard) eller reduced |
watchdog_enabled | boolean | nej | Aktivera övervakning för det här samtalet. Standardvärdet är false |
additional_audio_context | boolean | null | nej | Inkludera de senaste turerna av uppringarens ljud i stället för endast den senaste turen, vilket förbättrar korrigeringar och insamling av data med mycket stavning eller siffror med en liten ökning av latens och kostnad. Aktiveras som standard för inkommande sessioner och är avstängt för utgående telefonsamtal; null behåller standardvärdet |
storm_feedback_mode | string | nej | none, acknowledgement (standard) eller tick |
language | string | nej | Förkortning för primary_language |
primary_language | string | nej | Språkkod, normaliserad (standard en). Koder som inte kan lösas avvisar samtalet |
has_additional_languages | boolean | nej | Standardvärdet är false |
additional_languages | array of string | nej | Extra språk som agenten kan växla till |
native_voice_switching | boolean | nej | Standardvärdet är false. När samtalet växlar till ett annat språk byter du till en röst med språket som modersmål (matchad efter kön) i stället för att behålla den konfigurerade rösten |
background_track | string | null | nej | Id för bakgrundsljud eller null |
acknowledgement_prompt_mode | string | nej | auto (standard) eller manual (Storm-with-ack-produkter) |
acknowledgement_prompt | string | nej | Används när acknowledgement_prompt_mode="manual" |
silence_interval_seconds | integer | null | nej | 5–120. Sekunder av tystnad från uppringaren före en avstämning |
silence_max_checkins | integer | null | nej | 1–10 |
silence_checkins_enabled | boolean | nej | Standardvärdet är true |
connect_tone_enabled | boolean | nej | Standardvärdet är false |
voicemail_action | string | nej | prompt (standard), hangup eller message |
voicemail_message | string | nej | Används när voicemail_action="message" |
agent_name | string | nej | Visningsnamn som rapporteras till instrumentpaneler och widgeten |
org_name | string | nej | Organisationens visningsnamn för agentens persona |
tools | array | nej | Infogade scheman för funktionsverktyg (se Funktionsverktyg) |
call_id | integer | nej | Valfritt eko av begärans samtals-id; ignoreras |
Eftersom prompt och voice krävs avvisar {} eller ett svar som inte
klarar valideringen samtalet med 422 — det finns ingen reservagent på
den här vägen (ett nummer eller en nyckel i webhook-läge har ingen
tilldelad agent).
Begränsning av svarsstorlek
Exempel på hanterare
import hashlib
import hmac
import json
import os
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
WEBHOOK_SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]
def verify(body: bytes, signature: str) -> bool:
expected = hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")
@app.post("/thunderphone-webhook")
async def webhook(request: Request):
body = await request.body()
if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
raise HTTPException(status_code=401)
event = json.loads(body)
if event["type"] == "telephony.incoming":
caller = event["data"]["from_number"]
prompt = (
"Greet the caller as a San Francisco local…"
if caller.startswith("+1415")
else "You are a friendly customer support agent…"
)
return {
"prompt": prompt,
"voice": "john",
"product": "spark",
}
if event["type"] == "web.incoming":
return {
"prompt": "You are the website's helpful voice assistant…",
"voice": "john",
"product": "spark",
}
return {}import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;
function verify(body, signature) {
const expected = crypto
.createHmac("sha256", SECRET)
.update(body)
.digest("hex");
return signature &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
app.post(
"/thunderphone-webhook",
express.raw({ type: "application/json" }),
(req, res) => {
if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
if (event.type === "telephony.incoming" || event.type === "web.incoming") {
const caller = event.data.from_number || "web";
const prompt = caller.startsWith("+1415")
? "Greet the caller as a San Francisco local…"
: "You are a friendly customer support agent…";
return res.json({
prompt,
voice: "john",
product: "spark",
});
}
res.json({});
},
);Svar med funktionsverktyg
Koppla verktyg så att AI:n kan anropa dina API:er mitt i samtalet:
{
"prompt": "You are a booking assistant. Use the available tools to help customers schedule appointments.",
"voice": "john",
"product": "spark",
"tools": [
{
"type": "function",
"function": {
"name": "search_appointments",
"description": "Find available appointment slots",
"parameters": {
"type": "object",
"properties": {
"date": { "type": "string", "description": "YYYY-MM-DD" },
"service": { "type": "string" }
},
"required": ["date"]
}
},
"endpoint": {
"url": "https://api.example.com/appointments/search",
"method": "POST",
"headers": {
"X-Api-Key": "your-key"
}
}
}
]
}Snabbguide till produktnivåer
| Produkt | Latens | Resonemang | Bekräftelse |
|---|---|---|---|
spark | Lägst | Grundläggande | — |
bolt | Låg | Förbättrat | — |
storm-base | Medel | Starkt | — |
storm-base-with-ack | Medel | Starkt | Automatisk utfyllnad medan den tänker |
storm-extra | Högre | Djupgående | — |
storm-extra-with-ack | Högre | Djupgående | Automatisk utfyllnad medan den tänker |
Relaterat
Den icke-blockerande händelsen när samtalet avslutas.
Fullständigt JSON-schema för tools[] och det signerade endpoint-kontraktet.
Prenumerera med flera URL:er på telephony.incoming / web.incoming.
Mönster för prompts, verktyg och A/B-tester per uppringare.