Open in
telephony.incoming / web.incoming
Blokkerende webhook som former konfigurasjonen for et innkommende anrop i sanntid.
Når et innkommende telefonanrop når et nummer uten en tilordnet
stemmeagent, eller en nettwidgetøkt starter med en publiserbar nøkkel i
mode="webhook", sender ThunderPhone en blokkerende
telephony.incoming / web.incoming-forespørsel til din
eldre webhook-URL
og venter på et konfigurasjonssvar: opptil 13 sekunder for et
telefonanrop, inkludert ett nytt forsøk, og opptil 10 sekunder for en
widgetøkt (se Tidsavbrudd og nye forsøk). Bruk denne
utvekslingen til å velge prompt, stemme og verktøy dynamisk for hvert anrop —
se veiledningen for dynamisk samtalekonfigurasjon
for det fullstendige mønsteret.
Tidsavbrudd og nye forsøk
Svar raskt: innringeren venter på linjen mens du avgjør.
For en telefonsamtale (telephony.incoming):
- Det første forsøket kan ta opptil 10 sekunder.
- Hvis det får tidsavbrudd, ikke kan koble til eller får et
5xx-,408- eller429-svar, prøver ThunderPhone én gang til. Det nye forsøket får det som gjenstår av et 13-sekunders budsjett for hele utvekslingen: omtrent 3 sekunder etter et første forsøk som fikk tidsavbrudd, og nesten alle 13 etter en rask feil. Det gjøres ikke et nytt forsøk hvis det ville gjenstått mindre enn 2 sekunder. - Ethvert annet
4xx-svar, et svar over størrelsesgrensen, eller et2xx-svar med en brødtekst som ikke er en gyldig konfigurasjon, er endelig og prøves ikke på nytt. - Begge forsøkene sender den samme brødteksten, den samme
X-ThunderPhone-Signature-headeren og den sammeX-ThunderPhone-Idempotency-Key-headeren (telephony-incoming-<call_id>), slik at en håndterer med sideeffekter kan gjenkjenne det nye forsøket.
Det finnes ingen reserve-stemmeagent. Hvis ingen brukbar konfigurasjon kommer frem, blir
samtalen avsluttet med én gang i stedet for å la innringeren vente på en stille
linje. Den vises i samtalehistorikken som en mislykket, ikke-fakturert samtale med
end_reason inbound_config_webhook_failed, og den vanlige
telephony.complete-hendelsen sendes.
En widgetøkt (web.incoming) får ett enkelt forsøk på opptil 10
sekunder. Hvis det mislykkes, mislykkes øktforespørselen med 502 eller 422.
Forespørselsnyttelast
For telefonanrop (telephony.incoming):
{
"type": "telephony.incoming",
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
}
}| Felt | Type | Beskrivelse |
|---|---|---|
call_id | integer | Anrops-ID — stabil på tvers av alle hendelser for dette anropet |
from_number | string | E.164-nummeret til innringeren |
to_number | string | E.164-destinasjon (ett av ThunderPhone-numrene dine) |
For webwidget-økter (web.incoming) identifiserer data
innbyggingssiden i stedet for telefonnumre:
{
"type": "web.incoming",
"data": {
"call_id": 987654322,
"origin_domain": "https://example.com",
"publishable_key_prefix": "pk_live_a1b2"
}
}| Felt | Type | Beskrivelse |
|---|---|---|
call_id | heltall | Anrops-ID |
origin_domain | streng | Sidens opphav som er vert for widgeten, slik nettleseren sendte det. En klient som ikke er en nettleser, kan sende en hvilken som helst verdi, så ikke behandle den som bevis på hvor forespørselen kom fra |
publishable_key_prefix | streng | De første tegnene i den publiserbare nøkkelen som åpnet økten |
language, primary_language | streng | Finnes når widgetøkten ba om en språkoverstyring |
voice | streng | Finnes når widgetøkten ba om en stemmeoverstyring |
website_context | streng | Finnes når widgeten sendte sidekontekst per økt |
Overstyringsfeltene (language, voice, website_context og eventuelle
nettleser-variables) kommer bare når den publiserbare nøkkelen tillater dem i
sine nettleseroverstyringer. En
øktforespørsel som inneholder en overstyring nøkkelen ikke tillater, avvises før
denne webhooken kalles.
Svarskjema
Returner et JSON-objekt som beskriver stemmeagentkonfigurasjonen for dette anropet.
prompt og voice er påkrevd; alt annet er valgfritt.
{
"prompt": "You are a helpful booking assistant for Acme Restaurant.",
"voice": "john",
"product": "spark",
"background_track": null,
"tools": []
}| Felt | Type | Påkrevd | Beskrivelse |
|---|---|---|---|
prompt | streng | ja | Systeminstruks som styrer agenten |
voice | streng | ja | Stemme-ID fra GET /v1/voices, for eksempel john. voice_name godtas som et alias. Ukjente stemmer feiler valideringen og avviser samtalen |
product | streng | nei | Standard er spark. Tillatt: spark, bolt, storm-base, storm-base-with-ack, storm-extra, storm-extra-with-ack |
thinking_level | streng | nei | minimal, base (standard) eller extra. Overstyres for Storm-produkter: storm-extra* tvinger extra, andre storm-* tvinger base |
audio_context_mode | streng | nei | full (standard) eller reduced |
watchdog_enabled | boolsk | nei | Aktiver overvåking for denne samtalen. Standard er false |
additional_audio_context | boolsk | null | nei | Ta med de siste rundene med innringerlyd i stedet for bare den nyeste runden, noe som forbedrer rettelser og datainnsamling med mye staving/tall, med en liten økning i forsinkelse/kostnad. Er på som standard for innkommende økter og av for utgående telefonsamtaler; null beholder standardverdien |
storm_feedback_mode | streng | nei | none, acknowledgement (standard) eller tick |
language | streng | nei | Kortform for primary_language |
primary_language | streng | nei | Språkkode, normalisert (standard en). Koder som ikke kan løses, avviser samtalen |
has_additional_languages | boolsk | nei | Standard er false |
additional_languages | matrise av strenger | nei | Ekstra språk agenten kan bytte til |
native_voice_switching | boolsk | nei | Standard er false. Når samtalen bytter til et annet språk, bytt til en stemme som har språket som morsmål (matchet etter kjønn) i stedet for å beholde den konfigurerte stemmen |
language_switching | streng | null | nei | ask_first (standard) eller follow_caller; null er det samme som ask_first. Angir hvordan en flerspråklig agent håndterer at den andre personen snakker et annet av språkene dens. ask_first bytter straks når personen ber om språket eller sier en hel setning på det, og spør om dere skal fortsette på det etter noe kort, som et «ja». follow_caller bytter til ethvert konfigurert språk personen snakker, selv bare ett ord. Gjelder bare når additional_languages er angitt |
background_track | streng | null | nei | ID for bakgrunnslyd eller null |
acknowledgement_prompt_mode | streng | nei | auto (standard) eller manual (Storm-with-ack-produkter) |
acknowledgement_prompt | streng | nei | Brukes når acknowledgement_prompt_mode="manual" |
silence_interval_seconds | heltall | null | nei | 5–120. Sekunder med stillhet fra innringeren før en oppfølging |
silence_max_checkins | heltall | null | nei | 1–10 |
silence_checkins_enabled | boolsk | nei | Standard er true |
connect_tone_enabled | boolsk | nei | Standard er false |
pronunciation_dicts | boolsk | null | nei | Standard er false; null er det samme som false. Når verdien er true, bruker samtalens Cartesia-tale ThunderPhones uttaleordbok for samtalens språk, der en finnes. Fungerer for telefon- og widget-samtaler. Gjelder bare ThunderPhones egne Cartesia-stemmer, ikke stemmer på din egen Cartesia API-nøkkel. Brukes ikke med agent_id, der den lagrede agentens innstillinger gjelder |
voicemail_action | streng | nei | prompt (standard), hangup eller message |
voicemail_message | streng | nei | Brukes når voicemail_action="message" |
agent_name | streng | nei | Visningsnavn som rapporteres til instrumentpaneler og widgeten |
org_name | streng | nei | Organisasjonens visningsnavn for agentens persona |
tools | matrise | nei | Innebygde skjemaer for funksjonsverktøy (se Funksjonsverktøy) |
transfer_destinations | matrise av objekter | null | nei | Opptil 50 {name, number}-oppføringer som denne samtalen kan overføres til, validert på samme måte som en agents overføringsnumre. Agenten overfører etter navn og bare til disse; [] betyr ingen overføringer. Utelat feltet eller send null for å la agenten overføre til et nummer den velger |
timezone | streng | nei | IANA-tidssone for denne samtalen, for eksempel America/Chicago. Agenten oppgir datoen og klokkeslettet i denne, foran agentens egen tidssone. Tillatt også sammen med agent_id. Verdier som ikke er IANA-tidssoner, ignoreres. Se Angi tidssone per samtale |
call_id | heltall | nei | Valgfritt ekko av forespørselens samtale-ID; ignoreres |
Fordi prompt og voice er obligatoriske, avviser retur av {} eller et
svar som ikke består valideringen anropet: en telefonsamtale avsluttes
som beskrevet i Tidsavbrudd og nye forsøk, og en
forespørsel om widgetøkt mislykkes med 422. Det finnes ingen reserve for
statisk agent på denne banen (et nummer eller en nøkkel i webhook-modus har ingen tildelt
agent).
Grense for responsstørrelse
Eksempelbehandler
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 funksjonsverktøy
Legg ved verktøy slik at AI-en kan kalle API-ene dine midt i samtalen:
{
"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"
}
}
}
]
}Hurtigoversikt over produktnivåer
| Produkt | Forsinkelse | Resonnering | Bekreftelse |
|---|---|---|---|
spark | Lavest | Grunnleggende | — |
bolt | Lav | Forbedret | — |
storm-base | Middels | Sterk | — |
storm-base-with-ack | Middels | Sterk | Automatisk utfylling mens den tenker |
storm-extra | Høyere | Dyp | — |
storm-extra-with-ack | Høyere | Dyp | Automatisk utfylling mens den tenker |
Relatert
Den ikke-blokkerende hendelsen ved avslutning av samtale.
Fullstendig JSON-skjema for tools[] og den signerte endepunktkontrakten.
Abonner flere URL-er på telephony.incoming / web.incoming.
Mønstre for ledetekster, verktøy og A/B-tester per innringer.