ThunderPhone 2.0 ya está disponible.Empieza por tu cuenta desde 2¢/min.Lee el anuncio

Function Tools

Herramientas de función

Proporciona a tus agentes de IA herramientas de función que llaman a API externas durante la conversación: obtén datos de clientes, agenda citas, actualiza registros, con parámetros tipados.

Las herramientas de función permiten que tus agentes de IA invoquen API externas durante las llamadas telefónicas. Úsalas para consultar datos de clientes, verificar disponibilidad, reservar citas o realizar cualquier acción que admita tu backend.

Cómo funciona

  1. Defines herramientas con un esquema (qué argumentos acepta la herramienta)
  2. Proporcionas una configuración de endpoint (dónde ThunderPhone llama a tu API) o la omites para recibir llamadas de herramientas en el webhook de tu organización
  3. Durante una llamada, la IA decide cuándo usar una herramienta según la conversación
  4. ThunderPhone llama a tu endpoint con los argumentos de la herramienta
  5. La respuesta de tu API se devuelve a la IA para continuar la conversación
CapacidadDónde se ejecutaConfiguración
Herramientas integradasThunderPhoneInstrucciones del prompt; algunas herramientas también necesitan una configuración del agente
Conexiones de aplicacionesThunderPhone y el proveedor conectadoConecta la cuenta y adjunta acciones aprobadas
Conexiones de API y herramientas de funciónTu API HTTPDefine el endpoint y el esquema, o recibe llamadas de función mediante un webhook
Servidores MCPUn servidor MCP remotoAgrega el servidor, descubre sus herramientas y adjúntalo al agente

Esquema de la herramienta

Cada herramienta sigue esta estructura:

{
  "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
}

Configuración de la herramienta

CampoTipoObligatorioDescripción
timeoutnúmeroNoTiempo máximo de ejecución en segundos (predeterminado: 20, máximo: 180)

Definición de función

CampoTipoObligatorioDescripción
namecadenaSíIdentificador único de la herramienta
descriptioncadenaSíExplica a la IA cuándo usar esta herramienta
parametersobjetoSíEsquema JSON para los argumentos de la herramienta

Configuración del endpoint

CampoTipoObligatorioDescripción
urlcadenaSíURL del endpoint de tu API
methodcadenaNoMétodo HTTP (predeterminado: POST)
headersobjetoNoEncabezados personalizados que se incluirán

Dos rutas de invocación

La solicitud que recibe tu servidor depende de si la herramienta tiene un endpoint:

Herramienta con endpointHerramienta sin endpoint
Destino de la solicitudDirectamente a endpoint.urlLa URL de webhook heredada de tu organización
CuerpoFormato de solicitud seleccionado (argumentos sin envoltorio de forma predeterminada)Envoltorio telephony.tool / web.tool
EncabezadosTus endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature
Clave de firmaSecreto de webhook de la organizaciónSecreto de webhook de la organización

Ambas rutas son bloqueantes: la IA espera el resultado a mitad de la oración. El tiempo de espera predeterminado es de 20 s; configura el timeout de nivel superior de la herramienta para permitir una ejecución más larga, hasta el máximo de la plataforma de 180 s. Mantén los controladores rápidos. Una combinación está bien: en una llamada cuya organización tiene una URL de webhook, las herramientas con un endpoint se llaman directamente y las demás recurren al webhook.

Llamadas directas a endpoints

Cuando la IA invoca una herramienta que tiene un endpoint, ThunderPhone envía una solicitud a tu URL:

Encabezados de la solicitud

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

Los encabezados personalizados de tu endpoint.headers siempre se incluyen literalmente, además de estos encabezados con espacio de nombres de ThunderPhone:

  • X-ThunderPhone-Signature-V2 — HMAC-SHA256 con marca de tiempo sobre el método, la ruta, la consulta y el cuerpo, firmado con tu secreto de webhook de la organización. Verifica este y rechaza las solicitudes con más de 5 minutos de antigüedad; consulta Verificar firmas de webhook
  • X-ThunderPhone-Signature — HMAC-SHA256 heredado de los bytes exactos del cuerpo de la solicitud, con la misma clave, que aún se envía para los verificadores existentes
  • X-ThunderPhone-Call-ID — El ID de la llamada actual

Se establece Content-Type: application/json, a menos que tu endpoint.headers lo reemplace; un Content-Type personalizado tiene prioridad.

Formato de solicitud

Cada conexión de API tiene una configuración request_format. Elige vapi o retell cuando una herramienta importada deba seguir llamando a un endpoint creado para esa plataforma. Las conexiones nuevas y existentes usan standard de forma predeterminada.

Para POST / PUT / PATCH, ThunderPhone serializa el contenedor seleccionado de forma canónica (claves ordenadas, separadores compactos). Ambas firmas cubren estos bytes exactos.

Estándar

standard envía solo los argumentos de la herramienta, sin envoltorio. Este es el valor predeterminado y conserva el cuerpo de solicitud original de ThunderPhone byte por byte:

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

Vapi

vapi envía el mensaje de servidor tool-calls de Vapi con la estructura documentada por Vapi. El call.id es el ID de llamada de ThunderPhone. El id de la llamada de herramienta es único para la invocación y se reutiliza para coincidir con el resultado del endpoint. La llamada aparece tanto en toolCallList como en toolWithToolCallList, con function.arguments como un objeto 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" }
          }
        }
      }
    ]
  }
}

No se envían campos exclusivos de Vapi como assistant, artifact y el objeto call completo.

Devuelve el contenedor de respuesta de Vapi con el ID de llamada de herramienta correspondiente:

{
  "results": [
    {
      "toolCallId": "call_00112233445566778899aabbccddeeff",
      "result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
    }
  ]
}

ThunderPhone proporciona a la IA la cadena result sin el contenedor. También acepta un valor JSON para ofrecer compatibilidad con endpoints que ya devuelven uno. Un error en lugar de result se devuelve a la IA como un error de herramienta. Si el endpoint devuelve un contenedor de Vapi malformado o no coincidente, ThunderPhone registra una advertencia y recurre a su análisis de respuesta estándar en lugar de provocar un error en la llamada.

Retell

retell envía la solicitud documentada para una función personalizada de Retell:

{
  "name": "search_appointments",
  "call": { "call_id": "987654321" },
  "args": { "date": "2025-01-02", "service": "consultation" }
}

El objeto call incluye solo call_id. ThunderPhone no incluye campos específicos de Retell que no puede proporcionar de forma precisa al realizar el envío, como agent_id, transcript, transcript_object, metadata o variables dinámicas. Retell acepta cualquier cuerpo de respuesta; ThunderPhone lo transmite mediante el manejo de respuesta estándar.

Para GET / DELETE, los argumentos se envían como parámetros de consulta y el cuerpo está vacío independientemente de request_format. La firma V2 cubre la cadena de consulta tal como se envía; la firma heredada se calcula sobre la cadena de bytes vacía. Consulta Verificar firmas de webhooks.

Respuesta

Devuelve una respuesta JSON con el resultado de la herramienta:

{
  "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
  "timezone": "America/Los_Angeles"
}

La respuesta se formatea y se proporciona a la IA para continuar la conversación. Las respuestas que no son JSON se envuelven como {"data": "<text>"}; los tiempos de espera y fallas de conexión se reportan a la IA como errores, para que el agente pueda disculparse y continuar en lugar de quedarse bloqueado.

Envío en modo webhook

Las herramientas sin un endpoint se envían a la URL de webhook heredada de tu organización como una solicitud firmada telephony.tool (llamadas telefónicas) o web.tool (llamadas web). A diferencia de las notificaciones de auditoría entregadas a endpoints de webhook después de la ejecución, esta solicitud es la ejecución; tu respuesta HTTP es el resultado de la herramienta.

{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}

web.tool incluye origin_domain en lugar de from_number / to_number. Responde con el resultado de la herramienta como JSON: el mismo contrato de respuesta que las llamadas directas al endpoint. La solicitud se firma con el secreto de webhook de la organización, con ambos encabezados de firma, como cualquier otro webhook.


Verificación de firma

Las llamadas directas a herramientas se firman de la misma manera que los webhooks, con la clave secreta de webhooks de tu organización:

  • X-ThunderPhone-Signature-V2 firma la marca de tiempo, el método, la ruta y la consulta tal como se enviaron, así como los bytes exactos del cuerpo. Rechaza las solicitudes cuya marca de tiempo difiera más de 5 minutos de tu reloj.
  • X-ThunderPhone-Signature (heredado) firma únicamente los bytes del cuerpo, la cadena de bytes vacía para GET / DELETE. Aún se envía, sin cambios.

Los asistentes del SDK verifican el encabezado V2, incluida la ventana de 5 minutos:

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}
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 });
});

Las recetas completas —la cadena firmada, un ejemplo desarrollado, código sin el SDK, el caso GET / DELETE y la advertencia sobre no tener secreto— están en Verificar firmas de webhooks.


Ejemplo: flujo completo de reservas

Aquí tienes un conjunto de herramientas para un sistema completo de reserva de citas:

{
  "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" }
      }
    }
  ]
}

Buenas prácticas

Escribe descripciones claras

El campo description ayuda a la IA a entender cuándo usar la herramienta. Sé específico sobre lo que hace y cuándo es apropiado usarla.

Maneja los errores correctamente

Devuelve mensajes de error que la IA pueda entender: {"error": "No slots available for that date"} en lugar de errores 500 genéricos.

Mantén las respuestas concisas

Devuelve solo lo que la IA necesita para continuar la conversación. Las cargas útiles grandes ralentizan los tiempos de respuesta.

Usa los campos obligatorios con criterio

Marca los campos como required solo cuando sea realmente necesario. La IA le pedirá al usuario la información obligatoria antes de llamar a la herramienta.


Relacionado