---
title: Weixin
description: Community Weixin (WeChat) iLink bot adapter for Chat SDK with long polling, QR login, media, and typing indicators.
tagline: Community Weixin (WeChat) iLink bot adapter for Chat SDK. Talks to the iLink bot HTTP JSON APIs directly with long polling, QR login, media uploads, and typing indicators.
package: chat-adapter-weixin
---

# Weixin



## Install

<PackageInstall package="chat-adapter-weixin chat @chat-adapter/state-memory" />

The Weixin adapter requires a [state adapter](/docs/state-adapters). It stores the
long-polling cursor, per-user context tokens, dedupe markers, and thread history
through Chat SDK's `StateAdapter`. Any state adapter works — the example uses
`@chat-adapter/state-memory`; use Redis or Postgres in production.

## Quick start

```typescript title="lib/bot.ts" lineNumbers
import { Chat } from "chat";
import { createMemoryState } from "@chat-adapter/state-memory";
import { createWeixinAdapter } from "chat-adapter-weixin";

const bot = new Chat({
  userName: "mybot",
  state: createMemoryState(),
  adapters: {
    weixin: createWeixinAdapter({
      accountId: process.env.WEIXIN_ACCOUNT_ID,
      token: process.env.WEIXIN_BOT_TOKEN,
    }),
  },
});

bot.onDirectMessage(async (thread, message) => {
  await thread.post(`You said: ${message.text}`);
});

await bot.initialize();
```

Weixin iLink bots are 1:1 only, so messages arrive through `onDirectMessage`.
When called with no arguments, `createWeixinAdapter()` reads its credentials from
environment variables.

## Long polling, not webhooks

Unlike most platform adapters, Weixin has no inbound webhook. The adapter opens a
long-polling loop against the iLink `getUpdates` endpoint when you call
`bot.initialize()`, and tears it down on `disconnect()`. There is no
`/api/webhooks/weixin` route to register.

## QR login

The bundled CLI acquires `accountId` and `token` by scanning a QR code with the
Weixin app. It does not replace your Chat SDK state storage.

```bash
weixin-chat-adapter login --env   # print as WEIXIN_ env vars
weixin-chat-adapter login --json  # print as JSON
weixin-chat-adapter login --save --state-dir ./.weixin-dev
```

Use `--save` only as a local-development convenience. In production, pass the
resulting token through environment variables or your secret manager.

## Environment variables

| Variable              | Required | Description                                                                |
| --------------------- | -------- | -------------------------------------------------------------------------- |
| `WEIXIN_ACCOUNT_ID`   | Yes      | iLink bot account ID. Obtain it via `weixin-chat-adapter login`.           |
| `WEIXIN_BOT_TOKEN`    | Yes      | iLink bot token. Obtain it via `weixin-chat-adapter login`.                |
| `WEIXIN_BASE_URL`     | No       | iLink API base URL (default: `https://ilinkai.weixin.qq.com`).             |
| `WEIXIN_CDN_BASE_URL` | No       | CDN base URL for media (default: `https://novac2c.cdn.weixin.qq.com/c2c`). |
| `WEIXIN_BOT_USERNAME` | No       | Override the bot display name (default: `weixin-bot`).                     |
| `WEIXIN_BOT_TYPE`     | No       | iLink bot type (default: `3`).                                             |
| `WEIXIN_ROUTE_TAG`    | No       | Optional routing tag forwarded to the iLink API.                           |

## Configuration

All options are auto-detected from environment variables when not provided.

<TypeTable
  type={{
  accountId: {
    type: "string",
    description:
      "iLink bot account ID. Auto-detected from `WEIXIN_ACCOUNT_ID`. Required at runtime.",
  },
  token: {
    type: "string",
    description:
      "iLink bot token. Auto-detected from `WEIXIN_BOT_TOKEN`. Required at runtime.",
  },
  baseUrl: {
    type: "string",
    default: '"https://ilinkai.weixin.qq.com"',
    description: "iLink API base URL. Auto-detected from `WEIXIN_BASE_URL`.",
  },
  cdnBaseUrl: {
    type: "string",
    default: '"https://novac2c.cdn.weixin.qq.com/c2c"',
    description:
      "CDN base URL for media downloads. Auto-detected from `WEIXIN_CDN_BASE_URL`.",
  },
  userName: {
    type: "string",
    default: '"weixin-bot"',
    description:
      "Bot display name. Auto-detected from `WEIXIN_BOT_USERNAME`.",
  },
  botType: {
    type: "string",
    default: '"3"',
    description: "iLink bot type. Auto-detected from `WEIXIN_BOT_TYPE`.",
  },
  routeTag: {
    type: "string | number",
    description:
      "Optional routing tag forwarded to the iLink API. Auto-detected from `WEIXIN_ROUTE_TAG`.",
  },
  polling: {
    type: "WeixinPollingConfig",
    description:
      "Long-polling tuning: `enabled`, `longPollTimeoutMs`, `retryDelayMs`, `backoffDelayMs`, `maxConsecutiveFailures`.",
  },
  logger: {
    type: "Logger",
    default: "ConsoleLogger",
    description: "Custom logger instance.",
  },
}}
/>

## Thread ID format

```
weixin:{base64url(accountId)}:{base64url(userId)}
```

Both segments are base64url-encoded so IDs stay delimiter-safe. Every thread is a
1:1 conversation between the bot account and a single user, so `isDM()` always
returns `true`.

## Capabilities

* 1:1 direct messages via long polling
* Plain-text messages (markdown is flattened to plain text — Weixin renders no formatting)
* Media: images, files, voice, and video (inbound and outbound)
* Typing indicators
* Per-user context tokens and dedupe persisted through your `StateAdapter`

## Limitations

* **No group chats** — iLink bots are 1:1 only; `postChannelMessage` and mentions are not applicable.
* **Editing and deleting messages** are not supported by the iLink bot API — `editMessage` / `deleteMessage` throw `NotImplementedError`.
* **Reactions** are not supported — `addReaction` / `removeReaction` throw `NotImplementedError`.
* **Message history** is not exposed by the API — `fetchMessages` returns an empty array.
* All formatting (bold, italic, code blocks) is stripped to plain text.

## Feature support

<FeatureSupport />
