The Unthread Discord Bot connects your Discord server to Unthread so your community can open and manage support tickets without leaving Discord.
- Lets users open tickets with
/support - Turns new posts in selected Discord forum channels into tickets automatically
- Creates a Discord thread for each ticket so the conversation stays organized
- Syncs replies and updates between Discord and Unthread through Redis-backed webhooks
- Includes simple utility commands for health and version checks
- A user opens a ticket with
/supportor creates a forum post in a configured forum channel. - The bot creates the ticket in Unthread.
- The conversation is kept in sync between Discord and Unthread.
- A separate
webhook-serverservice receives Unthread webhooks and places them on Redis. - This bot reads those Redis events and updates Discord.
Before you deploy the bot, make sure you have:
- A Discord application and bot token
- A Discord server where you can install the bot
- An Unthread API key and Slack channel ID
- PostgreSQL
- Two Redis connections:
- one for bot state and cache
- one for webhook queue processing
- Node.js
^22.0.0 || ^24.0.0 || ^26.0.0if you want to run it without Docker - Bun
1.3.13for local development
This is the easiest option if you want a hosted setup.
-
Copy the environment file:
cp .env.example .env
-
Fill in the required values in
.env. -
Start the full stack:
docker-compose up -d
-
Check the logs if needed:
docker-compose logs -f server docker-compose logs -f webhook-server
-
Deploy slash commands once your bot token and guild ID are ready:
docker-compose exec server node dist/deploy_commands.js
-
Install dependencies:
bun install
-
Copy the environment file:
cp .env.example .env
-
Build the project:
bun run build
-
Start the bot:
bun run start
bun run startbuilds the project, deploys slash commands, and starts the bot. -
If you only want to deploy or force redeploy slash commands:
bun run deploycommand
For local development with auto-reload, use:
bun dev- Go to the Discord Developer Portal.
- Create a new application.
- Add a bot user.
- Enable the Message Content Intent in the bot settings.
Grant these permissions when you invite the bot:
- View Channels
- Send Messages
- Send Messages in Threads
- Read Message History
- Create Public Threads
- Create Private Threads
- Manage Threads
- Embed Links
- Use Slash Commands
Invite URL format:
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=1084479760448&integration_type=0&scope=bot+applications.commands
- Enable Developer Mode in Discord.
- Right-click your server.
- Select Copy ID.
- Put that value in
GUILD_ID.
Copy .env.example to .env and set these values.
| Variable | What it is for |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
CLIENT_ID |
Discord application ID |
GUILD_ID |
Discord server ID used for slash command deployment |
UNTHREAD_API_KEY |
Unthread API authentication |
UNTHREAD_SLACK_CHANNEL_ID |
Slack channel used by Unthread when creating conversations |
SLACK_TEAM_ID |
Slack workspace ID; current startup validation expects it |
POSTGRES_URL |
PostgreSQL connection string |
PLATFORM_REDIS_URL |
Redis connection for bot cache and state |
WEBHOOK_REDIS_URL |
Redis connection for webhook queue polling |
| Variable | What it is for |
|---|---|
UNTHREAD_WEBHOOK_SECRET |
Secret used by the webhook server for Unthread webhook validation |
| Variable | What it is for |
|---|---|
FORUM_CHANNEL_IDS |
Comma-separated Discord forum channel IDs for automatic ticket creation |
NODE_ENV |
Use development for verbose logs; default behavior is production-safe |
PORT |
Port used by the separate webhook server; default 3000 |
UNTHREAD_HTTP_TIMEOUT_MS |
Timeout for Unthread API requests; default 10000 |
WEBHOOK_POLL_INTERVAL |
Redis polling interval in milliseconds; default 5000 |
DATABASE_SSL_VALIDATE |
PostgreSQL SSL behavior |
DATABASE_SSL_CA |
Optional CA certificate value for PostgreSQL SSL |
DUMMY_EMAIL_DOMAIN |
Fallback email domain for Discord users without an email |
/supportopens a modal with:- Ticket title
- Summary
- Optional contact email
Important behavior:
/supportonly works in normal server channels/supportdoes not work inside threads/supportdoes not work in channels listed inFORUM_CHANNEL_IDS- If a user leaves the email blank, the bot falls back to an existing customer email or a generated
@discord.invalidemail
/pingshows API latency and WebSocket heartbeat/servershows server information/usershows user information/versionshows the running bot version
If you want forum posts to become tickets automatically:
- Add forum channel IDs to
FORUM_CHANNEL_IDS. - Make sure those IDs belong to real forum channels.
- Create a new forum post in one of those channels.
The bot will:
- validate that the parent channel is really a forum channel
- create a ticket in Unthread
- bind that Discord thread to the ticket
- post a confirmation embed in the thread
The included docker-compose.yml starts these services:
server- the Discord botwebhook-server- receives Unthread webhooks and writes them to Redispostgres-platform- PostgreSQL storageredis-platform- Redis for bot state and cacheredis-webhook- Redis for webhook queue messages
The bot itself does not expose an HTTP webhook endpoint.
Instead:
webhook-serverreceives Unthread webhook traffic- both services must connect to the same Redis instance for webhook syncing to work
webhook-serverwrites events to Redis usingREDIS_URL- the Discord bot reads those events using
WEBHOOK_REDIS_URL - outside
docker-compose, pointREDIS_URLandWEBHOOK_REDIS_URLat the same Redis instance
If you are testing locally, expose the webhook server and point Unthread to:
https://YOUR_PUBLIC_URL/webhook/unthread
You can use tools such as VS Code port forwarding or ngrok to make the local webhook server reachable from the internet.
- Make sure
CLIENT_ID,GUILD_ID, andDISCORD_BOT_TOKENare correct - Run
bun run deploycommandagain - Guild commands update quickly; command changes still need redeployment
- Make sure you are using it in a normal text channel, not inside a thread
- Make sure the bot has permission to create and manage threads in that channel
- Do not use
/supportinside a configured forum channel
- Check that
FORUM_CHANNEL_IDSonly contains forum channel IDs - Check the bot permissions in both the forum channel and the created thread
- Set
NODE_ENV=developmentif you want more detailed logs
- Make sure
webhook-serveris running - Make sure
WEBHOOK_REDIS_URLpoints to the Redis instance used by the webhook server - Make sure the public webhook URL in Unthread points to
/webhook/unthread - Check both
serverandwebhook-serverlogs
- Check
POSTGRES_URL,PLATFORM_REDIS_URL, andWEBHOOK_REDIS_URL - Make sure PostgreSQL and both Redis instances are reachable before starting the bot
- Review
DATABASE_SSL_VALIDATEif you are connecting to a managed PostgreSQL service
bun run lint
bun run type-check
bun run build
bun run test
bun run test:coverage
bun run test:integration
bun run cmd:deploy
bun run cmd:reset
bun run docker:build
bun run docker:run
bun run sbom:generate- Start with the GitHub Discussions help category
- Search existing issues
- Read the security policy for private security reports
Pull requests are welcome. Please target the dev branch.
- Read the contributing guide
- Follow the code of conduct
Like this project? Leave a star and consider supporting the maintainer:
Licensed under the GNU Affero General Public License v3.0.
Created by Waren Gonzaga under WG Technology Labs, with help from contributors.
💻 with ❤️ by Waren Gonzaga under WG Technology Labs, and Him 🙏
