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

Developer cookbook

Créer une intégration d

Permettez à votre agent d

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.

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

Tableau de bord

Ouvrez Connexions → APIs, créez ou modifiez la connexion API, passez l’éditeur de paramètres en JSON, puis ajoutez-y le format.

API d’intégrations

Créez la spécification avec POST /v1/integrations, ou mettez-la à jour avec PATCH /v1/integrations/{id}.

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

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.

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 :

"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é :

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" }
  }'
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 :

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 :

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

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

6. Tester la boucle

Lancez une session micro 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 :

{
  "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 ; 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.

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

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. »).

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.

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.

Gestion des versions

Chaque PATCH d'intégration crée une nouvelle révision. Consultez GET /v1/integrations/{id}/versions 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é.


Étapes suivantes