Skip to main content
Images and other files use the same conversation key as text. Encrypt bytes with the Chat XDK (encrypt_stream / decrypt_stream), upload via the /2/chat/media/upload routes (sidebar API reference → Media), then attach media_hash_key on encrypt_message. Include media.write with your DM scopes when uploading. Use hyphenated conversation ids in paths (:-). Prefer MIME/dimensions from decrypted bytes. This path is not the Posts media model (expansions=attachments.media_keys, media.fields=variants, etc.). Those parameters apply to Posts; E2EE X Chat blobs are addressed by media_hash_key and X Chat media download.

Encrypt

encrypt_stream / decrypt_stream process the whole payload in memory. For large files, stream_encryptor() / stream_decryptor() return incremental objects (StreamEncryptor / StreamDecryptor): feed chunks with push, then call finish once—finish errors if the stream was truncated.

Upload

Use the request bodies on the OpenAPI pages under API reference → Media. Prefer encrypted blob size where size is required. Finalize yields media_hash_key for attachments and download. Retry transient 5xx with backoff. Python/TypeScript may use the XDK when media helpers exist; otherwise POST with a Bearer token in any language.

Send with an attachment

Encrypt with a media attachment, then POST the send-message body (same field mapping as Getting Started).

Download and decrypt

Path: GET /2/chat/media/{conversation_id}/{media_hash_key}. Response body is ciphertext. On inbound messages, read media_hash_key from decrypted attachments / media_hashes. Pick the key by the event’s key version. Each decrypted message event carries the keyVersion (JS; key_version in the other bindings) its content was encrypted under. Decrypt an attachment with the conversation key for that version—conversationKeys.keys[event.keyVersion]—not the latest. After a key rotation (for example a member add), the latest key cannot decrypt media attached to older messages.

Tips

  • Use the same conversation key version as when the media was encrypted
  • Do not log plaintext media or raw keys
  • Detect MIME after decrypt
  • Web clients: encrypt/decrypt on the client when possible; keep OAuth tokens on your server
Full request and response schemas for each media route are under API reference → Media in the sidebar (initialize upload, append chunk, finalize upload, and download media).