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 -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 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
| contentType | Description |
|---|---|
text | Plain text message (default). |
image | Image attachment (JPEG, PNG, WebP). |
audio | Audio message (OGG, MP3, M4A). |
video | Video attachment (MP4, WebM). |
document | Document attachment (PDF, DOCX, etc.). |
react | Reaction 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:
| Value | Bucket | Retention |
|---|---|---|
TRANSIENT (default) | Expiring bucket | Erased by the retention job once the conversation's retention window elapses. The bucket's lifecycle policy may also expire the object. |
PERMANENT | Permanent bucket | Kept indefinitely. The retention job preserves it. |
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.