--- title: Chatbot con IA para tienda online: responde con tus políticas y dice dónde está el pedido description: Cómo crear un chatbot con IA para tienda online con la API de Zavu: responde con tus políticas de envío y devoluciones, consulta pedidos y pasa a una persona. date: 2026-10-19 author: Zavu Team locale: es source: https://www.zavu.dev/es/blog/chatbot-ia-tienda-online tags: AI Agents, Ecommerce, Tiendanube, WhatsApp, Telegram, Tutorial, API v2 --- # Chatbot con IA para tienda online: responde con tus políticas y dice dónde está el pedido ## TL;DR - **Responde con tus políticas.** El agente busca en una base de conocimiento con tus documentos de envíos, devoluciones y preguntas frecuentes antes de contestar. - **El estado del pedido sale de tu tienda.** Una tool `get_order_status` llama a tu endpoint HTTPS, que verifica la firma y devuelve el estado real. - **Lo pruebas sin escribirle a clientes.** `POST /v2/agents/{agentId}/test` devuelve la respuesta y la tool que eligió el modelo; con el proveedor `zavu` la prueba se cobra como una respuesta real. - **Empiezas en Telegram y cambias de canal después.** Telegram se conecta con una llamada a la API; WhatsApp e Instagram, desde **Canales** en el dashboard, con el mismo agente. - **Responder desde la bandeja no pausa al agente.** Solo lo pausa el paso `transfer` de un flujo, hasta que alguien de tu equipo responde. Una tienda online recibe casi siempre las mismas cinco preguntas: dónde está mi pedido, cuánto cuesta el envío, puedo devolverlo, lo tienen en otra talla, cuánto demora en llegar. Un chatbot con IA para tienda online vale la pena cuando responde eso con tus políticas reales y tus pedidos reales, no con lo que el modelo se imagina. Este tutorial lo arma sobre la API de Zavu, de punta a punta, con HTTP simple. Sirve igual si vendes en Tiendanube, Shopify o tu propio sitio. Al final tienes: - Un canal de chat conectado (Telegram para empezar, WhatsApp o Instagram cuando salgas a producción). - Un agente con IA que responde desde una base de conocimiento con tu política de envíos, de cambios y devoluciones, y tus preguntas frecuentes. - Una tool `get_order_status`: el agente llama a tu endpoint, tú verificas la firma y respondes con el estado del pedido desde tu tienda. - Una forma de probar todo sin escribirle a ningún cliente. - Una palabra clave que pasa la conversación a una persona, y el agente callado hasta que esa persona responde. Si prefieres hacerlo con clics, [el paso a paso del agente de soporte](/es/blog/agente-de-soporte-con-ia) arma la versión de Telegram desde el dashboard, sin código. Este post es la versión por API, con tus pedidos dentro del loop. ## Lo que necesitas - **Una cuenta de Zavu y una API key.** Todas las llamadas de abajo envían `Authorization: Bearer $ZAVUDEV_API_KEY`. - **Un canal donde te escriben tus clientes.** Cada uno cuesta distinto ponerlo en marcha: - **Telegram**: un token de bot de @BotFather. Una llamada a la API, sin revisión, sin cuenta de Meta. La forma más rápida de tener algo real para probar. - **WhatsApp**: una cuenta de WhatsApp Business por el registro de Meta, conectada desde **Canales** en el dashboard. Es el canal con más fricción para conectar, y en México, Colombia, Chile o Argentina suele ser el que tus clientes realmente usan. - **Instagram**: también pasa por Meta, desde **Canales** en el dashboard. - **Un endpoint HTTPS de tu lado** que sepa buscar un pedido. Tiene que ser público: Zavu rechaza una tool cuya URL no es una dirección HTTPS pública. - **Tus políticas en texto.** Envíos, cambios y devoluciones, medios de pago, lo que respondes todas las semanas por DM. Los SDKs (`@zavudev/sdk`, `zavudev`) llaman a la API v1. La API v2 se llama por HTTP, así que los ejemplos usan `curl` y `fetch`. Dos piezas de este armado todavía no tienen endpoint en v2, la base de conocimiento y las tools, y viven en v1 bajo `/v1/agents/{agentId}/...`, con la misma key y el mismo id de agente. La [guía de migración](https://docs.zavu.dev/api-v2/migrate-from-v1) lista lo que se queda en v1. Cada paso dice qué versión usa. ## 1. Conectar un canal (v2) Parte con Telegram. Crea un bot con @BotFather, copia el token y conéctalo: ```bash curl -X POST https://api.zavu.dev/v2/channels \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{"type": "telegram", "botToken": "123456789:AA..."}' ``` El `201` lista las entradas de la dirección que conectaste. La dirección de un bot de Telegram es `@nombredelbot`: ```json { "items": [ { "address": "@tienda_lupita_bot", "type": "telegram", "active": true, "v1SenderId": "kd7..." } ] } ``` Si Telegram rechaza el token, no se crea nada y recibes `400 invalid_bot_token`, con las palabras de Telegram en `details.telegramSaid`. Vuelve a copiar el token desde @BotFather. Revisa `active`. Una conexión que en tu plan cuesta dinero nace apagada; la enciendes con `POST /v2/channels/{address}/{type}/activate`, y la respuesta dice cuánto cobró en `chargedCents` y `monthlyCents`. Detalle en [Direcciones y canales](https://docs.zavu.dev/api-v2/addresses). WhatsApp e Instagram no se conectan con este endpoint. Se conectan desde **Canales** en el dashboard; una vez conectados aparecen en `GET /v2/channels` y el resto del tutorial funciona igual, con el número (por ejemplo `+5215512345678`) o el `@usuario` como dirección. ## 2. Crear el agente (v2) ```bash curl -X POST https://api.zavu.dev/v2/agents \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Asistente Tienda Lupita", "provider": "zavu", "model": "openai/gpt-4o-mini", "systemPrompt": "Atiendes a clientes de Tienda Lupita, una tienda online de México. Responde solo con la base de conocimiento y la tool get_order_status. Si la respuesta no está ahí, di que no lo sabes y ofrece hablar con una persona. Para revisar un pedido, pide el número de pedido y el email de la compra. Nunca inventes una fecha de entrega. Respuestas cortas: esto es un chat. Tutea al cliente." }' ``` `name`, `provider`, `model` y `systemPrompt` son obligatorios, y v2 nombra todos los que faltan en un solo `400`. `provider` es `openai`, `anthropic`, `google`, `mistral` o `zavu`. `zavu` es el proveedor administrado: no necesitas API key propia y cada respuesta se cobra de tu saldo en Zavu por token. Con cualquier otro proveedor envías `apiKey` y los tokens se cobran en tu cuenta de ese proveedor. Con `zavu`, `model` se escribe como `proveedor/modelo`, por ejemplo `openai/gpt-4o-mini`; el selector de modelos del dashboard lista el catálogo vigente. Dos cosas de la respuesta importan. El agente se crea **desactivado**, y su `channels` viene vacío: todavía no le responde a nadie. Guarda el `id`. ```javascript const res = await fetch("https://api.zavu.dev/v2/agents", { method: "POST", headers: { Authorization: `Bearer ${process.env.ZAVUDEV_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Asistente Tienda Lupita", provider: "zavu", model: "openai/gpt-4o-mini", systemPrompt: SYSTEM_PROMPT, }), }); const { agent } = await res.json(); console.log(agent.id, agent.enabled); // "kh7...", false ``` El prompt de sistema es donde fallan la mayoría de los chatbots. "Responde solo con la base de conocimiento" y "nunca inventes una fecha de entrega" son las dos líneas que evitan que un bot de ecommerce prometa lo que tu bodega no puede cumplir. ## 3. Cargar tus políticas en una base de conocimiento (v1) Una base de conocimiento es un conjunto de documentos que el agente busca antes de responder. Créala sobre el agente y agrega un documento por tema: ```bash curl -X POST https://api.zavu.dev/v1/agents/$AGENT_ID/knowledge-bases \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Políticas de la tienda", "description": "Envíos, cambios, pagos y preguntas frecuentes"}' ``` ```bash curl -X POST https://api.zavu.dev/v1/agents/$AGENT_ID/knowledge-bases/$KB_ID/documents \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Política de envíos", "content": "# Envíos\n\nLos pedidos pagados antes de las 14:00 (hora CDMX) salen el mismo día hábil.\nEnvío estándar a todo México: 3 a 6 días hábiles, gratis en compras desde $999 MXN.\nEnvío express a CDMX, Guadalajara y Monterrey: 1 a 2 días hábiles, $149 MXN.\nNo enviamos a apartados postales." }' ``` Repite para cambios y devoluciones, medios de pago (tarjeta, OXXO, transferencia, lo que uses) y preguntas frecuentes. Un documento por tema se recupera mejor que un documento largo con todo. Cada documento se procesa después de crearse; su `isProcessed` pasa a `true` cuando ya está dividido y se puede buscar. Si vendes en Tiendanube o Shopify, este texto ya lo tienes: está en las páginas de políticas de tu tienda. Pégalo tal cual y corrige lo que esté viejo. El agente se lo va a repetir a tus clientes tal como está escrito. ## 4. Darle una tool `get_order_status` (v1) La base de conocimiento responde "cuánto demora el envío". No puede responder "dónde está el pedido 1042". Para eso el agente llama a una tool: un endpoint HTTPS tuyo al que Zavu llama con argumentos que completa el modelo. ```bash curl -X POST https://api.zavu.dev/v1/agents/$AGENT_ID/tools \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "get_order_status", "description": "Consulta el estado de un pedido. Úsala cuando el cliente pregunte dónde está su pedido o cuándo llega. Necesita el número de pedido y el email de la compra.", "webhookUrl": "https://tiendalupita.mx/zavu/tools/order-status", "parameters": { "type": "object", "properties": { "order_number": { "type": "string", "description": "Número de pedido, por ejemplo 1042" }, "email": { "type": "string", "description": "Email usado en la compra" } }, "required": ["order_number", "email"] } }' ``` El `201` trae `tool.webhookSecret`. **Solo se devuelve al crear la tool**, nunca en una lectura posterior. Guárdalo. Si lo pierdes, rótalo con `POST /v1/agents/{agentId}/tools/{toolId}/webhook/secret`. ### Lo que recibe tu endpoint Zavu envía un `POST` con este cuerpo JSON: ```json { "tool": "get_order_status", "arguments": { "order_number": "1042", "email": "mariana@example.com" }, "context": { "conversationId": "js72...", "channel": "telegram", "contactPhone": "...", "messageId": "jd7..." }, "timestamp": 1760889600000 } ``` y estos headers: `X-Zavu-Tool` (el nombre de la tool), `X-Zavu-Timestamp`, y `X-Zavu-Signature`, el HMAC-SHA256 en hexadecimal del cuerpo crudo con el secreto de la tool. Sin prefijo y sin el timestamp mezclado. `contactPhone` es el identificador desde el que escribió el cliente: su número en WhatsApp, el chat id en Telegram. ### El endpoint ```javascript import express from "express"; import crypto from "node:crypto"; const app = express(); const SECRET = process.env.ZAVU_TOOL_SECRET; // Cuerpo crudo: la firma es sobre los bytes exactos que envió Zavu. app.post("/zavu/tools/order-status", express.raw({ type: "application/json" }), async (req, res) => { const expected = crypto.createHmac("sha256", SECRET).update(req.body).digest("hex"); const received = req.get("X-Zavu-Signature") ?? ""; const ok = received.length === expected.length && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected)); if (!ok) return res.status(401).json({ error: "firma inválida" }); const { arguments: args } = JSON.parse(req.body.toString("utf8")); // Tu tienda: la API de Tiendanube, la de Shopify o tu propia base de datos. const order = await findOrder(args.order_number); // No confirmes un pedido a quien no puede demostrar que es suyo. if (!order || order.email.toLowerCase() !== String(args.email).toLowerCase()) { return res.json({ found: false }); } res.json({ found: true, status: order.status, // "pagado" | "enviado" | "entregado" | ... carrier: order.carrier ?? null, // Estafeta, DHL, Servientrega, Andreani... trackingUrl: order.trackingUrl ?? null, estimatedDelivery: order.estimatedDelivery ?? null, }); }); app.listen(3000); ``` La misma verificación en Python: ```python import hmac, hashlib def valid(raw_body: bytes, signature: str, secret: str) -> bool: expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature or "") ``` Lo que devuelves es lo que lee el agente, así que devuelve datos, no prosa: un estado, una paquetería, un link de seguimiento, una fecha cuando la tengas. Una respuesta que no es 2xx le llega al agente como una llamada fallida y responde esquivando el error, así que un `found: false` con `200` da una mejor respuesta que un `404`. Mantén el endpoint rápido: el cliente está esperando la respuesta mientras corre. Pedir el email es una decisión tuya, no un requisito de Zavu. En Telegram no tienes un número para comparar con el pedido, y un número de pedido solo es fácil de adivinar. ## 5. Conectar el agente y encenderlo (v2) ```bash curl -X POST https://api.zavu.dev/v2/agents/$AGENT_ID/channels \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{"address": "@tienda_lupita_bot", "type": "telegram"}' ``` `address` y `type` son una entrada de `GET /v2/channels`. Una dirección responde con un agente a la vez: si otro agente ya la tiene, el `400` dice cuál. Una dirección que el proyecto no tiene es `404 not_found`. El SMS de una vía se rechaza con `409 channel_cannot_receive`, porque nadie puede responderle y el agente nunca contestaría. Después lo activas: ```bash curl -X PATCH https://api.zavu.dev/v2/agents/$AGENT_ID \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{"enabled": true}' ``` ## 6. Probarlo sin escribirle a nadie (v2) `POST /v2/agents/{agentId}/test` corre el prompt, el modelo y la base de conocimiento del agente contra un mensaje y te devuelve la respuesta en vez de enviarla. No sale ningún mensaje y no se registra ninguna ejecución. ```bash curl -X POST https://api.zavu.dev/v2/agents/$AGENT_ID/test \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "el email es mariana@example.com", "history": [ { "role": "user", "content": "hola, dónde está mi pedido 1042?" }, { "role": "assistant", "content": "Lo reviso. ¿Con qué email hiciste la compra?" } ] }' ``` La respuesta: ```json { "success": true, "text": "...", "knowledgeChunksUsed": 0, "plannedToolCalls": [ { "name": "get_order_status", "arguments": "{\"order_number\":\"1042\",\"email\":\"mariana@example.com\"}" } ], "executedToolCalls": [], "warnings": [ "The model was offered 1 tool (get_order_status) but none ran, since running them would reach your real handlers. ..." ], "inputTokens": 812, "outputTokens": 41, "latencyMs": 1430, "cost": 0.000146 } ``` Mira tres campos: - **`plannedToolCalls`**: la tool que eligió el modelo y los argumentos que completó. Por defecto no corre nada, así que la respuesta se detiene en esa elección. - **`knowledgeChunksUsed`**: pregunta "¿envían a apartados postales?" y debería ser mayor que cero. Cero significa que la respuesta no salió de tus documentos. - **`warnings`** (en inglés): lo que una prueba en seco no puede demostrar. Un agente desactivado, un agente sin canal conectado, tools ofrecidas pero no ejecutadas, flujos que no se evaluaron, metadatos de contacto que en una prueba no existen. Para correr el loop completo, el modelo llamando a tu endpoint real y respondiendo con el resultado, agrega `"executeTools": true`. Tu endpoint se llama de verdad, y `executedToolCalls` lista lo que corrió con `ok` y el `error` si hubo. Usa un pedido de prueba. Una prueba en un agente `zavu` se cobra igual que una respuesta real, al mismo precio por token, y el `cost` de la respuesta dice cuánto. Con el saldo en cero se rechaza con `402 insufficient_balance`; en un agente con tu propia key la paga tu proveedor. Después escríbele al bot desde tu propia cuenta de Telegram. Esa es la primera conversación real. ## 7. Ver las conversaciones y tomar el control En el dashboard, cada chat que atiende el agente aparece en **Conversaciones**, junto a los mensajes de todos los demás canales. Puedes abrir un hilo y responder tú. Conviene ser preciso con lo que hace esa respuesta. **Responder desde Conversaciones no pausa al agente.** El siguiente mensaje del cliente lo vuelve a contestar el agente. El agente solo se queda callado mientras hay un traspaso abierto, y un traspaso se abre cuando un flujo llega a un paso `transfer`. Así que dale al cliente una forma de pedir una persona. Los flujos son v1: ```bash curl -X POST https://api.zavu.dev/v1/agents/$AGENT_ID/flows \ -H "Authorization: Bearer $ZAVUDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Hablar con una persona", "trigger": { "type": "keyword", "keywords": ["asesor", "humano", "hablar con alguien"] }, "steps": [ { "id": "traspaso", "type": "transfer", "config": { "message": "Alguien de nuestro equipo te responde por aquí en breve." } } ], "enabled": true, "priority": 10 }' ``` Un trigger `keyword` busca la palabra dentro del mensaje, sin distinguir mayúsculas. Cuando se dispara, el cliente recibe el mensaje, la sesión queda `transferred` y el agente deja de responderle a ese contacto. Sus siguientes mensajes siguen llegando a Conversaciones. En cuanto alguien de tu equipo responde, desde Conversaciones o desde la API, el traspaso termina y el agente retoma el hilo. Nadie recibe un aviso por ti: no hay cola ni asignación. Si tu equipo no mira Conversaciones, pon un paso `tool` antes del `transfer` que avise a tu Slack o a tu sistema de soporte. Para la diferencia entre esto y un bot de menús, lee [agente de IA vs chatbot](/es/blog/ai-agent-vs-chatbot). ## Qué puede salir mal | Lo que ves | Por qué | Qué hacer | |---|---|---| | `400 invalid_bot_token` | Telegram rechazó el token | Cópialo de nuevo desde @BotFather | | `400` nombrando `name, provider, model, systemPrompt` | Faltan campos al crear | Envía los cuatro | | `400` "already answered by the agent ..." | La dirección ya tiene otro agente | Desconéctalo allá o usa otra dirección | | `409 channel_cannot_receive` | Conectaste SMS de una vía | Usa un canal donde el cliente pueda responder | | `402 insufficient_balance` al probar | Modelo de Zavu, saldo en cero | Carga saldo o usa tu propia key | | El agente responde pero inventa fechas | No está llamando a la tool | Revisa `plannedToolCalls`; afina la `description` de la tool | | `knowledgeChunksUsed: 0` | Documentos sin procesar o poco relevantes | Revisa `isProcessed`; divide documentos largos por tema | Los errores de v2 son planos, `{code, message, details}`. La [referencia de errores](https://docs.zavu.dev/api-v2/errors) los lista. ## Salir a producción: tu chatbot con IA para tienda online en WhatsApp - **Muévete al canal que usan tus clientes.** Conecta WhatsApp o Instagram desde **Canales** y luego `POST /v2/agents/{agentId}/channels` con esa dirección. Mismo agente, misma base de conocimiento, misma tool: así el bot de prueba en Telegram pasa a ser tu chatbot para WhatsApp para ecommerce sin reescribir nada. - **Costos de WhatsApp.** Meta cobra los mensajes de servicio que pasan de los 1,000 gratis por número al mes, y deja de entregarlos si la cuenta de WhatsApp Business no tiene medio de pago. [Aquí está cómo funciona](/es/blog/whatsapp-service-messages-payment-method). - **Costo del agente.** Con `zavu`, cada respuesta se cobra de tu saldo por token. Lo ves en las ejecuciones y estadísticas del agente (v1: `GET /v1/agents/{agentId}/executions` y `/stats`, o la página del agente en el dashboard). - **Límites.** Los límites diarios y mensuales de envío dependen de tu plan y tu verificación. La página de [límites de envío](https://docs.zavu.dev/concepts/sending-limits) tiene los números. - **Mantén las políticas al día.** Si cambia tu plazo de devolución, actualiza el documento (un `PATCH` sobre el documento lo vuelve a procesar). El bot repite lo que dice la base de conocimiento, incluido lo que está desactualizado. Si buscas el enfoque de chatbot para WhatsApp desde cero, [cómo crear un chatbot de WhatsApp](/es/blog/como-crear-chatbot-whatsapp) lo cubre. Para ver dónde encaja esto en la nueva API, lee [el anuncio de Zavu 2.0](/es/blog/zavu-2-0) y el [resumen de v2](https://docs.zavu.dev/api-v2/overview).