Messaging
Retrieving inbound media
Fetch customer-sent media through Yanola without exposing Meta credentials to your application.
What this guide covers
- Start with the media reference included in an inbound message or event.
- Retrieve media from trusted server code using the same project-scoped API key.
- Store customer media according to your privacy and retention requirements.
Start with a Yanola media ID
Inbound media is represented by an opaque Yanola ID beginning with med_. Obtain it from a Yanola-normalized inbound message or event, then retrieve it with GET /v1/media/{mediaId}. Raw Meta media IDs are not accepted.
The operation requires messages:read and is restricted to the key's Yanola Account, Agent Project, environment, and permitted Senders.
import { WhatsAppClient } from "@yanola/whatsapp"
const apiKey = process.env.YANOLA_API_KEY
if (!apiKey) {
throw new Error("Missing YANOLA_API_KEY")
}
const yanola = new WhatsAppClient({ apiKey })
const media = await yanola.media.retrieve("med_...")
console.log(media.mimeType)
console.log(media.downloadUrl)
console.log(media.expiresAt)Media response
| Field | Meaning |
|---|---|
id | Opaque Yanola med_ public ID. |
environment | live or test; always matches the API key environment. |
mimeType | Best available cached or provider MIME type. |
filename | Original or cached filename when available; otherwise null or omitted. |
sizeBytes | Known byte size when available; otherwise null or omitted. |
downloadUrl | Short-lived signed Yanola proxy URL. |
expiresAt | ISO 8601 time after which that download URL must not be used. |
Download before the URL expires
downloadUrl is a signed capability URL. It streams through Yanola without an Authorization header and does not reveal Meta credentials or Yanola's permanent cache location.
Use the URL before expiresAt. If it expires while the retained media still exists, call media.retrieve with the same med_ ID to obtain a new URL.
import { WhatsAppClient } from "@yanola/whatsapp"
const apiKey = process.env.YANOLA_API_KEY
if (!apiKey) {
throw new Error("Missing YANOLA_API_KEY")
}
const yanola = new WhatsAppClient({ apiKey })
const media = await yanola.media.retrieve("med_...")
const response = await fetch(media.downloadUrl)
if (!response.ok) {
throw new Error(`Media download failed: ${response.status}`)
}
const contentType =
response.headers.get("content-type") ?? media.mimeType
const bytes = await response.arrayBuffer()
console.log(contentType, bytes.byteLength)Anyone holding an unexpired download URL can use it. Do not write it to client-visible logs, analytics, or long-lived records.
Environment and retention boundaries
A Live Mode key cannot retrieve Test Mode media, and a Test Mode key cannot retrieve Live Mode media. Passing an environment query for the other mode returns environment_mismatch.
Media is available only during its Yanola retention period. After retention ends, retrieval returns the same not-found response used for missing or inaccessible media so scope details are not disclosed.
Store customer media responsibly
- Download from trusted server or worker code, not by exposing the API key in a browser.
- Validate the returned MIME type and enforce your own size and file-policy limits before processing.
- Encrypt retained customer media and grant access according to your application's privacy policy.
- Delete local copies when your business purpose or retention period ends.
- Do not attempt to derive Meta credentials, provider IDs, or permanent storage locations from Yanola responses.
Retrieval errors
| Status and code | Meaning | Action |
|---|---|---|
400 environment_mismatch | The requested environment differs from the key environment. | Remove the filter or use the matching key. |
403 insufficient_scope | The key lacks messages:read. | Use a key with read access. |
404 media_not_found | The ID is malformed, expired, missing, inaccessible, or in another environment. | Use the med_ ID from a visible Yanola event and check retention. |
503 media_not_ready | Inbound media is still being prepared. | Retry shortly; this error is retryable. |
503 media_unavailable | The retained media is temporarily unavailable. | Retry only when retryable is true. |
404 media_download_not_found | A signed download grant is expired, malformed, or no longer valid. | Retrieve metadata again to obtain a fresh URL. |