# SendEasy documentation (es) # Integraciones con IA y MCP Conecta tu asistente de IA a la documentación oficial para crear integraciones con SendEasy usando endpoints, payloads y autenticación documentados. ## Elige una opción | Opción | Uso | Dirección | | --------------- | ------------------------------------------ | -------------------------------------------------------------------------- | | `llms.txt` | Descubrir las referencias relevantes | [Índice en español](https://docs.sendeasy.app/es/llms.txt) | | `llms-full.txt` | Leer toda la documentación en español | [Documentación completa](https://docs.sendeasy.app/es/llms-full.txt) | | Markdown | Leer una página sin el HTML de la interfaz | [Ejemplo: API Pública](https://docs.sendeasy.app/es/server/api-publica.md) | | MCP | Permitir búsquedas y lecturas bajo demanda | `https://docs.sendeasy.app/mcp` | Los índices en portugués están en `/llms.txt` y `/llms-full.txt`; en inglés, en `/en/llms.txt` y `/en/llms-full.txt`. Añade `.md` a la URL de una página para obtener su versión Markdown. Para las páginas iniciales usa `/index.md`, `/en/index.md` o `/es/index.md`. ## Conectar por MCP Añade un servidor MCP remoto en un cliente compatible con Streamable HTTP: | Parámetro | Valor | | ------------- | ------------------------------- | | Nombre | `sendeasy-docs` | | URL | `https://docs.sendeasy.app/mcp` | | Transporte | Streamable HTTP | | Autenticación | Ninguna | El formato de configuración depende del cliente. En clientes que aceptan `mcpServers` con `url`: ```json { "mcpServers": { "sendeasy-docs": { "url": "https://docs.sendeasy.app/mcp" } } } ``` Herramientas disponibles: | Herramienta | Uso | | ------------- | ---------------------------------------------------------------------------------------------------------------- | | `search_docs` | Busca con `query`, `locale` (`pt`, `en`, `es`) y `limit` (1 a 10). | | `get_doc` | Lee el `path` exacto de la página. Para continuar páginas grandes, envía el `nextOffset` recibido como `offset`. | | `list_docs` | Lista páginas y descripciones en el idioma de `locale`. | El idioma predeterminado es portugués. Usa `locale: "es"` para buscar en español. `get_doc` sigue el idioma de la ruta, por ejemplo `/es/server/webhooks`. Los recursos MCP también ofrecen índices en `sendeasy://docs/pt`, `sendeasy://docs/en` y `sendeasy://docs/es`. Este MCP consulta documentación pública. No envía mensajes, modifica contactos ni accede a cuentas. No proporciones tokens de clientes al servidor de documentación. La integración se ejecuta en tu sistema mediante la API de SendEasy con las credenciales correspondientes. ## Ejemplo de petición al asistente ```text Usa la documentación oficial de SendEasy para crear una integración en Node.js que envíe un mensaje de WhatsApp cuando un lead complete mi formulario. Busca /api/messages/send y lee la referencia completa en español. Confirma el tipo de token, los campos obligatorios y el formato del teléfono. Usa una variable de entorno para el token, maneja errores y diferencia la aceptación en cola de la entrega final. Cita las fuentes y no inventes campos. ``` Sin MCP, pide al asistente que empiece por `https://docs.sendeasy.app/es/llms.txt` y siga los enlaces Markdown relevantes. ## Autenticación de la integración La URL base de la API es `https://backend.sendeasy.app`. Usa `Authorization: Bearer TU_TOKEN` con el token adecuado: | Operación | Token | Referencia | | -------------------- | ------------------------- | ----------------------------------------------------------------- | | `/api/messages/send` | Token del canal | [API Pública](https://docs.sendeasy.app/es/server/api-publica.md) | | `/api/v1/waba/*` | API Token | [WABA API](https://docs.sendeasy.app/es/server/waba.md) | | `/api/v1/email/*` | Token del canal de correo | [Email API](https://docs.sendeasy.app/es/server/email.md) | | `/api/v1/ai/*` | API Token | [Bots API](https://docs.sendeasy.app/es/server/bots.md) | | `/api/integration/*` | Token de la integración | [Custom](https://docs.sendeasy.app/es/integrations/custom.md) | Mantén las credenciales en el servidor de tu aplicación. Consulta la [Referencia Técnica](https://docs.sendeasy.app/es/server/referencia-tecnica.md) y los [formatos de webhooks](https://docs.sendeasy.app/es/server/webhooks.md). La aceptación de una solicitud no confirma la entrega final del mensaje. --- # Crear y configurar Bots AI 1. Abre el proyecto correcto y selecciona **Bots**. 2. Haz clic en **Agregar**, indica un nombre y escribe las instrucciones. 3. Define objetivo, tono, datos que debe solicitar, límites y transferencia a humano. 4. Elige el modelo y las opciones disponibles y guarda. 5. Abre el bot para agregar archivos y medios confiables a su base de conocimiento. Crear el bot no lo activa. Asócialo directamente a una o más etapas, o agrégalo como paso de un flow y asocia ese flow a las etapas necesarias. Si no responde, verifica que esté activo, que el bot o flow esté vinculado a la etapa correcta, que el flow esté guardado, que el canal esté conectado y que el ticket haya entrado en esa etapa. Sendy puede listar, crear y actualizar bots, administrar instrucciones, archivos y medios, vincular bots a etapas y agregar un bot a un flow. Activaciones y eliminaciones requieren confirmación. Además de la atención en WhatsApp, el bot puede consultarse desde tu propia aplicación: consulta la [Bots API](https://docs.sendeasy.app/es/server/bots.md). --- # Campañas Masivas Usa campañas para comunicación saliente a escala. ## Antes del lanzamiento 1. Valida la segmentación de audiencia. 2. Valida templates/contenido. 3. Configura ventanas de envío y límites de tasa. 4. Define manejo de opt-out. ## Después del lanzamiento * Monitorea entrega y engagement. * Monitorea tasa de errores por proveedor. * Pausa rápidamente si detectas anomalías. --- # Conectar Canales Usa esta página como checklist rápido para conectar canales en tu proyecto. ## Flujo básico 1. Abre el panel de tu proyecto. 2. Ve a **Conexiones**. 3. Haz clic en **Agregar Canal**. 4. Selecciona proveedor/tipo de canal. 5. Completa credenciales/verificación. 6. Valida el estado como **Conectado**. ## Consejos operativos * Mantén un canal como predeterminado cuando sea necesario. * Monitorea eventos de reconexión y salud de webhooks. * Guarda credenciales del proveedor de forma segura. --- # Crear y publicar flows de automatización Un flow es una automatización ejecutable. Puede enviar mensajes, esperar, llamar a un Bot AI, agregar etiquetas y mover un ticket a otra etapa. ## Creación manual 1. Abre **Flows** y haz clic en **Nuevo Flow**. 2. Nombra el flow y agrega los pasos en orden de ejecución. 3. Para usar IA, agrega un paso de Bot AI y selecciona un bot existente. 4. Guarda el flow y asócialo a las etapas necesarias. 5. Prueba con un contacto interno antes de activarlo para clientes. También puedes describir la secuencia completa a Sendy. Sendy debe mostrar la vista previa, pedir confirmación y crear el flow real. Un guion o ejemplo aislado no significa que el flow fue creado. Si no inicia, revisa la asociación con la etapa y la etapa del ticket. Si el bot no se ejecuta, confirma que el paso fue guardado. Los pasos de envío también requieren un canal conectado. --- # ActiveCampaign Integración bidireccional con **ActiveCampaign** para sincronizar contactos, listas y disparar mensajes automáticos a partir de eventos de marketing. **Documentación en construcción.** Esta página pronto incluirá el paso a paso de configuración, sincronización de listas, mapeo de campos y solución de problemas comunes. Por ahora, configura la integración en **Configuración → Integraciones → ActiveCampaign** dentro de tu cuenta de Sendeasy. --- # Reservas en tu sitio web (embed) Con el **embed**, el cliente elige el servicio y el horario y confirma la reserva **sin salir de tu sitio**. Pega una línea de código en la página: el script `embed.js` monta la reserva en un `iframe` seguro, servido por `https://calendar.sendeasy.app`. La forma más rápida es generar el código en el panel: **Calendario → Página pública → Compartir**. Ahí eliges el calendario, el servicio, el formato, el color y la campaña, copias el código listo y ves una vista previa. ## Requisitos * Las reservas en el sitio habilitadas para tu empresa. * Un calendario de **horarios** con **tipos de evento** (servicios) activos. Los calendarios de alojamiento no tienen embed: usa el enlace del calendario. * Tu sitio en **https**. Las páginas en `http` o abiertas como archivo local no pueden mostrar la reserva. ## Instalación Pega el código donde debe aparecer la reserva: ```html ``` `TU_CALENDARIO` es la dirección del calendario, la misma del enlace `https://calendar.sendeasy.app/c/TU_CALENDARIO`. Para poner la reserva dentro de un elemento tuyo, marca el elemento y carga el script una vez en la página: ```html
``` ## Formatos | `data-mode` | Qué aparece | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `inline` (predeterminado) | La reserva dentro de la página, justo después de la etiqueta (o dentro del elemento marcado). La altura sigue el contenido. | | `popup` | Un botón donde está la etiqueta. El clic abre la reserva en una ventana sobre la página. En un elemento marcado que no sea ` ``` * `window.SendeasyCalendar.open(opciones)` abre la ventana y devuelve `true` (o `false` con un calendario inválido). Opciones: `calendar` (obligatorio), `type`, `color`, `lang` y `utm` (`{ source, medium, campaign, term, content }`). * `window.SendeasyCalendar.close()` cierra la ventana abierta. ## Mensajes para tu sitio (postMessage v1) ```json { "source": "sendeasy", "v": 1, "type": "..." } ``` | `type` | Campos | Cuándo | | -------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ready` | | La reserva cargó. | | `resize` | `height` (px, hasta 20000) | Cambió la altura del contenido. El `embed.js` ya ajusta el formato `inline`. | | `slot_selected` | `date` (`AAAA-MM-DD`), `time` (`HH:mm`) | El cliente eligió un horario (fecha y hora locales del establecimiento). | | `booking.created` | `code`, `status`, `startAt` | La reserva fue creada. `code` es el código de 8 caracteres (o `null`), `status` es `HOLD`, `PENDING_PAYMENT`, `AWAITING_APPROVAL`, `CONFIRMED` o `CANCELLED`, y `startAt` es el inicio en ISO 8601 (UTC). | | `booking.payment_required` | `code` | La reserva necesita pago; el botón de pago abre el checkout en una pestaña nueva. | | `closed` | | El cliente cerró la reserva (Esc o Cerrar), en los formatos `popup` y `button`. | Los mensajes **no llevan datos personales** y la reserva **no acepta mensajes** de tu página. Revisa siempre el origen: ```js window.addEventListener('message', function (event) { if (event.origin !== 'https://calendar.sendeasy.app') return var data = event.data if (!data || data.source !== 'sendeasy' || data.v !== 1) return if (data.type === 'booking.created') { gtag('event', 'reserva_creada', { status: data.status }) } }) ``` ## CSP de tu sitio Si tu sitio envía `Content-Security-Policy`, permite el dominio del calendario para el script y para el `iframe`: ```http Content-Security-Policy: script-src 'self' https://calendar.sendeasy.app; frame-src https://calendar.sendeasy.app ``` Para fijar una versión con Subresource Integrity, lee la versión y el hash en los encabezados de `curl -sI https://calendar.sendeasy.app/embed.js` (los `X-Sendeasy-Embed-*`) y usa `embed.js?v=VERSION` con `integrity="sha384-HASH"` y `crossorigin="anonymous"`. ## Límites * **El pago siempre se abre en la pestaña principal**, en una pestaña nueva, fuera de tu sitio. El checkout y los enlaces de gestión nunca se muestran dentro de un `iframe`. * Solo sitios `https` y calendarios de horarios con tipos de evento. * El `iframe` no guarda nada entre visitas. * La página dentro del `iframe` no se indexa. Para aparecer en Google, usa la Página pública y el [Perfil de Empresa en Google](https://docs.sendeasy.app/es/integrations/calendario/google-business-profile.md). --- # Perfil de Empresa en Google El **Perfil de Empresa en Google** aparece en la Búsqueda y en Maps cuando alguien busca tu negocio. Con dos enlaces de Sendeasy en el perfil, el cliente reserva directo desde allí, y las reservas quedan registradas con el origen **Perfil de Empresa en Google**. El panel genera los dos enlaces: **Calendario → Página pública → Compartir**, sección **Perfil de Empresa en Google**, con el paso a paso y una lista de verificación. ## Los dos enlaces | Campo en Google | Enlace de Sendeasy | | ----------------------------------------- | ---------------------------------------------------------------------------------------- | | **Sitio web** | Tu Página pública, cuando está publicada. Sin ella, el enlace de reserva del calendario. | | **Enlaces de citas** (botón **Reservar**) | El enlace de reserva del calendario, opcionalmente ya en un servicio. | Los dos llevan siempre las mismas UTM: ```text utm_source=google&utm_medium=business_profile&utm_campaign=gbp ``` ```text https://calendar.sendeasy.app/p/clinica-centro?utm_source=google&utm_medium=business_profile&utm_campaign=gbp https://calendar.sendeasy.app/c/clinica?type=consulta-30&utm_source=google&utm_medium=business_profile&utm_campaign=gbp ``` ## Paso a paso 1. Entra en [business.google.com](https://business.google.com/) con la cuenta que administra el perfil. 2. Haz clic en **Editar perfil** y abre la sección **Contacto**. 3. En **Sitio web**, pega el enlace del Sitio web y guarda. 4. En **Enlaces de citas**, agrega el enlace del botón Reservar y guarda. Si aparece más de un proveedor, deja tu enlace como preferido. 5. Espera la revisión de Google (puede tardar algunos días) y revisa el botón **Reservar** en el perfil. 6. Abre los dos enlaces en una ventana privada y haz una reserva de prueba. ## Lista de verificación * [ ] Calendario con dirección pública y servicios activos. * [ ] Calendario vinculado a la Página pública, y la Página pública publicada. * [ ] Mismo nombre, dirección y teléfono en Google y en la Página pública. * [ ] Enlaces probados en una ventana privada. * [ ] Sitio web y Enlaces de citas guardados en Google. ## Cómo se mide el origen Quien llega por uno de estos enlaces y reserva queda con el origen **Perfil de Empresa en Google**. Lo decide `utm_medium=business_profile`; sin él, la visita de Google cuenta como búsqueda orgánica. Las visitas de un asistente de IA, del directorio de Sendeasy o de un anuncio de Google (con `gclid`) mantienen su propio origen. El origen vale para las reservas del mismo navegador en los 30 días siguientes, hasta la próxima visita con otro origen. Para reservar dentro de tu propio sitio, mira [Reservas en tu sitio web (embed)](https://docs.sendeasy.app/es/integrations/calendario/embed.md). --- # Integración con Calendario Permite que **cualquier aplicación** conecte un Google Calendar a un calendario de Sendeasy y mantenga ambos lados sincronizados — sin pasar por la consola. La conexión se realiza mediante la **API pública** (`/api/v1/calendars/...`), autenticada con el **token de API** de la empresa. Una vez conectada, Sendeasy se encarga de los **webhooks** y de la **sincronización** de eventos. **Traducción en progreso.** La guía completa está disponible en portugués: [Integração com Calendário](https://docs.sendeasy.app/integrations/calendario.md). ## Endpoints | Método | Endpoint | Descripción | | -------- | ---------------------------------------------------- | ----------------------------------- | | `GET` | `/api/v1/calendars` | Lista los calendarios de la empresa | | `GET` | `/api/v1/calendars/{id}/integrations/google/connect` | Obtiene la URL de autorización | | `GET` | `/api/v1/calendars/{id}/integrations/google` | Estado de la integración | | `PUT` | `/api/v1/calendars/{id}/integrations/google` | Actualiza las opciones | | `DELETE` | `/api/v1/calendars/{id}/integrations/google` | Desconecta la integración | --- # Integración Personalizada La integración **Custom** genera un token genérico que tu aplicación usa para llamar a la API pública de Sendeasy y enviar mensajes (WhatsApp, Email o SMS), sin necesidad de un conector preconstruido. La documentación completa del endpoint, payloads por canal y códigos de respuesta está en [Integraciones (Server)](https://docs.sendeasy.app/es/server/integracoes.md). Configura el token en **Configuración → Integraciones → Integración Personalizada** dentro de tu cuenta de Sendeasy. El token puede ser visualizado en cualquier momento por el administrador de la cuenta. --- # Eduzz — Eventos Sendeasy reacciona a 5 tipos de eventos de Eduzz: boleta/pix generada, pago confirmado, reembolso, cancelación y expiración. **Documentación en construcción.** Esta página pronto incluirá la lista completa de eventos con payload, validación HMAC-SHA256, mapeo para envío de WhatsApp y/o email y solución de problemas. --- # Eduzz Integración con **Eduzz** vía webhooks para reaccionar a eventos de venta (factura pagada, compra confirmada, reembolso, etc.) enviando mensajes automáticos por WhatsApp y email. **Documentación en construcción.** Esta página pronto incluirá el paso a paso de configuración, los eventos soportados, validación HMAC y solución de problemas comunes. Por ahora, configura la integración en **Configuración → Integraciones → Eduzz** dentro de tu cuenta de Sendeasy. --- # Eduzz — Configuración Paso a paso para conectar Eduzz y comenzar a recibir eventos de venta. **Documentación en construcción.** Esta página pronto incluirá la creación de la clave HMAC, la URL del webhook a registrar en Eduzz, el mapeo de eventos y una prueba de extremo a extremo. --- # GreatPages Integración de Sendeasy con la plataforma **GreatPages**, para recibir leads de landing pages e iniciar conversaciones automáticamente. **Documentación en construcción.** Esta página pronto incluirá el paso a paso de configuración, el formato del payload esperado y solución de problemas comunes. Por ahora, configura la integración en **Configuración → Integraciones → GreatPages** dentro de tu cuenta de Sendeasy. --- # Respondi Integración de Sendeasy con la plataforma **Respondi**, para recibir leads y dispararlos como conversaciones de WhatsApp. **Documentación en construcción.** Esta página pronto incluirá el paso a paso de configuración, el formato del payload esperado y solución de problemas comunes. Por ahora, configura la integración en **Configuración → Integraciones → Respondi** dentro de tu cuenta de Sendeasy. --- # WABA — Eventos del webhook Sendeasy reenvía todos los eventos recibidos de Meta a tu webhook configurado: mensajes recibidos, estado de entrega/lectura, fallas, cambios de plantilla, etc. **Documentación en construcción.** Esta página pronto incluirá el payload de cada tipo de evento (`messages`, `statuses`, `template_status_update`), firma HMAC y ejemplos. --- # WABA Integración con la **WhatsApp Business API** oficial (Cloud API). Permite el envío de mensajes autenticados, plantillas aprobadas y operación a gran escala dentro de las políticas oficiales de Meta. **Documentación en construcción.** Esta página pronto incluirá el paso a paso de configuración, vinculación del número, creación y aprobación de plantillas, y solución de problemas comunes. Por ahora, configura la integración en **Configuración → Integraciones → WABA** dentro de tu cuenta de Sendeasy. ## Solicitar compartir el contacto [Solicitar compartir el contacto](https://docs.sendeasy.app/es/integrations/waba/request-contact.md): Solicite el teléfono por WABA/BSUID o ZuckZapGo/LID con la API pública de SendEasy. --- # Solicitar compartir el contacto Envíe un mensaje con el botón nativo para compartir el contacto. La persona decide si desea compartir su teléfono con la empresa. ## Rutas y autenticación Utilice el token de API de la empresa, creado en **Configuración → Integraciones → Tokens de API**. Los tokens restringidos necesitan el alcance `waba`, incluso en la ruta unificada. Los tokens anteriores sin alcances siguen permitidos. `CHANNEL_UUID` debe pertenecer a la empresa del token. | POST | Autenticación | | ----------------------------------------------- | ---------------------------------------------------------------------------- | | `/api/v1/channels/CHANNEL_UUID/request-contact` | WABA o ZuckZapGo; token de API de la empresa | | `/api/v1/waba/CHANNEL_UUID/request-contact` | Solo WABA; token de API de la empresa | | `/api/messages/request-contact` | Canal asociado al token de conexión; Authorization: Bearer TOKEN\_DEL\_CANAL | ## Ejemplo WABA: solicitar por teléfono Reemplace `CHANNEL_UUID`, `SU_API_TOKEN` y el teléfono con los datos de su integración. Incluya el código de país y utilice solo dígitos. El texto debe tener entre 1 y 1024 caracteres. ```bash curl --request POST 'https://backend.sendeasy.app/api/v1/channels/CHANNEL_UUID/request-contact' \ --header 'Authorization: Bearer SU_API_TOKEN' \ --header 'Content-Type: application/json' \ --data '{"to":"5511999999999","body":"Comparta su contacto para continuar con la atención."}' ``` ## WABA: contacto sin teléfono Si recibe solo un BSUID, pase ese identificador opaco en `to` sin modificarlo. El username es un nombre visible y no sustituye al destinatario. SendEasy envía un mensaje nativo `request_contact_info` a Meta. La disponibilidad y las reglas de envío dependen de la cuenta y del proveedor. ```json { "to": "BR.Example123", "body": "Comparta su contacto para continuar con la atención." } ``` ## ZuckZapGo: teléfono o LID Para un canal WhatsApp con proveedor ZuckZapGo/WuzAPI, utilice la misma ruta unificada e incluya `id`, obligatorio con 1 a 128 caracteres alfanuméricos. El destinatario puede ser un teléfono, JID de teléfono o LID completo. Esta integración no acepta BSUID, username, grupos ni JID con sufijo de dispositivo. ```json { "to": "123456789012345@lid", "body": "Comparta su contacto para continuar con la atención.", "id": "ContactRequest202610060001" } ``` ## Respuesta y confirmación de entrega HTTP 200 devuelve la respuesta del proveedor. En WABA, `messages[0].id` identifica el mensaje aceptado. La aceptación no confirma la entrega: siga los eventos `statuses` de Meta o consulte la ruta de estado a continuación. Este endpoint no crea por sí solo un mensaje de salida ni un ticket en el inbox WABA. ```json { "messaging_product": "whatsapp", "contacts": [{ "input": "5511999999999", "wa_id": "5511999999999" }], "messages": [{ "id": "wamid.EXAMPLE" }] } ``` ```bash curl 'https://backend.sendeasy.app/api/v1/waba/CHANNEL_UUID/messages/MESSAGE_ID/status' \ --header 'Authorization: Bearer SU_API_TOKEN' ``` ## Cuando la persona comparte el contacto El contacto compartido sigue el flujo normal de mensajes entrantes y se conserva el payload original al reenviar webhooks. En la API oficial, la tarjeta tiene `origin: "contact_request"`; el teléfono está en `contacts[].phones[].wa_id`. En ZuckZapGo, el evento `Message` tiene `contactMessage.isSelfContact: true`; el vCard contiene la identidad compartida. La correlación se hace por conversación, dentro de la empresa y canal. Una tarjeta común de terceros no debe sustituir la identidad del remitente. ## Errores y reintentos El ID estable y la idempotencia descritos aquí se aplican a ZuckZapGo. Reutilice el mismo `id` y payload cuando se permita un reintento. SendEasy no reintenta automáticamente. Incluir `id` en WABA no añade idempotencia al envío. | Error | Significado | | ----------------------------------- | ------------------------------------------------------------------------------------------ | | `CONTACT_REQUEST_BODY_INVALID` | Texto vacío o superior a 1024 caracteres. | | `CONTACT_REQUEST_RECIPIENT_INVALID` | Destinatario inválido para el proveedor. | | `CONTACT_REQUEST_ID_REQUIRED` | ZuckZapGo exige un ID alfanumérico de 1 a 128 caracteres. | | `CHANNEL_NOT_FOUND` | Canal inexistente o fuera de la empresa del token. | | `CONTACT_REQUEST_DISPATCH_UNKNOWN` | Resultado incierto después de un timeout/fallo de transporte. No reenviar automáticamente. | | `contact_request_dispatch_unknown` | El proveedor no puede determinar si despachó. No reenviar ni generar otro ID. | | `contact_request_id_conflict` | El ID se reutilizó con datos diferentes. | Otros errores conservan el estado HTTP del proveedor y sus detalles en `provider`. Confirme que la cuenta permite este tipo de mensaje. Un fallo documentado por el proveedor como anterior al despacho puede permitir un reintento con el mismo ID y payload. Vea también [API Pública](https://docs.sendeasy.app/es/server/api-publica.md) y [WABA API](https://docs.sendeasy.app/es/server/waba.md). --- # WABA — Configuración Paso a paso para vincular un número a la WhatsApp Business API (Cloud API) en Sendeasy. **Documentación en construcción.** Esta página pronto incluirá los requisitos de Meta Business, creación del app en Meta for Developers, vinculación del número, verificación del webhook y prueba de envío. --- # WABA — Plantillas Las plantillas son mensajes pre-aprobados por Meta, usados para iniciar conversaciones fuera de la ventana de 24 h. **Documentación en construcción.** Esta página pronto incluirá creación vía Sendeasy, política de aprobación de Meta, variables y ejemplos de envío con `POST /api/v1/waba/:whatsappId/send`. --- # Webhook La integración **Webhook** envía eventos de tu proyecto Sendeasy (mensajes recibidos/enviados, estado de entrega, cambios de ticket, etc.) a una URL HTTPS tuya. Los webhooks se pueden configurar en dos alcances: * **General** — recibe eventos de todos los canales de la empresa. * **Por canal específico** — recibe eventos solo del canal elegido. La documentación completa, con el formato de los eventos y ejemplos, está en [Integraciones (Server)](https://docs.sendeasy.app/es/server/integracoes.md) y [Webhooks](https://docs.sendeasy.app/es/server/webhooks.md). Configura los webhooks en **Configuración → Integraciones → Webhook** dentro de tu cuenta de Sendeasy. --- # Configurar Kanban Kanban ayuda a tu equipo a gestionar etapas, responsables y SLA. ## Configuración recomendada 1. Define etapas por proceso de negocio. 2. Configura reglas de entrada para cada etapa. 3. Configura reglas de asignación. 4. Monitorea cuellos de botella por antigüedad de etapa. ## Buenas prácticas * Mantén nombres de etapa cortos y claros. * Evita semánticas duplicadas entre etapas. * Revisa métricas de etapas semanalmente. --- # Captura de Leads La captura de leads permite tracking de origen, enriquecimiento y enrutamiento. ## Campos base * Nombre * Teléfono * Correo * Origen / metadatos UTM ## Sugerencias de enrutamiento 1. Enruta por campaña/origen. 2. Asigna por región o cola. 3. Asocia etiquetas para automatizaciones posteriores. --- # Introducción Bienvenido a la documentación de Sendeasy. Esta sección en español está enfocada en onboarding, integraciones y referencias técnicas para que los equipos implementen rápido. ## Por dónde empezar 1. Crea tu proyecto. 2. Conecta canales. 3. Configura flujos de trabajo (Kanban, bots, automatizaciones). 4. Integra usando la API Server o integraciones Web. ## Documentación para desarrolladores Si tu equipo va a integrar APIs públicas y webhooks, empieza por: * [/es/server](https://docs.sendeasy.app/es/server.md) * [/es/server/referencia-tecnica](https://docs.sendeasy.app/es/server/referencia-tecnica.md) * [/es/server/api-publica](https://docs.sendeasy.app/es/server/api-publica.md) * [/es/server/webhooks](https://docs.sendeasy.app/es/server/webhooks.md) ¿Necesitas ayuda con un flujo de integración específico? Usa el chat de soporte dentro de la plataforma y comparte tus payloads de request/response para un troubleshooting más rápido. --- # Crear un proyecto y espacio de trabajo En Sendeasy, un **proyecto es el espacio de trabajo de la empresa**. Contiene equipo, canales, contactos, tickets, Kanban, bots, flows, calendarios e integraciones. No existe un segundo paso separado para “crear el ambiente”. ## Paso a paso 1. Inicia sesión en la [consola de Sendeasy](https://console.sendeasy.app/). 2. En **Proyectos**, haz clic en **Nuevo Proyecto**. 3. Ingresa empresa y los datos del responsable: nombre, correo y móvil. 4. Haz clic en **Siguiente**, elige el plan y pulsa **Crear proyecto**. 5. Completa el checkout cuando aparezca. 6. Abre la tarjeta del proyecto para entrar al espacio de trabajo. ## Orden recomendado 1. Conecta un canal en **Canales**. 2. Crea etapas de atención en **Kanban**. 3. Invita agentes y define permisos. 4. Configura bots o flows y asócialos a las etapas correctas. 5. Realiza una conversación de prueba controlada. Cada proyecto está aislado. Canales, datos y permisos no se comparten automáticamente. --- # Configurar y usar Sendpay Sendpay crea cobros y enlaces de checkout de Sendeasy. El gateway conectado procesa el valor y lo deposita directamente en tu cuenta. | Gateway | PIX | Tarjeta | | ------------ | --- | ------- | | Stripe | No | Sí | | Mercado Pago | Sí | Sí | | Asaas | Sí | Sí | El checkout Sendpay de Sendeasy ofrece PIX y tarjeta, no boleto bancario. ## Configuración 1. Abre **Sendpay > Configuración** y elige un gateway. 2. Copia las credenciales desde el panel oficial del gateway. 3. Ingresa las claves pública y secreta solicitadas. 4. Activa los métodos compatibles y guarda. 5. Registra la URL y el secreto del webhook de Sendeasy en el gateway. 6. Crea un cobro pequeño y comprueba la actualización del estado. Con Sendy, coloca la clave en el campo seguro **Credenciales > Sendpay** y solicita la configuración por chat. La clave temporal no entra en la conversación y el cambio siempre requiere confirmación. Nunca pegues secretos en el chat. Sendy puede informar si una clave está configurada, pero no revelar su valor. --- # API Pública (Server) ## URL base ```txt https://backend.sendeasy.app ``` ## Modelos de autenticación 1. Token de canal (`/api/messages/send`, `/api/v1/email/*`) 2. API token (`/api/v1/waba/*`, `/api/v1/ai/*`) 3. Token de integración (`/api/integration/generic`) ## Endpoints principales | Método | Endpoint | Auth | | ------ | -------------------------------- | ----------------------------------------------------------------------------------------------- | | `POST` | `/api/messages/send` | Token de canal | | `POST` | `/api/integration/generic` | Token de integración | | `POST` | `/api/v1/email/send` | Token del canal de email — ver [/es/server/email](https://docs.sendeasy.app/es/server/email.md) | | `GET` | `/api/v1/email/health` | Token del canal de email | | `POST` | `/api/v1/ai/completion` | API token — ver [/es/server/bots](https://docs.sendeasy.app/es/server/bots.md) | | `POST` | `/api/v1/ai/extract` | API token | | `POST` | `/api/v1/ai/search` | API token | | `GET` | `/api/v1/ai/health` | API token | | `POST` | `/api/v1/diagnostic-submissions` | API token — ver [/es/server/diagnosticos](https://docs.sendeasy.app/es/server/diagnosticos.md) | ## Contratos de éxito y error Ejemplo de éxito: ```json { "message": "Message queued successfully" } ``` Ejemplo de error: ```json { "error": "ERR_NO_PERMISSION" } ``` ## Estrategia de traducción Mantén `error` como clave canónica y resuelve mensajes por idioma en tu cliente. --- # Bots API Usar la IA de Sendeasy **desde tu propia aplicación** — un asistente dentro de tu producto, una rutina en tu backend, una pantalla que resume documentos — sin pasar por WhatsApp. El bot es quien carga las instrucciones: lo creas y editas en el panel, y aquí indicas el `botId`. Cambiar el comportamiento de la IA es editar el bot, no cambiar tu código. ## Requisitos previos 1. **API Token del proyecto** — en **Integraciones → WABA** → sección *API Tokens*, donde creas un token con nombre y puedes revocarlo. Es el mismo token de la WABA API y de la Email API: uno por proyecto, no uno por producto. 2. **Un bot creado** en el panel, con sus instrucciones y fuentes de conocimiento — consulta [/es/bot](https://docs.sendeasy.app/es/bot.md). Guarda el `botId`. 3. **Créditos de bot** disponibles en el plan del proyecto. Usa [`GET /api/v1/ai/health`](https://docs.sendeasy.app/es/server/bots.md#diagnostico-de-la-integracion) para verificar la integración sin gastar crédito. ## Autenticación Header obligatorio: ```http Authorization: Bearer ``` El token identifica el **proyecto**, y es él quien autoriza el `botId`: un bot de otro proyecto es rechazado, aunque conozcas el id. Es el **mismo API Token** que usa la [WABA API](https://docs.sendeasy.app/es/server/waba.md). Revocarlo en **Integraciones → WABA** tumba todas las integraciones que dependen de él, no solo esta. ## Endpoints | Método | Endpoint | Finalidad | Crédito | | ------ | ----------------------- | ----------------------------------------- | ---------------- | | `POST` | `/api/v1/ai/completion` | Generar texto con el prompt del bot | 1 por generación | | `POST` | `/api/v1/ai/extract` | Extraer contenido de un PDF | 1 por archivo | | `POST` | `/api/v1/ai/search` | Buscar en la base de conocimiento del bot | no consume | | `GET` | `/api/v1/ai/health` | Diagnóstico: ¿la integración está en pie? | no consume | Límite: **60 solicitudes por minuto** por API Token (`429 ERR_PUBLIC_AI_RATE_LIMIT`). El conteo es por token, no por IP: varios servidores que usan el mismo token comparten la cuota, y el mismo servidor con tokens distintos tiene cuotas separadas. ## Generación de texto ```txt POST /api/v1/ai/completion ``` ### Payload | Campo | Tipo | Obligatorio | Observaciones | | ---------------- | -------- | -------------- | ------------------------------------------------------------- | | `prompt` | `string` | Sí | el pedido en sí, hasta 120.000 caracteres | | `botId` | `string` | Uno de los dos | usa las instrucciones y el conocimiento de ese bot | | `instructions` | `string` | Uno de los dos | system prompt suelto, hasta 60.000 caracteres | | `responseFormat` | `string` | No | `text` (predeterminado) o `json` | | `timeoutMs` | `number` | No | entre 1.000 y 90.000; predeterminado del servicio si se omite | Indica `botId` **o** `instructions`. Sin ninguno de los dos la ruta responde `400`, en lugar de gastar crédito para devolver texto fuera de contexto. ### Ejemplo ```bash curl --location 'https://backend.sendeasy.app/api/v1/ai/completion' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer TU_API_TOKEN' \ --data '{ "botId": "tu-bot-id", "prompt": "Resume en dos párrafos la política de devoluciones." }' ``` Respuesta de éxito: ```json { "success": true, "result": "La política de devoluciones permite devolver en hasta 30 días..." } ``` Con `"responseFormat": "json"`, la respuesta llega parseada en `dados` en lugar de `result` — útil cuando el prompt pide una estructura y no quieres tratar el texto: ```json { "success": true, "dados": { "prazo_dias": 30, "exige_nota": true } } ``` Cada solicitud mantiene una conexión abierta hasta que la IA responde. Prefiere un `timeoutMs` corto en pantallas donde alguien está esperando, y deja el techo de 90s para rutinas en segundo plano. ## Extracción de PDF ```txt POST /api/v1/ai/extract ``` Envío `multipart/form-data` con el archivo en el campo `file`. Solo PDF, hasta **25 MB**. | Campo | Tipo | Obligatorio | Observaciones | | ------ | -------- | ----------- | -------------------------------------------------------------------------------------------------- | | `file` | `file` | Sí | el PDF, `application/pdf` | | `mode` | `string` | No | `text` (predeterminado) devuelve el texto en `result`; otros modos devuelven el payload en `dados` | ```bash curl --location 'https://backend.sendeasy.app/api/v1/ai/extract' \ --header 'Authorization: Bearer TU_API_TOKEN' \ --form 'file=@"/ruta/contrato.pdf"' \ --form 'mode="text"' ``` ```json { "success": true, "result": "CONTRATO DE PRESTACIÓN DE SERVICIOS..." } ``` La lectura usa OCR cuando el PDF está escaneado, así que los archivos grandes tardan. Si la IA no reconoce el contenido, la respuesta es `502` con la explicación del motivo — reenviar el mismo archivo no lo resuelve. ## Búsqueda en la base de conocimiento ```txt POST /api/v1/ai/search ``` Recupera fragmentos de las fuentes del bot, sin generar texto. Sirve para fundamentar una respuesta tuya, o para mostrar al usuario de dónde salió la información. | Campo | Tipo | Obligatorio | Observaciones | | --------- | -------- | ----------- | ------------------------------------------------------ | | `botId` | `string` | Sí | en qué bot buscar | | `query` | `string` | Sí | qué buscar, hasta 2.000 caracteres | | `topK` | `number` | No | cuántos fragmentos devolver, de 1 a 8 | | `filters` | `object` | No | restringe la búsqueda a ciertos documentos (ver abajo) | `filters` acepta dos campos, ambos listas de patrones de nombre de archivo (`%` coincide con cualquier tramo): | Campo | Efecto | | ----------------- | ----------------------------------------------------------------------------- | | `incluirArquivos` | busca **solo** en los documentos que coincidan; omitido, recorre toda la base | | `excluirArquivos` | descarta los documentos que coincidan, aunque estén incluidos | ```json { "botId": "tu-bot-id", "query": "plazo de garantía", "filters": { "incluirArquivos": ["manual-%", "politica-cambios.pdf"], "excluirArquivos": ["borrador-%"] } } ``` Sirve cuando la base es grande y ya sabes dónde está la respuesta: sin filtro, un documento extenso puede ocupar los lugares del resultado y tapar lo que importa. ```bash curl --location 'https://backend.sendeasy.app/api/v1/ai/search' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer TU_API_TOKEN' \ --data '{ "botId": "tu-bot-id", "query": "plazo de garantía", "topK": 3 }' ``` ```json { "success": true, "trechos": [ { "texto": "La garantía legal es de 90 días para productos duraderos...", "procedencia": "manual-del-cliente.pdf", "score": 0.82 } ] } ``` No consume crédito: es recuperación de contenido, no generación. Si la búsqueda falla, la respuesta llega con `trechos: []` en lugar de error — sin contexto tu respuesta pierde fundamento, no deja de existir. ## Diagnóstico de la integración ```txt GET /api/v1/ai/health ``` No consume crédito y no invoca al modelo. Responde las dos preguntas detrás de casi toda falla de integración: ¿el servicio de IA está en línea y todavía hay saldo? ```bash curl --location 'https://backend.sendeasy.app/api/v1/ai/health' \ --header 'Authorization: Bearer TU_API_TOKEN' ``` ```json { "success": true, "companyId": 42, "botrag": { "ok": true, "latenciaMs": 210 }, "creditos": { "disponivel": 4820, "mes": "9/2026" }, "checkedAt": "2026-09-01T12:30:00.000Z" } ``` * `botrag.ok: false` — el servicio de IA no está disponible; un campo `detalhe` acompaña la causa. Nada que hacer de tu lado salvo reintentar más tarde. * `creditos.disponivel: 0` — el proyecto se quedó sin créditos y las rutas de generación responderán `402`. Renueva el plan o espera la franquicia del mes siguiente. ## A qué bots llega el token El `botId` se resuelve **dentro del proyecto de tu API Token**. Un token de un proyecto no puede usar el bot de otro, aunque conozca el id: la ruta responde `403` sin generar nada ni tocar tu saldo. Un bot que no existe responde `404`. Aplica a `completion` y `search`, las dos rutas que resuelven un bot. `extract` no usa `botId`. Las instrucciones de un bot suelen ser fruto de mucho ajuste fino, así que las tratamos como contenido del proyecto: ninguna ruta devuelve el texto del prompt y ningún token llega al bot de otro proyecto. ## Créditos y reembolso * Cada generación (`completion`, `extract`) descuenta **1 crédito de bot** del proyecto. * Sin crédito, la ruta responde `402 ERR_NO_BOT_CREDITS` **antes** de cualquier cobro. * Si la generación falla **después** del cobro, el crédito se **reembolsa automáticamente** y la ruta responde `502`. * `search` y `health` no consumen nada. El saldo es el **mismo** del bot de atención en WhatsApp — no hay cuota separada para la API. Un volumen alto de llamadas aquí deja al bot de WhatsApp sin crédito, y viceversa. ## Códigos de error | HTTP | `error` | Cuándo ocurre | | ---- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | 400 | mensaje de validación | payload inválido, `prompt` ausente, ni `botId` ni `instructions`, archivo que no es PDF | | 400 | `INVALID_JSON` | el cuerpo no es JSON válido | | 400 | `FILE_TOO_LARGE` | PDF por encima de 25 MB | | 401 | `ERR_API_TOKEN_NOT_PROVIDED` / `ERR_API_TOKEN_INVALID_FORMAT` / `ERR_API_TOKEN_INVALID` / `ERR_API_TOKEN_AUTHENTICATION_FAILED` | header ausente o mal formado, token inválido o revocado | | 402 | `ERR_NO_BOT_CREDITS` | proyecto sin créditos de bot (no se cobró nada) | | 403 | mensaje de autorización | `botId` de un bot que no pertenece al proyecto del token | | 404 | mensaje de bot | `botId` inexistente | | 413 | `PAYLOAD_TOO_LARGE` | cuerpo JSON por encima de 10 MB | | 429 | `ERR_PUBLIC_AI_RATE_LIMIT` | más de 60 solicitudes por minuto | | 502 | mensaje en texto | falla al generar (crédito reembolsado); cuando la IA sabe el motivo, viene en el mensaje | | 504 | `GATEWAY_TIMEOUT` | la generación superó el tiempo límite | El `502` es el único que llega con mensaje libre en lugar de código — trátalo **por el status**, no por el texto, que puede cambiar. Los demás siguen el contrato de códigos de la plataforma, descrito en [/es/server/traducoes](https://docs.sendeasy.app/es/server/traducoes.md). ## Bots API × bot en WhatsApp El mismo bot, dos caminos — lo que cambia es quién conduce la conversación: | | Bots API (`/api/v1/ai/*`) | Bot en atención | | ------------ | ------------------------------------------- | ------------------------------------------- | | Quién llama | tu aplicación | el ticket, cuando entra en el sector | | Conversación | cada llamada es independiente | Sendeasy mantiene el historial en el ticket | | Uso típico | asistente y automatizaciones en tu producto | atención en WhatsApp | **Seguridad:** trata el token como una contraseña. Nunca lo incrustes en JavaScript del navegador, repositorios públicos o logs — mantén las llamadas en tu servidor, con el token en una variable de entorno o un secret manager. --- # Envíos de Diagnóstico Si tienes un formulario de diagnóstico (cuestionario, evaluación, autotest) que entrega el resultado al lead, esta API registra **cada intento de envío** en Sendeasy: quién lo completó, el resultado y si la entrega por WhatsApp y la creación del contacto en ActiveCampaign funcionaron. El historial aparece en **Leads → Envíos de Diagnóstico**, con búsqueda y exportación en CSV. Esta API solo registra el historial. No envía el mensaje ni crea el contacto — eso lo sigue haciendo tu formulario, la [API WABA](https://docs.sendeasy.app/es/server/waba.md) o la integración que ya utilizas. ## Antes de empezar 1. **Registra el proyecto.** En **Leads → Envíos de Diagnóstico**, abre **Proyectos aceptados** y registra un identificador para tu diagnóstico (ej.: `midiagnostico`). Mientras la lista esté vacía, no se registra ningún envío. 2. **Genera un API Token** del proyecto (Configuración → API). El identificador enviado debe coincidir **exactamente** con uno de los registrados. Esta validación es intencional: evita que un error de escritura cree un proyecto nuevo sin que nadie lo note. ## Registrar un envío | Método | Endpoint | Auth | | ------ | -------------------------------- | --------- | | `POST` | `/api/v1/diagnostic-submissions` | API Token | ### Cuerpo de la solicitud | Campo | Tipo | Obligatorio | Descripción | | ---------------------- | ------ | ----------- | ---------------------------------------------------------------------------- | | `project` | string | **Sí** | Identificador del diagnóstico, igual al registrado en "Proyectos aceptados". | | `name` | string | No | Nombre de quien lo completó. | | `email` | string | No | Correo de quien lo completó. | | `phone` | string | No | Teléfono con código de país y área (ej.: `5511999999999`). | | `result` | objeto | No | Respuestas, notas y dimensiones del diagnóstico. Formato libre. | | `whatsappStatus` | string | No | `success`, `failed` o `skipped`. | | `whatsappError` | string | No | Mensaje del error cuando el envío falla. | | `activeCampaignStatus` | string | No | `success`, `failed` o `skipped`. | | `activeCampaignError` | string | No | Mensaje del error cuando la integración falla. | | `source` | string | No | Origen del lead (campaña, página, anuncio). | Envía los campos de estado **después** de intentar el envío, con el resultado real de cada etapa. Eso es lo que permite encontrar las entregas fallidas. ### Ejemplo ```bash curl --location 'https://backend.sendeasy.app/api/v1/diagnostic-submissions' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer TU_API_TOKEN' \ --data '{ "project": "midiagnostico", "name": "Maria Souza", "email": "maria@ejemplo.com", "phone": "5511999999999", "result": { "media": 7.5, "categorias": { "procesos": 8, "personas": 7 } }, "whatsappStatus": "success", "activeCampaignStatus": "failed", "activeCampaignError": "timeout en la API", "source": "campana-instagram" }' ``` ### Respuesta ```json { "id": 42 } ``` ## Errores | HTTP | `error` | Qué hacer | | ----- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `400` | `PROJECT_REQUIRED` | Envía el campo `project`. | | `400` | `NO_PROJECTS_CONFIGURED` | Ningún proyecto registrado todavía: abre "Proyectos aceptados" en la pantalla y registra el identificador. | | `400` | `PROJECT_NOT_ALLOWED` | El identificador no está en la lista. La respuesta incluye `allowedProjects` con los valores aceptados — compáralos con lo que envía tu formulario. | | `401` | `ERR_API_TOKEN_NOT_PROVIDED` | Falta el header `Authorization: Bearer `. | | `401` | `ERR_API_TOKEN_INVALID` | Token inválido o desactivado. | Ambos errores de proyecto incluyen la lista aceptada: ```json { "error": "PROJECT_NOT_ALLOWED", "allowedProjects": ["midiagnostico"] } ``` ## Buenas prácticas * **Registra también los fallos.** Un envío con `whatsappStatus: "failed"` es justamente lo que quieres ver después. * **No bloquees al usuario.** Llama a esta API en segundo plano: si el registro falla, el lead debe seguir viendo su resultado. * **Guarda el token en el servidor.** El API Token da acceso al proyecto — nunca lo expongas en el navegador. --- # Email API Envío de emails transaccionales (restablecimiento de contraseña, avisos, confirmaciones) **a través de Sendeasy**, desde tu propia aplicación, autenticado por el **token del propio email** — cada email configurado en el proyecto tiene el suyo. El envío sale **por el email dueño del token** y consume **1 crédito de email** del proyecto. No informas el remitente: es el email del token. ¿Tienes más de un email? Usa el token de aquel por el que quieras enviar. ## Requisitos previos 1. **Token del email** — en **Canales → Email** (o en la tarjeta del email en **Canales**) → *Configuración* → sección **API de envío**. Cada email tiene su propio token; allí también puedes **generar uno nuevo** (el anterior deja de valer de inmediato). 2. El email con estado **Verificado** (mismo lugar). Sin esto la ruta responde `503 ERR_EMAIL_SENDER_NOT_CONFIGURED` y no sale nada, aunque haya crédito. 3. **Créditos de email** disponibles en el plan del proyecto. Usa [`GET /api/v1/email/health`](https://docs.sendeasy.app/es/server/email.md#diagnostico-de-la-integracion) para comprobar los puntos 1 y 2 sin gastar crédito. ## Autenticación Header obligatorio, con el token del email remitente: ```http Authorization: Bearer ``` El token identifica el **email remitente** y, a través de él, el proyecto — no hay `whatsappId`/`emailId` en la URL. **Compatibilidad:** el **API Token del proyecto** (el mismo de la WABA API) también se acepta en este header. En ese caso el remitente es el **primer email verificado** del proyecto — útil para integraciones hechas antes del token por email, pero para elegir el remitente usa el token del email. ## Endpoints | Método | Endpoint | Finalidad | Crédito | | ------ | ---------------------- | ------------------------------------------------- | ----------- | | `POST` | `/api/v1/email/send` | Enviar email transaccional | 1 por envío | | `GET` | `/api/v1/email/health` | Diagnóstico: ¿el email del token está verificado? | no consume | Límite: **20 solicitudes por minuto** por origen (`429 ERR_PUBLIC_EMAIL_RATE_LIMIT`). ## Envío de email ```txt POST /api/v1/email/send ``` ### Payload | Campo | Tipo | Obligatorio | Observaciones | | ---------- | --------------------- | -------------- | ---------------------------------------------------------------------------------------------------- | | `to` | `string` o `string[]` | Sí | hasta **10 destinatarios** por envío | | `subject` | `string` | Sí | hasta 200 caracteres | | `html` | `string` | Uno de los dos | cuerpo HTML, hasta 200.000 caracteres | | `text` | `string` | Uno de los dos | cuerpo en texto plano, hasta 20.000 caracteres | | `fromName` | `string` | No | nombre visible del remitente (hasta 80 caracteres). La dirección es siempre el email dueño del token | El remitente (`fromEmail`) **no se acepta** en el payload: la ruta usa siempre el email dueño del token. Esto impide que un token envíe en nombre de un email que no es tuyo. ### Ejemplo ```bash curl --location 'https://backend.sendeasy.app/api/v1/email/send' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer TOKEN_DEL_EMAIL' \ --data '{ "to": "cliente@ejemplo.com", "subject": "Restablecimiento de contraseña", "fromName": "Mi Plataforma", "html": "

¡Hola! Haz clic en el enlace de abajo para crear una nueva contraseña.

" }' ``` Respuesta de éxito: ```json { "success": true, "id": "9fbbb3a9-4c1e-4b1a-9d1e-0c2b7e3f5a61" } ``` El `id` es el identificador del envío en el proveedor. La respuesta confirma que el email fue **aceptado para entrega** — la entrega final depende del servidor del destinatario. ## Diagnóstico de la integración ```txt GET /api/v1/email/health ``` No consume crédito. Indica si el email del token ya está verificado — la causa más común de que un envío falle aun con crédito disponible. ```bash curl --location 'https://backend.sendeasy.app/api/v1/email/health' \ --header 'Authorization: Bearer TOKEN_DEL_EMAIL' ``` ```json { "ok": true, "senderConfigured": true, "sender": "no-reply@tuempresa.com", "domain": "tuempresa.com", "channelId": 12 } ``` Con `senderConfigured: false`, termina de verificar el email en **Canales → Email** antes de enviar. (Con el API Token del proyecto la respuesta viene sin `channelId` y dice si el proyecto tiene *algún* email verificado.) ## Créditos y reembolso * Cada envío aceptado descuenta **1 crédito de email** del proyecto. * Sin crédito, la ruta responde `402 ERR_NO_EMAIL_CREDITS` **antes** de cualquier débito. * Si el envío falla **después** del débito (remitente no configurado, proveedor no disponible), el crédito se **reembolsa automáticamente** y la ruta responde `502`/`503`. ## Códigos de error | HTTP | `error` | Cuándo ocurre | | ---- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------ | | 400 | `ERR_EMAIL_INVALID_BODY` | payload inválido (sin `to`/`subject`, sin `html` ni `text`, ningún destinatario válido) | | 400 | `ERR_EMAIL_TOO_MANY_RECIPIENTS` | más de 10 destinatarios en un envío | | 401 | `ERR_API_TOKEN_NOT_PROVIDED` / `ERR_API_TOKEN_INVALID` | header ausente o token inválido/inactivo | | 402 | `ERR_NO_EMAIL_CREDITS` | proyecto sin créditos de email | | 429 | `ERR_PUBLIC_EMAIL_RATE_LIMIT` | más de 20 solicitudes por minuto | | 502 | `ERR_EMAIL_SEND_FAILED` | fallo del proveedor al enviar (crédito reembolsado) | | 503 | `ERR_EMAIL_SENDER_NOT_CONFIGURED` | el email del token aún no está verificado (con token del email, antes de cualquier débito) | ## Email API vs. integración Custom Sendeasy ofrece dos formas de enviar email por API — elige según el tipo de token que tengas: | | Email API (`/api/v1/email/send`) | Custom (`/api/integration/generic`) | | ---------- | ---------------------------------------------------- | ----------------------------------------------------- | | Token | **token del email** (Canales → Email → API de envío) | token de integración ligado a **un canal** de email | | Remitente | el email dueño del token | el canal del token | | Uso típico | emails transaccionales desde tu propia aplicación | integraciones por canal, con `bcc`, `inReplyTo`, etc. | Detalles de la integración Custom en [/es/server/integracoes](https://docs.sendeasy.app/es/server/integracoes.md). **Seguridad:** trata el token como una contraseña. Nunca lo incrustes en JavaScript del navegador, repositorios públicos o logs — guárdalo en variables de entorno o en un secret manager en tu servidor. --- # Integraciones (Server) Sendeasy ofrece dos productos de integración: * **Webhook** — Sendeasy envía eventos de tu proyecto a una URL tuya. * **Custom (genérica)** — tu aplicación llama a Sendeasy para enviar mensajes. Los webhooks admiten **dos alcances**: * **General**: recibe eventos de **todos** los canales de la empresa. * **Por canal específico**: recibe eventos **solo** del canal elegido (una instancia de WhatsApp, una casilla de Email, un número SMS). Ambos alcances coexisten: puedes tener 1 webhook general + N webhooks por canal activos al mismo tiempo. Cada evento dispara la UNIÓN de los webhooks aplicables (general + específico). Los tokens **custom** siempre requieren un canal específico. ## Configuración en la UI Ve a **Configuración → Integraciones**. Verás una lista de integraciones. Para cada una: 1. Haz clic en **Nueva integración**. 2. Para webhook, elige el **alcance**: *General (todos los canales)* o *Canal específico*. 3. Si "Canal específico", elige el **tipo** (`whatsapp`/`email`/`sms`) y la instancia. 4. Para **webhook**: indica la URL y, opcionalmente, un bearer token enviado en el header `Authorization`. 5. Para **custom**: el token lo genera el sistema y se muestra **una sola vez**. Cópialo y guárdalo de inmediato. ## Envío de mensajes — `POST /api/integration/generic` Este endpoint pasa a tener **payloads distintos por tipo de canal**. El canal de destino se **deriva del token** — tu aplicación no envía `channelType` en el body. Un token creado para Email solo puede enviar Email; un token de SMS solo puede enviar SMS. Header obligatorio: ``` Authorization: Bearer ``` ### WhatsApp ```bash curl -X POST https://backend.sendeasy.app/api/integration/generic \ -H "Authorization: Bearer 12G3N3R1$..." \ -H "Content-Type: application/json" \ -d '{ "name": "Lead WhatsApp", "number": "5511999999999", "message": "Quiero hablar con ventas", "email": "lead@empresa.com", "url": "https://landing.com/?utm_source=meta", "files": ["https://landing.com/presentacion.pdf"], "sector": "12" }' ``` ```json { "success": true, "channel": "whatsapp", "messageId": null, "contactId": null } ``` | Campo | Tipo | Obligatorio | | --------- | ------------------------ | ----------- | | `number` | `string` (E.164 sin `+`) | sí | | `message` | `string` | sí | | `name` | `string` | no | | `email` | `string` | no | | `url` | `string` (UTM) | no | | `files` | `string[]` (URLs) | no | | `sector` | `string` (id de cola) | no | ### Email ```bash curl -X POST https://backend.sendeasy.app/api/integration/generic \ -H "Authorization: Bearer 12G3N3R1$..." \ -H "Content-Type: application/json" \ -d '{ "to": ["cliente@empresa.com"], "subject": "Confirmación de tu pedido", "html": "

¡Gracias por tu compra!

", "bcc": ["archivo@empresa.com"] }' ``` ```json { "success": true, "channel": "email", "messageId": "msg_abc123", "contactId": null } ``` | Campo | Tipo | Obligatorio | | --------------- | ------------------------------ | -------------- | | `to` | `string` o `string[]` (emails) | sí | | `subject` | `string` | sí | | `body` o `html` | `string` | uno de los dos | | `bcc` | `string` o `string[]` | no | | `text` | `string` | no | | `fromName` | `string` | no | | `inReplyTo` | `string` | no | | `references` | `string` o `string[]` | no | La dirección remitente (`fromEmail`) es la del canal vinculado al token. ### SMS ```bash curl -X POST https://backend.sendeasy.app/api/integration/generic \ -H "Authorization: Bearer 12G3N3R1$..." \ -H "Content-Type: application/json" \ -d '{ "to": "+5511999999999", "body": "Tu código: 123456" }' ``` ```json { "success": true, "channel": "sms", "messageId": "SMxxxxxxxx", "contactId": null } ``` | Campo | Tipo | Obligatorio | | ----------- | -------------------------- | ----------- | | `to` | `string` (E.164 con `+`) | sí | | `body` | `string` (≤ 1600 chars) | sí | | `mediaUrls` | `string[]` (URLs públicas) | no | | `metadata` | `object` | no | ## Códigos de respuesta | HTTP | `error` | Significado | | ---- | ------------------- | ----------------------------------------- | | 200 | — | Éxito | | 400 | `VALIDATION_ERROR` | Payload inválido — ver `details[]` | | 401 | `ERR_NO_PERMISSION` | Token ausente/inválido | | 402 | `PROVIDER_ERROR` | Sin créditos para el canal | | 404 | `CHANNEL_NOT_FOUND` | Canal eliminado tras crear el token | | 502 | `PROVIDER_ERROR` | Falla en el proveedor (Twilio, Resend, …) | | 410 | `GONE` | Endpoint legado descontinuado | ## Rotación de tokens Los tokens `custom` pueden rotarse en cualquier momento en **Configuración → Integraciones**. **La rotación invalida el token anterior de forma inmediata.** Recomendamos rotación periódica (90 días) e inmediata ante sospecha de filtración. ## Seguridad **Nunca** incrustes tokens en JavaScript del navegador, repositorios públicos, capturas de pantalla o logs. Trata el token como una contraseña. Almacénalo en variables de entorno, vault o secret manager. Si se expone, rótalo de inmediato. ## Dónde gestionar Toda la gestión de tokens y webhooks vive en la plataforma, en **Configuración → Integraciones**. Los tokens creados anteriormente siguen funcionando — solo revisa y ajusta la configuración en la nueva pantalla. --- # Integración Server Esta sección es el punto de entrada técnico para integraciones backend con Sendeasy. ## Qué incluye * **Referencia Técnica**: contratos prácticos de request/response. * **API Pública**: autenticación, payloads y catálogo de endpoints. * **Integraciones**: flujo público Generic. * **WABA API**: endpoints de templates y media. * **Email API**: envío de emails transaccionales vía `/api/v1/email/*`, con créditos del proyecto. * **Bots API**: generación de texto, extracción de PDF y búsqueda vía `/api/v1/ai/*`, con créditos de bot del proyecto. * **Webhooks**: eventos efectivamente reenviados al webhook de tu proyecto. * **Traducciones de Errores**: estrategia de catálogo multilenguaje. ## Orden recomendado de lectura 1. [/es/server/referencia-tecnica](https://docs.sendeasy.app/es/server/referencia-tecnica.md) 2. [/es/server/api-publica](https://docs.sendeasy.app/es/server/api-publica.md) 3. [/es/server/integracoes](https://docs.sendeasy.app/es/server/integracoes.md) 4. [/es/server/waba](https://docs.sendeasy.app/es/server/waba.md) 5. [/es/server/email](https://docs.sendeasy.app/es/server/email.md) 6. [/es/server/bots](https://docs.sendeasy.app/es/server/bots.md) 7. [/es/server/webhooks](https://docs.sendeasy.app/es/server/webhooks.md) 8. [/es/server/traducoes](https://docs.sendeasy.app/es/server/traducoes.md) --- # Referencia Técnica Runbook orientado a desarrolladores para integraciones reales: endpoint, payload, respuesta y comportamiento operacional. ## TL;DR de integración * Base URL: `https://backend.sendeasy.app` * Auth: `Authorization: Bearer ` * Formatos: `application/json` y `multipart/form-data` * Muchos flujos se encolan de forma asíncrona * Trata `error` como contrato estable de máquina ## Endpoint principal de envío `POST /api/messages/send` ```bash curl --location 'https://backend.sendeasy.app/api/messages/send' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer CHANNEL_TOKEN' \ --data '{ "number": "5511999999999", "body": "Hola desde la integración" }' ``` Respuesta típica: ```json { "message": "Message queued successfully" } ``` ## Referencia rápida WABA Prefijo: ```txt /api/v1/waba/:whatsappId ``` Ejemplos: * `GET /templates` * `POST /send` * `POST /send-text` * `POST /send-image` * `POST /send-document` * `POST /marketing-lite/send` ## Referencia rápida Email API Email transaccional desde el email dueño del token (cada email del proyecto tiene el suyo, en Canales → Email), 1 crédito de email por envío: * `POST /api/v1/email/send` — body `{ to, subject, html | text, fromName? }` * `GET /api/v1/email/health` — ¿el email del token está verificado? (sin crédito) Referencia completa en [/es/server/email](https://docs.sendeasy.app/es/server/email.md). ## Referencia rápida Bots API Generación de texto, extracción de PDF y búsqueda en la base de conocimiento, 1 crédito de bot por generación (el mismo saldo del bot de atención en WhatsApp). El prompt vive en el bot (`botId`); sin saldo la respuesta es `402 ERR_NO_BOT_CREDITS`: * `POST /api/v1/ai/completion` — body `{ prompt, botId | instructions, responseFormat? }` * `POST /api/v1/ai/extract` — multipart, campo `file` (PDF hasta 25 MB) * `POST /api/v1/ai/search` — body `{ botId, query, topK? }`; solo recuperación (sin crédito) * `GET /api/v1/ai/health` — ¿el servicio de IA está en línea y hay saldo? (sin crédito) Referencia completa en [/es/server/bots](https://docs.sendeasy.app/es/server/bots.md). ## Referencia rápida de webhooks Forma del payload enviado a tu URL de webhook: ```txt { event, channel, data, channelId } ``` Ejemplo: ```json { "event": "messages.upsert", "channel": "whatsapp", "data": {}, "channelId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ``` ## Estrategia de error ```js function normalizeApiError(status, body) { return { status, code: body?.error || 'UNKNOWN_ERROR', message: body?.message || null, } } ``` Para detalles completos por endpoint, continúa con las páginas de esta sección server (API Pública, Integraciones, WABA, Webhooks y Traducciones de Errores). --- # Traducciones de Errores (PT / EN / ES) Esta página centraliza traducciones de códigos de error usados en integraciones de Sendeasy. ## Paridad con frontend Usa el mismo modelo del frontend (`backendErrors`) para mantener consistencia entre: * mensajes mostrados en el panel web * mensajes mostrados en integraciones externas (portales, ERPs, apps propias) Estrategia recomendada: 1. Backend retorna `error` (código estable). 2. Tu cliente traduce vía catálogo por idioma. 3. Fallback al código de error cuando falte traducción. ## Uso en integración 1. Trata `error` como clave estable de máquina. 2. Muestra `message` cuando esté disponible. 3. Opcionalmente usa fallback a tu catálogo local. ## Códigos comunes | Código | Português | English | Español | | ------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------ | | `ERR_NO_PERMISSION` | Você não tem permissão para acessar este recurso. | You do not have permission to access this resource. | No tienes permiso para acceder a este recurso. | | `ERR_SESSION_EXPIRED` | Sessão expirada. Por favor, faça login. | Session expired. Please log in. | Sesión expirada. Por favor, inicia sesión. | | `ERR_API_TOKEN_NOT_PROVIDED` | API token não informado. | API token was not provided. | No se proporcionó API token. | | `ERR_API_TOKEN_INVALID` | API token inválido. | Invalid API token. | API token inválido. | | `ERR_API_TOKEN_INVALID_FORMAT` | Formato de API token inválido. | Invalid API token format. | Formato de API token inválido. | | `ERR_API_TOKEN_AUTHENTICATION_FAILED` | Falha na autenticação do API token. | API token authentication failed. | Fallo en la autenticación del API token. | | `ERR_SENDING_TEMPLATE` | Erro ao enviar template. | Error sending template. | Error al enviar plantilla. | | `ERR_SENDING_MESSAGE` | Erro ao enviar mensagem. | Error sending message. | Error al enviar mensaje. | | `ERR_SENDING_IMAGE` | Erro ao enviar imagem. | Error sending image. | Error al enviar imagen. | | `ERR_SENDING_VIDEO` | Erro ao enviar vídeo. | Error sending video. | Error al enviar video. | | `ERR_SENDING_AUDIO` | Erro ao enviar áudio. | Error sending audio. | Error al enviar audio. | | `ERR_SENDING_DOCUMENT` | Erro ao enviar documento. | Error sending document. | Error al enviar documento. | | `ERR_INVALID_WABA_RECIPIENT` | Destinatário WABA inválido. | Invalid WABA recipient. | Destinatario WABA inválido. | | `ERR_EMAIL_INVALID_BODY` | Payload de e-mail inválido. | Invalid email payload. | Payload de email inválido. | | `ERR_EMAIL_TOO_MANY_RECIPIENTS` | Muitos destinatários em um único envio (máximo 10). | Too many recipients in a single send (max 10). | Demasiados destinatarios en un solo envío (máximo 10). | | `ERR_NO_EMAIL_CREDITS` | Sem créditos de e-mail. | No email credits. | Sin créditos de email. | | `ERR_NO_BOT_CREDITS` | Sem créditos de bot. | No bot credits. | Sin créditos de bot. | | `ERR_PUBLIC_EMAIL_RATE_LIMIT` | Limite de requisições de e-mail excedido. | Email request limit exceeded. | Límite de solicitudes de email excedido. | | `ERR_PUBLIC_AI_RATE_LIMIT` | Limite de requisições de IA excedido. | AI request limit exceeded. | Límite de solicitudes de IA excedido. | | `ERR_EMAIL_SEND_FAILED` | Falha ao enviar e-mail. | Failed to send email. | Error al enviar email. | | `ERR_EMAIL_SENDER_NOT_CONFIGURED` | Projeto sem e-mail verificado para envio. | Project has no verified sender email. | Proyecto sin email verificado para envío. | | `ERR_WAPP_NOT_CONNECTED` | WhatsApp não está conectado. | WhatsApp is not connected. | WhatsApp no está conectado. | | `ERR_WIDGET_TOKEN_INVALID` | Token do widget inválido. | Invalid widget token. | Token del widget inválido. | | `ERR_WIDGET_TOKEN_NOT_PROVIDED` | Token do widget não informado. | Widget token was not provided. | No se proporcionó token del widget. | | `ERR_TRY_AGAIN_LATER` | Por favor, tente novamente mais tarde. | Please try again later. | Por favor, intenta de nuevo más tarde. | | `FILE_TOO_LARGE` | Arquivo muito grande. O tamanho máximo é de 10MB. | File too large. The maximum size is 10MB. | Archivo demasiado grande. El tamaño máximo es de 10MB. | ## Ejemplo en cliente ```js const fallbackTranslations = { ERR_NO_PERMISSION: { pt: 'Você não tem permissão para acessar este recurso.', en: 'You do not have permission to access this resource.', es: 'No tienes permiso para acceder a este recurso.', }, } function resolveErrorMessage(errorCode, locale, backendMessage) { return backendMessage || fallbackTranslations?.[errorCode]?.[locale] || errorCode } ``` Si tu producto es multilenguaje, versiona este catálogo junto al cliente para mantener una UX predecible entre releases. --- # WABA API Referencia para integrar WhatsApp Business API por rutas públicas de Sendeasy. ## Autenticación Header obligatorio: ```http Authorization: Bearer ``` ## Prefijo de rutas ```txt /api/v1/waba/:whatsappId ``` ## Templates | Método | Endpoint | Finalidad | | -------- | -------------------------- | ------------------- | | `GET` | `/templates` | Listar templates | | `POST` | `/templates` | Crear template | | `PUT` | `/templates/:templateId` | Actualizar template | | `DELETE` | `/templates/:templateName` | Eliminar template | ## Mensajes | Método | Endpoint | Finalidad | | ------ | ----------------------------- | --------------------------------------------------------- | | `POST` | `/send` | Enviar template (o texto simple con body específico) | | `POST` | `/send-text` | Enviar texto | | `POST` | `/send-image` | Enviar imagen | | `POST` | `/send-video` | Enviar video | | `POST` | `/send-audio` | Enviar audio | | `POST` | `/send-document` | Enviar documento | | `POST` | `/marketing-lite/send` | Enviar mensaje de marketing (Marketing Messages Lite API) | | `GET` | `/messages/:messageId/status` | Consultar historial de estados de un mensaje | ## Nueva API: mensaje de marketing Endpoint público: ```txt POST /api/v1/waba/:whatsappId/marketing-lite/send ``` Endpoint Meta usado por el backend: ```txt POST /{phone-number-id}/marketing_messages ``` Prerrequisitos: 1. Cuenta WABA con onboarding de Marketing Messages Lite finalizado. 2. Términos de Marketing Messages Lite aceptados en la cuenta Meta. 3. Template en categoría `MARKETING` con estado `APPROVED`. ### Payload | Campo | Tipo | Obligatorio | Observaciones | | ------------------------ | --------- | ----------- | ---------------------------------------------- | | `to` | `string` | Sí | número en formato internacional (solo dígitos) | | `templateName` | `string` | Sí | nombre de template aprobado en Meta | | `language` | `string` | No | por defecto: `pt_BR` | | `components` | `array` | No | componentes/parámetros del template | | `productPolicy` | `string` | No | `CLOUD_API_FALLBACK` (default) o `STRICT` | | `messageActivitySharing` | `boolean` | No | por defecto: `true` | | `bizOpaqueCallbackData` | `string` | No | regresa en webhook para correlación de campaña | ### Ejemplo: envío de mensaje de marketing ```bash curl --location 'https://backend.sendeasy.app/api/v1/waba/SEU_WHATSAPP_ID/marketing-lite/send' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer SEU_API_TOKEN' \ --data '{ "to": "5511999999999", "templateName": "promo_blackfriday", "language": "pt_BR", "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "Juan" } ] } ], "productPolicy": "CLOUD_API_FALLBACK", "messageActivitySharing": true, "bizOpaqueCallbackData": "campaign_blackfriday_2026" }' ``` Respuesta de éxito (ejemplo): ```json { "messages": [ { "id": "wamid.HBg..." } ] } ``` Error de límite/opt-out (ejemplo): ```json { "error": "MARKETING_LIMIT_REACHED", "message": "User has opted out of marketing messages or reached the limit", "details": "..." } ``` ## Ejemplo: envío de texto ```bash curl --location 'https://backend.sendeasy.app/api/v1/waba/SEU_WHATSAPP_ID/send-text' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer SEU_API_TOKEN' \ --data '{ "to": "5511999999999", "text": "¡Hola! Mensaje enviado por la WABA API." }' ``` ## Ejemplo: envío de template ```json { "to": "5511999999999", "templateName": "boas_vindas", "language": "pt_BR", "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "Juan" } ] } ] } ``` ### Template con header de medios (IMAGE / VIDEO / DOCUMENT) Cuando el template aprobado tiene un header de medios, hay que enviar el componente `header` con `parameters`. Usa `type` en minúsculas (`header`/`body`), y la imagen debe servirse desde una **URL pública propia** (S3, CDN, etc.) — no uses el `header_handle` devuelto en la definición del template, expira. ```json { "to": "5511999999999", "templateName": "promo_lanzamiento", "language": "es_CO", "components": [ { "type": "header", "parameters": [ { "type": "image", "image": { "link": "https://tu-cdn.com/banner.jpg" } } ] } ] } ``` Alternativamente, reemplaza `link` por `id` con el `media_id` devuelto por la subida en `/api/v1/waba/:whatsappId/media`. ## Consulta de estado de mensaje La Cloud API de Meta **no expone un endpoint público para consultar el estado bajo demanda** — el estado se entrega exclusivamente por webhook (`sent` / `delivered` / `read` / `failed`). Sendeasy persiste cada evento recibido y entrega el historial en esta ruta, con el mismo formato del payload del webhook. ```txt GET /api/v1/waba/:whatsappId/messages/:messageId/status ``` ### Ejemplo ```bash curl --location 'https://backend.sendeasy.app/api/v1/waba/TU_WHATSAPP_ID/messages/wamid.HBg.../status' \ --header 'Authorization: Bearer TU_API_TOKEN' ``` ### Respuesta ```json { "statuses": [ { "id": "wamid.HBgMNTUzMT...", "status": "sent", "timestamp": "1710000000", "recipient_id": "5511999999999" }, { "id": "wamid.HBgMNTUzMT...", "status": "failed", "timestamp": "1710000003", "recipient_id": "5511999999999", "errors": [ { "code": 131026, "title": "Message undeliverable" } ] } ] } ``` Cuando aún no hay eventos registrados para el `messageId`, la respuesta es `404` con `statuses: []` — basta con reintentar tras unos segundos. Solo los mensajes enviados por el canal WABA tienen estado persistido en esta ruta. ## Códigos de error comunes | Código | Cuándo ocurre | | ------------------------------ | ---------------------------------------------------- | | `WHATSAPP_NOT_FOUND` | `whatsappId` no existe | | `WHATSAPP_CREDENTIALS_MISSING` | canal sin credenciales WABA completas | | `MISSING_PARAMETERS` | faltan campos obligatorios del payload | | `MARKETING_LIMIT_REACHED` | contacto con opt-out o límite de marketing alcanzado | | `WHATSAPP_API_ERROR` | error devuelto por la API de Meta | ## Webhooks WABA (contrato público) Cuando el webhook del proyecto está habilitado, Sendeasy envía: 1. `event: "messaging"` para eventos de mensaje. 2. `event: "message_status"` para actualizaciones de estado de entrega. Ejemplo (`messaging`): ```json { "event": "messaging", "channel": "waba", "data": {}, "channelId": "uuid-del-canal" } ``` Ejemplo (`message_status`): ```json { "event": "message_status", "channel": "whatsapp", "data": { "messageId": "wamid.HBg...", "status": "read", "timestamp": "1713800010" }, "channelId": "uuid-del-canal" } ``` Valores comunes de `data.status`: * `sent` * `delivered` * `read` * `failed` ## Solicitar compartir el contacto [Solicitar compartir el contacto](https://docs.sendeasy.app/es/integrations/waba/request-contact.md): Solicite el teléfono por WABA/BSUID o ZuckZapGo/LID con la API pública de SendEasy. --- # Webhooks (Server) Esta página describe **lo que tu aplicación recibe** en los webhooks configurados cuando Sendeasy procesa eventos de canal y mensaje. ## Alcances: general y por canal Los webhooks tienen **dos alcances posibles**: * **General**: recibe eventos de **todos** los canales de la empresa. Piénsalo como el webhook "global" de la cuenta. * **Por canal específico**: recibe eventos **solo** del canal al que está vinculado. Ambos alcances coexisten. Para cada evento, Sendeasy dispara la **unión**: todos los webhooks generales activos de la empresa **más** el/los webhook(s) por canal de ese canal. Configura cuantos quieras de cada alcance. Ejemplo: la empresa tiene 2 instancias de WhatsApp y 1 casilla de Email. Puedes configurar: * 1 webhook en URL A → solo recibe eventos del WhatsApp Ventas. * 1 webhook en URL B → solo recibe eventos del WhatsApp Soporte. * 1 webhook en URL C → solo recibe eventos del Email Soporte. Configura desde **Configuración → Integraciones**. ## Flujo resumido 1. Sendeasy procesa un evento de canal/mensaje. 2. El payload se normaliza al contrato público. 3. Sendeasy busca todos los webhooks `enabled=true` vinculados al canal origen del evento. 4. Para cada webhook encontrado, hace `POST` a la URL configurada con el payload en el body. Si hay bearer token configurado, va en el header `Authorization`. ## Payload base | Campo | Tipo | Descripción | | ----------- | ------------------ | ------------------------------------------------------------------- | | `event` | `string` | nombre del evento | | `channel` | `string` | tipo de canal: `"whatsapp"`, `"email"` o `"sms"` | | `data` | `object` | payload específico del evento | | `channelId` | `string \| number` | identificador del canal — UUID (WhatsApp) o id numérico (Email/SMS) | ```json { "event": "messages.upsert", "channel": "whatsapp", "data": {}, "channelId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ``` ## Eventos por canal ### WhatsApp (`channel: "whatsapp"` o `"waba"`) #### `connection.update` ```json { "state": "open" } ``` Valores comunes: `open`, `close`. #### `messages.upsert` / `messages.set` / `send.message` ```json { "key": { "remoteJid": "5511999999999@s.whatsapp.net", "fromMe": false, "id": "3EB0XXXXXX" }, "message": { "conversation": "Hola, necesito ayuda" }, "messageTimestamp": "1634567890", "pushName": "Cliente" } ``` #### `messages.update` ```json { "keyId": "3EB0XXXXXX", "fromMe": true, "status": 3 } ``` `status`: `1` (sent), `2` (delivered), `3` (read). #### `messages.delete` ```json { "id": "3EB0XXXXXX", "fromMe": false, "remoteJid": "5511999999999@s.whatsapp.net" } ``` #### `call` ```json { "id": "CALL_ID_123", "from": "5511999999999@s.whatsapp.net", "status": "reject" } ``` #### WABA — `messaging` y `message_status` ```json { "event": "message_status", "channel": "whatsapp", "data": { "messageId": "wamid.HBg...", "status": "read", "timestamp": "1713800010" }, "channelId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ``` ### Email (`channel: "email"`) Disparado cuando la casilla vinculada al webhook recibe un email. ```json { "event": "messaging", "channel": "email", "channelId": 7, "data": { "email": { "direction": "inbound", "mailbox": "soporte@empresa.com", "from": { "email": "cliente@dominio.com", "name": "Cliente" }, "to": ["soporte@empresa.com"], "subject": "Consulta sobre el pedido #1234", "text": "Hola, quería saber...", "html": "

...

", "messageId": "", "receivedAt": "2026-04-28T13:45:00.000Z" } } } ``` ### SMS (`channel: "sms"`) Disparado cuando el número vinculado al webhook recibe un SMS. ```json { "event": "messaging", "channel": "sms", "channelId": 15, "data": { "sms": { "direction": "inbound", "from": "+5511999999999", "body": "Confirmar cita", "providerPayload": { "...": "raw provider data" } } } } ``` ## Buenas prácticas de consumo 1. Trata el procesamiento como asíncrono e implementa retry con backoff. 2. Usa idempotencia por `event` + identificadores (`key.id`, `keyId`, `data.email.messageId`, …). 3. Almacena el payload bruto para troubleshooting. 4. Valida el header `Authorization` cuando esté configurado en el webhook. 5. Configura timeouts cortos (≤ 5s) — si Sendeasy no recibe respuesta en 5s, el webhook se considera fallido y el evento se descarta (no hay retry automático). ## Dónde gestionar Los webhooks se configuran y activan en **Configuración → Integraciones** en la plataforma. --- # Integración Web Esta página cubre integraciones de Sendeasy basadas en navegador. ## Flujo típico 1. Activa acceso API para el canal deseado. 2. Configura orígenes permitidos. 3. Usa token bearer en requests. 4. Envía mensajes con payload JSON o multipart. Para integraciones server-to-server de nivel productivo, prioriza las guías de Server API en [/es/server](https://docs.sendeasy.app/es/server.md). --- # Crear Bot En esta página encontrarás una guía detallada paso a paso para crear y configurar un bot en Sendeasy. # Paso a paso para crear un bot ## 1. Acceder al proyecto * En la página principal de Sendeasy, **haz clic en el proyecto** donde quieres crear el bot. ## 2. Ir a la sección de Bots * Al entrar al proyecto, serás redirigido al **dashboard del proyecto**. * En el menú del dashboard, **haz clic en "Bots"**. ## 3. Agregar un nuevo bot * En la esquina superior derecha de la página de Bots, **haz clic en "+ Agregar"**. * Esto abrirá un pop-up con el formulario para crear el bot. ## 4. Completar la información del bot Completa los campos del formulario. Campos principales: ### Sector * **Selecciona el sector** donde el bot estará asociado (ej.: "Sector Atención Bot"). * El bot responderá automáticamente a todos los mensajes enviados por el cliente a ese sector. ### Nombre del bot * **Ingresa el nombre del bot**. Este campo es obligatorio y debe ser único. * El nombre se usará para identificar el bot dentro de la plataforma. ### Búsqueda de datos en tiempo real * **Activa la búsqueda en tiempo real** si necesitas que el bot consulte una fuente externa. * Al activarla, el bot podrá hacer fetch en tiempo real a una URL específica. * **Tipos soportados**: XML, JSON, HTML. ⚠️ **Importante**: No es compatible con proxy. El contratante es responsable por el uso indebido de esta funcionalidad. Asegúrate de que la URL sea legítima. ## 5. Guardar el bot * **Haz clic en "Guardar"** después de completar los campos. * Una vez guardado, el bot quedará activo para nuevas conversaciones asociadas al sector elegido. ## 6. Comportamiento del bot en atención * Cuando nuevas conversaciones se asocian al sector del bot, este inicia el pre-atendimiento. * **Bot desactivado automáticamente**: si un agente abre el ticket mientras el bot está activo, el bot se desactiva automáticamente. * **Respuesta automática**: si no, el bot responde todos los mensajes del cliente cada 10 segundos. **Ejemplo**: si el cliente envía tres mensajes seguidos ("Hola", "¿Cómo estás?", "Quiero saber los precios"), y no hay un intervalo mayor a 10 segundos entre cada uno, el bot responderá en secuencia, respetando el intervalo de 10 segundos. *** Sigue estos pasos para configurar bots de atención de forma eficiente y automatizada en Sendeasy. Para dudas o soporte adicional, contacta a nuestro equipo por el chat de la plataforma. --- # Chat Widget (antiguo) El Chat Widget por token de integración (`sendeasy-widget-chat-dev.js`, `window.sendeasyConfig`) fue reemplazado por el [Chat del sitio](https://docs.sendeasy.app/widgets/live-chat.md). Ya no recibe mantenimiento ni aparece en el panel. Los sitios que todavía lo usan siguen funcionando con el código ya instalado. Para migrar, crea un chat del sitio nuevo en **Chat del sitio > Nuevo widget**, pega el código generado y quita el antiguo. --- # Formulario de captura Formulario con los campos que elijas. Cada envío se convierte en un contacto (por teléfono, por correo o nuevo) y en un ticket pendiente en el sector elegido, con un mensaje que resume las respuestas. ## Instalación Créalo en **Chat del sitio > Nuevo widget > Formulario de captura** y pega el código: ```html ``` ## Modos * **Botón flotante**: un botón en la esquina abre el formulario. * **Pop-up automático**: se abre solo después del retraso, una vez por visita. * **Dentro de la página**: el formulario aparece donde coloques `
`. También puedes apuntar a un elemento con `data-target="#mi-elemento"` en la etiqueta del script. Sin el elemento, cae en el modo flotante. ## Campos Tipos disponibles: texto, correo, WhatsApp/teléfono, texto largo, lista de opciones y casilla. Cada campo puede ser obligatorio. El nombre, el correo y el WhatsApp se convierten en los datos del contacto; los demás van a la información extra. ## Otras configuraciones * Texto del botón enviar, mensaje después del envío y redirección opcional. * Consentimiento (casilla obligatoria con tu texto). * Etiquetas sobre los campos y marca en el pie. * Apariencia: color principal, color del texto, color de fondo, título, subtítulo y posición. ## Límites Por token: 10 envíos por minuto. Por dirección IP: 20 envíos cada 10 minutos. --- # Chat del sitio Chat en línea en el sitio, con bot y agentes en tiempo real. El visitante ingresa su nombre (y correo opcional), la conversación se convierte en un ticket pendiente en el sector elegido y el historial queda guardado en el navegador por 7 días. ## Instalación Crea el widget en **Chat del sitio > Nuevo widget > Chat del sitio** y pega el código generado antes de ``: ```html ``` ## Qué se puede configurar * **Atención**: nombre del widget, sector de entrada (donde caen los tickets), sitios permitidos e interruptor de activo. * **Apariencia**: color principal, color del texto, color de fondo, posición (derecha o izquierda), título y subtítulo de la ventana. * **Textos**: campo nombre, campo correo, botón iniciar, campo mensaje y botón enviar, en portugués, inglés y español. El chat usa el idioma del navegador del visitante. * **Token**: copiar o generar uno nuevo. ## Apariencia personalizada y el código Con la apariencia predeterminada el código es la línea de arriba. Si personalizas colores o textos, el panel genera un bloque con `window.liveChatConfig` y avisa que hay que pegar el código nuevo. El script también lee la configuración guardada en el panel, así que en sitios con el script actualizado el cambio vale incluso sin volver a pegar. Formato del bloque, como referencia: ```html ``` Lo que esté en `window.liveChatConfig` tiene prioridad sobre lo guardado en el panel. ## Límites Por token: 10 conversaciones iniciadas por minuto y 30 mensajes por minuto. Por dirección IP: 30 conversaciones cada 10 minutos. Por encima de eso el visitante ve un mensaje para intentarlo de nuevo en unos instantes. --- # Widgets del sitio Los widgets están en el panel en **Chat del sitio** (menú lateral o Productos). Creas el widget, copias una línea de código y la pegas en tu sitio antes de cerrar la etiqueta ``. Todo lo que cambies después en el panel vale en el sitio sin volver a pegar el código. ## Los tres tipos | Widget | Qué hace | Script | | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ---------------------- | | [Chat del sitio](https://docs.sendeasy.app/widgets/live-chat.md) | Conversación en tiempo real con bot y agentes. Cada conversación se convierte en un ticket. | `widget-live-chat.js` | | [Botón de WhatsApp](https://docs.sendeasy.app/widgets/whatsapp-flotante) | Botón flotante que abre una conversación en tu número, con agentes, horario y seguimiento. | `whatsapp-floating.js` | | [Formulario de captura](https://docs.sendeasy.app/widgets/formulario.md) | Formulario en el sitio que se convierte en contacto y ticket en el panel. | `form-widget.js` | ## Cómo crear uno 1. En el panel, abre **Chat del sitio** y haz clic en **Nuevo widget**. 2. Elige el tipo. 3. Ingresa el nombre y la dirección del sitio (por ejemplo `https://misitio.com`). El chat y el formulario aceptan un sector de entrada; el botón de WhatsApp pide el número. 4. Copia el código y pégalo en el sitio. Es una sola línea: ```html ``` Cambia `widget-live-chat.js` por `whatsapp-floating.js` o `form-widget.js` según el tipo. El panel ya genera el código correcto con tu token. ## Sitios permitidos El widget solo se abre en páginas de las direcciones registradas en **Sitios permitidos** (los subdominios de la primera dirección también valen). Las páginas fuera de la lista reciben `ERR_WIDGET_ORIGIN_NOT_ALLOWED` y no aparece nada. Para probar en otra dirección, agrégala a la lista. ## Token El token identifica el widget. Está en el código instalado, así que nunca lo publiques en repositorios abiertos. Si necesitas cambiarlo, usa **Generar nuevo token** en el detalle del widget y pega el código nuevo en el sitio. ## Desactivar sin quitar el código El interruptor **Widget activo** en el detalle apaga el widget en el sitio al instante, sin tocar el código instalado. ## Chat Widget antiguo El Chat Widget por token de integración (`sendeasy-widget-chat-dev.js`) fue reemplazado por el chat del sitio y ya no recibe mantenimiento. Los sitios que todavía lo usan siguen funcionando, pero la recomendación es migrar creando un chat del sitio nuevo en el panel. --- # Botón de WhatsApp Botón fijo en la esquina del sitio que abre una conversación en tu WhatsApp con el mensaje ya escrito. No crea canal ni ticket: la conversación ocurre en WhatsApp. ## Instalación Créalo en **Chat del sitio > Nuevo widget > Botón de WhatsApp**, ingresa el número con código de país y de área y pega el código: ```html ``` Todo lo que configures en el panel vale en el sitio sin volver a pegar el código. ## Qué se puede configurar * **WhatsApp**: número principal, mensaje inicial, incluir la página en el mensaje y canal vinculado (solo referencia en el panel). * **Botón**: texto, color del botón, color del ícono y del texto, retraso para aparecer, mostrar en todos los dispositivos, solo móvil o solo computadora, globo de llamada con texto. * **Agentes**: hasta 10, con nombre, función y número. Con más de uno, el clic abre una lista para que el visitante elija; cada agente puede tener su propio mensaje inicial. * **Horario de atención**: días, inicio, fin y zona horaria. Fuera del horario el botón sigue, pero muestra un aviso. * **Seguimiento (UTM)**: agrega `utm_source`, `utm_medium` y `utm_campaign` a la dirección de la página incluida en el mensaje. * **Posición**: derecha o izquierda.