API reference

Media

Create upload sessions, manage reusable library assets, inspect quota usage, and retrieve metadata-only media assets for message sends.

List assets

GET/v1/media

Query

FieldTypeNotes
limitinteger
range 1..100
starting_afterstring
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

FieldTypeNotes
objectrequired"list"
datarequiredobject[]
has_morerequiredboolean
next_cursorrequiredstring | 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

POST/v1/media/uploads201 Created

Request body

FieldTypeNotes
kindrequired"image" | "document" | "audio" | "video" | "sticker"
lifecyclerequired"single_use" | "library"
filenamestring
len 1..255
mime_typerequiredstring
len 1..255
byte_lengthrequiredinteger
range -∞..9007199254740991

Response

FieldTypeNotes
idrequiredstring
objectrequired"media_upload"
upload_urlrequiredstring
upload_methodrequired"PUT"
upload_headersrequiredobject
expires_atrequiredstring<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

POST/v1/media/uploads/{asset_id}/complete

Response

FieldTypeNotes
idrequiredstring
objectrequired"media_asset"
sourcerequired"customer" | "inbound_provider"
providerrequiredstring | null
provider_media_idrequiredstring | null
kindrequired"image" | "document" | "audio" | "video" | "sticker"
lifecyclerequired"single_use" | "library"
filenamerequiredstring | null
mime_typerequiredstring
byte_lengthrequiredinteger
range 0..9007199254740991
statusrequired"pending" | "ready" | "consumed" | "failed" | "expired" | "deleted"
downloadrequiredobject | null
Authenticated URL-minting affordance for ready/consumed assets; null when the status is not downloadable. Never contains the signed URL.
expires_atrequiredstring<ISO-8601> | null
upload_expires_atrequiredstring<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_atrequiredstring<ISO-8601> | null
consumed_atrequiredstring<ISO-8601> | null
consumed_by_message_idrequiredstring | null
deleted_atrequiredstring<ISO-8601> | null
failure_coderequiredstring | null
failure_messagerequiredstring | null
trace_idrequiredstring | null
created_atrequiredstring<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_atrequiredstring<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

GET/v1/media/storage-usage

Response

FieldTypeNotes
objectrequired"media_storage_usage"
used_bytesrequiredinteger
range 0..9007199254740991
limit_bytesrequiredinteger
range -∞..9007199254740991
available_bytesrequiredinteger
range 0..9007199254740991
percent_usedrequirednumber
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

GET/v1/media/{asset_id}

Response

FieldTypeNotes
idrequiredstring
objectrequired"media_asset"
sourcerequired"customer" | "inbound_provider"
providerrequiredstring | null
provider_media_idrequiredstring | null
kindrequired"image" | "document" | "audio" | "video" | "sticker"
lifecyclerequired"single_use" | "library"
filenamerequiredstring | null
mime_typerequiredstring
byte_lengthrequiredinteger
range 0..9007199254740991
statusrequired"pending" | "ready" | "consumed" | "failed" | "expired" | "deleted"
downloadrequiredobject | null
Authenticated URL-minting affordance for ready/consumed assets; null when the status is not downloadable. Never contains the signed URL.
expires_atrequiredstring<ISO-8601> | null
upload_expires_atrequiredstring<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_atrequiredstring<ISO-8601> | null
consumed_atrequiredstring<ISO-8601> | null
consumed_by_message_idrequiredstring | null
deleted_atrequiredstring<ISO-8601> | null
failure_coderequiredstring | null
failure_messagerequiredstring | null
trace_idrequiredstring | null
created_atrequiredstring<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_atrequiredstring<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

GET/v1/media/{asset_id}/download-url

Response

FieldTypeNotes
idrequiredstring
objectrequired"media_asset_download"
download_urlrequiredstring
expires_atrequiredstring<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

DELETE/v1/media/{asset_id}

Response

FieldTypeNotes
idrequiredstring
objectrequired"media_asset"
deletedrequired"true"

Upload bytes through a capability URL

PUT/v1/media/blobs/{token}

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

FieldTypeNotes
okrequired"true"

Download bytes through a capability URL

GET/v1/media/blobs/{token}

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.