API REST · v1
Documentación de BotwAIp
Integra tus sistemas con la plataforma para enviar mensajes y recibir eventos por webhook.
Introducción
La API de BotwAIp permite que tu CRM, ERP, e-commerce u otra aplicación envíe mensajes de WhatsApp a tus contactos y reciba notificaciones en tiempo real cuando ocurra algo relevante (cliente respondió, agente tomó la conversación, etc.).
Es una API REST con JSON. Todos los endpoints viven bajo https://panel.botwaip.com/api/external y se autentican con un header X-Bot-Api-Key.
¿Listo para empezar?
- Inicia sesión en el panel y entrá a Configuración → Integraciones de API.
- Crea una nueva API Key con un nombre que identifique tu sistema.
- Copia la key (solo se muestra una vez) y guardala en tu app.
- Usá los endpoints de abajo desde tu código.
Autenticación
Cada petición requiere el header X-Bot-Api-Key con la API Key generada desde el panel. Las keys tienen el formato bwk_ seguido de 32 caracteres hexadecimales aleatorios.
X-Bot-Api-Key: bwk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Múltiples keys por bot
Podés crear una key por cada sistema que integres (CRM, ERP, app móvil, etc.). Cada key tiene su propio set de webhooks, sus métricas de uso, y se puede revocar de forma independiente sin afectar a las demás.
Seguridad
- La key plain solo se muestra una vez al crearla. Si la perdés, generá una nueva y revocá la anterior.
- La BD guarda solo el hash SHA-256 — nadie puede recuperar tu key, ni siquiera el soporte.
- Si una key se compromete, revocala desde el panel: deja de funcionar inmediatamente.
- No incluyas la key en código fuente público ni en URLs (siempre va en header).
Canal de envío (WhatsApp)
Cada API Key está obligatoriamente asignada a un canal WhatsApp específico. El endpoint /messages/send envía siempre desde ese canal — no hay modo automático ni fallback, lo cual hace el comportamiento predecible y evita que un cambio en la configuración del bot afecte silenciosamente a tu integración.
Asignar el canal
El canal se elige al crear la API Key desde el panel. Si el bot tiene varios números de WhatsApp conectados (ej. Ventas, Soporte), seleccioná el que corresponde a esta integración. Si después necesitás cambiarlo, podés editarlo desde la lista de API Keys sin tener que generar una nueva — el cambio toma efecto inmediato.
Verificar el canal asignado
El endpoint GET /me devuelve el canal asignado a tu API Key y su estado:
{
"bot_id": "uuid",
"bot_name": "Mi Tienda",
"api_key": { "id": "uuid", "name": "CRM HubSpot" },
"whatsapp_configured": true,
"channel_status": "active", // "active" | "inactive" | "deleted"
"phone_number_id": "1234567890",
"display_phone_number": "+57 300 123 4567",
"verified_name": "Mi Tienda S.A.S.",
"active": true
}
channel_status tiene 3 valores:
"active"— el canal está OK, listo para enviar."inactive"— el canal existe pero fue desactivado./messages/sendva a fallar."deleted"— el canal fue eliminado. La API Key quedó huérfana y el admin debe asignarle un canal nuevo.
Errores relacionados al canal
| Código de error | Cuándo ocurre |
|---|---|
| channel_not_bound (422) | La API Key no tiene un canal asignado, generalmente porque el canal original fue eliminado. El admin debe asignarle un canal activo desde el panel o generar una nueva API Key. |
| bound_channel_inactive (422) | El canal asignado a la key fue desactivado. El admin debe reactivar el canal o asignarle otro a la key. |
| canal_sin_token (422) | El canal existe pero su configuración no tiene token de Meta. Reconfigurarlo desde el panel. |
Rate limiting
Cada API key tiene un límite de 60 peticiones por minuto (compartido entre todos los endpoints /api/external/*). Si lo superás, recibís un 429 Too Many Requests.
Para envíos masivos de notificaciones (más de 60/min), espaciá las peticiones desde tu sistema o pedinos un aumento de cuota.
Errores
Todos los errores devuelven un JSON con un campo error (código corto) y message (descripción human-readable). Los códigos HTTP siguen los estándares REST:
| Código | Significado |
|---|---|
| 200 | Éxito. La respuesta incluye el resultado. |
| 201 | Recurso creado (mensaje enviado, webhook creado, etc.). |
| 400 | Datos inválidos en el body. Revisá details. |
| 401 | API Key faltante, inválida o revocada. |
| 403 | Empresa suspendida o sin permisos. |
| 404 | Recurso no encontrado (webhook id inexistente, etc.). |
| 422 | Datos válidos pero estado incompatible (ventana 24h cerrada sin plantilla, variables de plantilla insuficientes, etc.). |
| 429 | Rate limit excedido. Esperá y reintentá. |
| 500 | Error interno. Reintentá; si persiste, contactá soporte. |
/api/external/me
Verifica que tu API Key es válida y devuelve metadata del bot al que pertenece. Útil para chequear conectividad al setup de tu integración.
Ejemplo
curl https://panel.botwaip.com/api/external/me \ -H "X-Bot-Api-Key: bwk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Respuesta 200
{
"bot_id": "uuid",
"bot_name": "Mi Tienda",
"company_id": "uuid",
"company_name": "Mi Empresa S.A.S.",
"whatsapp_configured": true,
"phone_number_id": "1234567890",
"active": true
}
/api/external/messages/send
Envía un mensaje de WhatsApp a un contacto desde tu sistema. Maneja automáticamente la ventana de 24h de Meta:
- Ventana abierta (el cliente escribió en las últimas 24h) → envía texto plano.
- Ventana cerrada o primer contacto → exige una plantilla aprobada en Meta.
Parámetros del body
| Campo | Tipo | Descripción |
|---|---|---|
| toobligatorio | string | Número de teléfono del destinatario con código de país. Acepta +57 300 123 4567 o 573001234567. |
| name | string | Nombre del contacto. Se guarda solo si es nuevo, no sobreescribe si ya existe. |
| text | string | Mensaje de texto plano. Solo válido si la ventana de 24h está abierta. Máximo 4096 caracteres. |
| template | object | Plantilla aprobada en Meta. Ver sección de plantillas. |
| force_template | boolean | Si es true, usa la plantilla aunque la ventana esté abierta. Default false. |
| on_reply | "ai" | "queue" | "close" | Qué hacer cuando el cliente responda. Default "ai". Ver sección de on_reply. |
| external_ref | string | Id arbitrario para trazabilidad (ej. "shopify-order-1234"). Se guarda en metadata de la conversación. |
Ejemplo: mensaje de texto (ventana abierta)
curl -X POST https://panel.botwaip.com/api/external/messages/send \
-H "X-Bot-Api-Key: bwk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "573001234567",
"text": "Tu pedido #1234 fue despachado y llega mañana."
}'
Respuesta 201
{
"success": true,
"mode": "text",
"window_open": true,
"on_reply": "ai",
"message": { ... },
"conversation_id": "uuid",
"contact_id": "uuid",
"wa_id": "573001234567"
}
Plantillas
Las plantillas se crean y aprueban en Meta Business Manager, luego aparecen en BotwAIp automáticamente. Para usarlas vía API:
Formato simplificado
{
"to": "573001234567",
"name": "Carlos López",
"template": {
"name": "pedido_despachado",
"language": "es",
"variables": ["Carlos", "1234", "23/05/2026"]
}
}
El array variables se mapea en orden a las variables del body de la plantilla ({{1}}, {{2}}, etc.). Si la plantilla tiene 3 variables y mandás 2 (o 4), la API devuelve 422 template_variables_mismatch sin enviar nada.
Formato avanzado (con header de imagen, botones, etc.)
Pasá los componentes de Meta directamente:
{
"to": "573001234567",
"template": {
"name": "promo_con_imagen",
"language": "es",
"variables": [
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "link": "https://tu-cdn.com/banner.jpg" } }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Carlos" },
{ "type": "text", "text": "20%" }
]
}
]
}
}
Política on_reply
Decidí qué pasa cuando el cliente RESPONDA a una notificación enviada vía API. Tres opciones:
| Valor | Comportamiento | Cuándo usarlo |
|---|---|---|
| "ai" | El asistente IA del bot responde normalmente. | Default. Notificaciones donde el cliente puede continuar la conversación con el bot. |
| "queue" | La conversación entra a cola humana. La IA no responde, un agente del panel la toma. | Cobros, soporte VIP, casos donde la respuesta requiere intervención humana. |
| "close" | El bot no responde. El mensaje queda guardado en el panel con nota interna. | Avisos unidireccionales (recordatorios, confirmaciones automáticas) donde no se espera diálogo. |
El valor se persiste en la conversación y aplica a todos los mensajes entrantes hasta que un agente lo cambie manualmente, o hasta que envíes otra notificación con un on_reply distinto (la última llamada gana).
Webhooks salientes
Tu servidor recibe un POST en tiempo real cuando ocurre un evento en BotwAIp. Útil para sincronizar mensajes entrantes con tu CRM, alertar a tu sistema cuando una conversación se cierra, etc.
Cómo suscribirse
- Andá a Configuración → Integraciones de API en el panel.
- Expandí la API Key del sistema que va a recibir los eventos.
- Click en "Nuevo webhook", pegá tu URL y seleccioná los eventos que querés recibir.
- Al guardar, copiá el secret que se muestra (es para validar HMAC; no se vuelve a mostrar).
Formato del POST
POST https://tu-sistema.com/webhook HTTP/1.1
Content-Type: application/json
X-Webhook-Event: message.inbound
X-Webhook-Signature: sha256=ab12cd34...
User-Agent: BotwAIp-Webhooks/1.0
{
"event": "message.inbound",
"timestamp": "2026-05-22T20:36:45.123Z",
"bot_id": "uuid",
"data": {
"conversation_id": "uuid",
"contact": { "id": "uuid", "wa_id": "573001234567", "name": "Carlos" },
"channel": "whatsapp",
"message": { "type": "text", "text": "Hola, quiero más info" },
"is_new_conversation": false
}
}
Tu endpoint debe responder con 2xx en menos de 8 segundos. Cualquier otra respuesta cuenta como fallo y se reintentará.
Alcance (scope) del webhook
Al crear el webhook elegís qué conversaciones generan eventos para tu URL:
| Scope | Comportamiento | Cuándo usarlo |
|---|---|---|
| all | Recibe eventos de TODAS las conversaciones del bot: orgánicas (cliente escribió directo), originadas por esta API key, y originadas por otras API keys del mismo bot. | Default. CRMs y sistemas que quieren registrar todo contacto con clientes. |
| api_origin | Solo recibe eventos de conversaciones que esta misma API key originó vía /messages/send. Ignora orgánicas y de otras keys. |
Automatizaciones narrow que solo trackean sus propias notificaciones (ej. "avisar al ERP cuando el cliente responda al recordatorio de pago"). |
El "origen" de una conversación se determina al primer mensaje saliente vía /messages/send — esa API Key queda registrada como la que originó la conv (campo api_origin_key_id en metadata). Si otra API Key envía mensajes después en la misma conversación, el origen sigue siendo la primera (no se sobreescribe).
Nota sobre conversaciones orgánicas
Si configurás scope='api_origin' pero un cliente escribe al WhatsApp del bot sin que vos hayas enviado primero una notificación, ese mensaje NO te llegará por webhook. Es el comportamiento esperado — la conversación no tiene origen API. Si querés capturar también ese caso, usá scope='all'.
Validación HMAC
Cada POST incluye X-Webhook-Signature: sha256=<hmac>. El HMAC se calcula con SHA-256 sobre el body raw usando el secret de la suscripción. Validá la firma antes de procesar para asegurarte que el POST realmente viene de BotwAIp.
Node.js / Express
import crypto from 'crypto';
import express from 'express';
const app = express();
const WEBHOOK_SECRET = process.env.BOTWAIP_WEBHOOK_SECRET;
// IMPORTANTE: usar raw body, no JSON parsed
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const expected = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (req.headers['x-webhook-signature'] !== expected) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString());
console.log('Evento recibido:', event.event, event.data);
res.status(200).end();
});
Python / Flask
import hmac, hashlib
from flask import Flask, request
app = Flask(__name__)
SECRET = os.environ['BOTWAIP_WEBHOOK_SECRET']
@app.post('/webhook')
def webhook():
body = request.get_data() # raw bytes
expected = 'sha256=' + hmac.new(
SECRET.encode(), body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(request.headers.get('X-Webhook-Signature', ''), expected):
return '', 401
event = request.get_json()
print('Evento:', event['event'], event['data'])
return '', 200
Tip de seguridad: usá comparación constante (hmac.compare_digest en Python, crypto.timingSafeEqual en Node) para prevenir timing attacks.
Catálogo de eventos
Seleccioná los eventos que querés recibir al crear la suscripción. Estos son los disponibles:
message.inbound
Un cliente envió un mensaje al bot (texto, audio, imagen, etc.).
Ver payload
{
"conversation_id": "uuid",
"contact": { "id": "uuid", "wa_id": "57...", "name": "Carlos", "phone": "57..." },
"channel": "whatsapp",
"message": {
"wa_message_id": "wamid.xxx",
"type": "text",
"text": "Hola"
},
"is_new_conversation": false
}
message.outbound.sent
El bot, un agente o tu propia API enviaron un mensaje saliente. Útil para reflejar la salida en tu CRM sin polling.
Ver payload
{
"conversation_id": "uuid",
"contact_id": "uuid",
"wa_id": "57...",
"channel": "whatsapp",
"mode": "template",
"template_name": "pedido_despachado",
"external_ref": "shopify-order-1234"
}
conversation.opened
Se creó una conversación nueva (primer contacto del cliente o nueva tras una cerrada).
conversation.queued
La conversación entró a cola humana (cliente pidió agente, on_reply=queue, etc.). Útil para alertar a tu sistema de tickets externo.
conversation.assigned
Un agente humano fue asignado a la conversación (automático o manual desde el panel).
conversation.closed
La conversación se cerró manualmente o por inactividad. El reason indica cuál.
Reintentos y auto-desactivación
Si tu endpoint responde con un código distinto de 2xx, o tarda más de 8 segundos, BotwAIp reintenta hasta 3 veces con backoff exponencial:
- 1° reintento: 1 segundo después
- 2° reintento: 4 segundos después
- 3° reintento: 10 segundos después
Si los 4 intentos fallan, se incrementa failures_count de la suscripción y se guarda last_error. Tras 20 fallos consecutivos, la suscripción se desactiva automáticamente para no consumir recursos contra un endpoint caído.
Para reactivar, andá al panel, abrí la API Key, y pulsá el botón de play del webhook. Esto resetea el contador de fallos a cero.
Consejos para endpoints estables
- Responde 2xx inmediatamente, antes de procesar el evento. Procesá en background.
- Tu endpoint debe ser idempotente — el mismo evento puede llegar más de una vez si hubo un timeout entre los reintentos.
- Usá el
wa_message_id(para mensajes) oconversation_id + timestampcomo clave de deduplicación.
BotwAIp · Documentación de la API