Install
- Python
- TypeScript
- Rust
- Go
- C#
- Java
Quick start
Decrypt a backlog, cache keys, decrypt one event, encrypt a reply. Wire the send body toPOST /2/chat/conversations/{id}/messages as in Getting Started.
- Python
- TypeScript
- Rust
- Go
- C#
- Java
Lifecycle and keys
Construct the SDK, store private keys (Juicebox PIN or a local key blob), register public keys with the Chat API, and set your registered public-key version after unlock or import. Callgenerate_keypairs once per device/app identity; post the registration payload to the public-keys endpoint. Use setup / unlock (and related PIN helpers) for Juicebox PIN storage on every binding. export_keys / import_keys (raw key-blob persistence for bots and servers) are available on the native bindings only—Python, Go, .NET, JVM, and Rust. The JS/WASM binding does not expose raw key export or import: in a browser any script that reaches the instance could exfiltrate the identity, so JS keeps keys inside Juicebox. A JS server that wants to avoid a Juicebox round-trip per request should reuse one unlocked Chat instance across requests, or run a native binding where key blobs are supported.
- Python
- TypeScript
- Rust
- Go
- C#
- Java
juicebox_config object (recommended—passed verbatim), a full sdk_config wrapper, or a bare token_map.
Optional: signature verification is on by default (reject_unverified = true)—call set_reject_unverified(false) to disable it (not recommended); update_config if Juicebox realm config changes; is_unlocked / has_identity_key for UI state. Full field lists live in the chat-xdk repo stubs.
Conversation keys
Three prepare methods each make one call do everything a key change needs: generate a fresh conversation key, encrypt it for every participant (from the public keys you pass), and sign the change. All return the samePreparedConversationChange shape, ready to POST—rename SDK field encrypted_key to encrypted_conversation_key in conversation_participant_keys, and map the action signatures into the required action_signatures body field.
Keep the raw key bytes for
encrypt_message and media; never pass the API’s encrypted envelope into encrypt.
Use extract_conversation_keys on key-change event payloads to rebuild { keys, latest_version }. decrypt_conversation_key unwraps a single ECIES blob.
- Python
- TypeScript
- Rust
- Go
- C#
- Java
prepare_group_create; new plus current roster for prepare_group_members_change)—see Groups for samples. Both return two action signatures; the POST must include both.
Decrypt
decrypt_events is for history and backlog: it pulls conversation keys from the stream, returns decrypted messages, and collects per-event errors instead of failing the whole batch. decrypt_event is for a single live event when you already have a key cache; it raises/throws on failure.
Pass signing keys so the SDK can verify senders. Map API public-key fields into SigningKeyEntry: public_key_version → public_key_version (same name), signing_public_key → public_key, public_key → identity_public_key, plus identity_public_key_signature and user_id. Verification is mandatory by default: omitting or passing an empty signing-key list does not skip it—signed events fail (collected in errors for decrypt_events, thrown for decrypt_event). To actually skip verification you must first call set_reject_unverified(false) (not recommended in production).
- Python
- TypeScript
- Rust
- Go
- C#
- Java
Encrypt and send helpers
encrypt_message builds the signed ciphertext for a text message (optional entities, attachments via media_hash_key, TTL, notify flags). Map the returned payload into the send-message body: encrypted_content → encoded_message_create_event, encoded_event_signature → encoded_message_event_signature, plus your message_id.
Use encrypt_reply, encrypt_add_reaction, and encrypt_remove_reaction for replies and reactions (sequence_id targets the parent). encrypt / decrypt are for UTF-8 metadata under the conversation key (for example an encrypted group name)—not message envelopes. encrypt_stream / decrypt_stream encrypt attachment bytes; see Media. Low-level sign / verify / verify_key_binding support advanced flows; conversation-key changes, group creates, and member adds are signed by the prepare methods.
The conversation id passed to encrypt_message / encrypt_reply can be any form you hold—A:B from events, A-B from listings or URL paths (in either order), or the bare recipient user id—the SDK canonicalizes it before signing. Group ids (prefixed with g) pass through unchanged.
- Python
- TypeScript
- Rust
- Go
- C#
- Java
Media streams
Encrypt file bytes with the same conversation key used for text, upload via Chat media APIs, and attachmedia_hash_key on encrypt_message. This is not the Posts media model (expansions=attachments.media_keys). Full upload/download flow: Media.
- Python
- TypeScript
- Rust
- Go
- C#
- Java
Incremental streaming for large media
For large files, avoid holding the whole payload in memory:stream_encryptor() / stream_decryptor() return a StreamEncryptor / StreamDecryptor you feed in chunks (about 1 MB each) with push(chunk), then call finish() once at the end. On decrypt, finish() detects a truncated stream (it fails if input ended before the final frame), so don’t treat pushed plaintext as complete until it succeeds.
- Python
- TypeScript
Utilities
Base64/hex helpers, MIME sniffing, and image dimensions are available as module-level functions (Python/JS/Rust/Go) orChatXdkUtilities (C#/Java)—useful when building attachment metadata without pulling in extra libraries.
- Python
- TypeScript
- Rust
- Go
- C#
- Java
Important types
These conceptual types show up across languages (exact field names differ; JS often uses camelCase event discriminators likemessage):
- SendPayload — return value of
encrypt_messageand related encrypt helpers; map into the Chat API send body. - PublicKeyRegistrationPayload — output of
generate_keypairs/ public-key getters for the add-public-key API. - SigningKeyEntry — sender public material passed into decrypt for signature verification.
- PreparedConversationChange — output of the three prepare methods: the derived or passed
conversation_id, the rawconversation_keybytes,conversation_key_version,participant_keys(user_id,encrypted_key,public_key_version), andaction_signatures(message_id,encoded_message_event_detail,signature,signature_version,public_key_version, optionalsignature_payload—omitted on key-change signatures because that payload embeds the plaintext key). - DecryptEventsResult — messages, optional errors, and extracted
conversation_keys.
docs/API.md, *.pyi, index.d.ts).
Errors
Python typically raisesValueError with a descriptive message (for example an invalid PIN). TypeScript/JavaScript throws Error. Go returns (value, error). Prefer decrypt_events for history so one bad event does not abort the batch; inspect the errors collection for partial failures.
Some verification errors are permanent. Signatures are immutable and verified by rebuilding the signed payload from the event itself, so an old event that fails with signature missing or no matching signing key or an ECDSA mismatch will fail on every future load—no retry, key refresh, or API call can heal it. Treat these as tombstones, not transient errors. Rotating the conversation key starts a clean, verifiable history from that point forward.
Next steps
Getting Started
Wire Chat XDK to the Chat API
Media
Stream encrypt and media REST
Real-time events
Webhooks and activity delivery
Troubleshooting
Common failures