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:

Lo que hace falta del lado del negocio:

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

typeCó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

HTTPerrorQué 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.