---
title: "Outils de fonction"
description: "É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écution | Configuration |
| --- | --- | --- |
| [Outils intégrés](/fr/guides/built-in-tools) | ThunderPhone | Instructions dans le prompt ; certains outils nécessitent également un paramètre d'agent |
| [Connexions d'applications](/fr/guides/connect-apps) | ThunderPhone et le fournisseur connecté | Connectez le compte et associez les actions approuvées |
| [Connexions API](/fr/guides/api-connections) et outils de fonction | Votre API HTTP | Définissez l'endpoint et le schéma, ou recevez les appels de fonction via un webhook |
| [Serveurs MCP](/fr/guides/mcp-servers) | Un serveur MCP distant | Ajoutez le serveur, découvrez ses outils et associez-le à l'agent |

---

## Schéma d'outil

Chaque outil suit cette structure :

```json
{
  "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

| Champ | Type | Obligatoire | Description |
|-------|------|----------|-------------|
| `timeout` | nombre | Non | Durée d'exécution maximale en secondes (valeur par défaut : `20`, maximum : `180`) |

### Définition de fonction

| Champ | Type | Obligatoire | Description |
|-------|------|----------|-------------|
| `name` | chaîne | Oui | Identifiant unique de l'outil |
| `description` | chaîne | Oui | Indique à l'IA quand utiliser cet outil |
| `parameters` | objet | Oui | Schéma JSON des arguments de l'outil |

### Configuration de l'endpoint

| Champ | Type | Obligatoire | Description |
|-------|------|----------|-------------|
| `url` | chaîne | Oui | URL de l'endpoint de votre API |
| `method` | chaîne | Non | Méthode HTTP (valeur par défaut : `POST`) |
| `headers` | objet | Non | En-têtes personnalisés à inclure |

<Note>
  La configuration `endpoint` n'est **pas** envoyée au modèle d'IA — elle est utilisée uniquement par ThunderPhone pour exécuter l'appel d'outil.
</Note>

---

## 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** `endpoint` | Outil **sans** `endpoint` |
|---|---|---|
| Destination de la requête | Directement vers `endpoint.url` | [URL webhook héritée](/api-reference/organizations#legacy-single-url-webhook) de votre organisation |
| Corps | Format de requête sélectionné (arguments bruts par défaut) | Enveloppe `telephony.tool` / `web.tool` |
| En-têtes | Vos `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` |
| Clé de signature | Secret webhook de l’organisation | Secret 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

```http
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](/fr/guides/verify-webhook-signatures)
- `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.

<Warning>
  La signature utilise comme clé le secret de webhook au niveau de l’organisation provenant de
  [`GET /v1/webhook`](/api-reference/organizations#legacy-single-url-webhook).
  Si votre organisation n’a jamais configuré le webhook historique, aucun
  secret n’existe et les appels d’outils ne contiennent **que** `X-ThunderPhone-Call-ID` —
  un gestionnaire qui échoue systématiquement lorsqu’une signature est absente les rejettera.
  Configurez le webhook historique pour obtenir un secret, ou ajoutez votre propre
  secret partagé dans `endpoint.headers`.
</Warning>

### 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 :

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

#### Vapi

`vapi` envoie le [message serveur `tool-calls`](https://docs.vapi.ai/tools/custom-tools) 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 :

```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 :

```json
{
  "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](https://docs.retellai.com/build/single-multi-prompt/custom-function) :

```json
{
  "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](/fr/guides/verify-webhook-signatures).

### Réponse

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

```json
{
  "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](/fr/webhooks/events)
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.

```json
{
  "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.

<Note>
  Les [endpoints de webhook](/fr/webhooks/endpoints) abonnés reçoivent également
  une **notification** `telephony.tool` / `web.tool` non bloquante
  **après** l’exécution de chaque outil (quel que soit le chemin utilisé), incluant la
  réponse de l’outil — utile pour les pistes d’audit. Consultez le
  [catalogue des événements](/fr/webhooks/events).
</Note>

---

## 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 :

<CodeGroup>
```python 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}
```

```javascript 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 });
});
```
</CodeGroup>

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](/fr/guides/verify-webhook-signatures).

---

## Exemple : flux de réservation complet

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

```json
{
  "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

<AccordionGroup>
  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

---

## Contenu associé

<CardGroup cols={2}>
  <Card title="Outils intégrés" icon="wrench" href="/fr/guides/built-in-tools">
    Déclenchez des actions d’appel gérées par la plateforme sans définir de point de terminaison.
  </Card>
  <Card title="Connexions d’applications" icon="plug" href="/fr/guides/connect-apps">
    Outils gérés par la plateforme pour HubSpot, Salesforce, Slack, Google
    Calendar, Google Sheets et Cal.com — aucun point de terminaison requis.
  </Card>
  <Card title="Serveurs MCP" icon="server" href="/fr/guides/mcp-servers">
    Connectez un serveur MCP et laissez l’agent appeler ses outils.
  </Card>
  <Card title="Connexions API" icon="code" href="/fr/guides/api-connections">
    Intégrations REST réutilisables que vous pouvez associer à des agents.
  </Card>
  <Card title="Vérifier les signatures des webhooks" icon="shield-check" href="/fr/guides/verify-webhook-signatures">
    Un assistant de vérification unique pour les webhooks et les appels d’outils.
  </Card>
</CardGroup>
