Open in
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
- Du definierar verktyg med ett schema (vilka argument verktyget accepterar)
- 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 - Under ett samtal avgör AI:n när ett verktyg ska användas baserat på konversationen
- ThunderPhone anropar din endpoint med verktygsargumenten
- Ditt API-svar skickas tillbaka till AI:n för att fortsätta konversationen
| Funktion | Var det körs | Konfiguration |
|---|---|---|
| Inbyggda verktyg | ThunderPhone | Promptinstruktioner; vissa verktyg kräver även en agentinställning |
| Appanslutningar | ThunderPhone och den anslutna leverantören | Anslut kontot och koppla godkända åtgärder |
| API-anslutningar och funktionsverktyg | Ditt HTTP API | Definiera endpoint och schema, eller ta emot funktionsanrop via en webhook |
| MCP-servrar | En fjärransluten MCP-server | Lä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ält | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
timeout | number | Nej | Längsta körtid i sekunder (standard: 20, högsta: 180) |
Funktionsdefinition
| Fält | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
name | string | Ja | Unik identifierare för verktyget |
description | string | Ja | Förklarar för AI:n när verktyget ska användas |
parameters | object | Ja | JSON Schema för verktygsargument |
Endpoint-konfiguration
| Fält | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
url | string | Ja | URL:en för din API-endpoint |
method | string | Nej | HTTP-metod (standard: POST) |
headers | object | Nej | Anpassade headers som ska inkluderas |
Två anropsvägar
Vilken begäran din server tar emot beror på om verktyget har en
endpoint:
Verktyg med endpoint | Verktyg utan endpoint | |
|---|---|---|
| Vart begäran skickas | Direkt till endpoint.url | Organisationens äldre webhook-URL |
| Brödtext | Valt begärandeformat (rena argument som standard) | telephony.tool / web.tool-kuvert |
| Rubriker | Dina endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature | Content-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature |
| Signeringsnyckel | Organisationens webhook-hemlighet | Organisationens 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-keyAnpassade 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-signaturerX-ThunderPhone-Signature— äldre HMAC-SHA256 för de exakta byten i begärandetexten, med samma nyckel, som fortfarande skickas för befintliga verifierareX-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-V2signerar 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örGET/DELETE. Den skickas fortfarande, oförändrad.
SDK-hjälpfunktionerna kontrollerar V2-huvudet, inklusive 5-minutersfönstret:
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}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
Initiera plattformshanterade samtalsåtgärder utan att definiera en slutpunkt.
Plattformshanterade verktyg för HubSpot, Salesforce, Slack, Google Calendar, Google Sheets och Cal.com — ingen slutpunkt krävs.
Anslut en MCP-server och låt agenten anropa dess verktyg.
Återanvändbara REST-integrationer som du kan ansluta till agenter.
En verifieringshjälp för webhooks och verktygsanrop.