Open in
Functietools
Geef je AI-agenten functietools die tijdens gesprekken externe API
Functietools stellen je AI-agenten in staat om tijdens telefoongesprekken externe API's aan te roepen. Gebruik ze om klantgegevens op te zoeken, beschikbaarheid te controleren, afspraken in te plannen of elke actie uit te voeren die je backend ondersteunt.
Zo werkt het
- Je definieert tools met een schema (welke argumenten de tool accepteert)
- Je geeft een
endpoint-configuratie op (waar ThunderPhone je API aanroept) — of laat deze weg om toolaanroepen op je organisatiewebhook te ontvangen - Tijdens een gesprek bepaalt de AI op basis van het gesprek wanneer een tool moet worden gebruikt
- ThunderPhone roept je endpoint aan met de toolargumenten
- Je API-respons wordt teruggekoppeld naar de AI om het gesprek voort te zetten
| Mogelijkheid | Waar het wordt uitgevoerd | Instellen |
|---|---|---|
| Ingebouwde tools | ThunderPhone | Promptinstructies; sommige tools vereisen ook een agentinstelling |
| App-koppelingen | ThunderPhone en de gekoppelde provider | Koppel het account en voeg goedgekeurde acties toe |
| API-koppelingen en functietools | Je HTTP API | Definieer het endpoint en schema, of ontvang functieaanroepen via een webhook |
| MCP-servers | Een externe MCP-server | Voeg de server toe, ontdek de tools en koppel deze aan de agent |
Toolschema
Elke tool volgt deze structuur:
{
"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
}Toolconfiguratie
| Veld | Type | Vereist | Beschrijving |
|---|---|---|---|
timeout | nummer | Nee | Maximale uitvoeringstijd in seconden (standaard: 20, maximum: 180) |
Functiedefinitie
| Veld | Type | Vereist | Beschrijving |
|---|---|---|---|
name | tekenreeks | Ja | Unieke identificatie voor de tool |
description | tekenreeks | Ja | Legt aan de AI uit wanneer deze tool moet worden gebruikt |
parameters | object | Ja | JSON-schema voor toolargumenten |
Endpointconfiguratie
| Veld | Type | Vereist | Beschrijving |
|---|---|---|---|
url | tekenreeks | Ja | De URL van je API-endpoint |
method | tekenreeks | Nee | HTTP-methode (standaard: POST) |
headers | object | Nee | Aangepaste headers om op te nemen |
Twee aanroeppaden
Welk verzoek je server ontvangt, hangt af van of de tool een
endpoint heeft:
Tool met endpoint | Tool zonder endpoint | |
|---|---|---|
| Waar het verzoek naartoe gaat | Rechtstreeks naar endpoint.url | De verouderde webhook-URL van je organisatie |
| Hoofdtekst | Geselecteerde verzoekindeling (standaard alleen argumenten) | telephony.tool / web.tool-envelop |
| Headers | Je endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature | Content-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature |
| Ondertekeningssleutel | Webhookgeheim van de organisatie | Webhookgeheim van de organisatie |
Beide paden zijn blokkerend — de AI wacht midden in een zin op het
resultaat. De standaardtime-out is 20 s; stel de timeout op het
hoogste niveau van de tool in om een langere uitvoering toe te staan, tot het
platformmaximum van 180 s. Houd handlers snel. Een combinatie is prima:
bij een gesprek waarvan de organisatie een webhook-URL heeft, worden tools met een endpoint
rechtstreeks aangeroepen en vallen de overige terug op de webhook.
Rechtstreekse endpointaanroepen
Wanneer de AI een tool met een endpoint aanroept, stuurt ThunderPhone
een verzoek naar je URL:
Verzoekheaders
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-keyAangepaste headers uit je endpoint.headers worden altijd
letterlijk opgenomen, naast deze headers met de ThunderPhone-naamruimte:
X-ThunderPhone-Signature-V2— HMAC-SHA256 met tijdstempel over de methode, het pad, de query en de body, met je webhookgeheim van de organisatie als sleutel. Verifieer deze en weiger verzoeken die ouder zijn dan 5 minuten; zie Webhookhandtekeningen verifiërenX-ThunderPhone-Signature— verouderde HMAC-SHA256 van de exacte bytes van de aanvraagbody, met dezelfde sleutel, nog steeds verzonden voor bestaande verificateursX-ThunderPhone-Call-ID— De ID van de huidige oproep
Content-Type: application/json wordt ingesteld, tenzij je endpoint.headers
dit overschrijven — een aangepaste Content-Type heeft voorrang.
Aanvraagindeling
Elke API-verbinding heeft een instelling request_format. Kies vapi of
retell wanneer een geïmporteerde tool een endpoint moet blijven aanroepen dat voor
dat platform is gebouwd. Nieuwe en bestaande verbindingen gebruiken standaard standard.
Voor POST / PUT / PATCH serialiseert ThunderPhone de geselecteerde
envelop canoniek (gesorteerde sleutels, compacte scheidingstekens). Beide handtekeningen
dekken deze exacte bytes.
Standard
standard verstuurt alleen de toolargumenten, zonder wrapper. Dit is de
standaardinstelling en behoudt de oorspronkelijke aanvraagbody van ThunderPhone byte voor byte:
{"date":"2025-01-02","service":"consultation"}Vapi
vapi verzendt het tool-calls-serverbericht van Vapi
in de vorm die Vapi documenteert. De call.id is de oproep-ID van ThunderPhone. De
tool-call-id is uniek voor de aanroep en wordt hergebruikt om het
resultaat van het endpoint te koppelen. De oproep verschijnt in zowel toolCallList als
toolWithToolCallList, met function.arguments als JSON-object:
{
"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-specifieke velden zoals assistant, artifact en het volledige call-object
worden niet verzonden.
Retourneer de response-envelop van Vapi met de overeenkomende tool-call-ID:
{
"results": [
{
"toolCallId": "call_00112233445566778899aabbccddeeff",
"result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
}
]
}ThunderPhone geeft de AI de onverpakte result-tekenreeks. Het accepteert ook een
JSON-waarde voor compatibiliteit met endpoints die er al een retourneren. Een
error in plaats van result wordt als toolfout aan de AI teruggegeven. Als het
endpoint een onjuist gevormde of niet-overeenkomende Vapi-envelop retourneert, registreert ThunderPhone
een waarschuwing en valt het terug op de standaardverwerking van responses in plaats van
de oproep te laten mislukken.
Retell
retell verstuurt de aanvraag die is gedocumenteerd voor een aangepaste functie van
Retell:
{
"name": "search_appointments",
"call": { "call_id": "987654321" },
"args": { "date": "2025-01-02", "service": "consultation" }
}Het call-object bevat alleen call_id. ThunderPhone voegt geen
Retell-specifieke velden toe die het bij verzending niet eerlijk kan leveren, zoals
agent_id, transcript, transcript_object, metadata of dynamische
variabelen. Retell accepteert elke responsebody; ThunderPhone geeft deze
door met de standaard responseverwerking.
Voor GET / DELETE worden de argumenten verzonden als queryparameters
en is de body leeg, ongeacht request_format. De V2-handtekening
dekt de querytekenreeks zoals verzonden; de verouderde handtekening wordt berekend over
de lege bytestring. Zie
Webhookhandtekeningen verifiëren.
Antwoord
Retourneer een JSON-antwoord met het toolresultaat:
{
"available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
"timezone": "America/Los_Angeles"
}Het antwoord wordt opgemaakt en aan de AI verstrekt om het gesprek
voort te zetten. Niet-JSON-antwoorden worden verpakt als {"data": "<text>"};
time-outs en verbindingsfouten worden als fouten aan de AI gemeld, zodat
de agent zich kan verontschuldigen en verder kan gaan in plaats van vast te lopen.
Dispatch in webhookmodus
Tools zonder een endpoint worden naar de verouderde webhook-URL
van je organisatie gestuurd als een ondertekend telephony.tool-verzoek
(telefoongesprekken) of web.tool-verzoek (webgesprekken). Anders dan de
auditmeldingen die na uitvoering bij webhookendpoints
worden afgeleverd, is dit verzoek de uitvoering — je HTTP-antwoord
is het toolresultaat.
{
"type": "telephony.tool",
"data": {
"call_id": 987654321,
"tool_name": "search_appointments",
"arguments": { "date": "2026-04-21" },
"from_number": "+14155550199",
"to_number": "+15551234567"
}
}web.tool bevat origin_domain in plaats van from_number /
to_number. Reageer met het toolresultaat als JSON — hetzelfde responscontract
als bij rechtstreekse endpointaanroepen. Het verzoek is ondertekend met het webhookgeheim van de organisatie, met beide handtekeningheaders, net als elke andere webhook.
Handtekeningverificatie
Directe toolaanroepen worden op dezelfde manier ondertekend als webhooks, met het webhookgeheim van je organisatie als sleutel:
X-ThunderPhone-Signature-V2ondertekent de tijdstempel, methode, het pad en de query zoals verzonden, plus de exacte bytes van de body. Weiger aanvragen waarvan de tijdstempel meer dan 5 minuten afwijkt van je klok.X-ThunderPhone-Signature(verouderd) ondertekent alleen de bytes van de body, of de lege bytereeks voorGET/DELETE. Deze wordt nog steeds ongewijzigd verzonden.
De SDK-helpers controleren de V2-header, inclusief het venster van 5 minuten:
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 });
});Volledige recepten — de ondertekende tekenreeks, een uitgewerkt voorbeeld, code zonder de
SDK, het geval GET / DELETE en de kanttekening over geen geheim — vind je in Webhookhandtekeningen verifiëren.
Voorbeeld: volledige boekingsflow
Hier is een set tools voor een volledig systeem voor afspraakboekingen:
{
"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" }
}
}
]
}Best practices
Schrijf duidelijke beschrijvingen
Het veld description helpt de AI te begrijpen wanneer de tool moet worden gebruikt. Wees specifiek over wat de tool doet en wanneer deze geschikt is.
Ga zorgvuldig om met fouten
Geef foutmeldingen terug die de AI kan begrijpen: {"error": "No slots available for that date"} in plaats van algemene 500-fouten.
Houd reacties beknopt
Geef alleen terug wat de AI nodig heeft om het gesprek voort te zetten. Grote payloads vertragen de responstijden.
Gebruik verplichte velden verstandig
Markeer velden alleen als required wanneer dat echt nodig is. De AI vraagt de gebruiker om verplichte informatie voordat de tool wordt aangeroepen.
Gerelateerd
Start door het platform beheerde oproepacties zonder een endpoint te definiëren.
Door het platform beheerde tools voor HubSpot, Salesforce, Slack, Google Calendar, Google Sheets en Cal.com — geen endpoint vereist.
Koppel een MCP-server en laat de agent de tools ervan aanroepen.
Herbruikbare REST-integraties die je aan agents kunt koppelen.
Eén verificatiehulp voor webhooks en toolaanroepen.