ThunderPhone 2.0 est disponible.En libre-service, à partir de 2 ¢/min.Découvrir l’annonce

Function Tools

Outils de fonction

Équipez vos agents vocaux IA d’outils de fonction qui appellent des API externes en cours de conversation — récupérer des données client, prendre des rendez-vous, mettre à jour des dossiers — avec des paramètres typés.

Les outils de fonction permettent à vos agents IA d'appeler des API externes pendant les appels téléphoniques. Utilisez-les pour rechercher des données client, vérifier les disponibilités, prendre des rendez-vous ou effectuer toute action prise en charge par votre backend.

Fonctionnement

  1. Définissez des outils avec un schéma (les arguments acceptés par l'outil)
  2. Fournissez une configuration endpoint (l'emplacement où ThunderPhone appelle votre API) — ou omettez-la pour recevoir les appels d'outils sur le webhook de votre organisation
  3. Pendant un appel, l'IA décide quand utiliser un outil en fonction de la conversation
  4. ThunderPhone appelle votre endpoint avec les arguments de l'outil
  5. La réponse de votre API est renvoyée à l'IA pour poursuivre la conversation
FonctionnalitéEmplacement d'exécutionConfiguration
Outils intégrésThunderPhoneInstructions dans le prompt ; certains outils nécessitent également un paramètre d'agent
Connexions d'applicationsThunderPhone et le fournisseur connectéConnectez le compte et associez les actions approuvées
Connexions API et outils de fonctionVotre API HTTPDéfinissez l'endpoint et le schéma, ou recevez les appels de fonction via un webhook
Serveurs MCPUn serveur MCP distantAjoutez le serveur, découvrez ses outils et associez-le à l'agent

Schéma d'outil

Chaque outil suit cette structure :

{
  "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
}

Configuration de l'outil

ChampTypeObligatoireDescription
timeoutnombreNonDurée d'exécution maximale en secondes (valeur par défaut : 20, maximum : 180)

Définition de fonction

ChampTypeObligatoireDescription
namechaîneOuiIdentifiant unique de l'outil
descriptionchaîneOuiIndique à l'IA quand utiliser cet outil
parametersobjetOuiSchéma JSON des arguments de l'outil

Configuration de l'endpoint

ChampTypeObligatoireDescription
urlchaîneOuiURL de l'endpoint de votre API
methodchaîneNonMéthode HTTP (valeur par défaut : POST)
headersobjetNonEn-têtes personnalisés à inclure

Deux chemins d'invocation

La requête reçue par votre serveur dépend de la présence ou non d'un endpoint pour l'outil :

Outil avec endpointOutil sans endpoint
Destination de la requêteDirectement vers endpoint.urlURL webhook héritée de votre organisation
CorpsFormat de requête sélectionné (arguments bruts par défaut)Enveloppe telephony.tool / web.tool
En-têtesVos endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature
Clé de signatureSecret webhook de l’organisationSecret webhook de l’organisation

Les deux chemins sont bloquants — l'IA attend le résultat au milieu d'une phrase. Le délai d'attente par défaut est de 20 s ; définissez le timeout de niveau supérieur de l'outil pour autoriser une exécution plus longue, jusqu'au maximum de la plateforme de 180 s. Gardez les handlers rapides. Un mélange est possible : lors d'un appel dont l'organisation dispose d'une URL webhook, les outils avec un endpoint sont appelés directement et les autres utilisent le webhook.

Appels directs à un endpoint

Lorsque l’IA invoque un outil doté d’un endpoint, ThunderPhone envoie une requête à votre URL :

En-têtes de requête

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

Les en-têtes personnalisés de votre endpoint.headers sont toujours inclus tels quels, ainsi que ces en-têtes avec espace de noms ThunderPhone :

  • X-ThunderPhone-Signature-V2 — HMAC-SHA256 horodaté couvrant la méthode, le chemin, la requête et le corps, utilisant comme clé votre secret webhook d'organisation. Vérifiez celui-ci et rejetez les requêtes datant de plus de 5 minutes ; voir Vérifier les signatures webhook
  • X-ThunderPhone-Signature — HMAC-SHA256 hérité des octets exacts du corps de la requête, avec la même clé, toujours envoyé pour les vérificateurs existants
  • X-ThunderPhone-Call-ID — L'ID de l'appel en cours

Content-Type: application/json est défini sauf si votre endpoint.headers le remplace — un Content-Type personnalisé prévaut.

Format de requête

Chaque connexion API dispose d’un paramètre request_format. Choisissez vapi ou retell lorsqu’un outil importé doit continuer à appeler un point de terminaison conçu pour cette plateforme. Les nouvelles connexions comme les connexions existantes utilisent standard par défaut.

Pour POST / PUT / PATCH, ThunderPhone sérialise l’enveloppe sélectionnée de manière canonique (clés triées, séparateurs compacts). Les deux signatures couvrent ces octets exacts.

Standard

standard envoie uniquement les arguments de l’outil, sans enveloppe. Il s’agit du comportement par défaut et il préserve le corps de requête d’origine de ThunderPhone octet pour octet :

{"date":"2025-01-02","service":"consultation"}

Vapi

vapi envoie le message serveur tool-calls de Vapi dans le format documenté par Vapi. Le call.id est l’ID d’appel ThunderPhone. L’id de l’appel d’outil est unique à l’invocation et est réutilisé pour associer le résultat du point de terminaison. L’appel apparaît dans toolCallList et toolWithToolCallList, avec function.arguments sous forme d’objet JSON :

{
  "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" }
          }
        }
      }
    ]
  }
}

Les champs propres à Vapi, tels que assistant, artifact et l’objet call complet, ne sont pas envoyés.

Renvoyez l’enveloppe de réponse Vapi avec l’ID d’appel d’outil correspondant :

{
  "results": [
    {
      "toolCallId": "call_00112233445566778899aabbccddeeff",
      "result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
    }
  ]
}

ThunderPhone fournit à l’IA la chaîne result non encapsulée. Il accepte également une valeur JSON pour assurer la compatibilité avec les points de terminaison qui en renvoient déjà une. Une valeur error à la place de result est renvoyée à l’IA comme une erreur d’outil. Si le point de terminaison renvoie une enveloppe Vapi mal formée ou non correspondante, ThunderPhone enregistre un avertissement et utilise l’analyse standard des réponses au lieu de faire échouer l’appel.

Retell

retell envoie la requête documentée pour une fonction personnalisée Retell :

{
  "name": "search_appointments",
  "call": { "call_id": "987654321" },
  "args": { "date": "2025-01-02", "service": "consultation" }
}

L’objet call inclut uniquement call_id. ThunderPhone n’inclut pas les champs propres à Retell qu’il ne peut pas fournir honnêtement lors de l’envoi, tels que agent_id, transcript, transcript_object, metadata ou les variables dynamiques. Retell accepte n’importe quel corps de réponse ; ThunderPhone le transmet en utilisant la gestion standard des réponses.

Pour GET / DELETE, les arguments sont envoyés sous forme de paramètres de requête et le corps est vide, quelle que soit la valeur de request_format. La signature V2 couvre la chaîne de requête telle qu’elle est envoyée ; la signature héritée est calculée sur la chaîne d’octets vide. Consultez Vérifier les signatures des webhooks.

Réponse

Renvoyez une réponse JSON contenant le résultat de l’outil :

{
  "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
  "timezone": "America/Los_Angeles"
}

La réponse est formatée et fournie à l’IA afin de poursuivre la conversation. Les réponses non JSON sont encapsulées sous la forme {"data": "<text>"} ; les délais d’expiration et les échecs de connexion sont signalés à l’IA comme des erreurs, afin que l’agent puisse s’excuser et poursuivre plutôt que de rester bloqué.

Envoi en mode webhook

Les outils sans endpoint sont envoyés à l’URL de webhook historique de votre organisation sous forme de requête signée telephony.tool (appels téléphoniques) ou web.tool (appels web). Contrairement aux notifications d’audit envoyées aux endpoints de webhook après l’exécution, cette requête est l’exécution — votre réponse HTTP constitue le résultat de l’outil.

{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}

web.tool contient origin_domain au lieu de from_number / to_number. Répondez avec le résultat de l’outil au format JSON — le même contrat de réponse que pour les appels directs à un endpoint. La requête est signée avec le secret webhook de l’organisation, avec les deux en-têtes de signature, comme tout autre webhook.


Vérification de signature

Les appels directs d'outils sont signés de la même manière que les webhooks, à l'aide du secret webhook de votre organisation :

  • X-ThunderPhone-Signature-V2 signe l'horodatage, la méthode, le chemin et la requête tels qu'ils sont envoyés, ainsi que les octets exacts du corps. Rejetez les requêtes dont l'horodatage s'écarte de plus de 5 minutes de votre horloge.
  • X-ThunderPhone-Signature (hérité) signe uniquement les octets du corps, la chaîne d'octets vide pour GET / DELETE. Il est toujours envoyé, sans modification.

Les assistants SDK vérifient l'en-tête V2, y compris la fenêtre de 5 minutes :

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

Les guides complets — la chaîne signée, un exemple détaillé, du code sans SDK, le cas GET / DELETE et l'avertissement concernant l'absence de secret — se trouvent dans Vérifier les signatures webhook.


Exemple : flux de réservation complet

Voici un ensemble d’outils pour un système complet de réservation de rendez-vous :

{
  "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" }
      }
    }
  ]
}

Bonnes pratiques

Rédiger des descriptions claires

Le champ description aide l’IA à comprendre quand utiliser l’outil. Indiquez précisément ce qu’il fait et quand il est approprié de l’utiliser.

Gérer les erreurs avec élégance

Renvoyez des messages d’erreur que l’IA peut comprendre : {"error": "No slots available for that date"} plutôt que des erreurs 500 génériques.

Garder les réponses concises

Renvoyez uniquement ce dont l’IA a besoin pour poursuivre la conversation. Les charges utiles volumineuses ralentissent les temps de réponse.

Utiliser les champs obligatoires avec discernement

Marquez les champs comme required uniquement lorsque cela est réellement nécessaire. L’IA demandera à l’utilisateur les informations obligatoires avant d’appeler l’outil.


Contenu associé