Functietools
Geef je AI-agenten functietools die tijdens een gesprek externe API
Functietools stellen je spraakagenten in staat om tijdens telefoongesprekken externe API's aan te roepen. Gebruik ze om klantgegevens op te zoeken, beschikbaarheid te controleren, afspraken te boeken of elke actie uit te voeren die je backend ondersteunt.
Hoe het werkt
- 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 organisatie-webhook te ontvangen - Tijdens een gesprek beslist de AI op basis van het gesprek wanneer een tool moet worden gebruikt
- ThunderPhone roept je endpoint aan met de toolargumenten
- Het antwoord van je API wordt teruggekoppeld aan de AI om het gesprek voort te zetten
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 | number | Nee | Maximale uitvoeringstijd in seconden (standaard: 20, maximum: 180) |
Functiedefinitie
| Veld | Type | Vereist | Beschrijving |
|---|---|---|---|
name | string | Ja | Unieke identificatie voor de tool |
description | string | 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 | string | Ja | De URL van je API-endpoint |
method | string | 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 |
| Body | Kale toolargumenten | telephony.tool / web.tool-envelop |
| Headers | Je endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature | Content-Type + 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 voor een langere uitvoering, 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: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-keyAangepaste headers uit je endpoint.headers worden altijd letterlijk
toegevoegd, plus twee headers met de ThunderPhone-naamruimte:
X-ThunderPhone-Signature— HMAC-SHA256 van de exacte bytes van de verzoekbody, met als sleutel je webhooksecret van de organisatieX-ThunderPhone-Call-ID— De ID van het huidige gesprek
Content-Type: application/json wordt ingesteld, tenzij je
endpoint.headers dit overschrijft — een aangepaste Content-Type heeft voorrang.
Verzoekbody
Voor POST / PUT / PATCH bevat de body alleen de argumenten van de tool
(zonder wrapper), canoniek geserialiseerd (gesorteerde sleutels, compacte
scheidingstekens):
{"date":"2025-01-02","service":"consultation"}Voor GET / DELETE worden de argumenten als queryparameters verzonden
en is de body leeg — de handtekening wordt dan berekend over de lege
byte-tekenreeks. Zie
Webhookhandtekeningen verifiëren.
Antwoord
Retourneer een JSON-antwoord met het resultaat van de tool:
{
"available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
"timezone": "America/Los_Angeles"
}Het antwoord wordt geformatteerd 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 als een ondertekend verzoek van
telephony.tool (telefoongesprekken) of web.tool (webgesprekken) naar de
verouderde webhook-URL van je organisatie gestuurd. Anders dan de
auditmeldingen die na uitvoering naar webhookendpoints worden
bezorgd, is dit verzoek de uitvoering — je HTTP-antwoord is het resultaat
van de tool.
{
"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. Antwoord met het toolresultaat als JSON — hetzelfde
antwoordcontract als voor rechtstreekse endpointaanroepen. Het verzoek wordt,
net als elke andere webhook, met de webhooksecret van de organisatie over de
ruwe body ondertekend.
Handtekeningverificatie
Rechtstreekse toolaanroepen worden op dezelfde manier ondertekend als webhooks:
- HMAC-SHA256 over de exacte bytes van de requestbody (de canonieke JSON — gesorteerde sleutels, geen extra witruimte)
- Met het webhookgeheim van je organisatie als sleutel
GET- /DELETE-tools ondertekenen de lege bytereeks
import hmac
import hashlib
def verify_tool_call(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
@app.post("/appointments/search")
async def search_appointments(request: Request):
body = await request.body()
signature = request.headers.get("X-ThunderPhone-Signature", "")
if not verify_tool_call(body, signature, WEBHOOK_SECRET):
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}app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-thunderphone-signature'] || '';
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!signature ||
signature.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
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 — waaronder het geval met een lege body 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 het boeken van afspraken:
{
"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 begrijpt: {"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 reactietijden.
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
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 agenten kunt koppelen.
Eén verificatiehelper voor webhooks en toolaanroepen.