ThunderPhone 2.0 is live.Direct zelf aan de slag, vanaf 2¢/min.Lees de aankondiging

Function Tools

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

  1. Je definieert tools met een schema (welke argumenten de tool accepteert)
  2. Je geeft een endpoint-configuratie op (waar ThunderPhone je API aanroept) — of laat deze weg om toolaanroepen op je organisatiewebhook te ontvangen
  3. Tijdens een gesprek bepaalt de AI op basis van het gesprek wanneer een tool moet worden gebruikt
  4. ThunderPhone roept je endpoint aan met de toolargumenten
  5. Je API-respons wordt teruggekoppeld naar de AI om het gesprek voort te zetten
MogelijkheidWaar het wordt uitgevoerdInstellen
Ingebouwde toolsThunderPhonePromptinstructies; sommige tools vereisen ook een agentinstelling
App-koppelingenThunderPhone en de gekoppelde providerKoppel het account en voeg goedgekeurde acties toe
API-koppelingen en functietoolsJe HTTP APIDefinieer het endpoint en schema, of ontvang functieaanroepen via een webhook
MCP-serversEen externe MCP-serverVoeg 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

VeldTypeVereistBeschrijving
timeoutnummerNeeMaximale uitvoeringstijd in seconden (standaard: 20, maximum: 180)

Functiedefinitie

VeldTypeVereistBeschrijving
nametekenreeksJaUnieke identificatie voor de tool
descriptiontekenreeksJaLegt aan de AI uit wanneer deze tool moet worden gebruikt
parametersobjectJaJSON-schema voor toolargumenten

Endpointconfiguratie

VeldTypeVereistBeschrijving
urltekenreeksJaDe URL van je API-endpoint
methodtekenreeksNeeHTTP-methode (standaard: POST)
headersobjectNeeAangepaste headers om op te nemen

Twee aanroeppaden

Welk verzoek je server ontvangt, hangt af van of de tool een endpoint heeft:

Tool met endpointTool zonder endpoint
Waar het verzoek naartoe gaatRechtstreeks naar endpoint.urlDe verouderde webhook-URL van je organisatie
HoofdtekstGeselecteerde verzoekindeling (standaard alleen argumenten)telephony.tool / web.tool-envelop
HeadersJe endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature
OndertekeningssleutelWebhookgeheim van de organisatieWebhookgeheim 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-key

Aangepaste 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ëren
  • X-ThunderPhone-Signature — verouderde HMAC-SHA256 van de exacte bytes van de aanvraagbody, met dezelfde sleutel, nog steeds verzonden voor bestaande verificateurs
  • X-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-V2 ondertekent 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 voor GET / DELETE. Deze wordt nog steeds ongewijzigd verzonden.

De SDK-helpers controleren de V2-header, inclusief het venster van 5 minuten:

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 });
});

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