Funktionsværktøjer
Giv dine AI-agenter funktionsværktøjer, der kalder eksterne API
Funktionsværktøjer giver dine AI-agenter mulighed for at kalde eksterne API'er under telefonopkald. Brug dem til at slå kundedata op, tjekke tilgængelighed, booke aftaler eller udføre enhver handling, som din backend understøtter.
Sådan fungerer det
- Du definerer værktøjer med et skema (hvilke argumenter værktøjet accepterer)
- Du angiver en
endpoint-konfiguration (hvor ThunderPhone kalder din API) — eller udelader den for at modtage værktøjskald på din organisations webhook - Under et opkald beslutter AI'en, hvornår et værktøj skal bruges, baseret på samtalen
- ThunderPhone kalder dit endpoint med værktøjsargumenterne
- Dit API-svar sendes tilbage til AI'en for at fortsætte samtalen
Værktøjsskema
Hvert værktøj følger denne struktur:
{
"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
}Værktøjskonfiguration
| Felt | Type | Påkrævet | Beskrivelse |
|---|---|---|---|
timeout | tal | Nej | Maksimal udførelsestid i sekunder (standard: 20, maksimum: 180) |
Funktionsdefinition
| Felt | Type | Påkrævet | Beskrivelse |
|---|---|---|---|
name | streng | Ja | Unik identifikator for værktøjet |
description | streng | Ja | Forklarer AI'en, hvornår dette værktøj skal bruges |
parameters | objekt | Ja | JSON Schema for værktøjsargumenter |
Endpoint-konfiguration
| Felt | Type | Påkrævet | Beskrivelse |
|---|---|---|---|
url | streng | Ja | URL'en til dit API-endpoint |
method | streng | Nej | HTTP-metode (standard: POST) |
headers | objekt | Nej | Tilpassede headers, der skal medtages |
To kaldstier
Hvilken anmodning din server modtager, afhænger af, om værktøjet har et
endpoint:
Værktøj med endpoint | Værktøj uden endpoint | |
|---|---|---|
| Hvor anmodningen sendes hen | Direkte til endpoint.url | Din organisations ældre webhook-URL |
| Brødtekst | Kun værktøjsargumenter | telephony.tool / web.tool-indpakning |
| Headers | Dine endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature | Content-Type + X-ThunderPhone-Signature |
| Signeringsnøgle | Organisationens webhook-hemmelighed | Organisationens webhook-hemmelighed |
Begge stier er blokerende — AI'en venter midt i en sætning på
resultatet. Standardtimeout er 20 s; angiv værktøjets øverste
timeout for at tillade en længere udførelse, op til platformens
maksimum på 180 s. Hold handlere hurtige. En blanding er fin:
I et opkald, hvis organisation har en webhook-URL, kaldes værktøjer med et
endpoint direkte, mens resten falder tilbage til webhooken.
Direkte endepunktskald
Når AI'en kalder et værktøj, der har et endpoint, sender ThunderPhone
en anmodning til din URL:
Anmodningsheaders
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-keyBrugerdefinerede headers fra dit endpoint.headers inkluderes altid
ordret samt to ThunderPhone-navnerumsheaders:
X-ThunderPhone-Signature— HMAC-SHA256 af de nøjagtige bytes i anmodningens brødtekst, med din organisations webhook-hemmelighed som nøgleX-ThunderPhone-Call-ID— ID'et for det aktuelle opkald
Content-Type: application/json angives, medmindre dit endpoint.headers
overskriver det — en brugerdefineret Content-Type har forrang.
Anmodningstekst
For POST / PUT / PATCH indeholder brødteksten kun værktøjs-
argumenterne (ingen indpakning), serialiseret kanonisk (sorterede nøgler,
kompakte separatorer):
{"date":"2025-01-02","service":"consultation"}For GET / DELETE sendes argumenterne som forespørgselsparametre,
og brødteksten er tom — signaturen beregnes derefter over den tomme
bytestreng. Se
Verificer webhook-signaturer.
Svar
Returner et JSON-svar med værktøjsresultatet:
{
"available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
"timezone": "America/Los_Angeles"
}Svaret formateres og gives til AI'en, så den kan fortsætte
samtalen. Ikke-JSON-svar indpakkes som {"data": "<text>"};
timeouts og forbindelsesfejl rapporteres til AI'en som fejl, så
agenten kan undskylde og fortsætte i stedet for at gå i stå.
Afsendelse i webhooktilstand
Værktøjer uden et endpoint sendes til din organisations ældre
webhook-URL som en signeret telephony.tool-anmodning (telefonopkald) eller
web.tool-anmodning (webopkald). I modsætning til revisionsnotifikationerne,
der leveres til webhook-endepunkter efter udførelse, er denne anmodning
selve udførelsen — dit HTTP-svar er værktøjsresultatet.
{
"type": "telephony.tool",
"data": {
"call_id": 987654321,
"tool_name": "search_appointments",
"arguments": { "date": "2026-04-21" },
"from_number": "+14155550199",
"to_number": "+15551234567"
}
}web.tool indeholder origin_domain i stedet for from_number /
to_number. Svar med værktøjsresultatet som JSON — den samme
svarskontrakt som ved direkte endepunktskald. Anmodningen signeres med
organisationens webhook-hemmelighed over den rå brødtekst, ligesom alle andre webhooks.
Signaturverifikation
Direkte værktøjskald signeres på samme måde som webhooks:
- HMAC-SHA256 over de nøjagtige bytes i request-bodyen (den kanoniske JSON — sorterede nøgler, intet ekstra mellemrum)
- Med din organisations webhook-hemmelighed som nøgle
GET- /DELETE-værktøjer signerer den tomme bytestreng
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 });
});Fuldstændige opskrifter — herunder tilfældet med tom body og forbeholdet om manglende hemmelighed — findes i Bekræft webhook-signaturer.
Eksempel: Komplet bookingflow
Her er et sæt værktøjer til et komplet system til tidsbestilling:
{
"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" }
}
}
]
}Bedste praksis
Skriv klare beskrivelser
Feltet description hjælper AI'en med at forstå, hvornår værktøjet skal bruges. Vær specifik om, hvad det gør, og hvornår det er relevant.
Håndter fejl elegant
Returner fejlmeddelelser, som AI'en kan forstå: {"error": "No slots available for that date"} frem for generiske 500-fejl.
Hold svar korte
Returner kun det, AI'en har brug for for at fortsætte samtalen. Store payloads sænker svartiderne.
Brug obligatoriske felter med omtanke
Markér kun felter som required, når det er helt nødvendigt. AI'en beder brugeren om obligatoriske oplysninger, før den kalder værktøjet.
Relateret
Platformadministrerede værktøjer til HubSpot, Salesforce, Slack, Google Calendar, Google Sheets og Cal.com — intet endpoint kræves.
Tilknyt en MCP-server, og lad agenten kalde dens værktøjer.
Genanvendelige REST-integrationer, som du kan tilknytte agenter.
En bekræftelseshjælper til webhooks og værktøjskald.