Saltar al contenido principal

Medios

Hay dos formas de enviar un archivo, y la elección cambia bastante el rendimiento.

Meta descarga el archivo de tu servidor en cada envío.

curl -X POST https://api.joinotify.com/v1/106540352242922/messages \n -H 'Authorization: Bearer sk_live_xxx' \n -H 'Content-Type: application/json' \n -d '{
"messaging_product": "whatsapp",
"to": "5541987111527",
"type": "image",
"image": { "link": "https://exemplo.com/catalogo.png" }
}'

Bueno para un archivo único, generado bajo demanda — una factura, un recibo. La URL debe ser pública, con el Content-Type correcto y sin redirección a una página de inicio de sesión.

Por id de medio​

Subes el archivo una vez y reutilizas el id.

1. Subida
curl -X POST https://api.joinotify.com/v1/106540352242922/media \
-H 'Authorization: Bearer sk_live_xxx' \
-F 'messaging_product=whatsapp' \
-F '[email protected];type=image/png'
{ "id": "1234567890" }
2. Envío, tantas veces como quieras
curl -X POST https://api.joinotify.com/v1/106540352242922/messages \n -H 'Authorization: Bearer sk_live_xxx' \n -H 'Content-Type: application/json' \n -d '{
"messaging_product": "whatsapp",
"to": "5541987111527",
"type": "image",
"image": { "id": "1234567890" }
}'

Para envíos masivos, siempre el id del medio. Con link, mil envíos son mil descargas en tu servidor. El archivo permanece en Meta 30 días.

La subida tiene su propio límite: 60 peticiones por minuto.

Límites por tipo​

TipoFormatos aceptadosTamaño
ImagenJPEG, PNG5 MB
VídeoMP4, 3GPP — H.264 + AAC16 MB
AudioAAC, AMR, MP3, MP4 Audio, OGG (solo OPUS)16 MB
Documentocualquiera100 MB
StickerWebP100 KB estático · 500 KB animado

Un vídeo con otro códec se acepta en la subida y falla en la entrega, sin error claro. Si un vídeo «desaparece», comprueba el códec antes que nada.

Recibir medios​

Cuando el cliente envía un archivo, el webhook trae solo el id — no el contenido:

{
"messages": [
{
"from": "5541999999999",
"id": "wamid.HBgN...",
"type": "image",
"image": { "id": "9876543210", "mime_type": "image/jpeg", "sha256": "..." }
}
]
}

Descargarlo son dos pasos.

1. Obtener la URL temporal
curl https://api.joinotify.com/v1/9876543210 \
-H 'Authorization: Bearer sk_live_xxx'
{
"url": "https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=...",
"mime_type": "image/jpeg",
"file_size": 184320,
"id": "9876543210"
}
2. Descargar — la cabecera Authorization es obligatoria
curl -L 'https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=...' \
-H 'Authorization: Bearer sk_live_xxx' \
-o recibido.jpg
Esa URL no es pública

Abrir el enlace en el navegador devuelve error, y ponerlo en un <img src> no funciona. Solo responde con la cabecera Authorization.

No guardes esa URL en tu base de datos como si fuera permanente: caduca. Descarga el archivo, guárdalo en tu propio almacenamiento y conserva tu URL.

Flujo recomendado al recibir​

async function descargarMedio(mediaId) {
const headers = { Authorization: `Bearer ${process.env.JOINOTIFY_TOKEN}` };

const meta = await fetch(`https://api.joinotify.com/v1/${mediaId}`, { headers });
const { url, mime_type } = await meta.json();

const archivo = await fetch(url, { headers });
const buffer = Buffer.from(await archivo.arrayBuffer());

// Guárdalo en tu almacenamiento y devuelve TU url.
return storage.guardar(buffer, { mimeType: mime_type });
}

Hazlo de forma asíncrona, fuera del handler del webhook — tu endpoint debe responder 2xx en pocos segundos, o la entrega se considera fallida y se reintenta.

Eliminar​

curl -X DELETE https://api.joinotify.com/v1/1234567890 \
-H 'Authorization: Bearer sk_live_xxx'

Los mensajes ya entregados con ese archivo siguen funcionando para el cliente. Eliminarlo solo libera el almacenamiento e impide nuevos envíos con ese id.