Product Management Engineering Product Design

Attendance Management System

Every Friday, over 100 workers across 14 service units come for prayers and meetings. Every week, team leaders took attendance manually. I built a Telegram bot that handles the entire thing automatically. Nobody takes a screenshot anymore.

Role
Solo. Product Management, Product Design, Engineering
Stack
Python, python-telegram-bot, Google Sheets, Google Apps Script, React, Vite, Railway, Vercel
Status
Users
100+ workers, 14 service units
Context
Built as team leader of a church service unit

Workers tap one button. The bot handles registration, reminders, closing, and reporting automatically.

I was one of those team leaders. Every week I took attendance manually, screenshots of who was present, then going to Google Sheets to mark each person. It was not hard. It was just a weekly tax on my time that added nothing.

The problem was not complicated: people already had Telegram open. The meeting was already happening in a group. All I needed was a button. So I built one.

The problem is not unique to this church. Any team leader responsible for tracking attendance in a recurring group setting — a community organisation, a volunteer unit, a weekly class — faces the same manual overhead. The system is built for CCI Akure but the problem it solves is everywhere.

100+
Workers tracked
14
Service units
0
Manual steps for workers
52
Fridays automated per year

Who it is built for

Two very different users, one system

The product serves two distinct users with completely different needs. Getting this right meant designing two separate experiences that share the same underlying data.

The worker
Oluwatobi, 24
Ambience unit
Uses Telegram every day. Not particularly technical. Comes to Friday prayers every week and just wants to mark attendance without fuss.
What she needs
One tap. No learning curve. A way to check her own record without asking anyone. Know how she ranks among her peers.
The leader
Pastor Emeka, 38
Church coordinator
Oversees all 14 service units. Used to chasing unit heads for attendance records. No single view of all units. Cannot see trends over time.
What he needs
Instant report on demand. Per-unit breakdown. Dashboard the whole leadership team can see. Ability to open attendance manually when meetings run late.

Success metrics

Defined before building

These were written into the PRD before a single line of code. Each one points to a real behaviour, not just a number.

Registration
80% of workers registered within 2 weeks
If workers do not register, nothing else works. This was the first gate.
Participation
70%+ check in each Friday
The whole point of the system is accurate records. Below 70% and the data is not trustworthy.
Speed
Under 10 seconds per worker
If checking in feels slow, people stop doing it. The tap-to-confirm flow had to be instant.
Data integrity
0% duplicate check-ins
A double-tap should never create two records. This was a hard requirement from day one.

System architecture

How the four layers connect

The system has four distinct layers. The Telegram bot is the only thing workers ever touch. Google Sheets is the source of truth that any coordinator can open and read directly. Apps Script turns that data into a JSON feed for the dashboard. And the React dashboard gives leadership a visual view of everything.

Workers and leaders interact via Telegram. Data flows through Sheets to the dashboard. Workers100+ members Leaders14 unit heads Telegram botPython, RailwayCheck-in, remindersReports, /mystats Google SheetsSource of truthWorkers_RegistryAttendance_Log, Teams Apps Script APIGoogle Apps ScriptJSON API, 5-min cache React dashboardVite, VercelLeaderboards, stats, trends Anyonepresent.withprensa.com reads

Four-layer architecture: Telegram bot handles all interactions, Google Sheets is the source of truth, Apps Script serves data to the dashboard.

User flow

Two paths, one outcome

Whether a worker has registered before or not, they end up in the same place: attendance marked. An unregistered worker who taps the button gets taken through a quick setup in DM and then marked present automatically. No worker misses attendance just because they had not registered yet.

Both paths end in the same place. No worker misses attendance because they had not registered yet. Worker taps button in groupFriday attendance open Registered? No Yes Alert in groupCheck DM prompted Registration in DMSelect unitType full name Auto-marked presentConfirmed in DM Marked presentUnder 2 seconds Private DM sentName, unit, time Group count updatesLive in the group Attendance recordedSaved to Google Sheets

Registered workers check in instantly. Unregistered workers go through a one-time setup in DM, then get marked present automatically.

Attendance lifecycle

The Friday timeline

The entire Friday attendance cycle runs without anyone doing anything. The bot opens attendance, sends reminders, and closes the window — all automatically and all relative to when attendance opened, not a fixed clock time.

Every Friday, automatically. No leader has to remember anything. Attendance opens7:35pm WATButton posted to group Reminder DMs sent7:45pm (+10 min) +10 min Attendance closes7:55pm (+20 min)Group message updated +10 min Timing is relative, not fixed. If a leader opens attendance manually at 9pm,reminders fire at 9:10pm and attendance closes at 9:20pm.

The entire lifecycle runs automatically. Leaders only need to intervene if the meeting runs late.

How it works

Six things that happen automatically

01
Automatic opening
Every Friday at 7:35pm the bot posts the attendance button to the group. No leader has to remember to do anything.
02
One tap check-in
Workers tap the button once. The bot confirms privately via DM with their name, unit, and time. The group message updates with the running count.
03
Reminder DMs
Ten minutes after opening, anyone who has not checked in gets a private reminder. Gentle, automatic, not a broadcast to the whole group.
04
Automatic close
Twenty minutes after opening, attendance closes. The timing is relative, so if a leader opens it manually at 9pm, everything still fires at the right intervals.
05
Reports on demand
Leaders type /report in a DM and get the full attendance breakdown by service unit with check-in times. Workers use /mystats to see their own record and streak.
06
Public dashboard
A web dashboard at present.withprensa.com shows unit leaderboards, member stats, and attendance trends. Google Apps Script serves the data with a 5-minute cache. Three fallback proxies ensure the dashboard loads across all environments.

The public dashboard shows unit leaderboards, personal attendance records, and trends across all time periods.

Design decisions

The choices that shaped the product

Each decision came from understanding the context deeply enough to know what the right call was.

Decision
Alternative
Reasoning
Relative timing
Fixed clock times
A fixed close time would shut attendance before it opened if a meeting ran late. Relative timing means the window always fires correctly from whenever it opens.
Google Sheets as database
PostgreSQL or Firebase
Leadership can open the sheet and read the raw data without needing to understand any system. Familiar tools working harder. The Apps Script layer turns it into a clean API for the dashboard.
In-memory double-click prevention
Database check only
A database check takes time. The in-memory lock fires instantly, giving the user immediate feedback. The database check stays as a secondary safeguard. Speed first, safety always.
Telegram as the interface
A dedicated mobile app
Workers already use Telegram daily for church communication. No app download, no new account, no learning curve. One tap in an existing tool beats a better app nobody opens.
Last-Friday auto-skip
Manual override each month
The last Friday of every month is night vigil, no regular meeting. Without this, the bot would post an attendance button to an empty room every month. You have to understand the context to build for it.
Reports sent to DM only
Posted in the group
Attendance data is sensitive. Individual records should not appear in a group of 100+ people. Leaders get full reports privately. Workers see only their own stats.
Dashboard built mobile-first
Desktop-first layout
Workers check their stats and leaders pull reports on their phones, not at a desk. The dashboard was designed for a small screen first and scaled up, not the other way round. Bottom navigation on mobile, sidebar on desktop.

Retention design

Making consistency feel worth it

Attendance systems usually feel like surveillance. This one needed to feel like something workers actually wanted to engage with. The answer was /mystats, a private command that shows each worker their own record, streak, and any badges they have earned.

Badges are not decorative. Each one represents a real pattern of behaviour worth recognising.

Perfect attendance
Has not missed a single meeting. The hardest badge to hold.
80% club
Attendance rate at or above 80%. The marker of consistent commitment.
Streak milestones
Current streak tracked alongside personal best. Keeps people going without breaking the chain.
Consistency
Long-term reliable attendance even without a perfect streak. Rewards the pattern, not just the number.

Known limitations

What the system cannot do yet

Good product work means being honest about constraints. These are documented and known before anyone encounters them.

01
First-time DM restriction. Telegram does not allow bots to DM users who have never messaged the bot first. Workers must send /start once before their first Friday or they cannot receive confirmations and reminders.
02
In-memory state resets on restart. If the bot restarts during an active attendance window, the live group count stops updating. The database records stay intact but the running count will not reflect new check-ins until the next session.
03
Dashboard data lags by up to 5 minutes. The Apps Script API caches responses to stay within Google quota limits. A worker who just checked in may not see their name on the dashboard immediately.

What comes next

What is still to build

The core system is solid and running. These are the things worth building next, in order of impact.

01
Absence notifications
Automatic alerts to unit heads when their team's attendance falls below a threshold. Right now leaders have to check the dashboard. This would surface the problem without requiring anyone to look.
02
Admin web panel
A simple interface for managing workers and units without touching the Google Sheet directly. The sheet works but it is not a great experience for non-technical coordinators.
03
WhatsApp integration
Some units communicate primarily on WhatsApp. Expanding check-in to WhatsApp Business API would remove the only remaining friction for workers who are not on Telegram.
04
Annual attendance reports
PDF export of full-year attendance history per worker and per unit. Useful for leadership reviews and recognition decisions.

What I learned

Building for people you know

01
Understanding the context is the real design work. The last-Friday auto-skip, the relative timing, the DM-only reports — none of these came from user research. They came from being in the room every week and knowing how the meeting actually runs. You cannot design for a context you do not understand.
02
Meet people where they already are. Building inside Telegram meant zero onboarding friction. Workers were already in the group. One tap and they were done. A dedicated app would have had better UI and worse adoption.
03
Familiar tools can do more than people think. Google Sheets as a database feels like a workaround. In practice it gave leadership instant visibility into raw data, made the system auditable without any extra tooling, and reduced the bus factor to zero. The right tool is the one people will actually open.
04
Attendance systems feel like surveillance unless you design against it. The badges and /mystats command were not features added for fun. They were a deliberate attempt to make the system feel like something workers owned rather than something done to them. That distinction matters for adoption.
05
Speed is a design decision. The in-memory double-click prevention exists because a database check takes long enough to feel slow. Instant feedback is not a nice-to-have when the entire interaction is a single tap. The system needed to feel immediate or people would tap twice every time.
View the live dashboard
Next project
Building Teams & Systems from Zero
View project →