Inspiration

Every corporate event lives or dies on one hire: the keynote speaker or the MC. And yet booking that person is still done the way it was done in 2005 — a chain of email intros through a speaker bureau that takes 20–30 % of the fee, week-long back-and-forth on availability, PDF contracts, and a separate scramble for technical requirements and travel logistics. Speakers hate it too: the bureau owns the client relationship, payouts are slow, and a new voice with no bureau has almost no way in.

We kept meeting brilliant speakers — especially outside the US/UK circuit — who could not get discovered, and event organizers who were overpaying for a shortlist of five names a bureau happened to represent. The gap between those two groups is a classic marketplace problem hiding behind an industry that never got its "Uber moment." We wanted to build the product that collapses find → agree → pay into a single evening, takes a fair cut instead of a punitive one, and treats an unrepresented speaker in Lagos or Manila exactly like a represented one in New York.

The idea for YapElite, and its core product features, were conceptualised by Loveness Nleya, a speaker and a member of my team on this project.

What it does

YapElite is a two-sided mobile marketplace for elite speakers, MCs and facilitators.

For organizers:

  • Discovery grid — a Bento-style browse of verified speakers with "Vibe Tags" (HighEnergy, Technical, Inspirational, Storytelling, Leadership…), price tiers, and a Sizzle Reel (their talk video).
  • One-Tap Booking — send a direct offer; the speaker accepts or declines from their dashboard. Confirm, and a 30 % deposit is captured into escrow.
  • Dais — an RFP board: post a brief, collect competitive bids, pick one.
  • Booking-scoped chat — realtime SignalR chat per booking, with read receipts and attachments.

For speakers (the Green Room):

  • Accept/decline offers in one tap, manage a real-time availability calendar, publish price tiers ("Rostrum"), and track past engagements ("Plinth").
  • Sessionize import — pull your existing speaker profile and session history straight from Sessionize so onboarding is 30 seconds, not 30 minutes.
  • Event Radar — a curated feed of conferences with open calls for speakers, so you always have somewhere to pitch.
  • Performance analytics: response rate, acceptance rate, repeat-booking rate, earnings.

Payments. Real-world speaking fees settle through Stripe Connect (organizer → platform escrow → speaker payout, milestone-based: 30 % deposit on confirm, 70 % on completion). This is a real-world service, so it correctly sits outside app-store IAP.

Monetization (RevenueCat). Everything digital is a RevenueCat-powered purchase: YapElite Pro for Speakers and YapElite Pro for Organizers subscriptions, plus one-off "Featured Boost" and "Priority RFP" consumables.

How we built it

Backend — layered modular monolith.

  • .NET 10 + ABP Framework 10.3 using strict Domain-Driven Design. Aggregates: Speaker, Booking (a formal state machine: PendingOffer → Accepted → Confirmed → InProgress → Completed, with Declined / Cancelled / Disputed branches), Rfp/Bid, EscrowPayment, ChatRoom, BookingLogistics.
  • PostgreSQL via EF Core; Redis for cache and SignalR backplane.
  • ABP Identity + OpenIddict for standards-compliant OAuth2/OIDC — the Flutter app authenticates with the RFC-6749 password grant against /connect/token.
  • SignalR for realtime chat and presence.
  • AI services (backend only for now): OpenAI Whisper for Sizzle Reel transcription, text-embedding-3-small for matchmaking, and Chat Completions for "why this speaker" briefs, each with a rule-based fallback when no API key is present. The app does not surface these yet; the screens are on the roadmap.
  • Stripe.NET for Stripe Connect escrow; a dev fallback keeps the flow testable without live keys.
  • Deployed on Azure Container Apps (managed identity → ACR + Key Vault, no long-lived secrets), Postgres Flexible Server, custom domain api.yapelite.com, Sentry for errors, OpenTelemetry → Azure Monitor. CI/CD is GitHub Actions with OIDC federation (5-job matrix: backend tests, Flutter analyze/test, iOS release, Android APK+AAB, release binaries).

Frontend — Flutter.

  • Riverpod (codegen) + go_router typed routes; freezed models; dio HTTP; signalr_netcore for chat.
  • Design system in code: "Elite Simplicity / Quiet Luxury" — Obsidian #0D0D0D / Midnight #0A1628 grounds, a single Electric Teal #00E5C8 accent, Inter/Lexend, 8px radius everywhere, Bento-box grids. Dark mode primary, full light-mode variant, persisted toggle.
  • flutter_stripe for the escrow payment sheet, RevenueCat purchases_flutter for the subscription paywall and consumables.
  • 100+ Flutter tests, 250+ backend xUnit tests, and a Maestro end-to-end suite covering registration, discovery, booking, RFP, chat and logout.

Practices. Strict TDD (every AppService has a mocked-repository xUnit test, every UI component has a widget test), a documented state ledger + handover per session, and a "3-Tap Rule": every primary goal — find, chat, book — completes in ≤ 3 taps.

Challenges we ran into

  • Two payment rails, one product. Real speaking fees must use Stripe (real-world service, exempt from IAP); digital Pro features must use store IAP via RevenueCat. Designing entitlements so the two never collide — and so a Pro speaker's lower platform take-rate is enforced server-side from the RevenueCat entitlement, not the client — took real thought.
  • ABP's global antiforgery filter silently rejected our public web account-deletion page's fetch() calls, because ABP sets an XSRF-TOKEN cookie on every response and then demands a matching token no static page can produce. curl never caught it (no cookie jar); a real browser did. Fixed via AbpAntiForgeryOptions.AutoValidateFilter.
  • The system back button backgrounded the whole app instead of running our PopScope callbacks — a known go_router bug fixed in 14.6.1; we were pinned to 13.2.0.
  • The entire custom-permission system rejected every policy-gated action for every user, always, since our ABP Identity migration — a missing AbpPermissionManagementDomainIdentityModule dependency that the xUnit suite (which fakes the current user) structurally could not catch. Only live Maestro testing surfaced it.
  • flutter test's default 800×600 viewport is wide enough to hide layout overflow bugs that every real phone shows — we now write widget tests at a real device size.
  • A shared-by-name local Postgres container got recreated by a sibling project mid-build and swapped our database out from under us.

Accomplishments that we're proud of

  • A complete, tested booking lifecycle — offer, escrow deposit, confirmation, auto-generated event documents, completion payout — not a mockup.
  • A design system disciplined enough that the landing site, the app, and the account-deletion page share one token map — and a brand ("Quiet Luxury") that actually reads as premium instead of loud.
  • Production-grade ops for a hackathon app: OIDC-only CI/CD, managed-identity secrets, custom domain + managed TLS, crash reporting, a first fully-green 5-job pipeline.
  • Google Play account-deletion & data-retention compliance shipped before it was urgent.

What we learned

  • Marketplaces are a trust-engineering problem, not a CRUD problem. Verified badges, escrow milestones, dispute states and transparent take-rates are the product; the list views are just the wrapper.
  • You cannot verify a cookie-dependent bug with curl. Test with the transport your users actually use.
  • RevenueCat entitlements are a clean server-side authority. Letting the backend read entitlement state (via webhook + REST) to set a speaker's take-rate turned "premium tier" from a UI flag into a real business rule.
  • Ship the compliance and the paywall early. Store review is the long pole; everything else can iterate after launch.

What's next for YapElite

  • AI matchmaking briefs — describe the event in a sentence and get ranked speakers with a "why this speaker" explanation. The matching engine exists on the backend; the in-app experience is next.
  • Auto-generated booking logistics — tech and hospitality requirements and a travel itinerary scaffolded when a booking is confirmed. The backend creates them today; the screens to view and edit them are next.
  • iCal sync for the speaker availability calendar.
  • Rehearsal Room — record a practice answer to a mock organizer question; Whisper transcribes; the LLM scores clarity, pace and filler words and gives feedback before the real stage.
  • Bureau mode — white-label multi-tenant workspaces so existing speaker bureaus run their whole roster on YapElite.
  • Web-to-app funnel (RevenueCat Funnels + Stripe) so an organizer can start a Pro checkout from a Google result and land in the app already subscribed.
  • Deeper Event Radar — auto-match a speaker's Vibe Tags to open CFPs and push "you'd be a fit for this" via OneSignal.
  • Payout rails beyond Stripe for emerging-market speakers (local payout partners).

Built With

  • abp-framework
  • azure-container-apps
  • csharp
  • dart
  • dio
  • docker
  • dotnet-10
  • flutter
  • freezed
  • github-actions
  • go-router
  • onesignal
  • openai-embeddings
  • openai-whisper
  • openiddict
  • postgresql
  • redis
  • revenuecat
  • riverpod
  • signalr
  • stripe
  • stripe-connect
Share this project:

Updates

Submission history