Brinda a tus clientes la tranquilidad que esperan con actualizaciones de pedidos en tiempo real enviadas directamente a sus teléfonos. La integración de Webflow y Twilio convierte cada cambio en el estado de un pedido en una línea directa de comunicación.
Algunas tiendas de Webflow E-commerce envían correos electrónicos transaccionales y se quedan ahí. Las tasas de apertura de SMS rondan el 98%, en comparación con el 20% aproximadamente del correo electrónico; para las confirmaciones de pedidos y actualizaciones de envío, esa diferencia es clave.
La configuración es más sencilla de lo que parece: un webhook de Webflow E-commerce se activa cuando se realiza o actualiza un pedido, un gestor de rutas en Webflow Cloud lo recibe y la API de mensajes de Twilio envía el SMS. Sin herramientas de automatización de terceros ni servicios adicionales entre el pedido y el teléfono del cliente.
En esta guía, exploramos cómo usar fetch directamente con la API de mensajes de Twilio, que funciona perfectamente en el entorno de ejecución Workers de Webflow Cloud desde el primer día.
¿Qué necesitas para enviar un SMS sobre el estado de un pedido desde Webflow E-commerce?
Necesitas un proyecto de Webflow Cloud, un sitio de Webflow E-commerce y una cuenta de Twilio con un número de teléfono. No se requiere infraestructura adicional; un único gestor de rutas se encarga del receptor del webhook, la validación de la firma y el envío del SMS.
Confirma que tienes todo esto antes de continuar:
- Un proyecto de Webflow Cloud ejecutando una aplicación Next.js, ya sea desplegada o en desarrollo local
- Un sitio de Webflow E-commerce con un plan de sitio Ecommerce (Standard, Plus o Advanced), que es lo que habilita Webflow Ecommerce y los webhooks de pedidos
- Un proceso de pago que recopile un número de teléfono a través de un campo de información adicional, ya que la carga útil estándar de pedidos de Webflow solo incluye nombre y correo electrónico (se explica en el paso 3 y en la resolución de problemas)
- Un token de sitio de Webflow con acceso de escritura para sitios (
sites:write), que es el alcance documentado para crear un webhook, generado en Configuración del sitio > Aplicaciones e integraciones > Acceso a la API. Los requisitos de alcance del webhook dependen del tipo de activador, así que añade acceso de lectura a Ecommerce si notas que tus activadores de comercio electrónico son rechazados - Una cuenta de Twilio con un número de teléfono verificado capaz de enviar SMS (una cuenta de prueba de Twilio sirve para realizar pruebas, pero requiere números de destinatario verificados durante el periodo de prueba)
- Node.js 22 o superior instalado localmente
Una vez que tengas todo esto, estarás listo para comenzar con la integración completa.
5 pasos para enviar SMS sobre el estado de pedidos desde Webflow E-commerce con Twilio
Webflow envía un webhook de ecomm_new_order o ecomm_order_changed a un gestor de rutas en tu aplicación Cloud. El gestor de rutas valida la firma del webhook (para evitar solicitudes falsificadas), extrae el número de teléfono del cliente y los detalles del pedido, y luego llama a la API de mensajes de Twilio. El cliente recibe un SMS en cuestión de segundos.
Aquí tienes los pasos a seguir.
1. Obtén tus credenciales de Twilio y tu número de teléfono
Inicia sesión en la consola de Twilio. Tu SID de cuenta se encuentra en el panel de control, bajo Información de la cuenta; cópialo.
Para la credencial, crea una clave de API en lugar de usar el token de autenticación (Auth Token). Twilio recomienda usar claves de API para todas las aplicaciones y tratar el token de autenticación como una credencial de pruebas locales; la ventaja práctica es la revocabilidad: una clave de API puede rotarse de forma independiente, mientras que rotar el token de autenticación invalida todas las demás integraciones de la cuenta al mismo tiempo.
Ve a Account > API keys & tokens, crea una clave estándar y copia el SID de la clave y el secreto. El secreto solo se muestra una vez.
A continuación, ve a Phone Numbers > Manage > Active Numbers y copia tu número de teléfono de Twilio. Si no tienes uno, ve a Phone Numbers > Buy a Number, filtra por capacidades de SMS y compra un número.
Durante la prueba, Twilio solo te permite enviar mensajes a números de teléfono que hayas verificado en Verified Caller IDs. Añade tu número de prueba en Phone Numbers > Verified Caller IDs > Add a new number antes de empezar a probar.
2. Añade las credenciales de Twilio a las variables de entorno de Webflow Cloud
Añade las siguientes variables de entorno a tu .env.local para el desarrollo local, y al entorno de producción abriendo el panel de Deployments del entorno y haciendo clic en Environment Variables:
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_API_KEY_SID=SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_API_KEY_SECRET=your_api_key_secret_here
TWILIO_FROM_NUMBER=+15551234567
WEBFLOW_SECRET_NEW_ORDER=secret_from_the_new_order_webhook
WEBFLOW_SECRET_ORDER_CHANGED=secret_from_the_order_changed_webhook
Los dos valores de Webflow son las claves secretas que se devuelven al crear cada webhook a través de la API (explicado en el paso 4). Los webhooks creados con un token de sitio después del 14 de abril de 2025 obtienen su propia `secretKey` en la respuesta de creación, por lo que cada disparador tiene su propia clave en lugar de una por sitio.
Para Twilio, es preferible usar una clave de API en lugar del token de autenticación de la cuenta. La recomendación de Twilio es "usar claves de API para todas las aplicaciones" y tratar el token de autenticación como una credencial de pruebas locales; así puedes revocar una clave de API sin romper el resto de las integraciones de la cuenta.
Crea una en Account > API keys & tokens. El par de autenticación básica pasa a ser el SID de la clave y su secreto, en lugar del SID de la cuenta y el token de autenticación.
Marca cada una como Secret al añadirla en Webflow Cloud; esto cifra el valor y lo oculta en el panel de control.
Ninguna de estas variables debe tener un prefijo NEXT_PUBLIC_. Cualquier variable con el prefijo NEXT_PUBLIC_ se incluye en el paquete JavaScript del navegador. Exponer tu token de autenticación de Twilio en un paquete público permitiría que cualquiera enviara mensajes SMS desde tu cuenta.
3. Crea el gestor de rutas (Route Handler) de SMS de pedidos en Webflow Cloud
Crea app/api/webhooks/order/route.ts. Este gestor recibe el webhook de Webflow, valida la firma HMAC usando la Web Crypto API (`crypto.subtle`, una API web estándar disponible en el entorno de ejecución de Workers) y llama a la API de mensajes de Twilio a través de fetch:
// app/api/webhooks/order/route.ts
import { NextResponse, type NextRequest } from 'next/server'
// Verify the Webflow webhook signature using the Web Crypto API.
// crypto.subtle is a standard Web API available in the Workers
// runtime, so it needs no Node crypto import and is fully portable.
async function verifyWebflowSignature(
secret: string,
timestamp: string,
rawBody: string,
providedSignature: string
): Promise<boolean> {
const data = `${timestamp}:${rawBody}`
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
)
const signatureBuffer = await crypto.subtle.sign(
'HMAC',
key,
new TextEncoder().encode(data)
)
const computedHex = Array.from(new Uint8Array(signatureBuffer))
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
// Validate the timestamp to block replay attacks. Parse first and
// reject anything unparseable: `parseInt('')` is NaN, and every
// comparison against NaN is false, so a naive `age > limit` check
// silently passes when the header is missing.
const sentAt = Number.parseInt(timestamp, 10)
if (!Number.isFinite(sentAt)) return false
const requestAge = Date.now() - sentAt
if (requestAge > 300_000 || requestAge < -60_000) return false
// Constant-time comparison, as in Webflow's own reference
// implementation. A plain === leaks timing information.
if (computedHex.length !== providedSignature.length) return false
let mismatch = 0
for (let i = 0; i < computedHex.length; i++) {
mismatch |= computedHex.charCodeAt(i) ^ providedSignature.charCodeAt(i)
}
return mismatch === 0
}
async function sendOrderSMS(to: string, body: string): Promise<void> {
const accountSid = process.env.TWILIO_ACCOUNT_SID!
const keySid = process.env.TWILIO_API_KEY_SID!
const keySecret = process.env.TWILIO_API_KEY_SECRET!
const from = process.env.TWILIO_FROM_NUMBER!
const url = `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`
// Authenticate with an API key rather than the account Auth Token,
// so this credential can be revoked on its own.
const credentials = btoa(`${keySid}:${keySecret}`)
const res = await fetch(url, {
method: 'POST',
headers: {
Authorization: `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ To: to, From: from, Body: body }).toString(),
})
if (!res.ok) {
const errorText = await res.text()
throw new Error(`Twilio error ${res.status}: ${errorText}`)
}
}
export async function POST(request: NextRequest) {
const rawBody = await request.text()
const timestamp = request.headers.get('x-webflow-timestamp') ?? ''
const signature = request.headers.get('x-webflow-signature') ?? ''
// Each webhook has its own secret, so read the trigger first and
// validate against the matching key.
const peeked = JSON.parse(rawBody) as { triggerType?: string }
const secret =
peeked.triggerType === 'ecomm_order_changed'
? process.env.WEBFLOW_SECRET_ORDER_CHANGED!
: process.env.WEBFLOW_SECRET_NEW_ORDER!
// Validate signature before processing anything.
const isValid = await verifyWebflowSignature(secret, timestamp, rawBody, signature)
if (!isValid) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
}
const event = JSON.parse(rawBody) as {
triggerType: string
payload: {
orderId: string
status: string
customerInfo?: {
fullName?: string
email?: string
}
customData?: { name?: string; textInput?: string }[]
purchasedItems?: { productName: string; count: number }[]
}
}
const { triggerType, payload } = event
// Webflow's order payload carries only name and email on customerInfo.
// A phone number collected through a checkout "Additional info" field
// arrives in the customData array, keyed by the field label you set.
const phone = payload.customData?.find((f) =>
f.name?.toLowerCase().includes('phone')
)?.textInput
// Only send SMS for new orders and status changes.
// Skip if no phone number on the order.
if (
(triggerType !== 'ecomm_new_order' && triggerType !== 'ecomm_order_changed') ||
!phone
) {
return NextResponse.json({ ok: true, skipped: true })
}
let message: string
if (triggerType === 'ecomm_new_order') {
const name = payload.customerInfo?.fullName?.split(' ')[0] ?? 'there'
message = `Hi ${name}, your order #${payload.orderId} is confirmed. We'll text you when it ships.`
} else {
// ecomm_order_changed
const statusMap: Record<string, string> = {
fulfilled: 'has shipped',
refunded: 'has been refunded',
disputed: 'has a dispute open',
}
const statusText = statusMap[payload.status]
// ecomm_order_changed fires on ANY change to the order, including
// edits to fields Webflow itself never sets, like the tracking
// number. Only text the customer for statuses worth a text, or
// editing a note will send "your order is now unfulfilled".
if (!statusText) {
return NextResponse.json({ ok: true, skipped: true })
}
message = `Order #${payload.orderId} update: your order ${statusText}. Questions? Reply to this message.`
}
await sendOrderSMS(phone, message)
return NextResponse.json({ ok: true })
}
La llamada request.text() lee el cuerpo sin procesar antes de cualquier análisis. Esto es importante para la validación de la firma: una vez que analizas el cuerpo con request.json(), los bytes originales desaparecen y no puedes reconstruir la cadena original que firmó Webflow.
4. Registra el webhook de comercio electrónico de Webflow
Registra los webhooks a través de la API para recibir la cabecera `x-webflow-signature` para la validación; los webhooks configurados a través del panel de control no-code no envían esa cabecera. Un webhook creado con un token de sitio después del 14 de abril de 2025 devuelve su propia `secretKey`, mientras que un webhook creado a través de una aplicación OAuth utiliza el secreto de cliente de la aplicación como clave de firma.
Siempre creo los webhooks a través de la API para mantener la configuración de firma consistente.
Envía una solicitud POST al endpoint de Webhooks de Webflow, una vez para los pedidos nuevos y otra para los cambios de estado de los pedidos:
# Register webhook for new orders
curl -X POST "https://api.webflow.com/v2/sites/YOUR_SITE_ID/webhooks" \
-H "Authorization: Bearer YOUR_SITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"triggerType": "ecomm_new_order",
"url": "https://your-app.webflow.io/api/webhooks/order"
}'
# Register webhook for order status changes
curl -X POST "https://api.webflow.com/v2/sites/YOUR_SITE_ID/webhooks" \
-H "Authorization: Bearer YOUR_SITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"triggerType": "ecomm_order_changed",
"url": "https://your-app.webflow.io/api/webhooks/order"
}'
Ambas respuestas de webhook incluyen un campo secretKey, y no son el mismo valor. Webflow asigna a cada webhook su propia clave secreta, así que guárdalas ambas y elige la correcta para cada disparador:
WEBFLOW_SECRET_NEW_ORDER=secret_from_the_first_response
WEBFLOW_SECRET_ORDER_CHANGED=secret_from_the_second_response
Conseguir que esta configuración sea correcta es fundamental, ya que cualquier error provoca fallos silenciosos. Si guardas un solo secreto para ambos webhooks, cada evento del otro fallará la validación de firma, devolverá un error 401 y se reintentará tres veces antes de que Webflow desactive ese webhook y te envíe un correo electrónico al respecto. Las confirmaciones seguirán funcionando mientras que las notificaciones de envío se detendrán silenciosamente, o viceversa.
Tu ID de sitio se encuentra en Configuración del sitio > General. Tu token de sitio está en Configuración del sitio > Apps e integraciones > Acceso a API > Generar token de API, con acceso de escritura para Sitios (`sites:write`).
5. Prueba el proceso completo de notificación de pedido por SMS de Twilio
La configuración de pagos de Webflow se encuentra en Configuración > Ecommerce > Pagos, y se conecta a Stripe o PayPal para procesar pagos reales. No existe un interruptor de entorno de pruebas (sandbox) documentado, así que planifica tu prueba teniendo esto en cuenta: realiza un pedido real en la tienda publicada y luego devuélvelo, o configura un producto con precio cero para que el proceso de pago se complete sin cargos.
Independientemente del método que elijas, en pocos segundos el ecomm_new_order webhook se activará, el gestor de rutas lo validará y procesará, y tu número verificado de Twilio recibirá el SMS.
Para depurar localmente, usa ngrok para exponer tu servidor local:
ngrok http 3000
Copia la URL HTTPS de ngrok y úsala como destino del webhook al registrarte. Actualiza WEBFLOW_WEBHOOK_SECRET con el secretKey de esa respuesta de registro.
Si el SMS no llega, revisa la consola de Twilio en Monitor > Logs > Messaging. Cada intento de envío se registra allí con su estado y cualquier código de error.
Vigila también el lado de Webflow. Cualquier respuesta que no sea 200 cuenta como un fallo, y Webflow reintentará tres veces más antes de rendirse. Los fallos repetidos desactivan el webhook y recibirás un correo electrónico al respecto, por lo que un secreto de firma incorrecto no solo descarta un mensaje; eventualmente desactivará la integración por completo.
¿Qué hace que fallen los SMS de estado de pedido en Webflow?
La mayoría de los fallos en los SMS de estado de pedido se deben a una de estas tres causas: la integración depende del SDK de Node de Twilio en lugar de `fetch`, la verificación de la firma del webhook está mal configurada o el pedido no incluía un número de teléfono al que enviar el mensaje. Cada uno suele fallar silenciosamente, así que ayuda identificar el síntoma detrás de cada uno.
Estos tres problemas explican la mayoría de los fallos en esta integración.
Los envíos de SMS funcionan localmente pero fallan en Webflow Cloud
El SDK de Node.js de Twilio es el culpable más común. Depende de las API de HTTP y sockets de Node, que el entorno de ejecución de Workers maneja de forma distinta a un servidor Node. Webflow Cloud habilita la compatibilidad con Node.js de forma predeterminada, y el soporte de Cloudflare para estos módulos ha mejorado con el tiempo, así que considéralo una causa probable a descartar, no un fallo garantizado.
Si ves un error de compilación o de ejecución como `Dynamic require of "node:net" is not supported` (o un error similar de módulo no compatible) en tus registros de Cloud, significa que el SDK incluyó una dependencia de Node que el entorno de ejecución no puede empaquetar. Sustituye el SDK por el patrón `fetch` directo del paso 3.
Nuestra guía sobre verificación por SMS de dos factores se topa con el mismo muro desde otro ángulo y utiliza la misma solución.
La validación de la firma del webhook falla
Las tres causas más comunes: el WEBFLOW_WEBHOOK_SECRET no coincide con el secretKey de la respuesta de registro del webhook; el cuerpo original (raw body) se analizó antes de la validación (usa request.text(), no request.json()); o la marca de tiempo es demasiado antigua porque el reloj de tu servidor está significativamente desincronizado.
Registra los valores de computedHex y providedSignature uno al lado del otro para acotar el problema.
El número de teléfono del cliente no aparece en la carga útil (payload)
La carga útil estándar de pedidos de Webflow solo incluye el nombre y el correo electrónico del cliente, por lo que el número de teléfono solo aparecerá si lo solicitas durante el proceso de pago.
En el Diseñador de Webflow, abre la página de Checkout y arrastra un elemento de Información adicional desde el panel Añadir al lienzo. No existe un interruptor de teléfono integrado: añade tu propio campo de entrada con etiqueta, y la etiqueta que elijas se convertirá en la clave bajo la cual llegará el valor en la matriz `customData` del pedido. Haz que el campo sea obligatorio si la confirmación por SMS es tu método principal de notificación.
Un paso que la gente suele pasar por alto: añade el elemento a ambas páginas, la de Checkout y la de Checkout (PayPal). Si olvidas la segunda, los pedidos pagados a través de PayPal llegarán sin número de teléfono, lo que parece un error intermitente en lugar de un elemento faltante.
Crea más notificaciones SMS de estado de pedido
El gestor de rutas de esta guía maneja los dos eventos de pedido más importantes: la realización y el cambio de estado. La carga útil `ecomm_order_changed` de Webflow incluye el objeto de pedido completo y actualizado, por lo que puedes ampliar el `statusMap` con cualquier estado que envíe Webflow (`pending`, `unfulfilled`, `dispute-lost`) y dirigir cada uno a una plantilla de SMS diferente.
Para tiendas con un gran volumen de pedidos, considera añadir una capa de limitación de tasa de Webflow Cloud KV Store antes de la llamada a Twilio para evitar el envío de mensajes SMS duplicados durante los reintentos rápidos de webhooks (Webflow reintenta los webhooks fallidos hasta 3 veces en intervalos de 10 minutos).
Explora Webflow + Twilio para ver el panorama general de lo que hacen ambas plataformas juntas, desde alertas de pedidos y envíos hasta verificación y mensajería bidireccional.
Preguntas frecuentes
¿Puedo usar el SDK de Node.js de Twilio en lugar de fetch?
No, no lo recomiendo en Webflow Cloud. El SDK de Twilio está construido en torno al cliente HTTP y al transporte de sockets de Node, que el entorno de ejecución de Workers no mapea correctamente, por lo que puede fallar al empaquetar o generar errores en el momento del despliegue, incluso cuando funciona en desarrollo local. El enfoque basado en `fetch` de esta guía es funcionalmente idéntico (misma API REST, misma autenticación) y se ejecuta de forma fiable en el entorno de ejecución de Workers.
¿Qué formato de número de teléfono guarda Webflow para los clientes?
Webflow guarda el número de teléfono exactamente como lo escribe el cliente durante el proceso de pago. Si tu SMS falla con un error de "número de teléfono no válido" de Twilio, es probable que el cliente haya introducido un formato local sin código de país. Añade instrucciones en el proceso de pago solicitando el formato internacional completo, o utiliza una biblioteca en el gestor de rutas para analizar y normalizar el número antes de enviarlo.555-1234libphonenumber-js
¿Cómo dejo de enviar SMS a los clientes que no los quieren?
Añade una casilla de verificación de consentimiento como campo de Información adicional en el proceso de pago de Webflow. Llega en la matriz `customData` del pedido como un valor de casilla de verificación, por lo que en el gestor de rutas puedes comprobar ese valor de consentimiento antes de llamar a `sendOrderSMS`. Twilio también gestiona las bajas por su parte, aunque de forma más limitada de lo que la gente cree.
Procesa automáticamente palabras clave estándar como STOP, UNSUBSCRIBE y CANCEL para números gratuitos y de código largo, solo para mensajes de una sola palabra y solo contra el número más reciente que envió un mensaje a esa persona. Si envías desde más de un número, tienes que gestionar la baja tú mismo.
¿Comparten los dos webhooks un secreto de firma?
No. Webflow emite para cada webhook creado con un token de sitio su propio secreto, así que regístralos ambos, guárdalos ambos y selecciona el correcto basándote en el identificador de la carga útil antes de validar. Compartir un secreto entre ambos significa que cada evento del otro webhook fallará la validación y, tras suficientes fallos, Webflow lo desactivará.secretKeytriggerType
¿Puedo enviar diferentes mensajes SMS para diferentes estados de pedido?
Sí. La carga útil del webhook incluye un campo de estado. Amplía el objeto en el gestor de rutas con cualquier cadena de estado que quieras manejar. Los estados que no estén en el mapa recurrirán a la plantilla genérica "tu pedido está ahora [estado]", por lo que ningún gestor fallará si Webflow añade un nuevo estado en el futuro.ecomm_order_changedstatusstatusMap





