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

Developer cookbook

Bygg en verktygsintegration (API)

Låt din röstagent anropa dina API:er mitt i ett samtal – sök i en databas, skapa ett ärende eller slå upp en beställning.

En verktygsintegration är en återanvändbar HTTP-slutpunkt som en röstagent kan anropa under ett samtal. Du ger ThunderPhone en JSON-schemabeskrivning av verktyget tillsammans med en slutpunkts-URL; röstagenten avgör när den ska anropa det utifrån samtalet, och ThunderPhone skickar den utgående HTTP-begäran från sina servrar och returnerar svaret till röstagenten.

Den här guiden visar hur du bygger ett verktyg för väderuppslagning från början till slut.

Verktygets uppbyggnad

Två delar:

  1. Schemat — en OpenAI-liknande funktionsdefinition ({type: "function", function: {name, description, parameters}}) som talar om för LLM:en vad verktyget gör och vilka argument det tar.
  2. Slutpunkten — URL:en som ThunderPhones servrar anropar när LLM:en beslutar att använda verktyget. Begäran är en JSON-POST med de argument som LLM:en har valt som brödtext.

1. Välj redigeringsmetod

Instrumentpanel

Öppna Anslutningar → API:er, skapa eller redigera API-anslutningen, växla parameterredigeraren till JSON och lägg till formatet där.

Integrations-API

Skapa specifikationen med POST /v1/integrations, eller uppdatera den med PATCH /v1/integrations/{id}.

Båda vägarna skapar en sparad integration. Koppla integrationen till röstagenten efter att du har sparat den. Agents API har inget skrivbart inbyggt fält för tools. Den här guiden använder integrations-API-vägen.

2. Skapa integrationen

curl -X POST https://api.thunderphone.com/v1/integrations \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Weather API",
    "spec": {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Return the current weather for a zip code.",
        "parameters": {
          "type": "object",
          "properties": {
            "zip": { "type": "string", "description": "5-digit US ZIP code" }
          },
          "required": ["zip"]
        }
      }
    },
    "endpoint_url":    "https://api.example.com/weather",
    "endpoint_method": "GET",
    "headers": [
      { "key": "X-Api-Key", "value": "your-provider-key" }
    ]
  }'

Spara det returnerade id-värdet (ett UUID). Headervärden är endast skrivbara: svar listar endast headernamn, och en senare PATCH kan skicka en header utan ett value för att behålla det lagrade värdet.

Ange format: "email" för adressparametrar

En parameter som tar emot en e-postadress bör ange det i sitt schema:

"email": { "type": "string", "format": "email", "description": "The caller's email address" }

format är mer än en ledtråd. För ett tolkat e-postschema, innan din slutpunkt anropas, trimmar ThunderPhone värdet, gör domänen till gemener, omvandlar de fristående engelska orden at, dot, underscore, dash och hyphen till sina tecken och tar bort blanksteg direkt runt @, ., _ och -. Orden har samma betydelse oavsett om transkriptet redan innehåller ett bokstavligt @: "john dot smith at gmail dot com" blir john.smith@gmail.com.

Alla andra interna blanksteg avvisas i stället för att tyst slås ihop. Talade avgränsarord stöds endast på engelska; andra språk eller okända former med blanksteg avvisas. Giltiga internationaliserade domäner och SMTPUTF8-lokala delar godtas. Punycode-indata förblir punycode och Unicode-domänindata förblir Unicode efter parsernormalisering, så ditt API tar emot den konventionella representation som uppringaren angav. Om det slutliga värdet är ogiltigt anropas verktyget inte. Agenten får invalid_email_argument, som instruerar den att bekräfta stavningen med uppringaren och skicka om den bokstavliga adressen.

En utelämnad valfri e-postadress lämnas orörd. null, en tom sträng eller en sträng som endast innehåller blanksteg lämnas också orörd när egenskapen är valfri eller kan vara null; samma värden avvisas för en obligatorisk e-postadress som inte kan vara null.

Lokala schemareferenser som #/$defs/email och #/definitions/email, samt anyOf, oneOf och allOf, granskas med cykel- och djupgränser. En icke-lokal eller olösbar $ref är en känd begränsning i valideringen och skickas vidare oförändrad, liksom ett anrop vars verktygsögonblicksbild saknar ett användbart schema. Håll e-postscheman lokala när du behöver att spärren tillämpas.

Parametrar utan ett framtvingat e-postformat skickas vidare exakt så som modellen producerade dem.

Format som stöds är date-time, time, date, duration, email, hostname, ipv4, ipv6 och uuid; endast email normaliseras och framtvingas i dag.

3. Testa slutpunkten i sandboxmiljön

Innan du kopplar integrationen till en agent skickar du en signerad begäran från ThunderPhones servrar för att bekräfta anslutningen:

curl -X POST https://api.thunderphone.com/v1/integrations/test-request \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":    "https://api.example.com/weather?zip=94110",
    "method": "GET",
    "headers": { "X-Api-Key": "your-provider-key" }
  }'
Response
{
  "ok": true,
  "status": 200,
  "elapsed_ms": 187,
  "response_headers": { "content-type": "application/json" },
  "response_preview": "{\"temperature_f\": 64, ...}"
}

Det här testet förstärker också ThunderPhones SSRF-skydd — begäranden till localhost eller privata IP-intervall returnerar 400 code=url_not_allowed.

4. Koppla integrationen till en agent

Koppla via integration_ids när du skapar eller uppdaterar en agent:

curl -X PATCH https://api.thunderphone.com/v1/agents/12 \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "integration_ids": ["f9b5a1a4-..."]
  }'

Du kan koppla många integrationer till en agent. Agentens instruktion kan referera till dem med namn — ”använd get_weather när den som ringer frågar om väderförhållanden” — eller så kan den upptäcka dem implicit utifrån schemabeskrivningarna.

5. Implementera slutpunkten

När agenten anropar verktyget skickar ThunderPhone en signerad POST-begäran till din endpoint_url:

POST /weather HTTP/1.1
Host: api.example.com
X-Api-Key: your-provider-key
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<HMAC-SHA256 hex>
X-ThunderPhone-Signature: <HMAC-SHA256 hex>
X-ThunderPhone-Call-ID: 987654321
Content-Type: application/json

{"zip": "94110"}

Din server svarar med JSON som skickas tillbaka till LLM:en:

{"temperature_f": 64, "condition": "Partly cloudy", "wind_mph": 8}

LLM:en tar emot svaret och säger en sammanfattning på naturligt språk till den som ringer.

6. Testa flödet

Kör en mikrofonsession mot agenten och ställ frågan som verktyget hanterar (”Hur är vädret i 94110?”). Samtalets transkription visar hela tur- och returflödet:

{
  "call_id": 987654321,
  "transcripts": [
    { "role": "user",
      "content": "What's the weather in 94110?" },
    { "role": "tool_call",
      "content": "{\"tool_call\": \"get_weather\", \"arguments\": {\"zip\": \"94110\"}}" },
    { "role": "tool_response",
      "content": "{\"tool_name\": \"get_weather\", \"response\": {\"temperature_f\": 64, \"condition\": \"Partly cloudy\"}}" },
    { "role": "agent",
      "content": "It's 64 degrees and partly cloudy." }
  ]
}

Du kan hämta detta via GET /v1/calls/{call_id}/transcript; den råa händelseströmmen (med tidsangivelser och ljudförskjutningar per post) finns på GET /v1/calls/{call_id}/history.

I samtalslistor och analysvyer rapporterar ThunderPhone nu testsamtal separat från verklig trafik. Browser Talk-tester och simuleringar kan återanvända ett riktigt telefonnummer och kan se ut som inkommande samtal, men de räknas inte in i produktionssamtalens totaler, betyg eller incidentmått.

Vanliga fallgropar

Agenten anropar aldrig verktyget

LLM:en avgör utifrån verktygets beskrivning. Om den som ringer ställer en fråga som inte matchar beskrivningen anropar modellen inte verktyget. Förtydliga beskrivningen (lägg till vanliga synonymer och formuleringar) eller nämn det uttryckligen i agentens instruktion (”När den som ringer frågar om vädret, använd get_weather.”).

Verktyget returnerar för mycket data

Svar över 6 kB trunkeras i förhandsvisningen av transkriptionen. Returnera bara de fält som LLM:en behöver — inte hela raden.

Tidsgränser

Verktygsslutpunkter har en standardtidsgräns på 10 sekunder. Om du behöver längre tid hanterar du det asynkront: returnera {"status": "pending", "request_id": "..."} och visa resultatet via ett separat verktygsanrop.

Versionshantering

Varje PATCH av en integration skapar en ny revision. Kontrollera GET /v1/integrations/{id}/versions för att se vem som ändrade vad. Om du förstör ett verktygs schema kan du återställa manuellt genom att PATCH:a tillbaka en äldre ögonblicksbild.


Nästa steg