---
title: "Créer une intégration d'outil (API)"
description: "Permettez à votre agent d'appeler vos API en pleine conversation — rechercher dans une base de données, créer un ticket, consulter une commande."
---

Une **intégration d’outil** est un point de terminaison HTTP réutilisable qu’un agent peut
appeler pendant un appel. Vous fournissez à ThunderPhone une description du schéma JSON
de l’outil ainsi qu’une URL de point de terminaison ; l’agent décide quand l’appeler
en fonction de la conversation, puis ThunderPhone effectue la requête HTTP sortante
depuis ses serveurs et renvoie la réponse à l’agent.

<Note>
  Le tableau de bord couvre la plupart des besoins en outils sans cette API : **Connexions
  → Applications** connecte Slack, HubSpot, Salesforce, Google Calendar,
  Google Sheets et Cal.com en quelques clics OAuth ; **Connexions →
  APIs** transforme n’importe quelle API HTTP en action d’agent (collez une commande cURL
  et un assistant IA rédige l’outil, avec une requête de test intégrée) ; et
  **Connexions → MCP** ajoute des serveurs MCP. Consultez
  [Connexions](/fr/guides/concepts). Ce guide présente l’API brute
  sous-jacente à l’interface APIs.
</Note>

Ce guide explique de bout en bout comment créer un outil de consultation de la météo.

## Anatomie d’un outil

Deux éléments :

1. **Le schéma** — une définition de fonction de style OpenAI
   (`{type: "function", function: {name, description, parameters}}`)
   qui indique au LLM ce que fait l’outil et quels arguments il accepte.
2. **Le point de terminaison** — l’URL appelée par les serveurs de ThunderPhone lorsque le
   LLM décide d’utiliser l’outil. La requête est un POST JSON dont le corps contient les
   arguments choisis par le LLM.

## 1. Choisir un éditeur

<CardGroup cols={2}>
  <Card title="Tableau de bord" icon="window-maximize">
    Ouvrez **Connexions → APIs**, créez ou modifiez la connexion API,
    passez l’éditeur de paramètres en **JSON**, puis ajoutez-y le format.
  </Card>
  <Card title="API d’intégrations" icon="plug">
    Créez la spécification avec `POST /v1/integrations`, ou mettez-la à jour avec
    `PATCH /v1/integrations/{id}`.
  </Card>
</CardGroup>

Les deux méthodes créent une intégration enregistrée. Attachez cette intégration à l’agent
après l’avoir enregistrée. L’API Agents ne possède aucun champ `tools` intégré accessible en écriture.
Ce guide utilise la méthode via l’API d’intégrations.

## 2. Créez l’intégration

```bash
curl -X POST https://api.thunderphone.com/v1/integrations \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Weather API",
    "spec": {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Return the current weather for a zip code.",
        "parameters": {
          "type": "object",
          "properties": {
            "zip": { "type": "string", "description": "5-digit US ZIP code" }
          },
          "required": ["zip"]
        }
      }
    },
    "endpoint_url":    "https://api.example.com/weather",
    "endpoint_method": "GET",
    "headers": [
      { "key": "X-Api-Key", "value": "your-provider-key" }
    ]
  }'
```

Enregistrez l’`id` renvoyé (un UUID). Les valeurs d’en-tête sont accessibles en écriture uniquement : les réponses répertorient uniquement les noms d’en-tête, et un `PATCH` ultérieur peut envoyer un en-tête sans `value` pour conserver la valeur stockée.

<Tip>
  Soignez réellement la `description` de l’outil et de chaque
  paramètre. Le LLM utilise ces chaînes à l’exécution pour décider s’il
  doit appeler l’outil et comment le faire. Des descriptions vagues → des
  appels d’outils vagues.
</Tip>

### Déclarez `format: "email"` sur les paramètres d’adresse

Un paramètre qui reçoit une adresse e-mail doit l’indiquer dans son
schéma :

```json
"email": { "type": "string", "format": "email", "description": "The caller's email address" }
```

`format` est plus qu’une indication. Pour un schéma d’e-mail résolu, avant
d’appeler votre endpoint, ThunderPhone supprime les espaces en début et fin de valeur,
met le domaine en minuscules, transforme les mots anglais isolés `at`, `dot`,
`underscore`, `dash` et `hyphen` en leurs caractères correspondants, et supprime les
espaces directement autour de `@`, `.`, `_` et `-`. Ces mots ont le même sens,
que la transcription contienne déjà ou non un `@` littéral :
`"john dot smith at gmail dot com"` devient
`john.smith@gmail.com`.

Tout autre espace interne est rejeté plutôt que joint silencieusement.
Les mots de séparation prononcés sont pris en charge uniquement en anglais ; les formes
espacées non anglaises ou non reconnues échouent de manière sécurisée. Les domaines
internationalisés valides et les parties locales SMTPUTF8 sont acceptés. Une entrée
Punycode reste en Punycode et une entrée de domaine Unicode reste en Unicode après la
normalisation de l’analyseur : votre API reçoit donc la représentation conventionnelle
fournie par l’appelant. Si la valeur finale est invalide, l’outil n’est **pas appelé**.
L’agent reçoit `invalid_email_argument`, qui lui indique
de confirmer l’orthographe avec l’appelant et de renvoyer l’adresse littérale.

Un e-mail facultatif omis n’est pas modifié. `null`, une chaîne vide ou une
chaîne ne contenant que des espaces ne sont pas non plus modifiés lorsque la propriété est facultative
ou nullable ; ces mêmes valeurs sont rejetées pour un e-mail obligatoire et non nullable.

Les références de schéma locales telles que `#/$defs/email` et
`#/definitions/email`, ainsi que `anyOf`, `oneOf` et `allOf`, sont inspectées
avec des limites de cycles et de profondeur. Un `$ref` non local ou impossible à résoudre
constitue une limite d’application connue et est transmis sans modification, tout comme un appel
dont l’instantané de l’outil ne comporte aucun schéma exploitable. Conservez les schémas d’e-mail
locaux lorsque vous devez appliquer ce contrôle.

Les paramètres sans format d’e-mail appliqué sont transmis exactement
tels que le modèle les a produits.

Les formats pris en charge sont `date-time`, `time`, `date`, `duration`,
`email`, `hostname`, `ipv4`, `ipv6` et `uuid` ; seul `email` est
normalisé et appliqué aujourd’hui.

## 3. Testez l’endpoint dans le sandbox

Avant de lier l’intégration à un agent, envoyez une requête signée
depuis les serveurs de ThunderPhone pour confirmer la connectivité :

```bash
curl -X POST https://api.thunderphone.com/v1/integrations/test-request \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":    "https://api.example.com/weather?zip=94110",
    "method": "GET",
    "headers": { "X-Api-Key": "your-provider-key" }
  }'
```

```json Response
{
  "ok": true,
  "status": 200,
  "elapsed_ms": 187,
  "response_headers": { "content-type": "application/json" },
  "response_preview": "{\"temperature_f\": 64, ...}"
}
```

Ce test renforce également les protections SSRF de ThunderPhone : les requêtes vers
localhost ou des plages d’IP privées renvoient `400 code=url_not_allowed`.

## 4. Lier l'intégration à un agent

Associez-la via `integration_ids` lorsque vous créez ou mettez à jour un agent :

```bash
curl -X PATCH https://api.thunderphone.com/v1/agents/12 \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "integration_ids": ["f9b5a1a4-..."]
  }'
```

Vous pouvez lier plusieurs intégrations à un même agent. Le prompt de l'agent peut
y faire référence par leur nom — « utilisez `get_weather` lorsque l'appelant demande
des informations sur les conditions météorologiques » — ou les découvrir implicitement à partir des
descriptions du schéma.

## 5. Implémenter l'endpoint

Lorsque l'agent invoque l'outil, ThunderPhone envoie une requête POST signée vers
votre `endpoint_url` :

```
POST /weather HTTP/1.1
Host: api.example.com
X-Api-Key: your-provider-key
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<HMAC-SHA256 hex>
X-ThunderPhone-Signature: <HMAC-SHA256 hex>
X-ThunderPhone-Call-ID: 987654321
Content-Type: application/json

{"zip": "94110"}
```

Votre serveur répond avec du JSON qui est renvoyé au LLM :

```json
{"temperature_f": 64, "condition": "Partly cloudy", "wind_mph": 8}
```

Le LLM assimile cette réponse et en donne un résumé naturel à
l'appelant.

<Warning>
  La signature est calculée sur le corps brut de la requête à l'aide du même
  `secret` que votre endpoint de webhook. **Vérifiez-la** — les endpoints d'outils
  sont exposés à Internet et soumis aux mêmes risques d'usurpation que les
  webhooks. Consultez
  [Vérifier les signatures de webhook](/fr/guides/verify-webhook-signatures).
</Warning>

## 6. Tester la boucle

Lancez une [session micro](/api-reference/mic-sessions) avec l'agent
et posez la question que votre outil traite (« Quel temps fait-il à
94110 ? »). La transcription de l'appel affiche l'aller-retour complet :

```json
{
  "call_id": 987654321,
  "transcripts": [
    { "role": "user",
      "content": "What's the weather in 94110?" },
    { "role": "tool_call",
      "content": "{\"tool_call\": \"get_weather\", \"arguments\": {\"zip\": \"94110\"}}" },
    { "role": "tool_response",
      "content": "{\"tool_name\": \"get_weather\", \"response\": {\"temperature_f\": 64, \"condition\": \"Partly cloudy\"}}" },
    { "role": "agent",
      "content": "It's 64 degrees and partly cloudy." }
  ]
}
```

Vous pouvez récupérer cela avec
[`GET /v1/calls/{call_id}/transcript`](/api-reference/calls#get-transcript) ;
le flux d'événements brut (avec le minutage de chaque entrée et les décalages audio) est disponible via
[`GET /v1/calls/{call_id}/history`](/api-reference/calls#get-history).

Dans les interfaces de liste d’appels et d’analytique, ThunderPhone distingue désormais les appels de test du trafic réel. Les tests et simulations Browser Talk peuvent réutiliser un vrai numéro de téléphone et sembler entrants, mais ils ne sont pas comptabilisés dans les totaux d’appels en production, les notes ou les métriques de problèmes.

## Pièges fréquents

<AccordionGroup>
  <Accordion title="L'agent n'appelle jamais l'outil">
    Le LLM décide en fonction de la description de l'outil. Si la question de l'appelant
    ne correspond pas à la description, le modèle n'invoquera pas
    l'outil. Précisez la description (ajoutez des synonymes et
    formulations courants) ou mentionnez-le explicitement dans le prompt de l'agent (« Lorsque
    l'appelant pose une question sur la météo, utilisez `get_weather`. »).
  </Accordion>

  <Accordion title="L'outil renvoie trop de données">
    Les réponses de plus de 6 kB sont tronquées dans l'aperçu de la transcription. Renvoyez
    uniquement les champs dont le LLM a besoin — pas toute votre ligne.
  </Accordion>

  <Accordion title="Délais d'expiration">
    Les endpoints d'outils ont un délai d'expiration par défaut de 10 secondes. Si vous avez besoin de plus de temps,
    traitez la requête de manière asynchrone : renvoyez `{"status": "pending", "request_id": "..."}`
    et exposez le résultat via un appel d'outil distinct.
  </Accordion>

  <Accordion title="Gestion des versions">
    Chaque `PATCH` d'intégration crée une nouvelle révision. Consultez
    [`GET /v1/integrations/{id}/versions`](/api-reference/integrations#version-history)
    pour voir qui a modifié quoi. Si vous cassez le schéma d'un outil, vous pouvez
    revenir en arrière manuellement en appliquant par PATCH un ancien instantané.
  </Accordion>
</AccordionGroup>

---

## Étapes suivantes

<CardGroup cols={2}>
  <Card title="Référence des intégrations" icon="plug" href="/api-reference/integrations">
    CRUD, transfert, historique des versions.
  </Card>
  <Card title="Spécification des Function Tools" icon="screwdriver-wrench" href="/fr/tools/overview">
    Grammaire complète du schéma JSON et contrat des endpoints signés.
  </Card>
  <Card title="Vérifier les signatures" icon="shield-check" href="/fr/guides/verify-webhook-signatures">
    Appliquez le modèle de signature de webhook aux endpoints des outils.
  </Card>
  <Card title="API de transcription + historique" icon="phone" href="/api-reference/calls">
    Inspectez l'aller-retour complet d'un appel d'outil.
  </Card>
</CardGroup>
