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

Function Tools

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

  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 organisatie-webhook te ontvangen
  3. Tijdens een gesprek beslist de AI op basis van het gesprek wanneer een tool moet worden gebruikt
  4. ThunderPhone roept je endpoint aan met de toolargumenten
  5. 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

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

Functiedefinitie

VeldTypeVereistBeschrijving
namestringJaUnieke identificatie voor de tool
descriptionstringJaLegt aan de AI uit wanneer deze tool moet worden gebruikt
parametersobjectJaJSON Schema voor toolargumenten

Endpointconfiguratie

VeldTypeVereistBeschrijving
urlstringJaDe URL van je API-endpoint
methodstringNeeHTTP-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
BodyKale toolargumententelephony.tool / web.tool-envelop
HeadersJe endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-SignatureContent-Type + 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 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-key

Aangepaste 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 organisatie
  • X-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
Python
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}
Node.js
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