Inspiration

We go to a lot of events. We enjoy them, we mean to follow up, and then we get home with two business cards and a blurred photo of a badge. The people we actually wanted to talk to are the ones we never got round to.

The tools that exist all solve this for one person at a time. NameDrop swaps one phone at a time and keeps no record. Linktree gives one person a page, not a group a list. Swapcard does the whole room, but it's built and priced for large ticketed conferences, not eighty people in a room above a pub.

Nobody had covered the room itself. So that's what Liinki does: your event, one shared contact list.

What it does

An organiser creates a list and shares one code. Everyone in the room scans it, picks which of their own details that list gets to see, and joins. Nobody has to swap details phone by phone, and nobody gets missed. The list stays after the event, so you can join later from home if the venue wifi gives up.

  • Three kinds of list. Public lists are a directory anyone can browse. Shared lists let people who join see each other. Collect lists send details to the organiser alone.
  • A card for every room. You save your details as named cards (one for work, one for a side project, one for your sports club) and choose which one each list sees.
  • A link in bio that stays. Your free liinki.co name works as a page and inside your email signature long after the event.

Joining is free for everyone in the room, always. Organisers who run lists again and again pay for the plan, through RevenueCat in the app and Stripe on the web.

How we built it

  • Mobile: Kotlin Multiplatform with Compose Multiplatform. Android and iOS render the same App() composable from commonMain. The iOS host is a thin SwiftUI TabView with one Compose view controller per tab.
  • Purchases: RevenueCat's KMP SDK. The paywall's layout, copy and prices come from the RevenueCat dashboard. The app never asks the SDK whether you're Pro; it asks our server, which hears from RevenueCat by webhook. A device can claim anything, and the endpoints that hand over other people's contact details are not the place to take a client's word for it.
  • Web and API: Next.js on Cloudflare Workers, Neon Postgres with Drizzle, and Stripe for web checkout. liinki.co serves short join links like liinki.co/l/CODE, short enough to read out loud in a room.
  • One design system: colours, spacing and type are defined once and used by both the website and the app.

Challenges we ran into

  • The web app had no API. It was almost entirely Next.js server actions with cookie-based auth, which a native client can't call. Before the phone could do anything beyond a paywall demo, we had to build a JSON API and a token-based sign-in path.
  • Kotlin/Native klibs aren't forward compatible. A library built with a newer Kotlin than ours fails the iOS link step with an error naming a file that exists on disk. Android builds fine either way, so it only shows up on iOS.
  • RevenueCat on iOS needs more than the Kotlin framework. The native code comes from PurchasesHybridCommon via Swift Package Manager, pinned to match the KMP version. iOS went from a paywall that couldn't complete a purchase to one that does, and the fix was one line.
  • App Review. Apple's first reply was a Guideline 2.1 information request. Our subscriptions were configured but never attached to the version, so the reviewer had nothing to buy.
  • A release that didn't open. R8 stripped a class WorkManager creates by name at start-up. Debug builds aren't minified, so every device test missed it. Two keep rules fixed it, and now we launch the release build before every upload.

What we learned

  • Turn the free plan's limits on the day a purchase can complete, not before. A limit nobody can lift is a wall, not a paywall.
  • Let the server decide entitlements, not the device.
  • Sharing UI across platforms paid off. Most of our bugs lived in platform bindings, not in the shared code. JetBrains Junie's review of the shared Kotlin found as much in the bindings as in the shared module.

What's next

A meetup pilot with real organisers, and putting Liinki on the table at more events.

Built With

Share this project:

Updates

Submission history