Postbacks de Facebook Messenger explicados: cómo manejar taps de botones en webhooks
13 may 2026
·
14 min de lectura
En esta guía: Qué es un postback · El flujo de datos · Los tres tipos de botón que generan postbacks · Esquemas exactos del payload del webhook · El detalle de la suscripción messaging_postbacks · Comparación entre postback y quick reply · Patrones de diseño de payload · Postbacks de referral desde anuncios · Sistema de enrutamiento en producción · Formato normalizado de SocialHook
¿Qué es un postback de Facebook Messenger?
Un postback es el evento de webhook que se dispara cuando un usuario toca un botón configurado con una cadena payload en Facebook Messenger. El payload es una cadena que defines tú al construir el botón — viaja desde tu servidor a Meta, al cliente del usuario y luego de regreso a tu servidor cuando se toca. Es la forma en que Messenger le dice a tu bot: "el usuario tomó una decisión deliberada, y aquí está cuál".
A diferencia de un mensaje de texto (donde el usuario escribe algo impredecible), un postback transporta exactamente el payload que configuraste — sin necesidad de NLP, sin coincidencia difusa, sin detección de intención. El usuario tocó "Rastrear mi pedido" y tu servidor recibe TRACK_ORDER. Determinista, limpio, confiable.
El mecanismo de postback impulsa tres de los elementos interactivos más importantes de Messenger:
Botones de postback dentro de mensajes con plantilla (button template, carousel de generic template)
Elementos del menú persistente (el icono de hamburguesa en la parte inferior de cada conversación)
Entradas de referral (usuarios que llegan vía enlaces m.me o anuncios Click-to-Messenger)
El flujo de datos completo: del tap al evento de webhook
🔘
Tú configuras
Botón con payload: "TRACK_ORDER"
👆
El usuario toca
Botón en la UI de Messenger
📡
Meta dispara
POST a tu URL de webhook
⚡
Tu servidor
event.postback .payload === "TRACK_ORDER"
El detalle crítico en ese flujo: el evento de webhook que dispara Meta es un evento de postback — llega bajo entry.messaging[].postback, no bajo entry.messaging[].message. Esta es la razón por la que muchos desarrolladores no lo detectan: escriben un handler de mensajes y se preguntan por qué los taps de botón nunca llegan. Consulta la sección de suscripción más abajo para entender por qué pasa esto y cómo solucionarlo.
Los tres tipos de botón que generan postbacks
Botón de postback
En plantillas
Aparece dentro de mensajes con button template, generic template (carousel) o media template. Permanece visible en la conversación después de ser tocado. Máximo 3 botones por button template, 3 por tarjeta de generic template. La etiqueta del botón tiene un máximo de 20 caracteres.
El payload llega en
event.postback.payload
Elemento de menú persistente
Siempre visible
Se configura una vez por Page a través de la Messenger Profile API — aparece como un icono de menú hamburguesa para todos los usuarios. Se permite una estructura anidada (2 niveles). Los elementos del menú con type "postback" disparan eventos de postback; type "web_url" abre una URL sin postback.
El payload llega en
event.postback.payload
Quick Reply
Desaparece tras el tap
Aparece como burbujas desplazables horizontalmente. Desaparece después de que el usuario toca una o envía otro mensaje. A pesar de disparar una cadena "payload", las quick replies NO llegan en eventos de postback — llegan en eventos de mensaje. Consulta la comparación más abajo.
El payload llega en
event.message.quick_reply.payload
Esquemas exactos del payload del webhook para cada tipo
Botón de postback y elemento de menú persistente
Postback event — full raw webhook payload
{
"object": "page",
"entry": [{
"id": "PAGE_ID",
"messaging": [{
"sender": { "id": "12345678901234" }, // PSID of user who tapped"recipient": { "id": "PAGE_ID" },
"timestamp": 1747231892100,
"postback": { // ← THIS IS THE POSTBACK OBJECT (not .message)"mid": "m_abc123...", // message ID of the button message"title": "Track My Order",// the button label the user saw and tapped"payload": "TRACK_ORDER"// ← YOUR PAYLOAD — what you configured on the button
}
}]
}]
}
// Extraction: event.postback.payload === "TRACK_ORDER"// event.postback.title === "Track My Order" (the label — useful for logging)// event.sender.id === PSID of the user who tapped
Quick reply (NO es un postback — llega en eventos de mensaje)
Quick reply event — lives in .message, not .postback
{
"messaging": [{
"sender": { "id": "12345678901234" },
"timestamp": 1747231892100,
"message": { // ← note: .message, not .postback"mid": "m_def456...",
"text": "Yes, please!", // the quick reply title sent as a message text"quick_reply": {
"payload": "CONFIRM_YES"// ← YOUR PAYLOAD — same concept, different location
}
}
}]
}
// Extraction: event.message.quick_reply.payload === "CONFIRM_YES"// event.message.text still contains the quick reply title text// Subscription needed: messages (NOT messaging_postbacks)
El detalle de la suscripción: por qué tus postbacks no están llegando
⚠️
Debes suscribirte a messaging_postbacks por separado
Esta es la razón #1 por la que los desarrolladores reportan "mis botones no funcionan" — se suscribieron a messages pero no a messaging_postbacks. Estas son suscripciones de campos de webhook separadas. Suscribirse a una no incluye la otra. Sin messaging_postbacks, tu endpoint POST nunca recibirá un evento de postback, incluso si tu botón está correctamente configurado con un payload.
✓ messages → mensajes de texto, adjuntos, quick replies
✓ messaging_postbacks → taps de botones, menú persistente, referrals
✗ NO incluido automáticamente
Graph API (de forma programática): haz POST a /PAGE_ID/subscribed_apps con subscribed_fields=messages,messaging_postbacks,message_reads
Botones de postback vs quick replies: cuándo usar cada uno
La distinción fundamental en el handler de tu webhook: los eventos de postback llegan en event.postback; los payloads de quick reply llegan en event.message.quick_reply. Tu router necesita verificar ambas ubicaciones.
Node.js — detect and extract all payload types
detectPayload.js
functionextractPayload(event) {
// 1. Standard postback button or persistent menu tapif (event.postback?.payload) {
return {
type: 'POSTBACK',
payload: event.postback.payload,
title: event.postback.title, // button label — useful for logging
};
}
// 2. Quick reply button tap (arrives in message event)if (event.message?.quick_reply?.payload) {
return {
type: 'QUICK_REPLY',
payload: event.message.quick_reply.payload,
title: event.message.text, // quick reply title sent as message text
};
}
// 3. Referral postback (user arrived via m.me link or ad)if (event.postback?.referral || event.referral) {
const ref = event.postback?.referral || event.referral;
return {
type: 'REFERRAL',
payload: event.postback?.payload || null,
ref: ref.ref, // your tracking parameter
source: ref.source, // 'SHORTLINK' | 'ADS' | 'CUSTOMER_CHAT_PLUGIN' | etc
};
}
// 4. Regular text message — no payloadif (event.message?.text) {
return {
type: 'TEXT',
payload: null,
text: event.message.text,
};
}
return { type: 'UNKNOWN', payload: null };
}
Patrones de diseño de payload: buenos vs malos
La cadena del payload es tuya para diseñarla — Facebook la trata como una cadena opaca. La forma en que diseñas tus payloads determina qué tan mantenible y depurable se vuelve la lógica de tu bot a escala. Aquí están los patrones que funcionan y los que causan problemas:
Fácil de enrutar con startsWith() o switch. El prefijo te dice el flujo, el sufijo te dice la elección. Legible en los logs. Lo suficientemente corto para mantenerse bien por debajo del límite de 1.000 caracteres.
JSON.stringify() el objeto, JSON.parse() al recibir. Úsalo solo cuando necesites pasar contexto que no se pueda inferir del estado de la conversación. Mantenlo compacto — cada carácter cuenta para los 1.000.
✗ Malo — palabras sueltas opacas
Payloads ambiguos y con colisiones
YES NO NEXT OK BACK
Estas colisionan entre flujos. ¿"YES" a qué? ¿En qué estado de la conversación? Si tienes dos button templates diferentes ambos usando "YES", tu router no puede distinguirlos sin verificar el estado de la conversación cada vez.
✗ Malo — vacíos o texto visible para el usuario
Payloads no legibles por máquina
"Yes, please!" "Track my order" ""
Las cadenas de payload vacías provocan que Facebook rechace la configuración del botón. Usar texto visible para el usuario como payload se rompe cuando cambias la etiqueta del botón sin actualizar tu router. Los payloads deben ser identificadores de máquina, no cadenas de visualización.
Versiona los payloads de tu menú persistente. El menú persistente se configura globalmente para todos los usuarios de tu Page. Si agregas un elemento de menú que cambia una cadena de payload, los usuarios existentes que aún no han abierto Messenger obtendrán el nuevo menú. Usa sufijos de versión como MENU_SUPPORT_V2 para que tu router maneje tanto los payloads viejos como los nuevos durante las transiciones. Nunca reutilices una cadena de payload para una acción diferente.
Postbacks de referral: rastreando puntos de entrada de campañas
Cuando un usuario hace clic en un enlace m.me con un parámetro ref — como m.me/yourpage?ref=EMAIL_CAMPAIGN_MAY — o llega vía un anuncio Click-to-Messenger, Facebook dispara un evento de referral. Así es como rastreas qué campaña, anuncio o punto de entrada trajo al usuario a Messenger.
Los eventos de referral tienen dos formas dependiendo de si el usuario está iniciando una nueva conversación o regresando a una existente:
Referral postback payloads — both forms
// Form 1: New conversation — arrives as a standalone referral event// (user has never messaged your Page before, or is starting fresh)
{
"referral": {
"ref": "EMAIL_CAMPAIGN_MAY", // your ?ref= parameter"source": "SHORTLINK", // 'SHORTLINK' | 'ADS' | 'CUSTOMER_CHAT_PLUGIN'"type": "OPEN_THREAD"// always "OPEN_THREAD" for user-initiated
}
// Note: no postback.payload — this is a pure referral with no button
}
// Form 2: Button click with referral context (e.g. persistent menu from ad landing)// Arrives as a postback event with BOTH payload AND referral
{
"postback": {
"title": "Get Started",
"payload": "GET_STARTED",
"referral": {
"ref": "AD_SUMMER_SALE",
"source": "ADS",
"type": "OPEN_THREAD"
}
}
// postback.payload = the button action// postback.referral.ref = the campaign tracking string
}
Para recibir eventos de referral, suscríbete a messaging_referrals. Esta es una tercera suscripción de webhook, separada de messages y messaging_postbacks. Sin ella, los eventos de referral de la Forma 1 (arranques en frío de nuevas conversaciones) nunca llegan. La Forma 2 (postback + referral) llega con messaging_postbacks suscrito. Suscríbete a las tres para una cobertura completa.
Sistema de enrutamiento en producción: arquitectura de handlers estructurada
Un switch sobre el payload funciona para 5 botones. Con 50 botones repartidos en 10 flujos, se vuelve inmantenible. Aquí está el patrón de producción: un registro de enrutamiento que mapea prefijos de payload a módulos de handler, con soporte para payloads JSON.
Cuando recibes eventos de Messenger a través de SocialHook, los postbacks vienen pre-extraídos y se entregan en el mismo formato normalizado que todos los demás tipos de eventos. El anidamiento crudo de entry[0].messaging[0].postback ya viene desempaquetado:
SocialHook normalized postback event
// Postback button tap:
{
"platform": "facebook",
"event": "postback",
"from": "12345678901234", // PSID"postback": {
"payload": "TRACK_ORDER", // your payload — ready to route"title": "Track My Order"// button label
},
"signature_verified": true
}
// Quick reply payload:
{
"platform": "facebook",
"event": "message.received",
"from": "12345678901234",
"message": {
"type": "quick_reply",
"payload": "CONFIRM_YES", // extracted from message.quick_reply.payload"body": "Yes, please!"// the quick reply title text
},
"signature_verified": true
}
// Your handler:
app.post('/webhook', async (req, res) => {
res.sendStatus(200);
const { event, from, postback, message } = req.body;
if (event === 'postback') awaitroutePostback(from, postback.payload);
if (message?.type === 'quick_reply') awaitroutePostback(from, message.payload);
});
FAQ
Preguntas frecuentes
¿Qué es un postback de Facebook Messenger?
Un postback es el evento de webhook que se dispara cuando un usuario toca un botón de postback o un elemento del menú persistente. Tú configuras una cadena payload en el botón — cuando se toca, Facebook envía esa cadena exacta a tu webhook en event.postback.payload. Es la forma en que tu bot recibe señales de intención estructuradas a partir de los taps de botón del usuario, sin necesidad de NLP ni de parseo de texto.
¿Por qué mis postbacks de Messenger no están llegando a mi webhook?
Causa más probable: te suscribiste a messages pero no a messaging_postbacks. Son suscripciones separadas. Solución: Messenger Settings → Webhooks → Edit Subscriptions → marca messaging_postbacks. Segunda verificación: tu botón tiene type: 'postback' (no type: 'web_url'). Tercera verificación: el campo payload de tu botón no está vacío (los payloads vacíos provocan un rechazo silencioso).
¿Cuál es la diferencia entre un postback y una quick reply en Messenger?
Ambos entregan una cadena de payload cuando se tocan, pero difieren en tres aspectos: (1) Ubicación en el webhook: postback → event.postback.payload; quick reply → event.message.quick_reply.payload. (2) Apariencia: los botones de postback persisten en la conversación; las quick replies desaparecen después de un tap. (3) Suscripción: los postbacks requieren messaging_postbacks; las quick replies llegan con messages.
¿Puedo usar JSON como payload de postback de Messenger?
Sí — haz JSON.stringify() de un objeto y usa la cadena resultante como payload. Al recibir, haz JSON.parse() para recuperarlo. Facebook trata el payload como una cadena opaca de hasta 1.000 caracteres. Usa payloads JSON cuando necesites pasar contexto estructurado (productId, cantidad, estado del flujo) que no se pueda inferir solo del estado de la conversación. Usa JSON compacto: elimina los espacios con JSON.stringify(obj) (sin argumento de espacio).
¿Cómo rastreo qué campaña trajo a un usuario a mi bot de Messenger?
Usa postbacks de referral. Agrega un parámetro ?ref=YOUR_CAMPAIGN_ID a tu enlace m.me (p. ej. m.me/yourpage?ref=EMAIL_MAY). Cuando el usuario abre Messenger, se dispara un evento de referral con tu cadena ref en event.referral.ref. Suscríbete a messaging_referrals para recibir eventos de referral de arranque en frío. Si el usuario toca un botón después de llegar, los datos del referral aparecen junto al postback en event.postback.referral.ref.
¿Cómo configuro un menú persistente de Messenger?
Haz POST una vez a graph.facebook.com/v21.0/me/messenger_profile con tu Page Access Token y un array persistent_menu. Cada elemento tiene un type (postback o web_url), un title y ya sea un payload (postback) o una url (web_url). Los cambios se aplican globalmente a todos los usuarios. Solo necesitas llamar a esta API cuando actualices el menú — persiste hasta que lo cambies. Los eventos de postback desde elementos del menú llegan de forma idéntica a los postbacks de botones de plantilla.
Conecta tu Facebook Page a SocialHook. Cada postback llega a tu handler con el payload ya extraído — sin parseo de webhook crudo, sin depurar suscripciones, sin código repetitivo de HMAC. Solo event.postback.payload exactamente cuando lo necesitas.