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

# Herramientas de función

> Permite que tus agentes de IA llamen a APIs externas durante las conversaciones

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` (donde ThunderPhone llama a tu API) o la omites para recibir llamadas a 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

<Note>
  Las herramientas de función son la vía para usar tu propia API. ThunderPhone también
  incluye herramientas administradas por la plataforma que no necesitan endpoint:
  [conexiones de aplicaciones](/es/guides/connect-apps) (HubSpot, Salesforce, Slack,
  Google Calendar, Google Sheets, Cal.com),
  [conexiones de API](/es/guides/api-connections) y
  [servidores MCP](/es/guides/mcp-servers).
</Note>

***

## Esquema de la herramienta

Cada herramienta sigue esta estructura:

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

### Definición de función

| Campo         | Tipo   | Obligatorio | Descripción                                        |
| ------------- | ------ | ----------- | -------------------------------------------------- |
| `name`        | string | Sí          | Identificador único de la herramienta              |
| `description` | string | Sí          | Indica a la IA cuándo usar esta herramienta        |
| `parameters`  | object | Sí          | Esquema JSON para los argumentos de la herramienta |

### Configuración del endpoint

| Campo     | Tipo   | Obligatorio | Descripción                                 |
| --------- | ------ | ----------- | ------------------------------------------- |
| `url`     | string | Sí          | URL del endpoint de tu API                  |
| `method`  | string | No          | Método HTTP (predeterminado: `POST`)        |
| `headers` | object | No          | Encabezados personalizados que se incluirán |

<Note>
  La configuración de `endpoint` **no** se envía al modelo de IA; ThunderPhone solo la usa para ejecutar la llamada a la herramienta.
</Note>

***

## Dos rutas de invocación

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

|                         | Herramienta **con** `endpoint`                                                 | Herramienta **sin** `endpoint`                                                                          |
| ----------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| Destino de la solicitud | Directamente a `endpoint.url`                                                  | La [URL de webhook heredada](/api-reference/organizations#legacy-single-url-webhook) de tu organización |
| Cuerpo                  | **Argumentos de la herramienta sin envoltorio**                                | Envoltorio `telephony.tool` / `web.tool`                                                                |
| Encabezados             | Tus `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature`                                                             |
| Clave de firma          | Secreto del webhook de la organización                                         | Secreto del webhook de la organización                                                                  |

Ambas rutas son **bloqueantes**: la IA espera el resultado a mitad de la
oración, con un tiempo de espera de **20 s**. Mantén los controladores rápidos.
Puedes combinarlas: en una llamada cuya organización tenga 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 solicitud

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

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

* `X-ThunderPhone-Signature` — HMAC-SHA256 de los bytes exactos del cuerpo de
  la solicitud, con clave basada en tu **secreto de webhook de la organización**
* `X-ThunderPhone-Call-ID` — El ID de la llamada actual

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

<Warning>
  La firma usa como clave el secreto de webhook a nivel de organización de
  [`GET /v1/webhook`](/api-reference/organizations#legacy-single-url-webhook).
  Si tu organización nunca configuró el webhook heredado, no hay
  secreto y las llamadas a herramientas solo incluyen
  `X-ThunderPhone-Call-ID`; un controlador que falle de forma estricta
  cuando falta una firma las rechazaría.
  Configura el webhook heredado para obtener un secreto o agrega tu propio
  secreto compartido en `endpoint.headers`.
</Warning>

### Cuerpo de la solicitud

Para `POST` / `PUT` / `PATCH`, el cuerpo contiene **solo** los argumentos de la
herramienta (sin envoltorio), serializados de forma canónica (claves ordenadas,
separadores compactos):

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

Para `GET` / `DELETE`, los argumentos se envían como **parámetros de consulta**
y el cuerpo está vacío; entonces la firma se calcula sobre la cadena de bytes
vacía. Consulta
[Verificar firmas de webhook](/es/guides/verify-webhook-signatures).

### Respuesta

Devuelve una respuesta JSON con el resultado de la herramienta:

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

## Despacho en modo webhook

Las herramientas **sin** un `endpoint` se envían a la URL de webhook heredada de
tu organización como una solicitud `telephony.tool` firmada (llamadas telefónicas)
o `web.tool` (llamadas web). A diferencia de las [notificaciones de auditoría](/es/webhooks/events)
que se entregan a los endpoints de webhook después de la ejecución, esta solicitud
**es** la ejecución: tu respuesta HTTP es el resultado de la herramienta.

```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` incluye `origin_domain` en lugar de `from_number` /
`to_number`. Responde con el resultado de la herramienta como JSON; se aplica el
mismo contrato de respuesta que para las llamadas directas a endpoints. La solicitud
se firma con el secreto de webhook de la organización sobre el cuerpo sin procesar,
como todos los demás webhooks.

<Note>
  Los [endpoints de webhook](/es/webhooks/endpoints) suscritos también
  reciben una **notificación** `telephony.tool` / `web.tool` no bloqueante
  **después** de que se ejecuta cada herramienta (independientemente de la ruta
  que la ejecutó), incluida la respuesta de la herramienta; resulta útil para
  registros de auditoría. Consulta el
  [catálogo de eventos](/es/webhooks/events).
</Note>

***

## Verificación de firma

Las llamadas directas a herramientas se firman de la misma manera que los webhooks:

* HMAC-SHA256 sobre los bytes exactos del cuerpo de la solicitud (el JSON canónico:
  claves ordenadas, sin espacios adicionales)
* Con la clave secreta del webhook de tu organización
* Las herramientas `GET` / `DELETE` firman la cadena de bytes vacía

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

Las recetas completas —incluido el caso de cuerpo vacío y la advertencia sobre no tener un secreto—
están en [Verificar firmas de webhooks](/es/guides/verify-webhook-signatures).

***

## Ejemplo: flujo de reserva completo

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

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

***

## Prácticas recomendadas

<AccordionGroup>
  <Accordion title="Escribe descripciones claras">
    El campo `description` ayuda a la IA a entender **cuándo** usar la herramienta. Especifica qué hace y cuándo es apropiado usarla.
  </Accordion>

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

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

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

***

## Relacionado

<CardGroup cols={2}>
  <Card title="Conexiones de aplicaciones" icon="plug" href="/es/guides/connect-apps">
    Herramientas administradas por la plataforma para HubSpot, Salesforce, Slack, Google
    Calendar, Google Sheets y Cal.com; no se requiere endpoint.
  </Card>

  <Card title="Servidores MCP" icon="server" href="/es/guides/mcp-servers">
    Conecta un servidor MCP y permite que el agente llame a sus herramientas.
  </Card>

  <Card title="Conexiones de API" icon="code" href="/es/guides/api-connections">
    Integraciones REST reutilizables que puedes conectar a agentes.
  </Card>

  <Card title="Verifica las firmas de webhooks" icon="shield-check" href="/es/guides/verify-webhook-signatures">
    Un asistente de verificación para webhooks y llamadas a herramientas.
  </Card>
</CardGroup>
