Guías
Migrar desde Twilio, 360dialog, YCloud o Kapso
Si hoy mandas WhatsApp con Twilio, 360dialog, YCloud o Kapso, pasar a heysteff es cambiar una llamada: todo sale por el mismo POST /v1/messages, y con ese mismo endpoint también llegas a Instagram, Messenger, SMS y correo. Esta guía junta las equivalencias de endpoints, webhooks y conceptos, y los pasos para cambiarte sin cortar el servicio.
Los datos de cada proveedor salen de su documentación pública (revisada en noviembre de 2026). Donde no pudimos confirmar un detalle, lo decimos de forma general: revisa la documentación de tu proveedor antes de borrar código.
Lo que cambia#
- Un formato para todos los canales. En vez del cuerpo de la Cloud API de Meta (
messaging_product,type,text.body) o de los parámetros de formulario de Twilio (To,From,Body), heysteff usachannel,to,contentycontent_type. Cambiarchannelainstagram,facebook,smsogmailmanda por otro canal sin tocar nada más. - Autenticación con Bearer.
Authorization: Bearer sk_live_…en todas las llamadas. Ver Autenticación. - El número no va en la URL. Con un solo número por canal no hace falta indicarlo; con varios, pasa
channel_connection_id. - Idempotencia en cada envío. El header
Idempotency-Keyhace que un reintento nunca mande el mensaje dos veces. - Además de la API: bandeja compartida en la web y en la app móvil, Shopify, servidor MCP y sandbox sin número propio.
Desde la Cloud API de Meta directa#
Si hoy llamas a graph.facebook.com con tu propio token de Meta, no tienes que reescribir nada: cambia la URL base a https://app.heysteff.com/api/meta/whatsapp/v21.0 (sirve cualquier versión) y el token por tu API key de heysteff (Authorization: Bearer sk_live_…). El phone_number_id sigue siendo el mismo. Para tu receptor de webhooks, crea el webhook con format: "meta": llega el mismo payload de Meta, firmado con X-Hub-Signature-256 y el secreto del webhook. Detalle en Compatible con Meta. Si vienes de un proveedor que expone la Cloud API (360dialog, Kapso), también puedes usar esa misma URL en vez de pasar a /v1.
Equivalencias de endpoints#
| Operación | heysteff | Twilio | 360dialog | YCloud | Kapso |
|---|---|---|---|---|---|
| Base y autenticación | https://app.heysteff.com/api/v1Authorization: Bearer | https://api.twilio.com/2010-04-01Basic auth | https://waba-v2.360dialog.ioD360-API-KEY | https://api.ycloud.com/v2X-API-Key | https://api.kapso.ai/meta/whatsapp/v24.0X-API-Key |
| Mandar un mensaje | POST /v1/messages | POST /Accounts/{AccountSid}/Messages.json (formulario) | POST /messages | POST /whatsapp/messages o /whatsapp/messages/sendDirectly | POST /{phone_number_id}/messages |
| Mandar una plantilla | El mismo POST /v1/messages con content_type: "template" | ContentSid y ContentVariables en el mismo POST | El mismo POST /messages con type: "template" | El mismo POST con type: "template" | El mismo POST con type: "template" |
| Listar plantillas | GET /v1/templates | GET https://content.twilio.com/v1/Content | GET /message_templates | Revisa su documentación (crear: POST /whatsapp/templates) | GET /{business_account_id}/message_templates |
| Subir un archivo | POST /v1/media (multipart, campo file), o una URL https pública en media.url | URL pública en MediaUrl | POST /media (multipart) | POST /whatsapp/media/{phoneNumber}/upload | POST /{phone_number_id}/media |
| Contactos | GET /v1/contacts, POST /v1/contacts | Lo manejas en tu sistema | Revisa su documentación | POST /contact/contacts | GET /{phone_number_id}/contacts |
| Conversaciones e historial | GET /v1/conversations, GET /v1/conversations/{id}/messages | Revisa su documentación | Revisa su documentación | Revisa su documentación | Revisa su documentación |
Antes y después, con un texto:
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Messages.json" \
-u "$TWILIO_API_KEY:$TWILIO_API_SECRET" \
--data-urlencode "From=whatsapp:+14155550100" \
--data-urlencode "To=whatsapp:+56912345678" \
--data-urlencode "Body=Hola Ana, tu pedido ya va en camino"curl https://app.heysteff.com/api/v1/messages \
-H "Authorization: Bearer $HEYSTEFF_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-1042-en-camino" \
-d '{ "channel": "whatsapp_cloud", "to": "+56912345678", "content": "Hola Ana, tu pedido ya va en camino" }'Y una plantilla en el formato de la Cloud API (360dialog, YCloud y Kapso usan este cuerpo o uno muy parecido):
{
"messaging_product": "whatsapp",
"to": "56912345678",
"type": "template",
"template": {
"name": "pedido_despachado",
"language": { "code": "es" },
"components": [{ "type": "body", "parameters": [
{ "type": "text", "text": "Ana" }, { "type": "text", "text": "#1042" }
] }]
}
}{
"channel": "whatsapp_cloud",
"to": "+56912345678",
"content_type": "template",
"template": { "name": "pedido_despachado", "language": "es", "body_params": ["Ana", "#1042"] }
}Si tu plantilla lleva encabezado con imagen o botones con variables, manda template.components con la forma de la Graph API de Meta: heysteff la respeta y gana sobre body_params. Más en Tipos de mensaje.
Webhooks y firma#
heysteff manda un solo formato para todos los canales: un POST JSON con el sobre { id, tenant_id, event, ts, data }. El mensaje entrante trae en data todo lo que necesitas para contestar (channel, external_user_id, channel_connection_id).
| heysteff | Twilio | 360dialog | YCloud | Kapso | |
|---|---|---|---|---|---|
| Dónde se configura | Consola, sección Webhooks | Consola de Twilio (entrantes) y StatusCallback por mensaje | POST /v1/configs/webhook o POST /waba_webhook | POST /v2/webhookEndpoints | POST /platform/v1/whatsapp/phone_numbers/{id}/webhooks |
| Mensaje entrante | message:received | POST de formulario a tu URL de entrada | Objeto messages del payload de Meta | whatsapp.inbound_message.received | whatsapp.message.received |
| Estado de un mensaje enviado | message:sent y message.failed | MessageStatus en StatusCallback | Objeto statuses | whatsapp.message.updated | whatsapp.message.sent, .delivered, .read, .failed |
| Header de la firma | X-Heysteff-Signature (v1= + hex) y X-Heysteff-Timestamp | X-Twilio-Signature | No documenta una firma: autentica con headers propios que defines al crear el webhook | YCloud-Signature (t=…,s=…) | X-Webhook-Signature |
| Qué se firma | HMAC-SHA256 de ${timestamp}.${cuerpo crudo} | HMAC-SHA1 de la URL más los parámetros del POST, con tu auth token | No aplica | HMAC-SHA256 de t + "." + cuerpo crudo | HMAC-SHA256 del cuerpo crudo |
| Reintentos y duplicados | Deduplica por X-Heysteff-Delivery-Id, que se repite en cada reintento | Revisa su documentación | Meta reintenta si no respondes 200 | Reintenta con espera creciente | Deduplica por X-Idempotency-Key |
Si vienes de YCloud, la firma es casi igual: cambia el header y el prefijo v1=. Si vienes de Kapso, suma el timestamp al string firmado. Si vienes de Twilio, deja de firmar la URL: heysteff firma el cuerpo crudo.
import crypto from 'node:crypto';
// rawBody: el cuerpo tal como llegó, sin parsear.
function isFromHeysteff(rawBody, timestamp, signature, secret) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = 'v1=' + crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}El SDK lo hace por ti con verifyWebhook, y heysteff webhooks trigger de la CLI te manda un aviso firmado a localhost para probar. Detalle en Webhooks.
Conceptos#
| Concepto | En heysteff | Cómo se llamaba antes |
|---|---|---|
| Número o cuenta | Un canal conectado (channel_connection_id). Un mismo espacio tiene números de WhatsApp, cuentas de Instagram y Messenger, SMS y correo. | Twilio: el From (whatsapp:+…) o un Messaging Service. 360dialog: la API key de cada número. YCloud: el from. Kapso: el phone_number_id en la URL. |
| Plantilla | Se manda por nombre e idioma (template.name, template.language) con body_params en orden. GET /v1/templates lista las aprobadas. | Twilio: un ContentSid de su Content API. En los proveedores con formato Cloud API, el mismo nombre e idioma de Meta. |
| Ventana de 24 horas | Igual que en todos: WhatsApp solo deja mandar texto libre dentro de las 24 horas siguientes al último mensaje del cliente; fuera de ella, una plantilla. Fuera de la ventana la API responde con un error claro en vez de aceptar un mensaje que no se va a entregar. | Es una regla de WhatsApp, no del proveedor: no cambia. |
| Sandbox | Una key sk_test_ y el número compartido de heysteff: unes hasta 5 teléfonos con un código desde la consola. Ver Sandbox. | Twilio: número compartido y join <código>. 360dialog: mensaje START a su número de sandbox. Kapso: código de 6 caracteres a su número de sandbox. |
| Bandeja y personas | Cada conversación también aparece en la bandeja de la web y de la app móvil. POST /v1/conversations/{id}/escalate se la pasa a una persona. | Depende del proveedor. |
Pasos para cambiarte#
- Crea tu cuenta y tu API key. Crea una cuenta developer y corre
curl -fsSL https://developers.heysteff.com/install.sh | bashy despuésheysteff setup: te lleva a la consola, guarda la key y manda un mensaje de prueba. - Prueba en el sandbox. Con una key
sk_test_cambia tus llamadas aPOST /v1/messagessin tocar tu número actual. - Mueve tu receptor de webhooks. Crea el webhook en la consola, guarda el secreto y cambia la verificación de la firma (ver arriba). Mientras migras, tu servidor puede aceptar los dos formatos.
- Conecta un número. En la consola, sección Números, conecta tu número con la ventana de alta de Meta o usa un número de heysteff. Si tu número hoy está en otro proveedor, sigue el proceso de Meta para llevarlo a otra cuenta o proveedor, y revisa con tu proveedor actual qué pasa con sus plantillas.
- Revisa tus plantillas.
GET /v1/templatesmuestra las aprobadas del número conectado. Si venías de Twilio, reemplaza cadaContentSidpor el nombre y el idioma de la plantilla. - Cambia a
sk_live_y apaga el proveedor anterior cuando veas pasar tus mensajes en la bandeja y en el registro de entregas.
¿Usas Claude Code, Cursor o Codex? Instala la skill de heysteff (ver Agentes de código) y pídele que migre tu integración con esta guía.