Instagram y Messenger
Los mensajes directos de Instagram y los de la
página de Facebook (Messenger) del negocio entran y
salen por la misma API que WhatsApp: el mismo
POST /v1/messages con un campo channel, y los
mismos webhooks firmados. Si ya integraste WhatsApp, sumar estos canales
es cambiar un campo.
La diferencia grande con WhatsApp: no hay plantillas.
Instagram y Messenger solo dejan responder dentro de las 24
horas del último mensaje de la persona. Pasado ese plazo no
hay forma de reabrir la conversación desde tu lado: hay que esperar a
que vuelva a escribir. Cuando eso pasa, la API devuelve
409 outside_messaging_window.
Conectar los canales
La conexión la hace una persona del negocio autorizando en el diálogo de Meta (no se puede hacer por API, igual que el Embedded Signup de WhatsApp). Dos caminos:
- Desde el panel: Instagram y Messenger → Conectar canales.
- Mandándole al dueño del negocio el link
https://waba.galgo.io/connect/{project}/{tenant_ref}/social.
Lo que hace falta del lado del negocio:
- Una página de Facebook que administre.
- Para Instagram, una cuenta profesional (empresa o
creador) vinculada a esa página. Si no está vinculada,
la conexión devuelve
no_instagram_accounty te dice cómo resolverlo.
Si el negocio administra varias páginas, el flujo le pide elegir cuál conectar. Instagram y Messenger cuelgan de la misma página: se pueden conectar los dos, o uno solo.
Estado de la conexión
GET /v1/social/accounts
Estado de los dos canales del negocio. Usalo para mostrar "Conectar" vs "Conectado ✓" en tu UI.
Scope: social.read.
GET /v1/social/accounts?tenant_ref=pizzeria-don-mario
Authorization: Bearer TU_API_KEY
Respuesta:
{
"tenant_ref": "pizzeria-don-mario",
"accounts": [
{
"channel": "instagram",
"connected": true,
"ready": true,
"subscribed_ok": true,
"page_id": "102938475610293",
"ig_user_id": "17841400000000000",
"name": "Pizzería Don Mario",
"username": "pizzeriadonmario",
"included_in_plan": true,
"connected_at": "2026-09-07T14:02:11+00:00"
},
{
"channel": "messenger",
"connected": false,
"ready": false,
"subscribed_ok": false,
"page_id": null,
"ig_user_id": null,
"name": null,
"username": null,
"included_in_plan": true,
"connected_at": null
}
]
}
connected es "el negocio conectó este canal";
ready es "además Meta nos está mandando los mensajes". Si
connected: true pero ready: false, la conexión
quedó a medias y hay que volver a pasar por el link de conexión — no lo
escondemos detrás de un solo campo.
Errores: 404 tenant_not_found.
Enviar mensajes
POST /v1/messages
El mismo endpoint de WhatsApp con channel. Si no mandás
channel, se asume whatsapp (nada cambia para
una integración existente).
Scope: messages.send.
El to no es un teléfono ni un @usuario: es
el id que Meta le da a la persona en ese canal (IGSID en Instagram, PSID
en Messenger). Te llega en el webhook message.received y es
estable para ese negocio.
POST /v1/messages
Authorization: Bearer TU_API_KEY
Content-Type: application/json
{
"tenant_ref": "pizzeria-don-mario",
"channel": "instagram",
"to": "6789012345678901",
"type": "text",
"text": { "body": "¡Hola! Sí, estamos abiertos hasta las 23." }
}
Respuesta:
{
"id": "019dcf00-...",
"tenant_ref": "pizzeria-don-mario",
"channel": "instagram",
"type": "text",
"status": "sent",
"to": "6789012345678901",
"message_id": "m_AbCdEf...",
"error_code": null,
"error_message": null,
"idempotency_key": null,
"created_at": "2026-09-07T14:10:00+00:00",
"updated_at": "2026-09-07T14:10:00+00:00"
}
message_id es el id del mensaje en Meta. (En los mensajes de
WhatsApp la respuesta sigue trayendo además waba_message_id,
como siempre.)
Tipos soportados
| type | Cómo se manda |
|---|---|
text |
"text": { "body": "..." } — hasta 1000 bytes en Instagram, 2000 caracteres en Messenger |
image, video, audio, file |
"image": { "link": "https://..." } (URL pública) o { "attachment_id": "..." } |
reaction |
"reaction": { "message_id": "m_...", "reaction": "love" }; con "action": "unreact" se quita |
Los tipos de WhatsApp que no existen en estos canales
(template, interactive, location,
contacts, sticker) devuelven
422 — no se ignoran en silencio.
Botones de respuesta rápida
Se agregan a un mensaje de texto:
{
"tenant_ref": "pizzeria-don-mario",
"channel": "messenger",
"to": "7654321098765432",
"type": "text",
"text": { "body": "¿Retirás o te lo llevamos?" },
"quick_replies": [
{ "content_type": "text", "title": "Retiro", "payload": "RETIRO" },
{ "content_type": "text", "title": "Delivery", "payload": "DELIVERY" }
]
}
Cuando la persona toca un botón, llega un webhook
message.postback con el payload que definiste.
Marcar como leído y "escribiendo…"
POST /v1/messages/mark-read
En estos canales el acuse es sobre la conversación, así que se identifica
con el to (la persona), no con el id de un mensaje.
Scope: messages.send.
POST /v1/messages/mark-read
Authorization: Bearer TU_API_KEY
{
"tenant_ref": "pizzeria-don-mario",
"channel": "messenger",
"to": "7654321098765432",
"typing_indicator": { "type": "text" }
}
typing_indicator es opcional; si lo mandás, además del visto
se muestra "escribiendo…".
La ventana de 24 horas
Es la regla que más va a afectar tu integración. Meta solo permite escribirle a alguien que escribió primero, y solo durante las 24 horas siguientes a su último mensaje. No hay plantillas como en WhatsApp para reabrir la conversación.
Cuando la ventana está cerrada:
HTTP/1.1 409 Conflict
{
"ok": false,
"error": "outside_messaging_window",
"message": "Pasaron más de 24 horas desde el último mensaje de la persona. Instagram y Messenger no tienen plantillas como WhatsApp: hay que esperar a que vuelva a escribir.",
"meta_code": 10
}
No reintentes: el error no se resuelve solo. Si tu
sistema encola respuestas (por ejemplo, una IA que tarda), conviene
chequear cuánto pasó desde el último message.received antes
de enviar.
Desconectar un canal
POST /v1/social/accounts/{channel}/disconnect
Scope: social.write.
POST /v1/social/accounts/instagram/disconnect
Authorization: Bearer TU_API_KEY
{ "tenant_ref": "pizzeria-don-mario" }
Deja de recibir mensajes de ese canal. Si el negocio tiene los dos
canales sobre la misma página, desconectar uno no afecta
al otro. La baja es honesta: si Meta rechaza la operación, devolvemos
502 y la conexión sigue activa — nunca decimos que se
desconectó algo que sigue andando.
Errores propios de estos canales
| HTTP | error | Qué pasó |
|---|---|---|
| 409 | channel_not_connected |
El negocio no tiene ese canal conectado (o quedó a medias). Mandalo al link de conexión. |
| 409 | outside_messaging_window |
Pasaron más de 24hs desde el último mensaje de la persona. |
| 422 | recipient_unavailable |
La persona borró la conversación, bloqueó al negocio, o el id no es de ese canal. |
| 422 | invalid_channel |
channel desconocido. Válidos: whatsapp, instagram, messenger. |
| 403 | plan_not_included |
El plan de tu cuenta no incluye ese canal. |
| 403 | meta_permission_denied |
Meta rechazó por permisos sobre la página. Suele resolverse reconectando el canal. |
| 503 | meta_token_invalid |
El acceso fue revocado desde Facebook. Hay que reconectar. |
Los errores generales (401, 403 insufficient_scope,
404 tenant_not_found, 429) son los mismos de
siempre: ver Errores.
Recibir los mensajes
Llegan a tu webhook_url con el mismo envelope firmado que
los de WhatsApp, y el campo channel te dice de dónde vienen.
Instagram y Messenger pueden tener cada uno
su propio webhook.
El detalle de cada evento está en
Eventos de Instagram y Messenger.