Skip to content

API Cloud (MEDIA v2)

These routes are the corrected shape: the channel first and the media identifier last, as in the rest of the channel API. The previous ones still work without changes and are documented on the Media page.

Besides the order, this shape fixes two things:

  • Download: the response always reports the content type, the real size of the file and a filename, and it does not forward the headers that only apply between Meta and the proxy. The previous route forwards Meta’s headers as they come, including the size Meta declares.
  • Upload: the file type sent to Meta is the one declared by the file part when Meta accepts it, and is inferred from the content when it is not. The previous route always infers it from the content, which mislabels several of the formats Meta accepts.

All routes share the base https://channels.chattigo.com/bsp-cloud-chattigo-isv and the Authorization: <JWT> authentication header (the raw JWT, with no Bearer or Basic prefix), obtained from POST /login.

{version} is client-chosen and forwarded to Meta as-is: Meta manages the Graph API version lifecycle, so it must never be pinned.

How it connects to the rest of the API

Downloading the media of an inbound message

  1. Request the token with POST /login and send it in the Authorization header on every call.
  2. Your webhook receives the inbound message payload. Two pieces of data the route needs come from it: the channel, in metadata.display_phone_number, and the media identifier, in messages[].audio.id.
  3. Download the file with GET /{version}/{did}/media/{media_id} and save it with the extension that matches the response Content-Type.
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.ogg

If you want to release that media at Meta, you delete it with DELETE /{version}/{did}/media/{media_id}.

Uploading a file and using it in a template

  1. Request the token with POST /login.
  2. Upload the file with POST /{version}/{did}/media, multipart with the file part.
  3. The response is {"h": "4::..."}. That value is the header_handle of the media header when you create or edit a template, as explained in Templates.
curl --request POST \
  --url 'https://channels.chattigo.com/bsp-cloud-chattigo-isv/{version}/{did}/media' \
  --header 'Authorization: <JWT>' \
  -F 'file=@/path/file.jpg'

To send media inside a message you use a public URL in link, not the handle: see Messages.

Download media

GET /{version}/{did}/media/{media_id}

Downloads the binary body of a WhatsApp media file with the channel first and the media identifier last. It returns the media Content-Type, the real size of the body and a suggested filename.

Parameters

Path

ParameterTypeRequiredDescription
versionStringYesMeta’s Graph API version. Forwarded as-is.
didStringYesChannel identifier.
media_idStringYesMedia identifier.

Identifiers in the webhook

ValueOrigin in the inbound payload
media_identry[].changes[].value.messages[].audio.id (same for image.id, document.id, video.id and sticker.id).
didentry[].changes[].value.metadata.display_phone_number, the same value the documentation calls waId in PATCH /webhooks/inbound.

The channel identifier this API uses is the phone number, not Meta’s phone_number_id.

In this shape the channel comes first and the media identifier last: /{version}/{did}/media/{media_id}. It is the same order as the delete route and the rest of the channel API.

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.ogg

The --output audio.ogg in the example is the name you save it with, not something you need to know in advance; the extension can be derived from the response Content-Type. --fail prevents an error body from being saved as if it were the media. If you prefer curl to use the name the response suggests, replace --output audio.ogg with --remote-header-name --remote-name.

For example, this inbound webhook shows the fields this route uses for the audio message:

{
  "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"
                }
              }
            ]
          }
        }
      ]
    }
  ]
}

the download uses did = 5492646715777 and 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.ogg

Response

Success (200)

Returns the binary media body with the headers:

HeaderDescription
Content-TypeMedia type that Meta reports for the file.
Content-LengthReal size of the response body.
Content-Dispositioninline; filename="<media_id>.<ext>", where the extension is derived from the served type.

Extension examples per served type:

Served typeExtension
audio/ogg.ogg
audio/amr.amr
audio/mpeg.mp3
audio/mp4.m4a
video/mp4.mp4
image/jpeg.jpg
application/pdf.pdf

Content-Disposition is a response header: it is never sent and it never needs to be known in advance, because the URL only needs the media identifier. It exists so a client that wants to save the file with the suggested name can use it; if you prefer to choose the name yourself, ignore it and rely on Content-Type, which is the essential piece of information.

Errors

CodeCase
401Invalid token or insufficient permissions.
MetaMeta’s errors are forwarded as-is, with its status code and body. For a media that does not exist, expired or is not accessible, Meta answers 404 with Unsupported get request. Object with ID '<media_id>' does not exist, cannot be loaded due to missing permissions, or does not support this operation....
A 404 with the plain-text body 404 page not found does not come from Meta: it means the URL does not match any registered route and no handler processed the request. Check that the path shape is /{version}/{did}/media/{media_id}.

Upload media

POST /{version}/{did}/media

Uploads a file to Meta with the channel first. The file type sent to Meta is the one declared by the file part of the multipart when Meta accepts it, and is inferred from the content otherwise. The result is the file handle used as the header_handle of a media header in a template, as explained in Templates.

Parameters

Path

ParameterTypeRequiredDescription
versionStringYesMeta’s Graph API version. Forwarded as-is.
didStringYesChannel identifier.

Body (multipart/form-data)

FieldTypeRequiredDescription
fileFileYesFile to upload.

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=@/path/file.jpg'

Response

Success (200)

{
  "h": "4::aW1hZ2............."
}

Types accepted by Meta

GroupTypes
Audioaudio/aac, audio/amr, audio/mpeg, audio/mp4, audio/ogg
Imagesimage/jpeg, image/png
Stickersimage/webp
Videosvideo/mp4, video/3gpp
Documentstext/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

Errors

CodeCase
400The file part is missing from the multipart form: {"message":"file require"}.
401Invalid token or insufficient permissions.
MetaMeta’s errors are forwarded as-is, with its status code and body.

Delete media

Deletion already uses the channel first: DELETE /{version}/{did}/media/{media_id}, documented on the Media page.