Media Attachments

Send and receive media attachments — images, audio, video, documents — within encrypted conversations.

Overview

Media messages follow the same end-to-end encryption scheme as text messages. The media file is encrypted with a per-message AES-256-GCM key, and the key is wrapped to both the recipient's and sender's RSA public keys.

Uploading Media

Upload encrypted media as multipart/form-data. The file bytes must already be encrypted by the client; the envelope fields describe how to decrypt them. The response is a Message object with a mediaId referencing the uploaded file.

cURL
curl -X POST https://api.ixblix.app/api/media/company \
  -H "X-API-Key: smci_xxxx" \
  -F "conversationId=conv_xxxx" \
  -F "file=@encrypted_photo.bin" \
  -F "iv=base64-iv" \
  -F "authTag=base64-auth-tag" \
  -F "encryptedKey=base64-rsa-wrapped-aes-key" \
  -F "selfEncryptedKey=base64-rsa-wrapped-aes-key-to-self" \
  -F "keyId=operator-key-1"

Downloading Media

Fetch the raw encrypted bytes and envelope headers:

cURL
curl https://api.ixblix.app/api/media/{mediaId}/content \
  -H "X-API-Key: smci_xxxx"

Metadata (envelope + file info) is available at GET /api/media/{mediaId}.

Content Types

contentTypeDescription
textPlain text message (default).
imageImage attachment (JPEG, PNG, WebP).
audioAudio message (OGG, MP3, M4A).
videoVideo attachment (MP4, WebM).
documentDocument attachment (PDF, DOCX, etc.).
reactReaction emoji (uses replyToId).

Receiving Media

When you receive a MESSAGE_RECEIVED webhook for a media message, fetch the message details with GET /api/messages/{conversationId}. The response includes the mediaId and envelope fields. Download the encrypted bytes via GET /api/media/{mediaId}/content and decrypt locally.

Storage

Media files are stored encrypted in an S3-compatible object storage. The backend only stores ciphertext — it never sees the plaintext. Access is controlled via the company API key or conversation token.

Storage class

Media is written to one of two buckets, selected per upload with the optional storageClass multipart field:

ValueBucketRetention
TRANSIENT (default)Expiring bucketErased by the retention job once the conversation's retention window elapses. The bucket's lifecycle policy may also expire the object.
PERMANENTPermanent bucketKept indefinitely. The retention job preserves it.
cURL
curl -X POST https://api.ixblix.app/api/media/company \
  -H "X-API-Key: smci_xxxx" \
  -F "conversationId=conv_xxxx" \
  -F "file=@encrypted_contract.bin" \
  -F "storageClass=PERMANENT" \
  -F "iv=base64-iv" \
  -F "authTag=base64-auth-tag" \
  -F "encryptedKey=base64-rsa-wrapped-aes-key" \
  -F "selfEncryptedKey=base64-rsa-wrapped-aes-key-to-self" \
  -F "keyId=operator-key-1"

Use PERMANENT for files that must outlive the retention window (e.g. signed contracts, receipts, audit evidence). Any other value is rejected with a VALIDATION_ERROR. The chosen class is returned as storageClass on the Media object.

Note: an explicit erasure request (LGPD/GDPR "right to erasure") removes permanent files too. PERMANENT only exempts a file from the automatic retention sweep, not from a customer-initiated deletion.