skip to content
Steel Ferguson

← Back to home

Basin Climbing — Member App

A cross-platform mobile app I designed, built, and shipped end-to-end for Basin Climbing & Fitness, a climbing gym in Waco, TX. From zero to live on the App Store in about five weeks, including the backend, AI chatbot integration, and rewards/redemption system used by real customers and staff.

Basin logo

The problem

Basin has ~14,000 customers, ~500 active members, and a small crew that wears every hat. The existing member experience lived across three places: a check-in barcode locked behind the gym-management system, a Shopify storefront for memberships, and email/SMS marketing. There was no single surface that a member could open on their phone to:

I built that surface.


What it does

Sign in without a password

A one-time code goes to phone (SMS via Twilio) or email (via SendGrid). The system looks the customer up in the gym’s data and signs them in. No passwords, no second factor, no friction at the front desk.

Sign in

Multi-member households

A single sign-in surfaces every person in the family. A parent can swipe through their kids’ and partner’s check-in barcodes and rewards as easily as their own. Family links come from the gym’s relationship data, scoped to high-confidence parent-child records — false positives (a friend who once booked a class for someone else’s kid) were a real bug I had to design around.

Home screen

Rewards system with QR redemption

Each family member’s earned rewards and in-progress milestones appear in a swipeable per-member carousel. Each card has its own QR code that opens the staff CRM filtered to that customer. Confetti fires the first time a new reward appears on the screen. Expiring-soon warnings light up in red 7 days before a reward expires.

Rewards

AI assistant (“Cliff”) for member questions

The app embeds a chat surface that talks to a separate AI agent service over an authenticated proxy. The agent knows who the member is (member ID passed via shared-key headers) so it can answer “how many times have I been here?” or “when does my membership renew?” without the member ever having to repeat themselves. Auth flows through once — they never sign in twice.

Chat

Account deletion built into the app

Per Apple’s App Store Guideline 5.1.1(v), every app with account creation must also offer in-app account deletion. The flow walks the user through what gets removed (sessions, push token, feedback contact info) and what is preserved (their gym membership, which lives separately).

Feedback channel directly from the app

Users can send feedback about either the app itself or the chatbot from a single screen. The form pre-tags the category based on where they opened it from (Home button → “app”; chat header → “Cliff”). Each Cliff-feedback message is automatically tagged with the chat session ID so we can correlate it back to the conversation.

Brand polish

Logo and screen flow follow the brand: Libre Franklin–adjacent typography, off-white background (#FFFDF5), dark text (#26241C), warm orange (#AF5436) for the check-in action. The welcome modal — pops up on first launch, accessible later via “About this app” — closes with: “We hope you enjoy our app. We made this for you with love. — Your local Basin team.”


Tech stack

Mobile (basin-app)

Backend (basin-app-api)

Distribution


Notable engineering decisions

Read-only against the gym’s data

The gym’s source of truth is a third-party gym management system (Capitan). I never write back to it. The app’s backend reads pre-computed customer/family/checkin data from S3 (refreshed 3x daily by a pipeline) and rewards data from Supabase (refreshed by the same pipeline). This made the app safe to ship fast and easy to roll back.

Family graph filtering

The first version of the household view surfaced anyone the data pipeline had ever inferred as a “family member” — which included anyone who’d once booked a class for someone else’s kid. Real user complaint: “why is Stephanie showing up under my account?” Fixed by trusting only high-confidence (explicit Capitan-API-declared) parent-child links and ignoring inferred ones (reservation bookings, last-name matches).

One QR for all rewards

First design had one QR per reward, encoding a custom URL scheme the staff would scan. Second design (after user feedback): a single QR per customer that opens the staff CRM page filtered to that customer. Staff sees everything at once, can mark redeemed in the existing dashboard, no new tooling needed.

Staging via EAS Update channels

Production builds and preview builds in TestFlight are subscribed to different EAS Update channels. A code change ships to preview first (a small group), gets tested on real phones in a day, then promotes to production. The preview build shows a yellow “PREVIEW BUILD” badge under the logo so testers always know which environment they’re on.

No analytics SDKs, no tracking

Deliberate: the app collects only what it needs to function (auth identifiers, feedback content, push tokens). No Mixpanel, no Amplitude, no Sentry yet. The App Store privacy questionnaire is short and honest as a result.

Demo account for Apple reviewers

Apple’s reviewer can’t read a real OTP email. I added a backend bypass where the email testing@basinclimbing.com plus the code 635251 always succeeds, mapped to a real demo customer with the richest possible account state (5 family members, 5 earned rewards, in-progress milestones). Apple approved the app on first technical review after this was in place.


Distribution status


Repos

Both private GitHub repos:


What I’d do next


Asset checklist for your site

Copy these into your images/ folder (or wherever your site expects them):

SourceSuggested name
/Users/steelferguson/daily_sessions/projects/basin-app/assets/basin_logo_dark.pngbasin_logo_dark.png
/Users/steelferguson/daily_sessions/screenshots-resized/IMG_9453.PNGsignin.png
/Users/steelferguson/daily_sessions/screenshots-resized/IMG_9454.PNGhome.png
/Users/steelferguson/daily_sessions/screenshots-resized/IMG_9455.PNGrewards.png
/Users/steelferguson/daily_sessions/screenshots-resized/IMG_9456.PNGchat.png
/Users/steelferguson/daily_sessions/play-store-feature-graphic.pngfeature_graphic.png (good for a wider hero)

If your screenshots-resized order doesn’t match the captions (sign-in → home → rewards → chat), swap the filenames in this checklist before copying.

← Back to home