XChat
XChat is X's end-to-end encrypted chat, a separate system from the legacy DMs API.
emusks has mostly reverse-engineered XChat for you. Sending an encrypted DM is pretty easy: set up an identity once, then message(). Emusks will handle all of the crypto for you.
js
import Emusks from "emusks";
const client = new Emusks();
await client.login("your_auth_token");
// one-time: create (or load) your encrypted-chat identity
const identity = await client.xchat.createIdentity({ pin: "2468" });
// ... persist `identity`, then on later runs: await client.xchat.loadIdentity(identity)
// send a fully end-to-end encrypted DM, in one call
await client.xchat.message("44196397", "hello from xchat ^w^");
// react, edit, mark read, delete - all one call too
await client.xchat.react("44196397", messageSequenceId, "🔥");
await client.xchat.edit("44196397", messageSequenceId, "fixed typo");You can also look things up without any setup of your own:
js
await client.xchat.isOnXChat("44196397"); // true
await client.xchat.canMessage("44196397"); // can you reach them over XChat?
await client.xchat.fingerprint(await client.xchat.publicKey("44196397"));
// 8435:8166:a7fb:5c30:...:8e4b:b1c6Yes this was all fucking vibecoded, I'm not manually reading like 15 megabytes of minified JS (yes XChat loads 15 megabytes of JS) to write this. It works well though. Thanks Claude!
Get started
| Section | Methods |
|---|---|
| Setup & identity | createIdentity (PIN-backed), loadIdentity, recover (PIN + session) |
| Sending messages | message, react, unreact, edit, markRead, markUnread, deleteMessages |
| Reading messages | conversations, read |
| Conversations | pin, unpin, setNickname, reportScreenCapture |
| Looking up users | publicKey, publicKeys, profile, permissions, canMessage, isOnXChat, fingerprint, callPermissions |
| Audio & video calls | call, answerCall, listenForCalls, startGroupCall, joinGroupCall, useWebRTC |
| Advanced | token, gql |