Volver al blog
Desarrolladores

Cómo descargar fotos y audios que te mandan por la API de WhatsApp

El webhook llega antes de que el archivo esté guardado. Después, GET /messages/:id trae mediaKey y GET /media/by-key devuelve una URL firmada. mediaId en ese GET queda null.

· 12 min
Cómo descargar fotos y audios que te mandan por la API de WhatsApp

Respuesta corta: la foto o el audio que manda el cliente no vienen en el webhook. message.received sale cuando el mensaje se guarda, y en ese momento mediaId y mediaKey suelen ser null. No hay un webhook público de “archivo listo”. Unos segundos después, GET https://api.waspytech.com/api/v2/messages/:id trae mediaKey. Con esa clave pedís GET /api/v2/media/by-key (scope media:read) y bajás la URL firmada antes de que venza. El default es 900 segundos. GET /media/:id es para cuando ya tenés el UUID de Waspy: en el GET del mensaje, mediaId queda null.

Es el comprobante de transferencia, el audio de “¿tienen el talle M?” o el PDF que te reenvían. Mandar vos una foto es otro pedido, y está en enviar foto, PDF y audio. La referencia de la bajada está en media.

El orden real

  1. Llega message.received. Si type es image, video, audio, document o sticker, Waspy encola la bajada desde Meta. El webhook ya salió.
  2. Esperá un par de segundos y leé GET /messages/:id con scope messages:read.
  3. Cuando mediaKey es un string (tenantId/uuid.ext), pedí la URL.
  4. Descargá esa URL antes de expiresIn. Si la necesitás de nuevo, pedí otra.
curl -sS -G "https://api.waspytech.com/api/v2/media/by-key" \
  --data-urlencode "key=TENANT_ID/UUID.jpg" \
  -H "Authorization: Bearer $WASPY_API_KEY"
{
  "data": {
    "id": "uuid-de-waspy",
    "url": "https://storage.googleapis.com/...",
    "expiresIn": 900,
    "mimeType": "image/jpeg",
    "fileName": null
  },
  "meta": { "requestId": "..." }
}

La url es de lectura, corta. No la publiques ni la guardes en un log que después se comparte. expiresIn es lo que vale para esa respuesta. El default del servidor es 900. El ejemplo de la documentación de media muestra 3600: no lo hardcodees.

Tres identificadores distintos

CampoQué esPara qué sirve
content.id en el primer webhookId de media de MetaNo es el parámetro de GET /media/:id
mediaKeyClave del archivo en el almacenamiento, con el prefijo de tu cuentaGET /media/by-key?key=
data.id de by-key, o un mediaId que ya tengasUUID de WaspyGET /media/:id
GET /media/:id exige un UUID. Si le pasás el content.id de Meta, la respuesta es 400 VALIDATION_ERROR.

En el GET del mensaje, el armado de la respuesta deja mediaId en null y copia mediaKey desde el contenido, cuando el worker ya lo escribió. El webhook sí intenta completar mediaId, pero lo hace en el mismo momento del alta, antes de que exista el archivo. Por eso el primer aviso llega vacío y releer el mensaje no llena mediaId.

La página de media todavía dice que, si mediaId viene null, esperes y releas el mensaje hasta que aparezca. Hoy el campo que aparece es mediaKey.

La URL firmada

curl -sS "https://api.waspytech.com/api/v2/media/UUID-DE-WASPY" \
  -H "Authorization: Bearer $WASPY_API_KEY"

Hace falta media:read. Un id de otra cuenta, o una clave que no empieza con el id de tu cuenta, responde 404 MEDIA_NOT_FOUND. No es un 403 que nombre al otro tenant.

En by-key, si el archivo no está, también es 404. Si la fila de media no existe pero el objeto sí, id puede venir null y la url igual sirve. En una bajada normal de un mensaje entrante, id viene con el UUID.

Qué tipos se bajan

El worker baja image, video, audio, document y sticker cuando el mensaje trae un id de media de Meta. Un texto, una ubicación o un contacto no tienen archivo.

En un audio, transcriptionStatus llega pending. El texto aparece después en transcription, cuando el estado pasa a completed (empty si no había habla, failed si no se pudo). Eso es el texto para tu sistema. Waspy no le contesta solo al cliente. El alta del webhook está en webhooks y el catálogo, en eventos.

Cada cuánto releer el mensaje

La v2 permite 120 pedidos por minuto por cuenta, compartidos por todas las claves. No hagas un loop cerrado contra GET /messages/:id. Un par de segundos entre lecturas alcanza: la bajada tiene que salir a Meta y volver. Si a los pocos intentos mediaKey sigue null, frenate. Los headers y el 429 RATE_LIMITED están en rate limits.

Esto no sube archivos

Las limitaciones dicen que el upload de la API v2 no está expuesto. Para mandar una foto, un PDF o un audio, POST /messages lleva link (https público) o id (un media id de Meta, no el UUID de Waspy ni el mediaKey). El pedido y los topes de tamaño están en enviar foto, PDF y audio.

Qué cobra Meta por mirar ese archivo

Bajarlo a tu servidor no es un mensaje de Meta. Lo que cobra es lo que vos respondés. Si el cliente acaba de escribir, la respuesta es un mensaje de servicio: 1.000 por número y por mes sin cargo y, desde el siguiente, en Argentina alrededor de ARS 37,68 (~US$0,026). La rate card es la fuente. El desglose está en cuánto cuesta la API.

Waspy no suma un cargo por el archivo. La clave de API está en Scale (US$150/mes) y Enterprise. La suscripción va aparte, desde US$70, en Planes.

En resumen

  • El webhook avisa el mensaje. El archivo llega unos segundos después, en mediaKey.
  • GET /media/by-key devuelve la URL firmada. expiresIn manda (default 900).
  • GET /media/:id pide el UUID de Waspy. El id de Meta no sirve ahí.
  • No hay POST /media.

Si el número ya está en la API, la clave se crea en Configuración → Claves de API. Si todavía no, abrí la cuenta: 10 días, sin tarjeta. El mapa del recurso está en la API.

Tu equipo atendiendo por WhatsApp en minutos

CRM facil pero potente. En 10 minutos tenes a tu equipo trabajando — no necesitas hablar con nadie.