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

Function Tools

Funktionsverktyg

Ge dina AI-agenter funktionsverktyg som anropar externa API:er mitt i samtalet — hämta kunddata, boka tider, uppdatera poster — med typade parametrar.

Funktionsverktyg gör det möjligt för dina röstagenter att anropa externa API:er under telefonsamtal. Använd dem för att slå upp kunddata, kontrollera tillgänglighet, boka tider eller utföra valfri åtgärd som din backend stöder.

Så fungerar det

  1. Du definierar verktyg med ett schema (vilka argument verktyget accepterar)
  2. Du tillhandahåller en endpoint-konfiguration (där ThunderPhone anropar ditt API) — eller utelämnar den för att ta emot verktygsanrop på din organisations webhook
  3. Under ett samtal avgör AI:n när ett verktyg ska användas baserat på konversationen
  4. ThunderPhone anropar din endpoint med verktygsargumenten
  5. Ditt API-svar skickas tillbaka till AI:n för att fortsätta konversationen
FunktionVar det körsKonfiguration
Inbyggda verktygThunderPhonePromptinstruktioner; vissa verktyg kräver även en agentinställning
AppanslutningarThunderPhone och den anslutna leverantörenAnslut kontot och koppla godkända åtgärder
API-anslutningar och funktionsverktygDitt HTTP APIDefiniera endpoint och schema, eller ta emot funktionsanrop via en webhook
MCP-servrarEn fjärransluten MCP-serverLägg till servern, identifiera dess verktyg och koppla den till agenten

Verktygsschema

Varje verktyg följer den här strukturen:

{
  "type": "function",
  "function": {
    "name": "search_appointments",
    "description": "Find available appointment slots for a given date",
    "parameters": {
      "type": "object",
      "properties": {
        "date": {
          "type": "string",
          "description": "Date in YYYY-MM-DD format"
        },
        "service": {
          "type": "string",
          "description": "Type of service (e.g., 'consultation', 'follow-up')"
        }
      },
      "required": ["date"]
    }
  },
  "endpoint": {
    "url": "https://api.example.com/appointments/search",
    "method": "POST",
    "headers": {
      "X-Api-Key": "your-api-key"
    }
  },
  "timeout": 120
}

Verktygskonfiguration

FältTypObligatorisktBeskrivning
timeoutnumberNejLängsta körtid i sekunder (standard: 20, högsta: 180)

Funktionsdefinition

FältTypObligatorisktBeskrivning
namestringJaUnik identifierare för verktyget
descriptionstringJaFörklarar för AI:n när verktyget ska användas
parametersobjectJaJSON Schema för verktygsargument

Endpoint-konfiguration

FältTypObligatorisktBeskrivning
urlstringJaURL:en för din API-endpoint
methodstringNejHTTP-metod (standard: POST)
headersobjectNejAnpassade headers som ska inkluderas

Två anropsvägar

Vilken begäran din server tar emot beror på om verktyget har en endpoint:

Verktyg med endpointVerktyg utan endpoint
Vart begäran skickasDirekt till endpoint.urlOrganisationens äldre webhook-URL
BrödtextValt begärandeformat (rena argument som standard)telephony.tool / web.tool-kuvert
RubrikerDina endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature
SigneringsnyckelOrganisationens webhook-hemlighetOrganisationens webhook-hemlighet

Båda vägarna är blockerande — AI:n väntar mitt i meningen på resultatet. Standardtidsgränsen är 20 s; ange verktygets timeout på toppnivå för att tillåta längre körning, upp till plattformens högsta gräns på 180 s. Håll hanterare snabba. En blandning fungerar bra: i ett samtal vars organisation har en webhook-URL anropas verktyg med en endpoint direkt, medan övriga använder webhooken som reserv.

Direktanrop till ändpunkter

När AI:n anropar ett verktyg som har en endpoint skickar ThunderPhone en begäran till din URL:

Begärandehuvuden

POST /appointments/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-ThunderPhone-Signature-V2: t=1767225600,v1=5f2c...
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key

Anpassade HTTP-huvuden från dina endpoint.headers inkluderas alltid ordagrant, tillsammans med dessa ThunderPhone-namnområdesindelade HTTP-huvuden:

  • X-ThunderPhone-Signature-V2 — tidsstämplad HMAC-SHA256 för metod, sökväg, frågesträng och begärandetext, med din organisations webhook-hemlighet som nyckel. Verifiera denna och avvisa begäranden som är äldre än 5 minuter. Se Verifiera webhook-signaturer
  • X-ThunderPhone-Signature — äldre HMAC-SHA256 för de exakta byten i begärandetexten, med samma nyckel, som fortfarande skickas för befintliga verifierare
  • X-ThunderPhone-Call-ID — Det aktuella samtals-ID:t

Content-Type: application/json anges om inte dina endpoint.headers åsidosätter det — en anpassad Content-Type gäller.

Begärandeformat

Varje API-anslutning har en inställning för request_format. Välj vapi eller retell när ett importerat verktyg ska fortsätta anropa en endpoint som är byggd för den plattformen. Nya och befintliga anslutningar använder standard som standard.

För POST / PUT / PATCH serialiserar ThunderPhone det valda kuvertet kanoniskt (sorterade nycklar, kompakta avgränsare). Båda signaturerna omfattar exakt dessa byte.

Standard

standard skickar endast verktygsargumenten, utan omslutning. Detta är standardläget och bevarar ThunderPhones ursprungliga begärandetext byte för byte:

{"date":"2025-01-02","service":"consultation"}

Vapi

vapi skickar Vapis tool-calls-servermeddelande i det format som Vapi dokumenterar. call.id är ThunderPhone-anrops-ID:t. Verktygsanrops- id är unikt för anropet och återanvänds för att matcha endpointens resultat. Anropet visas i både toolCallList och toolWithToolCallList, med function.arguments som ett JSON-objekt:

{
  "message": {
    "type": "tool-calls",
    "timestamp": 1725000000123,
    "call": { "id": "987654321" },
    "toolCallList": [
      {
        "id": "call_00112233445566778899aabbccddeeff",
        "type": "function",
        "function": {
          "name": "search_appointments",
          "arguments": { "date": "2025-01-02", "service": "consultation" }
        }
      }
    ],
    "toolWithToolCallList": [
      {
        "type": "function",
        "function": { "name": "search_appointments" },
        "toolCall": {
          "id": "call_00112233445566778899aabbccddeeff",
          "type": "function",
          "function": {
            "name": "search_appointments",
            "arguments": { "date": "2025-01-02", "service": "consultation" }
          }
        }
      }
    ]
  }
}

Vapi-specifika fält som assistant, artifact och hela call-objektet skickas inte.

Returnera Vapis svarsformat med matchande verktygsanrops-ID:

{
  "results": [
    {
      "toolCallId": "call_00112233445566778899aabbccddeeff",
      "result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
    }
  ]
}

ThunderPhone ger AI:n den uppackade result-strängen. Det accepterar även ett JSON-värde för kompatibilitet med endpoints som redan returnerar ett sådant. Ett error i stället för result returneras till AI:n som ett verktygsfel. Om endpointen returnerar ett felaktigt eller omatchat Vapi-kuvert loggar ThunderPhone en varning och faller tillbaka till sin standardtolkning av svar i stället för att misslyckas med anropet.

Retell

retell skickar begäran som dokumenteras för en anpassad Retell- funktion:

{
  "name": "search_appointments",
  "call": { "call_id": "987654321" },
  "args": { "date": "2025-01-02", "service": "consultation" }
}

call-objektet innehåller endast call_id. ThunderPhone inkluderar inte Retell-specifika fält som det inte kan ange korrekt vid utskick, till exempel agent_id, transcript, transcript_object, metadata eller dynamiska variabler. Retell accepterar valfri svarskropp; ThunderPhone skickar den vidare med standardhanteringen av svar.

För GET / DELETE skickas argumenten som frågeparametrar och brödtexten är tom oavsett request_format. V2-signaturen omfattar frågesträngen som den skickas; den äldre signaturen beräknas över den tomma bytesträngen. Se Verifiera webhook-signaturer.

Svar

Returnera ett JSON-svar med verktygsresultatet:

{
  "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
  "timezone": "America/Los_Angeles"
}

Svaret formateras och skickas till AI:n för att fortsätta konversationen. Icke-JSON-svar omsluts som {"data": "<text>"}; tidsgränser och anslutningsfel rapporteras till AI:n som fel, så att röstagenten kan be om ursäkt och gå vidare i stället för att fastna.

Dirigering i webhook-läge

Verktyg utan en endpoint skickas till din organisations äldre webhook-URL som en signerad begäran av typen telephony.tool (telefonsamtal) eller web.tool (webbsamtal). Till skillnad från revisionsnotiserna som levereras till webhookändpunkter efter körning är denna begäran körningen — ditt HTTP-svar är verktygsresultatet.

{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}

web.tool innehåller origin_domain i stället för from_number / to_number. Svara med verktygsresultatet som JSON — samma svarskontrakt som för direkta endpoint-anrop. Begäran signeras med organisationens webhook-hemlighet, med båda signaturhuvudena, precis som alla andra webhooks.


Signaturverifiering

Direkta verktygsanrop signeras på samma sätt som webhooks, med din organisations webhook-hemlighet som nyckel:

  • X-ThunderPhone-Signature-V2 signerar tidsstämpeln, metoden, sökvägen och frågesträngen som de skickas, samt de exakta byte i brödtexten. Avvisa begäranden vars tidsstämpel skiljer sig mer än 5 minuter från din klocka.
  • X-ThunderPhone-Signature (äldre) signerar endast byte i brödtexten, den tomma bytesträngen för GET / DELETE. Den skickas fortfarande, oförändrad.

SDK-hjälpfunktionerna kontrollerar V2-huvudet, inklusive 5-minutersfönstret:

Python
import json
 
from fastapi import FastAPI, HTTPException, Request
from thunderphone import verify_signature
 
app = FastAPI()
 
@app.post("/appointments/search")
async def search_appointments(request: Request):
    body = await request.body()
    query = request.scope.get("query_string", b"").decode("latin-1")
    path = request.url.path + (f"?{query}" if query else "")
 
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=path,
        body=body,
    ):
        raise HTTPException(status_code=401)
 
    data = json.loads(body)
    date = data["date"]
 
    # Look up availability
    slots = await get_available_slots(date)
 
    return {"available_slots": slots}
Node.js
import express from "express";
import { verifySignature } from "@thunderphone/sdk";
 
app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
  const ok = verifySignature({
    header: req.header('X-ThunderPhone-Signature-V2'),
    secret: WEBHOOK_SECRET,
    method: req.method,
    path: req.originalUrl,
    body: req.body,
  });
  if (!ok) return res.status(401).send('Invalid signature');
 
  const { date, service } = JSON.parse(req.body);
 
  // Look up availability
  const slots = getAvailableSlots(date, service);
 
  res.json({ available_slots: slots });
});

Fullständiga recept — den signerade strängen, ett genomarbetat exempel, kod utan SDK, fallet GET / DELETE och förbehållet om ingen hemlighet — finns i Verifiera webhook-signaturer.


Exempel: Komplett bokningsflöde

Här är en uppsättning verktyg för ett komplett system för tidsbokning:

{
  "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": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "book_appointment",
        "description": "Book an appointment at a specific time",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "time": { "type": "string", "description": "HH:MM format" },
            "customer_name": { "type": "string" },
            "customer_phone": { "type": "string" }
          },
          "required": ["date", "time", "customer_name"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/book",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "cancel_appointment",
        "description": "Cancel an existing appointment",
        "parameters": {
          "type": "object",
          "properties": {
            "confirmation_number": { "type": "string" }
          },
          "required": ["confirmation_number"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/cancel",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    }
  ]
}

Bästa praxis

Skriv tydliga beskrivningar

Fältet description hjälper AI:n att förstå när verktyget ska användas. Var specifik med vad det gör och när det är lämpligt.

Hantera fel smidigt

Returnera felmeddelanden som AI:n kan förstå: {"error": "No slots available for that date"} i stället för generiska 500-fel.

Håll svaren kortfattade

Returnera endast det AI:n behöver för att fortsätta samtalet. Stora nyttolaster fördröjer svarstiderna.

Använd obligatoriska fält med omsorg

Markera fält som required endast när det verkligen behövs. AI:n ber användaren om obligatorisk information innan verktyget anropas.


Relaterat