UC-026 — Download Media from Inbound Messages
| Field | Value |
|---|---|
| ID | UC-026 |
| Goal | Receive inbound messages with media and download the attached files |
| Channel | WhatsApp, RCS |
| Complexity | ⭐⭐ Intermediate |
| Estimated time | 15 minutes |
| APIs involved | Webhook INBOUND, GET /api/files/{mediaKey} |
Real-world scenarios
- AssicuraFacile — Accident photos: The customer sends photos of their car damage via WhatsApp. The operator downloads them to attach to the insurance claim.
- StudioLegale Rossi — Documents via WhatsApp: A client sends a PDF contract for a quick review. The firm downloads it and archives it in the document management system.
- RadioTaxi Milano — Voice messages: The driver sends a voice message with directions to the pickup point. The system downloads it and transcribes it automatically.
Media download flow
The diagram illustrates the complete flow: the user sends a media file, the gateway notifies your server via webhook with the mediaKey, and your application downloads the file through the dedicated endpoint.
Prerequisites
- Active API Key with message read permissions
- Webhook endpoint configured for INBOUND events (see Webhook guide)
- WhatsApp Business number or RCS agent registered on the platform
- Local or cloud storage (S3, Azure Blob) for file saving
Step 1 — Configure the webhook for inbound messages
Make sure your webhook endpoint is registered to receive INBOUND type events. When a user sends a message with an attachment, you will receive a notification like this:
{
"channel": "WHATSAPP",
"eventType": "INBOUND",
"messageId": "wamid.HBgNMzkzNDcxMjM0NTY3FQIAEhggQTJDNEU2RjgtMTIzNC01Njc4",
"from": "+393471234567",
"to": "+393801234567",
"timestamp": "2026-04-09T14:22:10Z",
"content": {
"type": "IMAGE",
"caption": "Foto danno paraurti anteriore",
"mimeType": "image/jpeg",
"mediaKey": "mk_a1b2c3d4e5f6g7h8i9j0"
}
}
:::tip Supported media types
Supported types include IMAGE (JPEG, PNG), DOCUMENT (PDF, DOCX), AUDIO (OGG, MP3), VIDEO (MP4) and STICKER. Each type has specific size limits defined by the carrier.
:::
Step 2 — Download the media file via mediaKey
Use the mediaKey received in the webhook to obtain the file:
curl -X GET "https://lora-api.agiletelecom.com/api/files/mk_a1b2c3d4e5f6g7h8i9j0" \
-H "X-Api-Key: YOUR_API_KEY"
Response — Download URL
{
"mediaKey": "mk_a1b2c3d4e5f6g7h8i9j0",
"url": "https://cdn.agiletelecom.com/media/mk_a1b2c3d4e5f6g7h8i9j0.jpg?token=eyJhbG...&expires=1744210930",
"mimeType": "image/jpeg",
"fileSize": 245780,
"expiresAt": "2026-04-09T15:22:10Z"
}
:::warning URL with expiration
The pre-signed URL has a limited duration (typically 60 minutes). Download the file immediately after receiving the URL. If the URL has expired, repeat the GET /api/files/{mediaKey} call to get a new one.
:::
Step 3 — Download and save the file
Use the pre-signed URL to download the actual file:
curl -o "sinistro_paraurti.jpg" \
"https://cdn.agiletelecom.com/media/mk_a1b2c3d4e5f6g7h8i9j0.jpg?token=eyJhbG...&expires=1744210930"
Behind the scenes — Media lifecycle
- Reception: When the user sends a file, the carrier (Meta for WhatsApp, Google for RCS) temporarily stores it on their servers.
- Notification: The gateway receives the inbound message and generates a unique
mediaKeythat maps the file on the carrier. - Secure proxy: The
GET /api/files/{mediaKey}call does not download the file directly from the gateway, but returns a pre-signed URL pointing to the CDN. This avoids overloading the gateway with large file transfers. - Expiration: Files on carrier servers have limited retention (typically 30 days for WhatsApp). After expiration, the file will no longer be available. Download and archive it as soon as possible.
- Size limits: WhatsApp supports files up to 16 MB for images, 100 MB for videos and 100 MB for documents.
Step 4 — Process and archive
Once downloaded, the file can be processed according to your needs:
# Example: save to a directory organized by date and case
PRATICA_ID="SIN-2026-04-0892"
DATA=$(date +%Y%m%d)
DEST_DIR="/archivio/sinistri/${PRATICA_ID}/${DATA}"
mkdir -p "$DEST_DIR"
mv sinistro_paraurti.jpg "${DEST_DIR}/foto_001.jpg"
echo "File archived in: ${DEST_DIR}/foto_001.jpg"
Expected result
| Step | Action | Result |
|---|---|---|
| 1 | Webhook INBOUND received | eventType: "INBOUND", content.mediaKey present |
| 2 | GET /api/files/{mediaKey} | Pre-signed URL with mimeType and fileSize |
| 3 | Download from pre-signed URL | Binary file downloaded successfully |
| 4 | Archiving | File saved to business storage |
Complete end-to-end example
Here is the full AssicuraFacile scenario, from webhook reception to file saving:
#!/bin/bash
# Inbound media handling script for AssicuraFacile
MEDIA_KEY="mk_a1b2c3d4e5f6g7h8i9j0"
API_KEY="YOUR_API_KEY"
BASE_URL="https://lora-api.agiletelecom.com"
# 1. Get the download URL
RESPONSE=$(curl -s -X GET "${BASE_URL}/api/files/${MEDIA_KEY}" \
-H "X-Api-Key: ${API_KEY}")
DOWNLOAD_URL=$(echo "$RESPONSE" | jq -r '.url')
MIME_TYPE=$(echo "$RESPONSE" | jq -r '.mimeType')
FILE_SIZE=$(echo "$RESPONSE" | jq -r '.fileSize')
echo "Type: ${MIME_TYPE}, Size: ${FILE_SIZE} bytes"
# 2. Download the file
EXTENSION="${MIME_TYPE##*/}"
curl -s -o "media_inbound.${EXTENSION}" "$DOWNLOAD_URL"
echo "File downloaded: media_inbound.${EXTENSION}"
# 3. Archive in the case management system
PRATICA_ID="SIN-2026-04-0892"
mv "media_inbound.${EXTENSION}" "/archivio/sinistri/${PRATICA_ID}/"
echo "Archived in case ${PRATICA_ID}"
Variants
Handling messages with multiple attachments
If the user sends multiple files, you will receive a webhook for each attachment. Iterate over the mediaKey values:
MEDIA_KEYS=("mk_aaa111" "mk_bbb222" "mk_ccc333")
for KEY in "${MEDIA_KEYS[@]}"; do
URL=$(curl -s -X GET "${BASE_URL}/api/files/${KEY}" \
-H "X-Api-Key: ${API_KEY}" | jq -r '.url')
curl -s -o "file_${KEY}.jpg" "$URL"
done
Common errors
404 Not Found — Invalid or expired mediaKey
{
"status": "fail",
"data": {
"error": "Media not found or expired"
}
}
Solution: The mediaKey may have expired (carrier retention exceeded). Make sure to process webhooks promptly. WhatsApp files are available for approximately 30 days.
401 Unauthorized — Missing API Key
{
"status": "fail",
"data": {
"authentication": "Invalid or missing API key"
}
}
Solution: Verify that the X-Api-Key header is present and that the key is active.
Expired pre-signed URL
If the download fails with an HTTP 403 error, the pre-signed URL has expired. Request a new URL:
# The URL has expired, request a new one
curl -X GET "https://lora-api.agiletelecom.com/api/files/mk_a1b2c3d4e5f6g7h8i9j0" \
-H "X-Api-Key: YOUR_API_KEY"
Next steps
- UC-009 — Two-Way Conversation: Manage complete conversations with inbound replies
- UC-008 — Delivery Tracking via Webhook: Learn more about webhook configuration
- UC-003 — Send WhatsApp Template: Send WhatsApp messages with approved templates
- Webhook Guide: Complete webhook configuration documentation