Cómo enviar fotos, PDF y audios por la API de WhatsApp
Para mandar una foto, un PDF o un audio por la API de Waspy: el POST real, los topes de Meta y cómo bajar el archivo que te mandó el cliente. Sin un endpoint de subida inventado.
Respuesta corta: para mandar una foto, un PDF o un audio por la API de Waspy hacés POST https://api.waspytech.com/api/v2/messages con type en image, document, audio o video, y un link https público o un id de media de Meta. No existe POST /api/v2/media para subir el archivo: GET /api/v2/media/:id solo baja algo que ya está guardado. Si la ventana de 24 horas está cerrada, ese pedido responde 403 SERVICE_WINDOW_EXPIRED y hay que usar una plantilla.
Sirve para el caso de todos los días en una tienda argentina: el cliente te manda la foto de la transferencia, o vos le tenés que pasar el PDF del remito mientras el chat sigue abierto. La referencia está en docs.waspytech.com. Los límites de tamaño son los de la Cloud API de Meta.
Qué tipo manda qué
La autenticación es Authorization: Bearer wspy_... y el scope del envío es messages:write. El phoneNumberId es el UUID de GET /api/v2/channels, no el id de teléfono de Meta. El detalle de la clave, los errores 401 y la idempotencia está en API de WhatsApp para desarrolladores.
type | Campos | Tope al bajar el link | Notas |
|---|---|---|---|
image | link o id, caption opcional | 5 MB | JPEG o PNG. Waspy normaliza fotos que Meta rechaza (PNG de 16 bits, WebP) antes de subirlas |
document | link o id, caption y filename opcionales | 100 MB | PDF, texto, Word, Excel, PowerPoint |
audio | link o id | 16 MB | No lleva caption. AAC, MP3, M4A, AMR, OGG |
video | link o id, caption opcional | 16 MB | MP4 o 3GPP |
id de esos objetos es un media id de Meta (el que devuelve Graph al subir un archivo), no el UUID de Waspy. Si mandás el UUID de GET /media/:id en image.id, Meta no lo reconoce.
Mandar la foto de un producto
Dentro de la ventana (el cliente escribió hace menos de 24 horas) el cuerpo es este. El header Idempotency-Key evita un segundo envío si el script se corta: la respuesta queda guardada 24 horas.
curl -sS https://api.waspytech.com/api/v2/messages \
-H "Authorization: Bearer $WASPY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-1847-foto" \
-d '{
"phoneNumberId": "UUID-DEL-CANAL",
"to": "+5491155554444",
"type": "image",
"image": {
"link": "https://cdn.mitienda.com.ar/vestido-azul.jpg",
"caption": "Vestido azul, talle M. Te queda este."
},
"clientRef": "pedido-1847"
}'
Un 201 devuelve el mensaje en data (id, status, conversationId). El teléfono va en E.164: para un celular argentino, +549 más el código de área sin 0 y el número sin el 15.
El PDF del remito es el mismo pedido con type: "document" y document.filename (por ejemplo remito-1847.pdf). Sin id ni link, Meta responde 132018 (Either one of media ID or link must be present).
La ventana sigue mandando
Una foto o un PDF no son una plantilla. Si serviceWindowExpiresAt ya pasó, o la conversación es nueva y ese campo está vacío, la API responde 403 SERVICE_WINDOW_EXPIRED y no encola nada. Lo consultás con GET /api/v2/conversations/:id (scope conversations:read): el campo viene en la conversación.
No hace falta crear el contacto antes. El envío lo crea si no existe, pero no abre la ventana. Para escribirle a alguien que no te escribió, la vía es una plantilla aprobada, explicada en la regla de las 24 horas. Una plantilla con imagen en el encabezado se arma en el panel: la API REST no tiene POST /templates.
Qué hace Waspy con el link
El pedido se acepta enseguida y el envío corre en un worker. Con un link:
- Tiene que ser
https.http,localhost, un host interno o una IP privada no se bajan. - Waspy sigue hasta 5 redirects, con un tope de 60 segundos, y corta si el archivo pasa el tamaño de la tabla.
- Si la bajada sale bien, sube el archivo a Meta y manda el mensaje por
id. La foto pasa por un normalizador para no morir con el error 131053 de Meta (el mismo que aparece cuando Meta no puede usar el archivo). - Si la bajada o la subida fallan, el plan B es pasarle a Meta el
linkoriginal. Si Meta tampoco puede bajarlo, el mensaje quedafailed.
Un id de Meta se reenvía tal cual, sin bajar nada. El link de la API pública no es el mediaKey del panel: ese campo lo usa la bandeja cuando el archivo ya está en el almacenamiento de Waspy, y no hace falta mandarlo vos.
Bajar la foto o el audio que mandó el cliente
Cuando entra un mensaje, el webhook message.received trae el mensaje. Si es una foto, un audio o un documento, mediaId es el UUID para bajarlo. La descarga desde Meta es asíncrona: en el primer aviso mediaId puede venir vacío. En ese caso releé GET /api/v2/messages/:id (scope messages:read) y, cuando aparezca, pedí la URL:
curl -sS "https://api.waspytech.com/api/v2/media/UUID-DEL-MEDIA" \
-H "Authorization: Bearer $WASPY_API_KEY"
Hace falta el scope media:read. La respuesta trae url (firmada), expiresIn, mimeType y fileName. El vencimiento por defecto es 900 segundos (15 minutos): cuando vence, pedís otra. Un id de otro tenant responde 404 MEDIA_NOT_FOUND.
GET /api/v2/media/by-key?key= es el camino viejo, cuando el mensaje solo trae mediaKey. La clave tiene que empezar con el id de tu cuenta; si no, también es 404.
En un audio, transcriptionStatus suele llegar pending en el webhook. Unos segundos después, GET /messages/:id trae transcription cuando el estado pasa a completed. Eso es el texto del audio para tu sistema. No es una respuesta automática al cliente: Waspy no le contesta solo.
El alta del webhook, la firma y los reintentos están en webhooks de WhatsApp API.
Cuánto cobra Meta por ese archivo
La foto dentro de la ventana es un mensaje de servicio, igual que un texto. Desde el 1 de octubre de 2026 cada número tiene 1.000 gratis por mes; desde el 1.001, en Argentina, alrededor de ARS 37,68 (~US$0,026) por mensaje entregado. Un PDF fuera de la ventana no se puede mandar como archivo suelto: va en una plantilla, y si es un aviso de pedido (sin promo) es utilidad, al mismo precio. El desglose está en cuánto cuesta la API y en la rate card de Meta.
Waspy no suma un cargo encima del archivo. Meta le factura los mensajes al negocio. La API está en los planes Scale (US$150/mes) y Enterprise; la suscripción va aparte, en Planes.
Waspy, la Cloud API directa y un BSP de acceso
| Camino | Cómo mandás el archivo | Qué más tenés que armar |
|---|---|---|
| Cloud API de Meta, directa | Subís el binario a /{phone-number-id}/media y después mandás el id | Token, webhooks de Meta, ventana, reintentos |
| Twilio o 360dialog | La API de ese proveedor | El inbox, si lo querés |
| Z-API | Una sesión tipo WhatsApp Web | No es la API oficial |
| API de Waspy | link https o id de Meta en POST /api/v2/messages | Nada más para que el equipo lo vea en la bandeja |
En resumen
- Enviá con
POST /api/v2/messagesy unlinkhttps o unidde Meta. No hay upload en/api/v2. - Foto 5 MB, audio y video 16 MB, documento 100 MB.
- Fuera de las 24 horas, 403
SERVICE_WINDOW_EXPIRED. - Lo que manda el cliente se baja con
GET /api/v2/media/:id(URL firmada, 15 minutos).
Si el número de la tienda ya está en la API, la clave se crea en Configuración → Claves de API con el plan Scale. Si todavía no, abrí la cuenta: 10 días gratis, sin tarjeta.
