Beeper Matrix

Matrix adapter built and maintained by Beeper. Runs over Matrix sync (no webhooks) and works with Beeper conversations and bridged networks like WhatsApp, Telegram, Instagram, and Signal.

Vendor-official adapter maintained by Beeper, not Vercel or Chat SDK contributors. For feature requests, bug reports, and support, file an issue on the adapter's repo.

Install

Requires Node.js 22+.

pnpm add @beeper/chat-adapter-matrix

Quick start

createMatrixAdapter() reads its configuration from environment variables when called without arguments.

lib/bot.ts
import { Chat } from "chat";
import { createMemoryState } from "@chat-adapter/state-memory";
import { createMatrixAdapter } from "@beeper/chat-adapter-matrix";

const matrix = createMatrixAdapter();

const bot = new Chat({
  userName: process.env.MATRIX_BOT_USERNAME ?? "beeper-bot",
  state: createMemoryState(),
  adapters: { matrix },
});

bot.onNewMention(async (thread, message) => {
  await thread.subscribe();
  await thread.post(`Hi ${message.author.userName}. Mention me or run /ping.`);
});

bot.onSlashCommand("/ping", async (event) => {
  await event.channel.post("pong");
});

await bot.initialize();

The standard Chat SDK building blocks — Threads, Direct Messages, subscriptions, and handlers — work the same here. The Matrix adapter just maps them onto Matrix rooms and threaded replies.

Authentication

Access token

Use a long-lived access token from your Matrix homeserver:

lib/bot.ts
import { createMatrixAdapter } from "@beeper/chat-adapter-matrix";

createMatrixAdapter({
  baseURL: process.env.MATRIX_BASE_URL,
  auth: {
    type: "accessToken",
    accessToken: process.env.MATRIX_ACCESS_TOKEN,
    userID: process.env.MATRIX_USER_ID,
  },
});

Username and password

lib/bot.ts
createMatrixAdapter({
  baseURL: process.env.MATRIX_BASE_URL,
  auth: {
    type: "password",
    username: process.env.MATRIX_USERNAME,
    password: process.env.MATRIX_PASSWORD,
    userID: process.env.MATRIX_USER_ID,
  },
});

The adapter persists the resulting login session through your state adapter, so subsequent restarts reuse the existing session instead of logging in again.

Configuration

lib/bot.ts
createMatrixAdapter({
  baseURL: process.env.MATRIX_BASE_URL,
  auth: {
    type: "accessToken",
    accessToken: process.env.MATRIX_ACCESS_TOKEN,
    userID: process.env.MATRIX_USER_ID,
  },
  recoveryKey: process.env.MATRIX_RECOVERY_KEY,
  commandPrefix: "/",
  roomAllowlist: ["!room:beeper.com"],
  inviteAutoJoin: {
    inviterAllowlist: ["@alice:beeper.com", "@ops:beeper.com"],
  },
  matrixSDKLogLevel: "error",
});

Defaults worth knowing:

  • Persistence kicks in automatically whenever the Chat instance has a state adapter — Redis is recommended for restart durability.
  • deviceID is inferred from the auth payload when possible, then read back from state, and only generated as a last resort.
  • recoveryKey enables E2EE and key-backup bootstrap.
  • inviteAutoJoin: {} enables invite auto-join. Pass inviterAllowlist to scope it to specific accounts.

Advanced options

createMatrixAdapter({
  baseURL: process.env.MATRIX_BASE_URL,
  auth: {
    type: "accessToken",
    accessToken: process.env.MATRIX_ACCESS_TOKEN,
    userID: process.env.MATRIX_USER_ID,
  },
  e2ee: {
    useIndexedDB: false,
    cryptoDatabasePrefix: "beeper-matrix-bot",
  },
  persistence: {
    keyPrefix: "my-bot",
    session: {
      ttlMs: 86_400_000,
    },
    sync: {
      persistIntervalMs: 10_000,
    },
  },
});

Environment variables

When you call createMatrixAdapter() with no arguments, the adapter reads only these variables:

VariableRequiredDescription
MATRIX_BASE_URLYesMatrix homeserver base URL
MATRIX_ACCESS_TOKENYes*Access token for access-token auth
MATRIX_USERNAMEYes*Username for password auth
MATRIX_PASSWORDYes*Password for password auth
MATRIX_USER_IDNoUser ID hint
MATRIX_DEVICE_IDNoExplicit device ID override
MATRIX_RECOVERY_KEYNoEnables E2EE and key-backup bootstrap
MATRIX_BOT_USERNAMENoMention-detection username
MATRIX_COMMAND_PREFIXNoSlash command prefix (default: /)
MATRIX_INVITE_AUTOJOINNoEnable invite auto-join
MATRIX_INVITE_AUTOJOIN_ALLOWLISTNoComma-separated Matrix user IDs allowed to invite the bot
MATRIX_SDK_LOG_LEVELNoMatrix SDK log level

*Use either MATRIX_ACCESS_TOKEN, or MATRIX_USERNAME plus MATRIX_PASSWORD.

Persistence

For production, pair the adapter with a durable state adapter such as Redis:

lib/bot.ts
import { Chat } from "chat";
import { createRedisState } from "@chat-adapter/state-redis";
import { createMatrixAdapter } from "@beeper/chat-adapter-matrix";

const matrix = createMatrixAdapter({
  baseURL: process.env.MATRIX_BASE_URL,
  auth: {
    type: "accessToken",
    accessToken: process.env.MATRIX_ACCESS_TOKEN,
    userID: process.env.MATRIX_USER_ID,
  },
  recoveryKey: process.env.MATRIX_RECOVERY_KEY,
});

const bot = new Chat({
  userName: process.env.MATRIX_BOT_USERNAME ?? "beeper-bot",
  state: createRedisState({ url: process.env.REDIS_URL }),
  adapters: { matrix },
});

The state adapter persists:

  • Generated or inferred device IDs
  • Password-login sessions
  • DM room mappings
  • Matrix sync snapshots
  • E2EE secrets bundles when E2EE is enabled

Thread model

  • A Matrix room is a Chat SDK channel.
  • Top-level room messages live on the channel timeline.
  • Matrix threaded replies map to Chat SDK threads using roomID + rootEventID.
  • openDM(userId) reuses an existing direct room when one is already in state and creates a new one otherwise.

Message history APIs

The adapter implements the standard fetch surface:

  • fetchMessage(threadId, messageId)
  • fetchMessages(threadId, options)
  • fetchChannelMessages(channelId, options)
  • fetchThread(threadId)
  • fetchChannelInfo(channelId)
  • listThreads(channelId, options)
  • openDM(userId)

Limitations

  • handleWebhook() returns 501 by design — Matrix uses sync polling, not webhooks.
  • Cards, modals, and ephemeral messages are not implemented (Matrix has no native equivalent).
  • Native streaming is not implemented at the adapter layer.
  • Slash commands are parsed from plain text messages because Matrix does not emit native slash command events.

Feature support

Messaging

FeatureSupported
Post message
Edit message
Delete message
File uploads
Streaming
Scheduled messages

Rich content

FeatureSupported
Card format
Buttons
Link buttons
Select menus
Tables
Fields
Images in cards
Modals

Conversations

FeatureSupported
Slash commandsPrefix-parsed
Mentions
Add reactions
Remove reactions
Typing indicator
DMs
Ephemeral messages
User lookup
Parent subject
Native client
Custom API endpointbaseURL

Message history

FeatureSupported
Fetch messages
Fetch single message
Fetch thread info
Fetch channel messages
List threads
Fetch channel info
Post channel message