Media
Create upload sessions, manage reusable library assets, inspect quota usage, and retrieve metadata-only media assets for message sends.
List assets
Query
| Field | Type | Notes |
|---|---|---|
limit | integer | range 1..100 |
starting_after | string | len 1..∞ |
lifecycle | "single_use" | "library" | |
status | "pending" | "ready" | "consumed" | "failed" | "expired" | "deleted" | |
kind | "image" | "document" | "audio" | "video" | "sticker" | |
source | "customer" | "inbound_provider" | Filter by how the asset entered this environment: customer for media you uploaded, inbound_provider for attachments captured from inbound messages. Omit to list both — the default is unchanged and returns every source. |
Response
| Field | Type | Notes |
|---|---|---|
objectrequired | "list" | |
datarequired | object[] | |
has_morerequired | boolean | |
next_cursorrequired | string | null |
Attachments captured from inbound messages are assets in the same environment, so they appear in this list next to the files you uploaded. Separate them with source: customer for your uploads, inbound_provider for inbound-captured media. Omitting the parameter returns both, which is the unchanged default, so an existing integration keeps its current results.
Each asset reports download as a relative GET method/path while its status is ready or consumed, and null otherwise. This points to the authenticated URL-minting route below; it is never the signed URL itself.
Create an upload session
Request body
| Field | Type | Notes |
|---|---|---|
kindrequired | "image" | "document" | "audio" | "video" | "sticker" | |
lifecyclerequired | "single_use" | "library" | |
filename | string | len 1..255 |
mime_typerequired | string | len 1..255 |
byte_lengthrequired | integer | range -∞..9007199254740991 |
Response
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
objectrequired | "media_upload" | |
upload_urlrequired | string | |
upload_methodrequired | "PUT" | |
upload_headersrequired | object | |
expires_atrequired | string<ISO-8601> | pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
Complete an upload
Response
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
objectrequired | "media_asset" | |
sourcerequired | "customer" | "inbound_provider" | |
providerrequired | string | null | |
provider_media_idrequired | string | null | |
kindrequired | "image" | "document" | "audio" | "video" | "sticker" | |
lifecyclerequired | "single_use" | "library" | |
filenamerequired | string | null | |
mime_typerequired | string | |
byte_lengthrequired | integer | range 0..9007199254740991 |
statusrequired | "pending" | "ready" | "consumed" | "failed" | "expired" | "deleted" | |
downloadrequired | object | null | Authenticated URL-minting affordance for ready/consumed assets; null when the status is not downloadable. Never contains the signed URL. |
expires_atrequired | string<ISO-8601> | null | |
upload_expires_atrequired | string<ISO-8601> | pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
completed_atrequired | string<ISO-8601> | null | |
consumed_atrequired | string<ISO-8601> | null | |
consumed_by_message_idrequired | string | null | |
deleted_atrequired | string<ISO-8601> | null | |
failure_coderequired | string | null | |
failure_messagerequired | string | null | |
trace_idrequired | string | null | |
created_atrequired | string<ISO-8601> | pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
updated_atrequired | string<ISO-8601> | pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
Storage usage
Response
| Field | Type | Notes |
|---|---|---|
objectrequired | "media_storage_usage" | |
used_bytesrequired | integer | range 0..9007199254740991 |
limit_bytesrequired | integer | range -∞..9007199254740991 |
available_bytesrequired | integer | range 0..9007199254740991 |
percent_usedrequired | number | range 0..∞ |
These totals include inbound-captured assets — inbound media consumes the same environment quota as media you upload. When the environment is full, an inbound attachment cannot be stored: after bounded retries its media descriptor on message.received settles at status: "failed" with failure.code: "inbound_media_download_failed", while the message itself is still delivered. Free space with DELETE /v1/media/{asset_id}, which works for inbound and uploaded assets alike.
Retrieve metadata
Response
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
objectrequired | "media_asset" | |
sourcerequired | "customer" | "inbound_provider" | |
providerrequired | string | null | |
provider_media_idrequired | string | null | |
kindrequired | "image" | "document" | "audio" | "video" | "sticker" | |
lifecyclerequired | "single_use" | "library" | |
filenamerequired | string | null | |
mime_typerequired | string | |
byte_lengthrequired | integer | range 0..9007199254740991 |
statusrequired | "pending" | "ready" | "consumed" | "failed" | "expired" | "deleted" | |
downloadrequired | object | null | Authenticated URL-minting affordance for ready/consumed assets; null when the status is not downloadable. Never contains the signed URL. |
expires_atrequired | string<ISO-8601> | null | |
upload_expires_atrequired | string<ISO-8601> | pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
completed_atrequired | string<ISO-8601> | null | |
consumed_atrequired | string<ISO-8601> | null | |
consumed_by_message_idrequired | string | null | |
deleted_atrequired | string<ISO-8601> | null | |
failure_coderequired | string | null | |
failure_messagerequired | string | null | |
trace_idrequired | string | null | |
created_atrequired | string<ISO-8601> | pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
updated_atrequired | string<ISO-8601> | pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
asset_id is a Tyxter mda_* identifier. A Meta inbound image.id or audio.id is a different provider handle and is not accepted here.
Follow the response's download.path when download is non-null. Inbound message and message.received media descriptors carry the same affordance when their status is consumed.
Create a download URL
Response
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
objectrequired | "media_asset_download" | |
download_urlrequired | string | |
expires_atrequired | string<ISO-8601> | pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
Requires messages:read. It accepts a tenant-scoped ready or consumed Tyxter asset and mints a five-minute GET /v1/media/blobs/{token} capability URL. This is the supported download path for inbound WhatsApp media; refresh it after expires_at and do not store it as durable application data.
Delete media
Response
| Field | Type | Notes |
|---|---|---|
idrequired | string | |
objectrequired | "media_asset" | |
deletedrequired | "true" |
Upload bytes through a capability URL
Send the raw bytes to the opaque, signed upload_url returned by POST /v1/media/uploads, using the returned upload_headers.Content-Type. The short-lived token is the sole authority, so this request does not use an API key. Never construct or persist this URL yourself.
Response
| Field | Type | Notes |
|---|---|---|
okrequired | "true" |
Download bytes through a capability URL
Streams raw bytes for a signed preview_url or provider fetch URL with the stored Content-Type. The response is binary, not JSON, so it has no schema table. The token is short-lived, grants access by itself, and must be treated as a secret.
GET /v1/media/{asset_id} returns metadata only. It never returns raw bytes, base64, or object storage keys. Uploads default to single_use; set lifecycle: "library" for media that must be reused. Send media through POST /v1/messages with message.media.asset_id.