Skip to main content

UC-026 — Download Media from Inbound Messages

FieldValue
IDUC-026
GoalReceive inbound messages with media and download the attached files
ChannelWhatsApp, RCS
Complexity⭐⭐ Intermediate
Estimated time15 minutes
APIs involvedWebhook 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
  1. Reception: When the user sends a file, the carrier (Meta for WhatsApp, Google for RCS) temporarily stores it on their servers.
  2. Notification: The gateway receives the inbound message and generates a unique mediaKey that maps the file on the carrier.
  3. 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.
  4. 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.
  5. 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

StepActionResult
1Webhook INBOUND receivedeventType: "INBOUND", content.mediaKey present
2GET /api/files/{mediaKey}Pre-signed URL with mimeType and fileSize
3Download from pre-signed URLBinary file downloaded successfully
4ArchivingFile 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

References