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

# Crea un endpoint para consultar pedidos

> Ejecuta un backend con pedidos de ejemplo, verifica el formato POST de Visito y conéctalo a una herramienta personalizada.

Este ejemplo proporciona un endpoint para la [guía de herramientas personalizadas](/es/product-guides/ai-agent/custom-tools). Usa pedidos ficticios y módulos incluidos en Node.js; no requiere paquetes adicionales ni una base de datos.

Implementa **Visito → tu endpoint**. Registrar una herramienta no crea el backend por ti.

## Ejecuta el ejemplo

Con Node.js instalado, guarda este código como `order-status-tool.mjs`:

```javascript theme={null}
import { createServer } from "node:http";

const secret = process.env.TOOL_DEMO_SECRET;
if (!secret) throw new Error("Set TOOL_DEMO_SECRET before starting.");

const server = createServer(async (req, res) => {
  const reply = (status, data) => {
    res.writeHead(status, { "content-type": "application/json" });
    res.end(JSON.stringify(data));
  };
  if (req.method !== "POST" || req.url !== "/visito/order-status") {
    reply(404, { error: "route_not_found" });
    return;
  }
  if (req.headers.authorization !== `Bearer ${secret}`) {
    reply(401, { error: "unauthorized" });
    return;
  }
  try {
    let raw = "";
    let bytes = 0;
    for await (const chunk of req) {
      bytes += chunk.length;
      if (bytes > 4096) {
        reply(413, { error: "request_too_large" });
        return;
      }
      raw += chunk;
    }
    let body;
    try {
      body = JSON.parse(raw);
    } catch {
      reply(400, { error: "invalid_json" });
      return;
    }
    const number = body?.arguments?.order_number;
    if (typeof number !== "string" || !/^A-[0-9]{4}$/.test(number)) {
      reply(400, { error: "invalid_order_number" });
      return;
    }
    // Synthetic fixtures only. A-5000 simulates an unavailable upstream.
    if (number === "A-5000") {
      reply(503, { error: "order_service_unavailable" });
      return;
    }
    if (number !== "A-1003") {
      reply(200, { found: false, order_number: number });
      return;
    }
    reply(200, {
      found: true,
      order_number: number,
      status: "out_for_delivery",
      delivery_estimate: "today"
    });
  } catch {
    if (!res.headersSent) reply(500, { error: "internal_error" });
    else res.end();
  }
});
server.requestTimeout = 10000;
server.headersTimeout = 10000;
server.listen(4100, "127.0.0.1", () => {
  console.log("Demo endpoint: http://127.0.0.1:4100/visito/order-status");
});
```

Inicia el servidor:

```bash theme={null}
TOOL_DEMO_SECRET=local-demo-only node order-status-tool.mjs
```

`local-demo-only` es un valor público de ejemplo para pruebas en tu máquina. Usa un secreto privado nuevo antes de exponer el endpoint fuera de ella. El servidor devuelve datos fijos y no verifica la identidad del cliente; no es un servicio de pedidos de producción.

## Comprueba la solicitud y la respuesta

En otra terminal, ejecuta:

```bash theme={null}
curl --fail-with-body http://127.0.0.1:4100/visito/order-status \
  -H 'Authorization: Bearer local-demo-only' \
  -H 'Content-Type: application/json' \
  --data '{"arguments":{"order_number":"A-1003"},"meta":{"source":"developer_test"}}'
```

Respuesta esperada con HTTP `200`:

```json theme={null}
{
  "found": true,
  "order_number": "A-1003",
  "status": "out_for_delivery",
  "delivery_estimate": "today"
}
```

Prueba estas variantes antes de conectar Visito:

| Cambio en la solicitud                                   | Resultado esperado                                                 |
| -------------------------------------------------------- | ------------------------------------------------------------------ |
| Usa `A-9999`                                             | HTTP `200`, `found: false`. La consulta terminó sin coincidencias. |
| Usa `A-5000`                                             | HTTP `503`, fallo simulado del servicio externo.                   |
| Omite `order_number` o envía un número en lugar de texto | HTTP `400`, `invalid_order_number`.                                |
| Envía JSON inválido                                      | HTTP `400`, `invalid_json`.                                        |
| Omite o cambia el token Bearer                           | HTTP `401`, `unauthorized`.                                        |

El esquema ayuda al agente a construir los argumentos; valida también las solicitudes en tu backend. No exijas metadatos de conversación en las pruebas directas: el endpoint de prueba de Visito envía `meta.tenantId` y `meta.source: "developer_test"`. Las llamadas desde conversaciones incluyen `meta.tenantId`, `meta.conversationKey`, `meta.conversationId`, `meta.channel` y `meta.eventId` cuando corresponden.

## Haz accesible el endpoint

La URL guardada debe funcionar **desde el backend de Visito**, no solo desde tu navegador.

| Dónde se ejecuta Visito               | Configuración del endpoint                                                                                                                                                                         |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Visito alojado                        | Despliega el ejemplo con HTTPS y autenticación, o usa un túnel HTTPS de desarrollo hacia el puerto `4100`. Usa datos ficticios y un secreto privado. Detén el túnel al terminar.                   |
| Servicios totalmente locales          | La URL de loopback solo funciona si el backend que llama comparte el entorno de red de tu máquina.                                                                                                 |
| Servicios locales de Visito en Docker | `localhost` apunta al contenedor que hace la llamada. Usa un nombre de servicio o dirección del host accesible y ajusta la dirección de escucha del ejemplo. Por defecto solo escucha en loopback. |

En un contenedor puede ser necesario cambiar `127.0.0.1` por `0.0.0.0` y configurar el acceso al puerto. Restringe el acceso al entorno de pruebas. Una URL de loopback guardada en Visito alojado no permite acceder a tu laptop.

Usa la URL resultante con la ruta `/visito/order-status` en **Build → Tool calls**. Selecciona **POST**, **Bearer** y el secreto de tu endpoint. Copia el esquema y la descripción de la [guía del producto](/es/product-guides/ai-agent/custom-tools#1-agrega-la-definicion).

### Conecta una API existente

Una herramienta GET envía argumentos como parámetros de consulta, por ejemplo `?order_number=A-1003`. Una herramienta POST envía `{ "arguments": { ... }, "meta": { ... } }`. No envía un objeto de pedido plano ni sustituye argumentos en plantillas de rutas.

Si tu proveedor espera `/orders/A-1003`, otro cuerpo de solicitud o credenciales OAuth que se renuevan, resuelve esos detalles en tu endpoint adaptador. Valida la solicitud, consulta al proveedor y devuelve un JSON pequeño. Las credenciales del proveedor permanecen en tu servidor; el secreto de la herramienta autentica a Visito frente al adaptador.

## Prueba la definición guardada

Puedes mantener **Activa** apagada para esta prueba por API. Obtén el ID con `GET https://platform-api.visitoai.com/m2m/v1/tools` usando una clave con `tools:read`. Define `VISITO_API_KEY` en tu terminal desde un almacenamiento seguro. Para ejecutar la prueba, la clave debe pertenecer al espacio de pruebas y tener `tools:execute`.

Sustituye `YOUR_TOOL_ID` antes de ejecutar:

```bash theme={null}
curl --fail-with-body \
  "https://platform-api.visitoai.com/m2m/v1/tools/YOUR_TOOL_ID/test" \
  -H "Authorization: Bearer $VISITO_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"input":{"order_number":"A-1003"}}'
```

La prueba llama a tu endpoint y crea un log. Comprueba `ok: true` y `output`; una ejecución fallida puede devolver HTTP `200` desde la API de prueba de Visito con `ok: false` y un `error`. Que curl termine correctamente no basta.

Esta clave API de Visito es distinta de `TOOL_DEMO_SECRET`. No la pegues en el campo Secreto de la herramienta para autenticarte con este ejemplo.

## Prueba el comportamiento del agente

En un espacio sin canales de clientes activos, habilita la definición y abre un chat nuevo en Playground:

* “¿Dónde está el pedido A-1003?” debe mostrar actividad completada y una respuesta basada en el estado.
* “¿Ya enviaron mi pedido?” debe pedir el número.
* “¿Dónde está el pedido A-9999?” debe mostrar actividad completada y explicar que no se encontró el pedido.
* “¿Dónde está el pedido A-5000?” debe mostrar actividad fallida y reconocer el error de consulta.

Son resultados esperados, no sesiones capturadas. Revisa **Build → Tool calls → Logs de actividad** para comprobar la entrada y respuesta reales. Consulta los [ejemplos de Playground](/es/product-guides/ai-agent/custom-tools#3-prueba-la-experiencia-en-playground) para distinguir lo que ve el operador de lo que recibe el cliente.

## Sustituye el ejemplo por tu sistema

Reemplaza los datos fijos con una lectura autorizada de tu sistema de pedidos. Autentica a Visito, verifica que el solicitante pueda ver el pedido y devuelve solo los campos necesarios. Los metadatos de conversación aportan contexto, pero no prueban que el pedido pertenezca al cliente.

Usa estados claros y estimaciones de entrega basadas en tu sistema. No devuelvas un estado de envío si el proveedor falla. Limita las solicitudes y responde dentro del timeout; Visito también puede limitar el tiempo mediante su presupuesto de ejecución.

Al terminar, desactiva la definición de demostración, detén el servidor con **Ctrl+C** y cierra cualquier túnel de desarrollo. Mantén las cancelaciones y otras modificaciones en herramientas con autorización separada.
