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

Function Tools

Outils de fonction

Donnez à vos agents vocaux IA des outils de fonction qui appellent des API externes en pleine 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, réserver 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 (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

Schéma de l’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
timeoutnombreNonTemps d’exécution maximal en secondes (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 (par défaut : POST)
headersobjetNonEn-têtes personnalisés à inclure

Deux chemins d’appel

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 de webhook héritée de votre organisation
CorpsArguments bruts de l’outilEnveloppe telephony.tool / web.tool
En-têtesVos endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature
Clé de signatureSecret du webhook de l’organisationSecret du webhook de l’organisation

Les deux chemins sont bloquants — l’IA attend le résultat en pleine phrase. Le délai d’expiration par défaut est de 20 s ; définissez le timeout de premier niveau de l’outil pour autoriser une exécution plus longue, jusqu’au maximum de la plateforme de 180 s. Gardez les gestionnaires rapides. Un mélange est possible : lors d’un appel dont l’organisation possède une URL de 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 qui possède 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: 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 verbatim, ainsi que deux en-têtes dans l’espace de noms ThunderPhone :

  • X-ThunderPhone-Signature — HMAC-SHA256 des octets exacts du corps de la requête, avec votre secret de webhook de l’organisation comme clé
  • 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.

Corps de la requête

Pour POST / PUT / PATCH, le corps contient uniquement les arguments de l’outil (sans enveloppe), sérialisés de manière canonique (clés triées, séparateurs compacts) :

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

Pour GET / DELETE, les arguments sont envoyés en tant que paramètres de requête et le corps est vide — la signature est alors calculée sur la chaîne d’octets vide. Consultez Vérifier les signatures de webhook.

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 mise en forme et fournie à l’IA pour 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 continuer plutôt que de rester bloqué.

Distribution en mode webhook

Les outils sans endpoint sont distribués à l’URL de webhook historique de votre organisation sous la forme d’une 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 à la place 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 de webhook de l’organisation sur le corps brut, 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 :

  • HMAC-SHA256 sur les octets exacts du corps de la requête (le JSON canonique — clés triées, sans espaces superflus)
  • Avec le secret webhook de votre organisation comme clé
  • Les outils GET / DELETE signent la chaîne d’octets vide
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 });
});

Les recettes complètes — y compris le cas du corps vide et la réserve concernant l’absence de secret — sont disponibles dans Vérifier les signatures de webhook.


Exemple : flux de réservation complet

Voici un ensemble d’outils pour un système complet de prise 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 dans quels cas il est approprié.

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 c’est réellement nécessaire. L’IA demandera à l’utilisateur les informations obligatoires avant d’appeler l’outil.


Pages associées