Upload Media For Message
Objective
To upload media files directly onto Meta servers — either via a publicly accessible URL or from a local file — without requiring a permanent public hosting URL.
Benefits:
- Better security since files do not need to be hosted on a public URL
- Eliminates dependency on external hosting
Before You Start
This guide assumes you have read the Before You Start section.
You will need:
- A user access token
Authentication
Media Upload APIs are accessed using a Bearer token. Use the endpoint below to generate one.
Request Type: POST
UAT Endpoint: https://apiuat.aclwhatsapp.com/auth/realms/ipmessaging/protocol/openid-connect/token
curl --location \
'https://apiuat.aclwhatsapp.com/auth/realms/ipmessaging/protocol/openid-connect/token' \
--header 'cache-control: no-cache' \
--header 'content-type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=ipmessaging-client' \
--data-urlencode 'username=username' \
--data-urlencode 'password=password'
Header Values
| Name | Value |
|---|---|
| cache-control | no-cache |
| content-type | application/x-www-form-urlencoded |
Data Object Values
| Name | Description | Value |
|---|---|---|
| grant_type | Authentication method | password |
| username | Provided by Sinch at onboarding | |
| client_id | Fixed value | ipmessaging-client |
| password | Provided by Sinch at onboarding |
Response
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAi",
"expires_in": 300,
"token_type": "Bearer",
"not-before-policy": 0,
"session_state": "f2cd48bf-d162-4754-b2c7-bdc9c97064b3",
"scope": "email profile"
}
| Name | Description |
|---|---|
| access_token | Token to be used in subsequent API calls |
| expires_in | Token validity in seconds |
| token_type | Fixed value — Bearer |
Media Upload
The media upload for messages is a 2-step process. The media must be pre-uploaded to Meta servers before being used in carousel or media ID messages.
Step 1 — Upload Media & Get Acknowledgement ID
Send a POST request to upload your media. You can upload via a public URL or directly from a local file.
Method A — Upload via Public URL
UAT Endpoint: https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media/upload
Production Endpoint: https://api.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media/upload
curl --location \
'https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/177384039855054/media/upload' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{
"mediaUrl": "https://download.samplelib.com/mp3/sample-3s.mp3",
"mediaFormat": "audio/mpeg",
"always_upload": false
}'
Header Values
| Name | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer token |
Data Object Values
| Name | Description | Value |
|---|---|---|
| mediaFormat | MIME type of the media as supported by Meta | e.g. application/pdf |
| mediaUrl | Publicly accessible media URL | |
| always_upload | Re-upload even if a mediaId already exists | true / false |
Method B — Upload from Local File
Use this method to upload a media file directly from your local system without requiring a public URL.
UAT Endpoint: https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media/file/upload
Production Endpoint: https://api.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media/file/upload
curl --location \
'https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/<waba_id>/media/file/upload' \
--header 'Authorization: Bearer <token>' \
--form 'mediaFormat="image/png"' \
--form 'always_upload="true"' \
--form 'file=@"/<path-to-local-file>"'
Header Values
| Name | Value |
|---|---|
| Authorization | Bearer token |
Data Object Values
| Name | Description | Value |
|---|---|---|
| mediaFormat | MIME type of the media as supported by Meta | e.g. application/pdf |
| always_upload | Re-upload even if a mediaId already exists | true / false |
| file | Path to the local file | e.g. /home/user/image.png |
Response (both methods)
{
"acknowledgementId": "1752915620015378432",
"acceptedTime": 1706762626066
}
Error & Exceptions
- Duplicate Request (without always_upload) — Multiple requests for the same file with
always_upload: false.
{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"acknowledgementId": "1752915620015378432",
"error": "duplicate.media.upload.request"
}
- Duplicate Request (with always_upload) — Multiple requests for the same file with
always_upload: true, but the existing media was uploaded within the last 7 days.
{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"acknowledgementId": "1752915620015378432",
"error": "duplicate.media.upload.request"
}
If the media exists but was uploaded more than 7 days ago, the system will allow re-upload and generate a new acknowledgement ID.
- Rate Limit Error — Enterprises can upload a maximum of 5 media files per minute. Maximum file size is 20 MB. Exceeding either limit returns the following errors:
{
"error": "request.limit.exceeded"
}
{
"error": "File size limit exceeds"
}
Step 2 — Fetch Media ID using Acknowledgement ID
After receiving the acknowledgement ID, use the request below to retrieve the Media ID required for sending WA messages.
UAT Endpoint: https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media
Production Endpoint: https://api.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media
Request Type: POST
curl --location \
'https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{"acknowledgementId": "<acknowledgementId>"}'
Header Values
| Name | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer token |
Data Object Values
| Name | Description | Value |
|---|---|---|
| acknowledgementId | ID generated in Step 1 | 1752915620015378432 |
Response
{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"acknowledgementId": "1752915620015378432",
"mediaUrl": "https://download.samplelib.com/mp3/sample-3s.mp3",
"expiryTime": 1707972226000,
"status": "success"
}
Response Details
| Field | Description |
|---|---|
| mediaId | Use this to send the media message |
| acknowledgementId | Provided in the request |
| mediaUrl | The URL of the uploaded media |
| expiryTime | Timestamp until which media is available on Meta servers. Re-upload required after expiry. |
Error & Exceptions
- Upload In Progress — Media upload is still being processed at Meta servers.
{
"acknowledgementId": "1755474606652657662",
"mediaUrl": "",
"expiryTime": null,
"status": "media.generation.in.progress",
"errors": [
{
"desc": "Media upload is in progress"
}
]
}
- Media Not Found — Media has expired.
{
"acknowledgementId": "1755473752088383468",
"status": "media.not.found"
}
- Media Generation Failed — Upload failed at Meta servers.
{
"acknowledgementId": "1755477593324261376",
"mediaUrl": "",
"expiryTime": null,
"status": "media.generation.failed",
"errors": [
{
"code": "502",
"desc": "Something went wrong while generating mediaId, please try again"
}
]
}
Step 3 — Fetch Media Details by Media ID
Retrieve media details using the mediaId to check the expiry timestamp and plan re-uploads before expiry.
UAT Endpoint: https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media
Production Endpoint: https://api.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media
Request Type: POST
curl --location \
'https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2"}'
Header Values
| Name | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer token |
Data Object Values
| Name | Description | Value |
|---|---|---|
| mediaId | Media ID generated after upload | 6fba7ef1-f845-4063-b96a-f814058213f2 |
Response
{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"acknowledgementId": "1752915620015378432",
"mediaUrl": "https://download.samplelib.com/mp3/sample-3s.mp3",
"expiryTime": 1707972226000,
"status": "success"
}
Response Details
| Field | Description |
|---|---|
| mediaId | Media ID of the uploaded media |
| acknowledgementId | Acknowledgement ID from the original upload request |
| mediaUrl | The URL of the uploaded media |
| expiryTime | Timestamp until which media is available on Meta servers. Re-upload required after expiry. |
Error & Exceptions
- Media Not Found — Media has expired.
{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"status": "media.not.found"
}