Bridges one (or more) Discord channels to one (or more) Matrix rooms.
- Discord → Matrix: forwards messages, replies (as Matrix rich replies), and reactions (unicode emoji)
- Matrix → Discord: forwards messages via a Discord webhook (impersonates Matrix displayname/avatar), replies as quoted text (
>), and reactions - Optional edit forwarding in both directions
- Node.js 18+ (Node 20+ recommended)
- A Discord server where you can add bots + create webhooks
- A Matrix account (ideally a dedicated “bot” account)
- An unencrypted Matrix room (E2EE is not supported)
npm install- Go to the Discord Developer Portal: https://discord.com/developers/applications
- Click New Application → give it a name.
- In the left sidebar: Bot → click Add Bot.
- Under Token, click Reset Token (or Copy) and save it for your
config.json.
In the same Bot page, enable:
- MESSAGE CONTENT INTENT (required so the bot can read message text)
Reactions usually work without special privileged intents, but you must still give the bot channel permissions (next step).
- In the left sidebar: OAuth2 → URL Generator.
- Scopes:
- ✅ bot
- Bot permissions (minimum recommended for this bridge):
- ✅ View Channels
- ✅ Read Message History
- ✅ Add Reactions
Then copy/open the generated URL and add the bot to your server.
Tip: You can also construct an invite URL like:
https://discord.com/api/oauth2/authorize?client_id=YOUR_APPLICATION_CLIENT_ID&scope=bot&permissions=0
But using the URL Generator is simpler because it calculates permissions for you.
Matrix → Discord messages are posted via a webhook (so they can show Matrix display name + avatar).
- Open your Discord server.
- Right-click the target channel → Edit Channel.
- Go to Integrations → Webhooks → New Webhook.
- Copy the Webhook URL.
Create a dedicated Matrix user for the bridge bot (recommended so you don’t use your personal account token).
- Matrix clients (pick one):
- Element Web: https://app.element.io/
- Element Desktop: https://element.io/get-started
Sign up on your homeserver (for example matrix.org) and log in.
In Element:
- Click your profile picture → All settings.
- Help & About.
- Scroll to Advanced → copy Access Token.
This token goes into matrix.accessToken in config.json.
The bot must be in the Matrix room you want to bridge.
- Invite the bot user to the room (Element: room → Room info → People → Invite).
- This project uses auto-join on invites, so the bot should accept the invite automatically.
Important: the room must be unencrypted (no E2EE), otherwise the bot won’t be able to read messages.
In Element, open the room → Room info → Settings → Advanced and copy the Internal room ID. It looks like:
!someroomid:example.org
- Discord User Settings → Advanced → enable Developer Mode.
- Right-click the target channel → Copy Channel ID.
Copy the example config:
cp config.example.json config.jsonFill in values in config.json:
discord.botToken: Discord bot token from the Developer Portalmatrix.homeserverUrl: your homeserver base URL (example:https://matrix.org)matrix.accessToken: access token for the Matrix bot usermatrix.botUserId(optional but recommended): the Matrix bot user ID (example:@mybot:matrix.org)bridge.mappings[]:discordChannelId: the channel ID to listen todiscordWebhookUrl: webhook URL for that channel (Matrix → Discord)matrixRoomId: Matrix room ID to send to
Notes:
bridge.statePathstores message-id mappings so replies/reactions can map across platforms.bridge.matrixSyncPathstores Matrix sync state so the bot can resume properly.
Development mode (recommended while setting up):
npm run devProduction build:
npm run build
npm start- Discord → Matrix replies: sent as Matrix rich replies (reply threading)
- Matrix → Discord replies: sent as quoted text using
> - Reactions: unicode emoji reactions are mirrored both ways (custom Discord emoji are not)
-
Discord messages not arriving:
- Ensure MESSAGE CONTENT INTENT is enabled for the bot in the Developer Portal.
- Ensure the bot has access to the channel and can read message history.
- Ensure
discordChannelIdmatches the channel you’re testing in.
-
Matrix messages not arriving:
- Ensure the bot user is actually in the room.
- Ensure the room is unencrypted.
- Verify
matrix.homeserverUrlandmatrix.accessToken.
-
Matrix → Discord “impersonation” doesn’t work:
- Matrix → Discord uses a webhook; make sure
discordWebhookUrlis the webhook URL for the channel.
- Matrix → Discord uses a webhook; make sure
-
Error:
Unknown Webhook/ DiscordAPIError10015:- The webhook URL in
discordWebhookUrlis invalid (deleted webhook or regenerated token). - Create a new webhook in the target channel and replace
discordWebhookUrlinconfig.json.
- The webhook URL in
-
Reactions don’t mirror Matrix → Discord:
- The Discord bot must have permission to Add Reactions in that channel.
- Only unicode emoji reactions are supported.
- Matrix messages are sent by the Matrix bot user (Matrix has no native “webhook impersonation”). The bridge includes author info in the message formatting.
- Custom Discord emojis are currently not mirrored to Matrix (unicode emoji reactions work).
- E2EE rooms are not supported.
If you’ve seen a Discord↔Matrix bridge that “feels native” (each Discord user appears as themselves in Matrix, replies/threading look correct, edits/reactions work reliably, etc.), it’s usually not a simple bot that posts messages.
Most mature bridges use a Matrix Application Service (AS) bridge design:
-
Application Service registration on the Matrix homeserver
- The bridge is trusted by the homeserver via an AS registration file (contains tokens + namespace rules).
- This lets the bridge create/“own” many virtual users on Matrix.
-
Puppeting / virtual users
- For each Discord user, the bridge creates a corresponding Matrix “ghost” user (or connects the user’s own Matrix account).
- That’s how messages on Matrix appear as the real Discord author instead of
Bot: Alice: hello.
-
Real state + storage
- Uses a real database (SQLite/Postgres) for message ID mapping, reactions, edits, membership, and resuming after restarts.
-
Protocol-aware mapping
- Handles Discord mentions/roles/channels, attachments, threads, replies, embeds, stickers, edits, deletes, and rate limits.
- Implements backoff/retry so a single API failure doesn’t crash the bridge.
Examples of popular open-source bridges (for reference):
mautrix-discord(Python, mature “puppeting” bridge): https://docs.mau.fi/bridges/go/discord/index.htmlmatrix-appservice-discord(Node, AS bridge): https://github.com/matrix-org/matrix-appservice-discord
This project is intentionally simpler: it’s a relay bridge (Discord bot + Matrix bot + Discord webhook). It’s easier to run, but it can’t match the UX of a full AS/puppeting bridge without a larger redesign.
- Treat
config.jsonlike a secret (it contains tokens). Do not commit it. - If you accidentally leaked a token, rotate it immediately:
- Discord bot token: Developer Portal → Bot → Reset Token
- Matrix access token: depends on homeserver/client; easiest is create a new token (or new bot account) if your client doesn’t support rotation.