API Cloud (MEDIA v2)
Estas rutas son la forma corregida: el canal primero y el identificador de la media al final, como en el resto de la API del canal. Las anteriores siguen funcionando sin cambios, documentadas en la página Media.
Además del orden, esta forma corrige dos cosas:
- Descarga: la respuesta siempre informa el tipo de contenido, el tamaño real del archivo y un nombre de archivo, y no reenvía los headers que solo valen entre Meta y el proxy. La ruta anterior reenvía los headers de Meta tal como vienen, incluido el tamaño que declare Meta.
- Subida: el tipo de archivo que se envía a Meta es el que declara el part
filecuando Meta lo acepta, y se infiere del contenido cuando no. La ruta anterior siempre lo infiere del contenido, y eso etiqueta mal varios de los formatos que Meta acepta.
Todas las rutas comparten la base https://channels.chattigo.com/bsp-cloud-chattigo-isv y el header de autenticación Authorization: <JWT> (el JWT en crudo, sin prefijo Bearer ni Basic), obtenido en POST /login.
{version} lo elige el cliente y se reenvía a Meta tal cual: la versión de Graph API la gestiona Meta, por lo que no debe fijarse.
Cómo se conecta con el resto de la API
Descargar la media de un mensaje entrante
- Pedí el token con
POST /loginy mandalo en el headerAuthorizationde todas las llamadas. - Tu webhook recibe el payload del mensaje entrante. De ahí salen los dos datos que necesita la ruta: el canal, en
metadata.display_phone_number, y el identificador de la media, enmessages[].audio.id. - Descargá el archivo con
GET /{version}/{did}/media/{media_id}y guardalo con la extensión que corresponde alContent-Typede la respuesta.
curl --request POST \
--url 'https://channels.chattigo.com/bsp-cloud-chattigo-isv/login' \
--header 'Content-Type: application/json' \
--data '{"username": "<user>", "password": "<password>"}'curl --fail --request GET \
--url 'https://channels.chattigo.com/bsp-cloud-chattigo-isv/{version}/5492646715777/media/980301304349102' \
--header 'Authorization: <JWT>' \
--output audio.oggSi querés liberar esa media en Meta, la eliminás con DELETE /{version}/{did}/media/{media_id}.
Subir un archivo y usarlo en una plantilla
- Pedí el token con
POST /login. - Subí el archivo con
POST /{version}/{did}/media, multipart con el partfile. - La respuesta es
{"h": "4::..."}. Ese valor es elheader_handledel header de media cuando creás o editás una plantilla, como se explica en Templates.
curl --request POST \
--url 'https://channels.chattigo.com/bsp-cloud-chattigo-isv/{version}/{did}/media' \
--header 'Authorization: <JWT>' \
-F 'file=@/ruta/archivo.jpg'Para enviar una media dentro de un mensaje se usa una URL pública en link, no el handle: consultá Messages.
Descargar media
GET /{version}/{did}/media/{media_id}
Descarga el cuerpo binario de una media de WhatsApp con el canal primero y el identificador de la media al final. Devuelve el Content-Type de la media, el tamaño real del cuerpo y un nombre de archivo sugerido.
Parámetros
Path
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
version | String | Sí | Versión de Graph API de Meta. Se reenvía tal cual. |
did | String | Sí | Identificador del canal. |
media_id | String | Sí | Identificador de la media. |
Identificadores en el webhook
| Valor | Origen en el payload inbound |
|---|---|
media_id | entry[].changes[].value.messages[].audio.id (igual para image.id, document.id, video.id y sticker.id). |
did | entry[].changes[].value.metadata.display_phone_number, el mismo valor que la documentación llama waId en PATCH /webhooks/inbound. |
El identificador de canal que usa esta API es el número de teléfono, no el phone_number_id de Meta.
/{version}/{did}/media/{media_id}. Es el mismo orden que la ruta de eliminación y que el resto de la API del canal.Request
GET /{version}/{did}/media/{media_id}
curl --fail --request GET \
--url 'https://channels.chattigo.com/bsp-cloud-chattigo-isv/{version}/{did}/media/{media_id}' \
--header 'Authorization: <JWT>' \
--output audio.oggEl --output audio.ogg del ejemplo es el nombre con el que guardás vos, no un dato que debas conocer de antemano; la extensión puede derivarse del Content-Type de la respuesta. --fail evita que un cuerpo de error se guarde como si fuera la media. Si preferís que curl use el nombre que sugiere la respuesta, reemplazá --output audio.ogg por --remote-header-name --remote-name.
Por ejemplo, este webhook inbound muestra los campos que usa esta ruta para el mensaje de audio:
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5492646715777",
"phone_number_id": "106540352242922"
},
"messages": [
{
"from": "5491122334455",
"id": "wamid.HBgNNTQ5MjY0NjcxNTc3NxUCABIYFj",
"timestamp": "1758552655",
"type": "audio",
"audio": {
"id": "980301304349102",
"mime_type": "audio/ogg; codecs=opus"
}
}
]
}
}
]
}
]
}la descarga se realiza con did = 5492646715777 y media_id = 980301304349102:
curl --fail --request GET \
--url 'https://channels.chattigo.com/bsp-cloud-chattigo-isv/{version}/5492646715777/media/980301304349102' \
--header 'Authorization: <JWT>' \
--output audio.oggResponse
Éxito (200)
Devuelve el cuerpo binario de la media con los headers:
| Header | Descripción |
|---|---|
Content-Type | Tipo de la media que reporta Meta para el archivo. |
Content-Length | Tamaño real del cuerpo de la respuesta. |
Content-Disposition | inline; filename="<media_id>.<ext>", donde la extensión se deriva del tipo servido. |
Ejemplos de extensión según el tipo servido:
| Tipo servido | Extensión |
|---|---|
audio/ogg | .ogg |
audio/amr | .amr |
audio/mpeg | .mp3 |
audio/mp4 | .m4a |
video/mp4 | .mp4 |
image/jpeg | .jpg |
application/pdf | .pdf |
Content-Disposition es un header de respuesta: no se envía y no hace falta conocerlo de antemano, porque la URL solo necesita el identificador de la media. Está para que un cliente que quiera guardar el archivo con el nombre sugerido lo use; si preferís elegir el nombre vos mismo, ignoralo y quedate con el Content-Type, que es el dato esencial.
Errores
| Código | Caso |
|---|---|
401 | Token inválido o con permisos insuficientes. |
| Meta | Los errores de Meta se reenvían tal cual, con su código de estado y su cuerpo. Para una media que no existe, expiró o no es accesible, Meta responde 404 con Unsupported get request. Object with ID '<media_id>' does not exist, cannot be loaded due to missing permissions, or does not support this operation.... |
404 con el cuerpo en texto plano 404 page not found no proviene de Meta: significa que la URL no coincide con ninguna ruta registrada y ningún handler procesó la petición. Verifica que la forma de la ruta sea /{version}/{did}/media/{media_id}.Subir media
POST /{version}/{did}/media
Sube un archivo a Meta con el canal primero. El tipo de archivo que se envía a Meta es el que declara el part file del multipart cuando Meta lo acepta, y se infiere del contenido en caso contrario. El resultado es el file handle que se usa como header_handle en el header de media de una plantilla, como se explica en Templates.
Parámetros
Path
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
version | String | Sí | Versión de Graph API de Meta. Se reenvía tal cual. |
did | String | Sí | Identificador del canal. |
Body (multipart/form-data)
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
file | File | Sí | Archivo a subir. |
Request
POST /{version}/{did}/media
curl --request POST \
--url 'https://channels.chattigo.com/bsp-cloud-chattigo-isv/{version}/{did}/media' \
--header 'Authorization: <JWT>' \
-F 'file=@/ruta/archivo.jpg'Response
Éxito (200)
{
"h": "4::aW1hZ2............."
}Tipos aceptados por Meta
| Grupo | Tipos |
|---|---|
| Audio | audio/aac, audio/amr, audio/mpeg, audio/mp4, audio/ogg |
| Imágenes | image/jpeg, image/png |
| Stickers | image/webp |
| Videos | video/mp4, video/3gpp |
| Documentos | text/plain, application/pdf, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.openxmlformats-officedocument.presentationml.presentation |
Errores
| Código | Caso |
|---|---|
400 | Falta el part file en el formulario multipart: {"message":"file require"}. |
401 | Token inválido o con permisos insuficientes. |
| Meta | Los errores de Meta se reenvían tal cual, con su código de estado y su cuerpo. |
Eliminar media
La eliminación ya usa el canal primero: DELETE /{version}/{did}/media/{media_id}, documentada en la página Media.