Home Service Pass logo

Home Service Pass

Hospitality Salt Lake City, UT Website Published Apr 27, 2026 · Updated Apr 27, 2026 A Benmore Technologies case study
Application Development Desktop Application django Enterprise integration Node.js React SME Startup Web Development

Home Service Pass logo

Home Service Pass — A Membership Operating System for the Home

A Benmore Technologies case study, in partnership with Home Service Pass.

Client: Brad Pierce & Eddie Juarez FDE: Arkash Jain

Home Service Pass banner


1. Introduction

Overview:

Home Service Pass (HSP) is a membership marketplace for physical home services — bin cleaning, lawn care, pet-waste pickup, blind cleaning, HVAC, plumbing, and more — delivered in person at the customer's address. Customers buy an annual pass (or a monthly plan) and redeem it for free or discounted services from a vetted network of local providers. Providers run their entire job pipeline — bookings, quotes, subscriptions, photos, payouts — from a dedicated dashboard. Admins govern the whole platform from a centralized backoffice.

Available on the App Store, Google Play, and on the web at homeservicepass.com, HSP replaces the friction of finding, vetting, scheduling, and paying for home services with a single annual membership and a phone-first experience.

Download on the App Store App home screen + provider dashboard composite

Challenge:

Building a three-sided marketplace (customers, providers, admins) that:

  • Bills physical home services through Stripe without triggering Apple's 30% IAP commission on a mobile binary
  • Stays SMS-compliant under TCPA + CTIA on every OTP, booking confirmation, and reminder
  • Handles standard one-off bookings, custom-quote jobs, and recurring subscriptions in a single booking pipeline
  • Survives the round-trip data-corruption traps that every multi-client app eventually hits
  • Ships to iOS, Android, and the web from one Django source of truth

2. The Problem

Background:

The U.S. home-services market is a ~$650B industry dominated by lead-generation platforms (Angi, Thumbtack, HomeAdvisor) that charge providers per-lead fees of $15–$100, often for leads that never convert. Customers search a category, get a list of unfamiliar providers, request quotes from three of them, get cold-called for a week, and still don't know who to trust.

For providers, the math is brutal: the lead-gen take rate eats the margin on small jobs, and there's no recurring revenue floor. For homeowners, the experience is fragmented — different app for the lawn guy, different number for the HVAC tech, no single relationship with anyone who knows the house.

HSP's founders set out to flip the model: one annual membership, one app, a curated provider network paid in full, and a clear value proposition for both sides of the market.

Pain Points:

  • Fragmented service discovery — Homeowners juggle five apps, three text threads, and a binder of business cards to keep a house running. There was no single membership-shaped relationship for the home.
  • Lead-gen platforms tax small providers to death — Local one- and two-person shops were paying per-lead fees that ate 20–40% of their margin on small jobs, with no guarantee of conversion and no recurring revenue layer.
  • Trust is one-shot — Without a membership relationship, every booking starts cold. Customers re-vet, re-explain, re-negotiate every time.
  • Subscriptions are clunky — Recurring services (pet-waste pickup, lawn care) require pickup scheduling, photo proof of service, partial-month proration, and pause/cancel flows that off-the-shelf SaaS doesn't handle.
  • Mobile compliance is a minefield — Selling memberships through a mobile app means navigating Apple's IAP rules, Google Play's billing policy, TCPA consent on every SMS, CTIA STOP-keyword handling, and Stripe webhook idempotency — all without breaking the existing customer base each time you ship.
  • Data corruption from naive masking — Securing sensitive fields (bank accounts, payment data) in API responses by masking them in-place silently corrupts the data on the next save round-trip from a mobile binary that can't be atomically upgraded with the backend.

The downstream impact: providers were burning out on lead-gen treadmills, customers were defaulting to "I'll deal with it later," and any platform attempting to fix it kept getting pulled into the gravity well of either Apple's IAP commission or a half-implemented compliance regime.


3. Our Solution

Discovery Process:

The founding ask was clear: a membership product for the home, sold once a year, redeemable across a vetted provider network, with a real mobile experience for customers and a real workflow tool for providers. We narrowed the platform around one principle: HSP sells physical services delivered in person — the architecture must stay aligned with that fact at every layer, from payment routing (Stripe, never IAP) to compliance copy (App Store reviewer notes) to the customer-facing landing page disclaimers that prove it.

Everything else — the pass plans, the booking flow, the subscription engine, the admin panel — could iterate. Getting the physical-services compliance spine wrong would either get the app rejected, hand 30% to Apple, or break SMS opt-out compliance on the first audit.

Core Value Proposition:

Give homeowners one annual membership that covers their house, and give local service providers a recurring, lead-gen-free customer relationship — on a phone, on the web, with the compliance and payment plumbing handled.

Proposed Solution:

  • Pass Purchasing & Plans — Stripe Checkout for guest and authenticated buyers. Three plan types (Full Pass, Annual Discount, Monthly), gift-pass purchases for a recipient, and Apple Wallet .pkpass generation for active passes.
  • Booking & Quote Pipeline — Request → provider quote → customer approval → scheduling → in-progress → completion → invoice, with role-scoped permissions on every transition. Standard bookings, custom-quote jobs, and subscription bookings all flow through one model with type-aware branching.
  • Subscription Engine — Recurring services (Waggy Scoop pet-waste pickup, recurring lawn) with Stripe billing, weekly / biweekly / twice-weekly / bimonthly / monthly frequencies, $75 acquisition fee amortized over the first months, $10/mo HSP-member discount for the first 6 months, pause / resume / cancel state machine, photo upload on each pickup, and pricing-tier snapshots so price changes don't retroactively rewrite history.
  • Provider Wizard — A single atomic flow that creates a service, member discount, promotional discount, pricing plan, and subscription tier in one transaction — replacing what used to be five separate admin steps prone to half-finished state.
  • Mobile App (Customer-Only Surface) — React Native + Expo app for pass activation, SMS-OTP login, service browsing, booking, subscription management, photo viewing, and pass display. The web frontend is reserved for providers and admins; customers exist only on mobile by deliberate architectural rule.
  • Admin Dashboard — Pass management, batch corporate orders, provider onboarding, financial reports (LTV / CAC), dispute resolution, payout reconciliation, and a centralized settings panel for services, discounts, and pricing plans.
  • Referral System — Unique alphanumeric referral codes backed by Stripe promotion codes, with revenue attribution per referrer.
  • Two-Factor Authentication — TOTP-based 2FA for providers and admins with recovery-code generation and a re-authentication flow for sensitive actions.
  • SMS Compliance Layer — TCPA consent capture at signup with timestamped audit trail, CTIA STOP / HELP / START keyword handling, and a sms_opt_out flag enforced at every send-site.
  • Structural Hardening Pack (v1.7.6)CloudinaryUploadService consolidating seven duplicated upload sites into one, role_required(role, message) permission factory replacing 20+ inline if request.user.role != ... checks, and IsAdminOrProvider / IsBookingParticipant shared permission classes — all with the legacy {"error": ...} 403 response shape preserved byte-for-byte so no mobile or web client breaks.
  • Round-Trip-Safe Masking (v1.8.1 reland) — Masked sensitive fields (bank account, routing) live under new field names (bank_account_number_masked); the original field returns empty on read and is skipped on write if empty or sentinel-prefixed. Customer service_interests returns raw codes (round-trip safe) plus a separate service_interests_display_names field for UI labels.

Technology Stack:

  • Backend: Django 5.1, Django REST Framework, Celery + Redis, PostgreSQL 16, drf-spectacular (OpenAPI 3.0), SimpleJWT
  • Web Frontend: Next.js 16, React 19, TypeScript, Tailwind CSS, Sentry
  • Mobile: React Native, Expo (iOS + Android), Axios 1.x with bodyless-POST handling
  • Hosting: Heroku (backend, with release-step migrations), Vercel (web frontend), App Store + Google Play (mobile)
  • Database: PostgreSQL 16 (production), Redis 7 (cache + Celery broker)
  • Payments: Stripe (Checkout, off-session subscription billing, promotion codes, webhooks)
  • Notifications: Twilio (SMS, A2P 10DLC registered), Mailjet (transactional email), Cloudinary (image hosting + CDN)
  • Tooling: Docker Compose dev environment, GitHub Actions CI, pre-commit hooks, Ruff lint

4. Implementation

Backend & Infrastructure:

  • Django REST API serving the customer mobile app, the provider/admin web frontend, and Stripe webhooks from a single source of truth
  • PostgreSQL on Heroku with release-step migrations and post-deploy smoke checks
  • Celery + Redis for asynchronous background work (subscription billing cycles, SMS sends, Cloudinary uploads, scheduled reminders)
  • Sentry instrumentation on every endpoint, with PII scrubbing (session_id, OTP tokens, stripe_*, mfa_secret, totp_secret, etc.) on URL query strings and request bodies before transmission

Web Frontend:

  • Next.js 16 App Router with React 19, TypeScript, and Tailwind
  • Provider and admin dashboards only — by deliberate platform invariant, no customer-facing surface on the web
  • OpenAPI-typed API client generated from the Django drf-spectacular schema for type-safe integration

Mobile Application Development:

  • Built cross-platform in React Native + Expo for iOS and Android from a single codebase
  • SMS-OTP login as the single auth path for customers (every customer has a verified phone number — email-only customer accounts no longer exist)
  • Stripe Checkout opened via WebBrowser.openAuthSessionAsync so all purchases route through the web payment flow — explicitly avoiding Apple IAP and Google Play Billing under the physical-services exemption

Key Features Implemented:

  • Three Pass Plans — Full Pass, Annual Discount, and Monthly, with is_active computed from expiry_date and cancellation status, batch corporate-order support via batch_id, and Apple Wallet .pkpass generation.
  • Subscription State Machinepending_first_service → active → pausing/paused → canceling/canceled with billing_anchor_date set on first pickup + 30 days, snapshot of pricing tier at time of signup, $75 acquisition fee amortized into early payments, and a 6-month $10 HSP-member discount.
  • Provider Wizard Atomic Flow — Service, member discount, promotional discount, pricing plan, and subscription tier creation wrapped in a single transaction so a failure halfway through never leaves the catalog in inconsistent state.
  • Round-Trip-Safe Masking Pattern — A platform invariant after the v1.8.0 incident: masked fields must use a new field name and the original field must stay empty (or be skipped on write) so existing client binaries — which can't be atomically upgraded with the backend — don't echo masked sentinels back into the database.
  • Permission Factory With Legacy Shape Pinningrole_required(role, message) returns a fresh BasePermission subclass per call (so DRF's module-level permission_classes lists keep distinct entries) and raises a custom APIException whose detail is the dict {"error": message}, preserving the legacy 403 response shape every existing client expects.
  • Cloudinary URL Invariant — All image fields store and return full https://res.cloudinary.com/... URLs. Relative paths render blank in React Native and Next.js with no error, so view-level guards return None for any value that doesn't start with https:// and seed data uses authoritative Cloudinary URLs.
  • Bodyless POST Discipline — Every Axios POST/PUT/PATCH/DELETE in mobile and web passes {} as the second argument, even when the endpoint takes no body, because Axios 1.x strips Content-Type on bodyless calls and DRF returns 415. Documented as a platform invariant after biting the project twice (MFA login, account deletion OTP).

Technical Challenges Addressed:

  1. Apple/Google Mobile Compliance Without Losing 30% — Selling annual home-service memberships from a mobile binary normally triggers Apple IAP + 30% commission. We routed all purchases through Stripe Checkout in a web session opened from the app under Apple Guideline 3.1.5(a) (physical goods/services consumed outside the app — the same exemption Uber, DoorDash, and Airbnb use). Reviewer notes in mobile-app/store-meta/ and a customer-facing "in-person services delivered at your address" disclaimer in the landing copy keep the physical-services framing visible to reviewers.
  2. The 14-Minute Production Revert (v1.8.0 → v1.8.1) — A masking refactor masked bank_account_number and service_interests in-place under the same field name. The mobile app pre-filled forms with setX(profile.x) and POSTed x back on Save — silently writing the masked sentinel "****1234" back to the database for any provider who saved Settings, and wiping service_interests to empty on the next round-trip. A 5-agent post-merge review caught it; production was reverted at v136 14 minutes after v135 deployed. The reland (v1.8.1) introduced the new-field-name invariant now embedded in CLAUDE.md, plus a regression test (test_round_trip_does_not_corrupt) that pins the contract.
  3. TCPA / CTIA SMS Compliance End-to-End — Twilio A2P 10DLC registration, sms_consent + sms_consent_at captured at signup, sms_opt_out honored at every send-site, STOP / HELP / START keyword handlers, and a Twilio-side opt-out mirror so the platform never re-sends to a number that opted out via reply.
  4. Sentry PII Scrubbing on Request URLs — The original Sentry scrubber only walked dict-shaped data and missed string-value paths like request.url, leaking Stripe session_id and OTP tokens into error events. v1.8.1 added _scrub_query_string + _scrub_request_strings helpers covering URL query strings and request body strings before Sentry transmission.
  5. Deep Health Check Without DoS or Thread Hangs/api/health/deep/ makes outbound checks to Stripe and Twilio. The original ThreadPoolExecutor with-block waited on the orphan thread on exit, allowing 21s+ hangs and worker exhaustion under upstream slowness; Stripe checks mutated module-level stripe.api_key (not thread-safe under concurrent payment flows). v1.8.1 switched to bare-instance + finally pool.shutdown(wait=False, cancel_futures=True), per-call api_key= parameter, a 60s response cache, and a DeepHealthThrottle (60/min/IP) so monitoring polls cap at one upstream ping per minute.

5. Results

Product Outcomes:

  • Live on iOS App Store, Google Play, and on the web at homeservicepass.com — full three-sided marketplace running in production
  • Three-sided pipeline working end-to-end — customers buying passes, providers fulfilling bookings, admins reconciling payouts and managing the catalog
  • Subscription engine in production for recurring services (Waggy Scoop pet-waste pickup) with weekly/biweekly/twice-weekly/bimonthly/monthly cadences, photo proof, and pause/cancel flows
  • Stripe-routed purchases — zero Apple IAP or Google Play Billing surface area, full physical-services exemption preserved on both stores

What Customers Are Saying:

Customer review of Home Service Pass — early adopter

Customer review of Home Service Pass — second adopter

Technical Achievements:

  • One Backend, Three Clients — A single Django REST API serves the React Native customer app, the Next.js provider/admin web frontend, and Stripe webhooks, with type-safe integration via OpenAPI codegen from drf-spectacular.
  • Compliance Spine That Holds — Apple + Google physical-services exemption preserved across every release, TCPA + CTIA SMS compliance enforced at the model and view layer, Sentry PII scrubbing covering URL query strings and request body strings, and Stripe webhook idempotency on every billing cycle.
  • Structural Hardening Without Breaking Anything — Twenty inline role checks migrated to a role_required factory and seven Cloudinary upload sites consolidated into one service — with the legacy {"error": ...} 403 response shape pinned by tests so no mobile or web client noticed the refactor.
  • Round-Trip-Safe Data Contracts — After the v1.8.0 revert, masking now lives behind new field names enforced by CLAUDE.md invariants and regression tests, so a mobile binary that can't be atomically upgraded with the backend never echoes a masked sentinel back into the database.
  • Bank-Grade Audit Trail — Every booking state transition, every subscription billing cycle, every payout, and every SMS opt-out event leaves a timestamped record. Reconciliation is queryable, not reconstructable from logs.

Business Impact:

  • Recurring Revenue for Local Providers — Subscription services give one- and two-person shops a recurring revenue floor that lead-gen marketplaces structurally can't offer.
  • One Membership, One Relationship — Customers replace five apps and a binder of business cards with a single annual pass and a single phone-first experience.
  • Zero Apple/Google Tax — All membership and subscription revenue routes through Stripe under the physical-services exemption, preserving margin that competitors selling digital subscriptions through IAP lose 30% of by default.
  • Provider Onboarding Compressed — The single-transaction provider wizard collapses what was previously a five-step manual admin flow (service + member discount + promotional discount + pricing plan + subscription tier) into one atomic submit.
  • Operational Resilience — A 14-minute production revert produced a permanent platform invariant, a regression test, and a documented mechanism check now applied to every masking change. The class of bug doesn't ship twice.

6. Lessons Learned

Key Takeaways:

  • Never Mask a Field In-Place If Any Client Posts It Back — The v1.8.0 incident is now the most-cited learning in the codebase. A mobile binary cannot be atomically upgraded with the backend, so any in-place reshape of a field that gets pre-filled into a form and POSTed on Save will corrupt the underlying data within a single save cycle. Always introduce a new field name; leave the original empty and skip-on-write.
  • Never Modify an Existing Endpoint — Modifying an endpoint's response shape silently breaks every client that already consumes it. The platform rule is absolute: if behavior needs to change, ship a new endpoint alongside the old. The cost of one extra route is always lower than the cost of a broken existing one.
  • Compliance Is an Architecture, Not a Checklist — Apple's physical-services exemption, Google Play's payments carve-out, TCPA consent, CTIA STOP keywords, and Sentry PII scrubbing aren't features you add later. They are platform invariants that have to be enforced at the model, view, deploy, and copy layers from day one — and re-enforced on every PR review.
  • Refactor Behind a Pinned Contract — The structural hardening pack (role_required, CloudinaryUploadService, shared permission classes) was only safe because it pinned the legacy response shape with byte-for-byte tests. Big refactors land cleanly when the external contract is locked first and the internal mechanics move underneath it.
  • Axios 1.x Bodyless POST Will Bite You Twice — Documented as a platform invariant after biting the project twice (MFA login, account deletion OTP). Pass {} as the second argument on every bodyless POST/PUT/PATCH/DELETE. The five-character habit is cheaper than the 415 debugging session.
  • One Customer-Facing Surface, Period — Customers exist only on mobile. Providers and admins exist only on web. Refusing to build customer surfaces on web (and provider/admin surfaces on mobile) keeps the parity matrix tractable and prevents the "where does this live" debate on every new feature.

7. Conclusion

Summary:

Home Service Pass replaces the lead-gen treadmill and the five-apps-per-house experience with a single annual membership, a curated provider network, and a phone-first interface — backed by a Django + Next.js + React Native stack that holds Apple/Google physical-services compliance, Stripe billing safety, TCPA/CTIA SMS compliance, and round-trip-safe data contracts at the platform level.

The project demonstrates how disciplined attention to compliance invariants, response-shape pinning, and round-trip-safe API design can deliver a three-sided marketplace that survives the one constraint every mobile-first SaaS eventually hits: the binary in the customer's pocket is the slowest thing to upgrade, and the architecture has to respect that fact.


⭐ A Note on the Mission

Home Service Pass is, on its surface, a membership for the home. Underneath, it's a thesis: that local service providers deserve a recurring relationship with their customers, that homeowners deserve one trusted relationship for their house instead of five fragmented ones, and that the 30% Apple/Google rake on physical services is a tax on the wrong people.

Every platform invariant in this codebase — never modify an existing endpoint, never mask in-place, always Stripe never IAP, always SMS-compliant, always round-trip safe — exists because someone, somewhere, would have lost data, lost money, or lost compliance if we hadn't enforced it.

We are excited to keep building infrastructure for the homes and small businesses that deserve it.