> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Outils de fonction

> Permettez à vos agents IA d’appeler des API externes pendant les conversations

Les outils de fonction permettent à vos agents IA d’invoquer 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 selon 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

<Note>
  Les outils de fonction constituent l’approche où vous fournissez votre propre API. ThunderPhone propose également
  des outils gérés par la plateforme qui ne nécessitent aucun endpoint :
  [connexions d’applications](/fr/guides/connect-apps) (HubSpot, Salesforce, Slack,
  Google Calendar, Google Sheets, Cal.com),
  [connexions API](/fr/guides/api-connections) et
  [serveurs MCP](/fr/guides/mcp-servers).
</Note>

***

## Schéma d’outil

Chaque outil suit cette structure :

```json theme={null}
{
  "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"
    }
  }
}
```

### Définition de fonction

| Champ         | Type   | Obligatoire | Description                             |
| ------------- | ------ | ----------- | --------------------------------------- |
| `name`        | string | Oui         | Identifiant unique de l’outil           |
| `description` | string | Oui         | Indique à l’IA quand utiliser cet outil |
| `parameters`  | object | Oui         | Schéma JSON des arguments de l’outil    |

### Configuration de l’endpoint

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

<Note>
  La configuration `endpoint` n’est **pas** envoyée au modèle d’IA — elle est uniquement utilisée 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 de webhook héritée](/api-reference/organizations#legacy-single-url-webhook) de votre organisation |
| Corps                     | **Arguments bruts de l’outil**                                                 | Enveloppe `telephony.tool` / `web.tool`                                                                |
| En-têtes                  | Vos `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature`                                                            |
| Clé de signature          | Secret du webhook de l’organisation                                            | Secret du webhook de l’organisation                                                                    |

Les deux chemins sont **bloquants** — l’IA attend le résultat en plein milieu
d’une phrase — avec un délai d’expiration de **20 s**. Gardez les gestionnaires rapides. Vous pouvez les combiner :
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 comme solution de repli.

## Appels directs de point de terminaison

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

### En-têtes de requête

```http theme={null}
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,
  utilisant comme clé votre **secret de webhook d’organisation**
* `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 hérité, il n’existe aucun
  secret et les appels d’outils ne contiennent **que** `X-ThunderPhone-Call-ID` —
  un gestionnaire qui échoue systématiquement en l’absence de signature les rejetterait.
  Configurez le webhook hérité pour obtenir un secret, ou placez votre propre
  secret partagé dans `endpoint.headers`.
</Warning>

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

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

Pour `GET` / `DELETE`, les arguments sont envoyés sous forme de **paramètres de requête**
et le corps est vide — la signature est alors calculée sur la chaîne
d’octets vide. Consultez
[Verifier les signatures de webhook](/fr/guides/verify-webhook-signatures).

### Réponse

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

```json theme={null}
{
  "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é.

## Distribution en mode webhook

Les outils **sans** `endpoint` sont distribués à l’URL de webhook héritée 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](/fr/webhooks/events)
livrées aux points de terminaison 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 theme={null}
{
  "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 de point de terminaison. La requête est signée avec le secret de webhook de l’organisation
sur le corps brut, comme tout autre webhook.

<Note>
  Les [points de terminaison webhook](/fr/webhooks/endpoints) abonnés reçoivent également
  une **notification** non bloquante `telephony.tool` / `web.tool`
  **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 :

* 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

<CodeGroup>
  ```python Python theme={null}
  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}
  ```

  ```javascript Node.js theme={null}
  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 });
  });
  ```
</CodeGroup>

Les recettes complètes — y compris le cas du corps vide et la mise en garde concernant l'absence de secret —
sont disponibles dans [Vérifier les signatures des webhooks](/fr/guides/verify-webhook-signatures).

***

## Exemple : flux de réservation complet

Voici un ensemble d'outils pour un système complet de prise de rendez-vous :

```json theme={null}
{
  "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édigez des descriptions claires">
    Le champ `description` aide l’IA à comprendre **quand** utiliser l’outil. Indiquez précisément ce qu’il fait et à quel moment il est approprié de l’utiliser.
  </Accordion>

  <Accordion title="Gérez 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="Gardez les réponses concises">
    Renvoyez uniquement ce dont l’IA a besoin pour poursuivre la conversation. Les payloads volumineux ralentissent les temps de réponse.
  </Accordion>

  <Accordion title="Utilisez 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 requises avant d’appeler l’outil.
  </Accordion>
</AccordionGroup>

***

## Associés

<CardGroup cols={2}>
  <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 endpoint 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 de webhooks" icon="shield-check" href="/fr/guides/verify-webhook-signatures">
    Un assistant de vérification pour les webhooks et les appels d’outils.
  </Card>
</CardGroup>
