>set display
PHOSPHOR
MODE
BOOT SCREEN
SOUND
← BACK TO PROJECTS
FOUNDER · SAAS · 2021 → NOW

Ticketeer

Ticket-based support for Discord communities. A dashboard, a bot, and five years of finding out what breaks when a few thousand servers depend on you.

Backend
Laravel 12 on PHP 8.3, Octane via Swoole
Data
PostgreSQL, Redis for cache and queues
Front end
Vue 3 + TypeScript, Hybridly, Tailwind 4
Bot
Node 20 + TypeScript, discord.js
Scale
4,200+ communities · 3.4M+ users · 337K+ tickets
Rating
4.31 / 5 on top.gg
The ticketeer.bot homepage

Every Discord community over a certain size ends up doing support in DMs, which is a terrible place to do support. Nobody else on staff can see it, there's no record, and the moment whoever was handling it goes to bed the thread dies.

Ticketeer turns that into tickets. Someone clicks a button on a panel, they get a channel or a thread, the staff who should see it can see it, and when it closes there's a transcript nobody had to screenshot.

That part is simple enough. What it's actually been is five years of learning that the ticket isn't the product, everything around the ticket is.

Three processes and a bus

It runs as three separate things. The Laravel app serves the dashboard and owns the database. A separate Node process runs the discord.js bot and holds the gateway connection and every shard. Cloudflare Workers sit out front for the CDN and for proxying inbound webhooks.

Redis is the bus between the web app and the bot. That split is the most important structural thing in the project and it wasn't really a decision, it was forced. A Discord gateway connection is long lived and stateful, an HTTP app is neither, and making one process be both means your bot drops offline every time you deploy the website.

Around that: Horizon for the queues, Reverb for WebSocket broadcasting to open dashboards, Pulse for monitoring, Backblaze B2 for file storage, Sentry on both runtimes, Stripe for subscriptions.

Transcripts are the feature people stay for

The transcript renders Discord flavored markdown properly. Not close enough, properly, including the parts nobody thinks about until they're wrong. Nested blockquotes, spoilers, custom emoji, mention resolution for users and roles and channels that might not exist any more, code fences with the right language, and reply chains that point at the right message.

Attachments come along too, up to 25 MB for images and 10 MB for text files, instead of leaving a transcript full of Discord CDN links that expire.

It sounds like a small thing and it's the most common reason people upgrade. When a moderation decision gets appealed six months later the transcript is the only thing anyone has, and it has to be trustworthy enough to settle an argument.

Configurability is the product

The thing I underestimated at the start is how differently every community wants this to work. A game studio's support server and a 200 person friend group want almost nothing in common, and neither of them will accept "that's not how it works."

So most of the surface area is configuration:

PieceWhat it does
Ticket groupsIndependent settings, permissions and log channels per group
PanelsButtons and dropdowns that open tickets, laid out how you want
FormsQuestions asked at open time, answers attached to the ticket
Custom messagesEvery embed, welcome, close, events, fully editable
PermissionsRole-based per group, with an optional strict mode
NotesPrivate threads for staff to talk without the requester seeing
ExpirationAuto-close or delete inactive tickets on your own timers
Priority, bulk actions, logsThe unglamorous operational half

Messages support template variables with conditional logic, so a close message can say something different depending on how the ticket ended. Since April 2026 that includes referencing individual form answers directly with {$ticket.form.0.value}, which people had been asking for by building increasingly cursed workarounds.

2026 was the big year

March was the biggest release the project has had, and it was mostly things that had been sat on the list for two years:

There's also a Discord Activity, a small dashboard that runs inside Discord itself, so configuring the bot doesn't mean alt tabbing to a browser and logging in.

Billing, and the free tier problem

Premium is Stripe subscriptions plus standalone license keys. The license keys exist because a lot of Discord server owners are teenagers without a credit card, and only taking recurring card payments quietly cuts all of them out.

There's also a voting path, vote for the bot on top.gg and earn premium keys. It costs nothing to give away, it moves the bot up a ranking that actually drives installs, and it gives people who genuinely can't pay a way to get the paid features. That trade has been worth it every year.

What five years actually taught me

Most of the engineering that matters in a small SaaS isn't in the feature list. It's the ticket that got created twice because Discord delivered the interaction twice. It's the guild that removed a role your permission model assumed was there. It's the transcript job surviving an attachment host being down. It's working out that a migration on a table with 337,000 rows and live traffic needs a plan, and that you're the one who has to be awake for it.

The other half is that people will use every configuration combination you allow, including the ones you were sure nobody would want. If a setting can be set to something stupid then it's set to that in production somewhere right now, and that server's owner is going to open a ticket about it.

Five years in production and still growing, which after a while stops feeling like luck.