Work/MarathoMN
Case 01 / 04 · 2026
Fig 01Public calendar — bilingual, terrain-coded · live
Fig 02Member shell — the accounts layer over the calendar
Fig 03Review queue — the approval gate before the calendar01Public calendar · the whole 2026 season at a glance.
02Signed-in app · calendar, communities, notifications.
03Council review · proposals awaiting publish.
Role
Strategy, Product, Engineering
Stack
Next.js · TypeScript · Notion · Resend · Vercel Blob · Vercel
Mongolia's marathon season on one bilingual calendar — running clubs propose their own races into a council-reviewed pipeline that publishes to a shared calendar and emails every runner.
Challenge
A runner in Ulaanbaatar planning a 2026 season has no single place to look. The races exist — thirty-odd of them, road and trail and steppe ultra, spread from June to November — but the schedule lives in a dozen club Facebook pages, a few poster JPGs, and word of mouth. The closest thing to a portal, marathon.mn, reads like a government notice board. Nobody owns the calendar, so nobody can trust it.
The deeper problem is who keeps it current. A schedule maintained by one editor goes stale the week they get busy. A schedule anyone can edit becomes spam. MarathoMN had to be curated and open at the same time — authoritative enough that a runner plans around it, yet fed by the clubs who actually run the races.
Approach
We started where the trust is: the calendar. A single bilingual season view — Mongolian first, English a toggle away — that shows the whole year at a glance. Desktop gets a wall-calendar grid; a phone gets an agenda list. The real data shapes the edges: June 13 collides three races into one day, the Mongol Gobi ultra spans a week as a single bar, October is empty and says so. Each race is colour-coded by terrain and links straight out to the club's own registration.
Then we built the part that keeps it alive. Runners sign in by email — a six-digit code, no password — and the calendar grows a second layer: communities. Any runner can start a club page or join one; inside, members post, and the page's creator can see their roster and promote moderators. Crucially, a club admin can't touch the public calendar directly. They propose a race, and it enters a queue.
The spine
The whole product turns on one path. A member posts in their club. A club admin turns an idea into a proposal — name, distances, terrain, a cover image. That proposal lands in the council's review queue, never on the public calendar. The council approves it, and three things happen at once: the race goes live on the shared calendar, every opted-in runner gets an email, and an in-app notification fires. One approval, one source of truth, everyone informed. That sequence — propose, review, publish, notify — is the case for the whole thing existing.
Outcome
- The 2026 season — road, trail, mountain, steppe, track, and a multi-day Gobi ultra — readable in one glance, in two languages.
- A three-role model — council, club admin, member — with a real approval gate between a proposal and the public calendar.
- Passwordless sign-in, club communities with post feeds and rosters, and a council review queue, all live.
- Email on publish to opted-in runners, sent from our own subscriber list rather than a rented newsletter — so the email looks exactly like the site.
- Live at marathomn.run, on its own domain, as the studio's proof that a community platform can ship on a lean backend.
Stack reasoning
Notion is the database — on purpose. The council edits races in a tool they already understand, and every other table (users, communities, memberships, posts, proposals, notifications) sits behind a thin lib/store seam so a table can move to Postgres later without a single screen changing. Auth is passwordless and stateless: a six-digit code over Resend, then an HMAC-signed httpOnly cookie is the session — no store to keep, no code table to expire. Images go to Vercel Blob, because Notion's own file URLs are roughly one-hour signed links and useless as a persistent src. The whole thing is Next.js App Router on Vercel, bilingual from the first line.
What we would do differently
Notion is a fast, honest start and a deliberate ceiling — it rate-limits writes and isn't built for a busy social feed. The seam is already there; the move is to migrate the social tables (posts, memberships, notifications) to Postgres while leaving the council's race-editing in Notion, where the human workflow actually belongs. The second thing is reach: the email list is ours, which is the right call, but the next milestone is a digest — a weekly "what's coming up" rather than one mail per race.


