Apogee: How We Built Our Open Source Slack Bot for Karma

Artificial intelligence-generated image of a desk with a MacBook and a screen to illustrate the post How we built our open source Slack bot to give Orbitant karma.

Building your own tool when you spot a need is incredibly satisfying. Unlike day-to-day client work, there are usually no ambiguous requirements, no endless meetings, no never-ending debates about button colors. It’s just you facing a concrete problem, with your code editor and the stack you know how to solve it.

That’s how Apogee was born: we wanted a way to publicly recognize our team members’ work. I’m not here to tell you about the most complex or technically innovative project we’ve built, but rather the one that represents who we are and gives us the most satisfaction (and gossip) on a daily basis. A simple, pragmatic development whose code is available in our GitHub repo for anyone who wants to use it.

What Apogee Does and Why It Exists

The name Apogee refers to the highest point in an orbit, the farthest from the gravitational center. That’s why the bot talks about “meters” (orbital altitude meters): every time someone receives karma, they literally rise in their orbit. When they lose karma, they descend. We like the space metaphor and it fits our identity.

The mechanics are simple: you mention a colleague in Slack, add ++ signs to give them karma or — to subtract it, and the bot publicly registers the transaction. The more signs, the more points you move:

@carlos ++ Thanks for fixing the deploy I broke on Friday

The bot automatically confirms:

@carlos's apogee increased by 1 meters for a new value of 36.

We have an exclusive channel called #thnx and the bot only works there. This isn’t a technical restriction but a deliberate design choice: the goal is to make recognition visible to the entire organization. When someone unblocks a task that had half the team stuck, when another person contributes their knowledge, when you receive that unexpected and helpful tip… all those moments—and many more—deserve public acknowledgment.

As someone who’s been part of distributed teams for over ten years, I know that remote work can create a visibility problem: many valuable contributions happen in private conversations or threads, in one-on-one calls, in moments when the entire organization isn’t present. Apogee makes that recognition not only public and explicit but also permanent: not just in the channel history but in the data stored by the application.

We’ve long seen that well-implemented gamification reinforces positive behaviors. Though we’re also aware it can carry a risk of toxic competitiveness and vanity metrics. That’s why Apogee’s recognition isn’t tied to money or evaluations: karma doesn’t affect bonuses or performance reviews. The moment you introduce material incentives, people game the system instead of using it genuinely. This guide on development team gamification is really useful.

Next.js and Vercel as Technical Stack Pillars

When designing this open source Slack bot for internal use, we had two conditions to meet: minimal cost and fast implementation. That led us to seek tools offering free features suited to our needs and to discard elaborate architectures.

Next.js as Framework 

Next.js lets you write frontend and server logic in the same project. Server Actions eliminate the need to set up REST APIs for simple operations: you define a function, mark it as a server action, and you can call it from the client. For a bot that basically receives webhooks and writes to a database, it’s the perfect balance between simplicity and power.

Vercel for Deployment 

We chose Vercel as infrastructure for its native integration with Next.js. Every push to main automatically deploys to production, every push to staging creates an independent testing environment. For projects that need to iterate quickly, it eliminates all the friction of traditional CI/CD. Serverless has known limitations (timeouts, cold starts), but for this specific use case it was sufficient.

Turso as Database

Turso gives us PostgreSQL-compatible with a very generous free plan. Plus, it includes a decent web interface to query data without needing to install heavy clients. The data model is intentionally simple: a transaction table that records each operation with the complete message, sender, receiver, karma amount, timestamp, and accumulated total. It works perfectly and allows all the queries we need.

Slack API Integration

The integration with Slack API is where the interesting work lies. We configured the bot through event subscriptions: every time someone writes a message in #thnx, Slack sends a POST to our endpoint with all the details.

There are two critical aspects to this integration:

  • Authenticity validation: We can’t trust that every POST actually comes from Slack. Anyone with our endpoint URL could try to send fake data. Slack signs each request with HMAC using a signing secret only we know. We validate that signature before processing any message.
  • Immediate response: Slack expects a 200 OK in less than 3 seconds. If it doesn’t receive it, it assumes the event wasn’t delivered and resends it, creating potential infinite loops. In Vercel we use waitUntil o separate the HTTP response from the actual processing:

javascript

waitUntil(processMessage(body));

return new Response("OK", { status: 200 });

We return the OK immediately, but the serverless function continues executing the processing in the background.

Validation Logic: What We Allow and What We Don’t

Message processing follows a clear flow. First, a regular expression extracts user mentions and karma symbols. Then we apply validations:

  • Trying to give yourself karma? Rejected. 

`@carlos you can't give meters to yourself. Apogee is watching you!.`

  • Trying to remove karma from yourself? Also rejected.

`@carlos you can't take away meters from yourself. Apogee is watching you!.`

  • Have you moved more than 15 points (positive or negative) in the last 2 weeks? Limit reached.

`@carlos you have reached the meters' limit. Being too generous or mean lately?`

That 2-week rolling window prevents excessively generous or overly vengeful users from disproportionately distorting scores. We want karma to reflect a sustained pattern of contributions, not punctual explosions of generosity or revenge—though we have almost no examples of the latter.

The limit to the #thnx channel reinforces two architectural principles:

  • Transparency: everyone sees who recognizes whom
  • Equity: the goal is to prevent teams with more established internal dynamics from accumulating advantages simply by being on a project with more recognition ceremonies

Roadmap: What’s Coming

Previous versions of this bot that we had at other companies included features we plan to recover:

Query commands to interrogate the bot through Slack slash commands and get rankings, a specific user’s position, or general statistics. Right now there’s no query interface beyond getting directly into the database.

Karma to entire teams: mention a user group or team (@marketing ++) and have the bot distribute karma among all its members. Useful for recognizing collaborative work where individualizing is complicated or unfair.

LLM-generated weekly summary: an automated message that tells what happened during the week in an informal tone. “This week Carlos has been the most generous, Victor seems to be coming in with a controversial mood, and team X has received karma from practically the entire company.” That kind of narrative that makes data more digestible and entertaining.

We’re also considering making installation easier for other organizations, publishing it in the Slack App Directory with a simplified configuration flow.

Open Source on GitHub

All of Apogee’s code is available in our GitHub repository. The repository includes:

  • Complete Slack API configuration instructions
  • Database schema with Turso
  • Required environment variables
  • All processing, validation, and storage logic

If you want to implement a similar open source Slack bot in your organization, everything you need is documented. This initial MVP has been developed during a solo coding session in paring with AI on a cold winter night, surely has many things that can be improved. If you find ways to enhance the implementation or add features, pull requests are welcome.

Building your own tools to solve internal problems has value that goes beyond the resulting code. It demonstrates execution capacity, mastery of the technical stack, and willingness to create solutions when it makes sense rather than always depending on third parties. Apogee is an example of that: we identified a need, chose the most pragmatic stack, implemented clean and functional, and shared it openly. That’s how we work.

Follow our journey building AI agents from the inside