ThunderPhone 2.0 är här.Kom igång på egen hand, från 2 ¢/min.Läs lanseringsnyheten

Webhooks

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 webbwidgets-session 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 på ett konfigurationssvar: upp till 13 sekunder för ett telefonsamtal, inklusive ett återförsök, och upp till 10 sekunder för en widgetsession (se Timeouter och återförsök). Använd detta utbyte för att dynamiskt välja en prompt, röst och verktyg per samtal — se guiden för dynamisk samtalskonfiguration för flödet från början till slut.

Tidsgränser och återförsök

Svara snabbt: uppringaren väntar kvar i samtalet medan du bestämmer dig.

För ett telefonsamtal (telephony.incoming):

  • Det första försöket kan ta upp till 10 sekunder.
  • Om det får en timeout, inte kan ansluta eller får ett svar med 5xx, 408 eller 429 försöker ThunderPhone en gång till. Återförsöket får det som återstår av en budget på 13 sekunder för hela utbytet: cirka 3 sekunder efter ett första försök som fick timeout, nästan alla 13 efter ett snabbt fel. Inget återförsök görs när mindre än 2 sekunder skulle återstå.
  • Alla andra 4xx-svar, ett svar över storleksgränsen eller ett 2xx vars brödtext inte är en giltig konfiguration är slutgiltiga och försöks inte igen.
  • Båda försöken skickar samma brödtext, samma X-ThunderPhone-Signature och samma X-ThunderPhone-Idempotency-Key-header (telephony-incoming-<call_id>), så att en hanterare med sidoeffekter kan känna igen återförsöket.

Det finns ingen reservröstagent. Om ingen användbar konfiguration kommer fram avslutas samtalet direkt i stället för att låta uppringaren vänta i en tyst linje. Det visas i samtalshistoriken som ett misslyckat, ej debiterat samtal med end_reason inbound_config_webhook_failed, och det vanliga telephony.complete-eventet skickas.

En widgetsessions (web.incoming) får ett enda försök på upp till 10 sekunder. Om det misslyckas misslyckas sessionsbegäran med 502 eller 422.

Begärans nyttolast

För telefonsamtal (telephony.incoming):

{
  "type": "telephony.incoming",
  "data": {
    "call_id":     987654321,
    "from_number": "+14155550199",
    "to_number":   "+15551234567"
  }
}
FältTypBeskrivning
call_idintegerSamtals-ID — stabilt i alla händelser för detta samtal
from_numberstringE.164-nummer för den som ringer
to_numberstringE.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ältTypBeskrivning
call_idheltalSamtals-id
origin_domainsträngSidans ursprung som är värd för widgeten, enligt vad webbläsaren skickade. En klient som inte är en webbläsare kan skicka vilket värde som helst, så behandla det inte som bevis på var begäran kom ifrån
publishable_key_prefixsträngDe första tecknen i den publicerbara nyckel som öppnade sessionen
language, primary_languagesträngFinns när widgetsessionen begärde en språköverstyrning
voicesträngFinns när widgetsessionen begärde en röstöverstyrning
website_contextsträngFinns när widgeten skickade sidkontext per session

Överstyrningsfälten (language, voice, website_context och eventuella variables från webbläsaren) skickas endast när den publicerbara nyckeln tillåter dem i sina webbläsaröverstyrningar. En sessionsbegäran som innehåller en överstyrning som nyckeln inte tillåter avvisas innan denna webhook anropas.


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ältTypKrävsBeskrivning
promptsträngjaSystemprompt som styr agenten
voicesträngjaRöst-id från GET /v1/voices, t.ex. john. voice_name accepteras som alias. Okända röster klarar inte valideringen och samtalet avvisas
productsträngnejStandardvärdet är spark. Tillåtna värden: spark, bolt, storm-base, storm-base-with-ack, storm-extra, storm-extra-with-ack
thinking_levelsträngnejminimal, base (standard) eller extra. Åsidosätts för Storm-produkter: storm-extra* tvingar extra, andra storm-* tvingar base
audio_context_modesträngnejfull (standard) eller reduced
watchdog_enabledboolesknejAktivera övervakning för det här samtalet. Standardvärdet är false
additional_audio_contextboolesk | nullnejInkludera de senaste turerna av uppringarens ljud i stället för bara den senaste turen, vilket förbättrar korrigeringar och insamling av uppgifter med mycket stavning eller siffror med en liten ökning av latens och kostnad. Är aktiverat som standard för inkommande sessioner och inaktiverat för utgående telefonsamtal; null behåller standardvärdet
storm_feedback_modesträngnejnone, acknowledgement (standard) eller tick. Anger även vad uppringaren hör medan ljudmodellerna bearbetar en tur när audio_tier är hearing
languagesträngnejFörkortning för primary_language
primary_languagesträngnejSpråkkod, normaliserad (standardvärde en). Koder som inte kan lösas upp avvisar samtalet
has_additional_languagesboolesknejStandardvärdet är false
additional_languagesmatris av strängarnejYtterligare språk som agenten kan växla till
native_voice_switchingboolesknejStandardvärdet är false. När samtalet växlar till ett annat språk byter du till en röst med det språket som modersmål (matchad efter kön) i stället för att behålla den konfigurerade rösten
language_switchingsträng | nullnejask_first (standard) eller follow_caller; null är samma som ask_first. Anger hur en flerspråkig agent hanterar när den andra personen talar ett annat av agentens språk. ask_first växlar direkt när personen ber om språket eller säger en hel mening på det, och frågar om den ska fortsätta på språket efter något kort, som ett "ja". follow_caller växlar till vilket konfigurerat språk personen än talar, även vid ett enda ord. Gäller endast när additional_languages är angivet
audio_tiersträng | nullnejEndast product: "bolt". standard (standard; "" och null betyder samma sak) kör tre transkriberare (samtal som behandlar PHI använder endast transkriberare som omfattas av BAA). lean sänker till en transkriberare, som hör fel oftare. hearing uppgraderar till standard plus en kraftfull ensemble av ljudmodeller som lyssnar på uppringarens ljud under vissa turer, vilka tar omkring 1–2 sekunder längre. Andra produkter ignorerar detta
endpointingsträng | nullnejfaster (200 ms), fast (300 ms), normal (500 ms, standard) eller patient (800 ms); null är samma som normal. Anger hur länge en Bolt v2-agent väntar efter att uppringaren slutat tala innan modellen ombeds om ett svar. Kortare väntetider kan svara tidigare men skickar fler begäranden som uppringarens nästa ord gör inaktuella, vilket ökar modellkostnaden. Andra produkter ignorerar detta
eagernesssträng | nullnejFöråldrat: namnet på endpointing fram till den 19 oktober 2026, läses fortfarande. Ignoreras när endpointing är angivet
intelligencesträng | nullnejexpress, lite eller standard (standard); null är samma som standard. Intelligensnivån för en bolt-agent i Bolt v2-pipelinen: vilken modell som skriver dess svar. express är ~600 ms till första ordet, billigare än Standard och följer instruktioner minst tillförlitligt; lite är ~900 ms, billigast och mindre tillförlitlig; standard är ~900 ms och bäst på att följa instruktioner. Express- och Lite-svar som bara skulle anropa ett verktyg skickas till Standard-modellen. Andra produkter ignorerar detta
background_tracksträng | nullnejId för bakgrundsljud eller null
acknowledgement_prompt_modesträngnejauto (standard) eller manual (Storm-with-ack-produkter)
acknowledgement_promptsträngnejAnvänds när acknowledgement_prompt_mode="manual"
silence_interval_secondsheltal | nullnej5–120. Sekunder av tystnad från uppringaren före en avstämning
silence_max_checkinsheltal | nullnej1–10
silence_checkins_enabledboolesknejStandardvärdet är true
silence_final_actionsträngnejEfter den sista avstämningen: hang_up (standard) eller transfer till silence_transfer_destination
silence_transfer_destinationsträngnejNamnet på en post i den här konfigurationens transfer_destinations; krävs med transfer. Utan transfer_destinations kan överföringen inte genomföras och agenten lägger på
silence_final_messagesträngnej≤ 300 tecken, läses upp som skrivet; "" (standard) använder standardraden
connect_tone_enabledboolesknejStandardvärdet är false
pronunciation_dictsboolesk | nullnejStandardvärdet är false; null är samma som false. När värdet är true använder samtalets Cartesia-tal ThunderPhones uttalslexikon för samtalets språk, där ett finns. Fungerar för telefon- och widget-samtal. Gäller endast ThunderPhones egna Cartesia-röster, inte röster på din egen Cartesia API-nyckel. Används inte med agent_id, där den sparade agentens inställningar gäller
voicemail_actionsträngnejprompt (standard), hangup eller message
voicemail_messagesträngnejAnvänds när voicemail_action="message"
agent_namesträngnejVisningsnamn som rapporteras till instrumentpaneler och widgeten
org_namesträngnejOrganisationens visningsnamn för agentens persona
toolsmatrisnejInfogade scheman för funktionsverktyg (se Funktionsverktyg)
transfer_destinationsmatris av objekt | nullnejUpp till 50 poster med {name, number} som det här samtalet kan överföras till, validerade som en agents överföringsnummer. Agenten överför efter namn och endast till dessa; [] betyder inga överföringar. Utelämna det eller skicka null för att låta agenten överföra till ett nummer som den väljer
timezonesträngnejIANA-tidszon för det här samtalet, till exempel America/Chicago. Agenten anger datum och tid i den, före agentens egen tidszon. Tillåts även tillsammans med agent_id. Värden som inte är IANA-zoner ignoreras. Se Ange tidszonen per samtal
call_idheltalnejValfritt eko av begärans samtals-id; ignoreras

Eftersom prompt och voice krävs avvisas samtalet om du returnerar {} eller ett svar som inte klarar valideringen: ett telefonsamtal avslutas enligt beskrivningen i Timeouts och återförsök, och en begäran om en widgetsession misslyckas med 422. Det finns ingen reservlösning med statisk agent 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

Python (FastAPI)
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 {}
Node.js (Express)
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

ProduktLatensResonemangBekräftelse
sparkLägstGrundläggande—
boltLågFörbättrat—
storm-baseMedelStarkt—
storm-base-with-ackMedelStarktAutomatisk utfyllnad medan den tänker
storm-extraHögreDjupgående—
storm-extra-with-ackHögreDjupgåendeAutomatisk utfyllnad medan den tänker

Relaterat