How wx talks to WeChat
wx serve connects to WeChat’s servers the way a WeChat desktop client does: as a logged-in device on your account. Everything else is built on four exchanges.
1. Staying logged in
At login, WeChat issues credentials tied to a device identity, which the daemon stores (encrypted) in its data folder. While connected, the daemon sends the heartbeats WeChat asks for. After a restart it reconnects with the saved credentials; it never starts a new QR login by itself.
If WeChat rejects the current push, heartbeat or sync session as requiring authentication, the daemon signs out locally and stops reconnecting. Account status reports authenticated: false and connection: "disconnected"; account events include auth_required, the request source and the service code when available. Your profile, history and saved credentials remain. Run wx auth login to sign in again. Temporary connection failures continue to retry.
2. Receiving
- The daemon keeps a push connection open. A push only says that something changed; it carries no content.
- The daemon asks for everything since its last sync key. WeChat returns a batch of changes and a new key.
- The raw batch is saved first, so nothing is lost if writing fails halfway.
- The changes are written to the profile: new messages, unread counts, contacts and groups.
- Only then does each new message become a
message.committedevent. Clients follow the event stream, fetch the message, and acknowledge how far they’ve read.
If the push connection drops, the daemon polls instead until it’s back (incomingTransport in wx accounts status). Anything that arrived while the daemon was offline comes in a catch-up batch when it reconnects. It’s stored, but marked as catch-up so clients don’t treat it as new.
3. Sending
Text goes straight to WeChat. Media is first prepared (images checked, video and audio converted with FFmpeg, voice encoded to WeChat’s SILK format), encrypted, uploaded to WeChat’s CDN and read back. Only then is the message that points to it sent. Every send has an idempotency key, so a retry never sends twice. See Sends and recovery.
4. Media and read state
Downloads. A received image, file, video or voice message only references an encrypted file on WeChat’s CDN. When a client asks for it, the daemon downloads it, decrypts it, checks its size and hash, and caches it in the profile. Cached files keep working after WeChat’s copy expires.
Read state. Marking a chat read clears its unread count in the profile and sends WeChat a read notice, so your phone and other devices can clear it too. Reading history doesn’t do this; only an explicit read does.
Supported read, chat-open and chat-exit notices from another device also update local unread counts. Chat-open and chat-exit handling covers existing visible ordinary chats and preserves messages newer than the notice. It does not send a read notice back.
Clients
Clients only see the HTTP API and events. Each decides what to do with a message:
- The CLI prints events (
wx events watch) and lets you reply. - OpenClaw checks who sent it, downloads media, runs the agent, replies and marks the chat read. See Who the bot answers and Read receipts.
- Your own code can do the same through the HTTP API.