
Project Overview: What Bridleway Needed to Be and Why It Exists
Bridleway started because of a Freakonomics Radio episode. Economist Mark Paul described his side business importing sport horses and the complete lack of price transparency in the market. Similar horses listed at wildly different prices with no justification, most sales happening through Facebook groups, and no centralized platform that buyers could trust. His observation that there's no Zillow for horses stuck with me. For a web developer, that's a product specification.
The project launched in November 2025 and has been in continuous development since. The original December 2025 case study on this blog covered the initial prototype. What exists now is a fundamentally different application: over 77,000 lines of TypeScript across more than 400 files, nearly 50 test files, a growing blog, over a thousand horse listings aggregated from six auction house sources, a custom escrow system built on Stripe Connect, a three-tier seller verification system with KYC document handling, a fraud detection engine, encrypted messaging, a custom-built CRM with email drip campaigns, and a full admin panel. The site is live at bridleway.co in free beta, with verified sellers and growing.
This case study explains how each system works and why it was built the way it was.
How the Tech Stack Fits Together
Bridleway is a Next.js 14 application using the App Router with TypeScript throughout. The database layer uses Drizzle ORM with PostgreSQL, which gives type-safe queries without the overhead of Prisma's query engine. Authentication runs through NextAuth with multiple provider types: Google OAuth, Facebook OAuth, GitHub OAuth (all conditionally loaded based on whether credentials are configured), and a credentials provider that handles email/password login with optional TOTP and WebAuthn MFA challenges.
The frontend uses React 18 with Tailwind CSS and a custom design system built on charcoal, cream, forest, and gold color tokens. Lucide React provides the icon system. The component library is organized across directories covering admin, analytics, auth, blog, dashboard, external listings, horses, layout, listings, messages, payments, providers, search, settings, payments integration, UI primitives, and verification flows.
Payments and seller payouts run through Stripe with a full Connect integration. Transactional and marketing emails go through Resend. Image hosting uses Cloudinary. Caching and rate limiting use Upstash Redis. Phone verification uses Twilio Verify. Error monitoring runs through Sentry. Structured logging uses Winston. Input validation uses Zod schemas throughout. The entire application deploys on Replit's Autoscale infrastructure.
How the Database Schema Works: Drizzle ORM with PostgreSQL
The database schema is defined in a single file that covers every table, enum, and relation. Drizzle ORM was chosen over Prisma for a specific reason: Drizzle generates SQL that you can read and reason about, and it doesn't require a separate query engine binary. On Replit's infrastructure, eliminating that overhead matters.
The schema defines a set of PostgreSQL enums that enforce data integrity at the database level rather than in application code. Role enums distinguish buyers, sellers, professionals, and admins. Verification enums track users through progressively verified states. Source enums enumerate every auction house the system can aggregate from: DreamHorse, HorseClicks, EquineNow, eHorses, RanchWorldAds, Keeneland, Fasig-Tipton, OBS, Tattersalls, Heritage Place, and Billings Livestock.
The core tables cover users with full profile data and verification state, addresses for billing and shipping, verification documents with encrypted metadata, horses with breed, bloodline, sire, dam, height, gender, and discipline fields, health records for vaccination and medical history, listings with pricing and moderation status, messages for buyer-seller communication, saved searches and favorites for user engagement, external listings for aggregated auction data, escrow transactions and milestones for the payment pipeline, audit logs for security events, reports for content moderation, CRM contacts, outreach tracking for the drip campaign system, subscriber alerts for notifications, and newsletter tracking for marketing emails.
Every table uses UUID primary keys, automatic timestamps, and Drizzle's relations system for type-safe joins. The schema enforces foreign key relationships with cascade deletes where appropriate, such as verification documents being deleted when a user is removed.
How Authentication and Security Work
The authentication system is more complex than most marketplace applications require, but for a platform handling financial transactions on high-value livestock, the security surface had to be right.
The NextAuth configuration sets up a credentials provider that accepts email, password, an optional TOTP code, and an optional WebAuthn response. The authorize function implements account lockout after repeated failed login attempts. The lockout state is stored per-user in the database rather than in a global rate limiter, so lockouts persist across server restarts and scale across instances.
Password hashing uses argon2 exclusively. The codebase previously used bcryptjs but migrated entirely to argon2 for its resistance to GPU-based attacks and its memory-hard design. The TOTP implementation is built from scratch using Node.js crypto HMAC functions with base32 encoding, rather than pulling in a third-party TOTP library. WebAuthn support allows hardware key authentication as a second factor.
The middleware stack layers multiple security concerns. Every request passes through security headers that set a strict Content Security Policy. CSRF protection uses token generation and validation on state-changing requests. Rate limiting operates in two tiers: Redis-based rate limiting when available, with stricter limits on authentication endpoints, and an in-memory fallback when Redis isn't configured.
How Seller Verification Works: A Three-Tier System
Trust is the central problem in any marketplace, and it's especially acute in horse sales where individual transactions can reach six figures. Bridleway implements a three-tier verification system that progressively unlocks platform capabilities as sellers prove their identity.
- Tier 1: Basic verification with email confirmed and phone verified via Twilio
- Tier 2: Government ID verification through Stripe Identity with document capture, liveness checks, and data extraction. Sensitive metadata is encrypted at the field level using AES-256-GCM
- Tier 3: Business verification for commercial operations
The escrow system enforces verification thresholds: high-value transactions require Tier 2 verification from the buyer. The threshold and maximum escrow amount are configurable.
How the Escrow System Works: Stripe Connect and a 7-Day Inspection Period
The escrow service is the most critical financial component. It manages the full lifecycle of a horse transaction through multiple states: from initial creation through funding, inspection, approval or dispute, and finally release or refund.
The workflow starts when a buyer initiates an escrow on a listing. The system validates that the buyer exists, checks whether the transaction amount requires additional identity verification, verifies the horse listing belongs to the claimed seller, and calculates the effective commission rate. The platform charges a flat escrow fee plus a percentage commission, though sellers can have per-account commission overrides with expiration dates for early adopter incentives and negotiated rates.
Payments flow through Stripe Connect. When a seller onboards, the system creates a Stripe Connect account which handles account creation (individual or company), onboarding URL generation, dashboard access links, payment validation, balance queries, and account update webhooks. The Connect integration supports the full onboarding flow: charges enabled, details submitted, payouts enabled, and pending verification requirements.
Once a buyer funds an escrow, the transaction enters an inspection period. During this time, the buyer can approve the horse, schedule an inspection, or open a dispute. If the buyer takes no action, an auto-release timer eventually releases funds to the seller, with a warning email sent before the deadline.
For high-value transactions, SMS verification through Twilio is required before funds can be released. Every escrow state transition generates a milestone record, creating a complete audit trail. The system sends targeted emails at each stage: escrow initiated, funded, released, disputed, buyer approved, inspection reminder, and auto-release warning.
How Auction House Aggregation Works: Source Adapters and the External Listing Pipeline
The auction house aggregation system is what gives Bridleway its listing volume. The platform shows over a thousand horses, the vast majority sourced from external auction houses. The system is architected as a pluggable adapter pattern with a central aggregator orchestrating six independent source adapters.
The aggregator class provides the orchestration layer. It exposes methods for importing individual listings, batch importing, syncing from a single source, syncing all sources at once, marking stale listings as sold, cleaning up expired listings, and generating statistics. Each sync operation returns detailed results: imported count, updated count, errors, total found, sync timestamp, and error messages.
A base adapter abstract class defines the interface and provides shared functionality: configurable retry logic and request throttling to be respectful of source sites. Each auction house gets its own concrete adapter for Billings Livestock, Keeneland, Fasig-Tipton, OBS, Tattersalls, and Heritage Place.
The external listing data interface normalizes data across all sources: external ID, source identifier, source URL, name, breed, age, gender, color, height, discipline, price, location, description, images, videos, seller info, and original post date. When a listing is imported, the aggregator checks for existing records by source and external ID. If found, it updates the existing record rather than creating a duplicate.
How Fraud Detection and Content Scanning Work
Horse marketplaces are targets for scams. The platform implements fraud detection at multiple layers.
The fraud detection module evaluates risk across several dimensions: message content, user behavior, listing quality, transaction patterns, and IP reputation. Each check produces signal objects with a type, severity level, numeric score, and description. The signals are aggregated into an overall risk assessment with a recommendation to allow, flag for review, or block.
The message content scanner uses pattern matching to detect attempts to move transactions off-platform. It catches phone numbers in multiple formats, email addresses, references to payment apps and services, cryptocurrency mentions, and explicit off-platform language. Obfuscation detection catches attempts to disguise contact information with spaces, dashes, or letter substitution.
The content moderation system scans listing descriptions for embedded URLs and detects obfuscation patterns where sellers try to hide contact information in listing text. Combined, these systems create overlapping protection: the fraud engine evaluates risk holistically, the content scanner catches specific evasion attempts in messages, and the moderation system screens listing content before it goes live.
How the Messaging System Works: Encrypted and Scanned
Buyer-seller communication runs through a secure messaging service. Messages are stored in the database and pass through the content scanner before delivery. The system includes email throttling to prevent notification fatigue. If a buyer and seller are actively messaging, the system batches notification emails rather than sending one for every message.
Encryption for sensitive data uses AES-256-GCM with field-level encryption keys derived through a key derivation function. Each encryption operation generates a random initialization vector and produces an authentication tag for tamper detection. This is used for verification document metadata and other sensitive fields rather than for message content, which is stored in cleartext but scanned.
How the CRM and Outreach System Work
The CRM is custom-built. The contacts system tracks leads through a status pipeline from initial contact through to listed, unresponsive, or unsubscribed. Each contact has a channel (social media DM, platform message, email, or phone) and a listing status. Outreach history is tracked per contact, and individual email sends are logged with delivery status.
The lead ingest API accepts contacts from external sources with API key authentication. Input sanitization strips control characters and enforces length limits before storing contacts. The outreach template system supports multiple email templates with response tracking.
The email drip campaign runs on a scheduled basis and moves contacts through the pipeline: new leads get an initial outreach, non-responders get follow-ups with different templates, and the system marks contacts as complete or unresponsive after the sequence finishes. The newsletter system sends marketing emails through Resend with delivery tracking.
How the Admin Panel Works
The admin panel provides management sections for users, CRM contacts, escrow transactions, content moderation, newsletters, reports, security audit logs, and seller verification.
The audit log records security-relevant events: login attempts, permission changes, verification state transitions, escrow actions, and admin operations. The verification management section lets admins review pending verification documents, approve or reject them with review notes, and track the full verification timeline for each user.
How the Blog and SEO Strategy Work
The blog lives at /blog with statically generated pages. Each post includes SEO metadata (title, description, keywords), Open Graph images, canonical URLs, and structured data. The blog covers buying guides (first-time buyers, breed guides, pre-purchase vet checks, price guides by discipline), selling guides (listing optimization, tire-kicker filtering, valuation), and market analysis (price trends, auction house guides).
The site implements structured data throughout using JSON-LD. The blog has BreadcrumbList schema, individual posts get Article schema, and horse listings get Product schema with proper pricing and availability fields. The homepage features structured data for the Organization and the listing aggregation from named auction houses.
How the Notification System Works
The email notification system handles over a dozen distinct email types: password reset, verification updates (with tier-specific messaging), welcome emails, escrow lifecycle emails (initiated, funded, released, disputed, approved, inspection reminders, auto-release warnings), new message alerts, newsletters, admin notifications, and new listing alerts.
The subscriber alerts system lets users set up notifications for new listings matching their criteria (breed, discipline, location, price range) and receives emails when matching horses are listed.
Deployment and Infrastructure
The application deploys on Replit's Autoscale infrastructure with the custom domain at bridleway.co routing through Replit's deployment pipeline. The database is Replit's managed PostgreSQL.
The design system went through a standardization pass, replacing ad-hoc Tailwind classes with the charcoal/cream/forest/gold token system across the codebase. Color contrast was upgraded for WCAG AA compliance.
What the Platform Looks Like by the Numbers
- Over 77,000 lines of TypeScript across more than 400 source files
- Nearly 50 test files covering API endpoints, React components, database integration, and E2E authentication flows
- Over a thousand horse listings aggregated from six auction house sources
- A growing blog with buying guides, selling guides, and market analysis
- Six auction house source adapters with a pluggable architecture for adding more
- Over a dozen email notification types covering the full user lifecycle
- A complete admin panel for user, content, escrow, and verification management
- Deployed on Replit Autoscale with a custom domain
What's Next
Bridleway is in free beta. The immediate goal is onboarding more verified sellers to build the direct listing inventory alongside the aggregated auction listings. The pricing model has three planned tiers (Trainer, Barn, and Dealer) for when the platform exits beta, with the free tier remaining available for individual sellers.
The technical roadmap includes expanding the source adapter library to additional auction houses and classified sites, building out the seller analytics dashboard with market intelligence from aggregated pricing data, and improving the fraud detection engine with behavioral analysis beyond the current pattern-matching approach.
The escrow system and the verification pipeline are production-ready. The challenge now is the classic marketplace cold-start problem: sellers want buyers, buyers want listings, and the content and auction aggregation strategy is the mechanism for bridging that gap.
Tech Stack Summary
- Framework: Next.js 14 with App Router, TypeScript
- Database: PostgreSQL with Drizzle ORM
- Auth: NextAuth with Google/Facebook/GitHub OAuth + credentials + TOTP + WebAuthn MFA
- Payments: Stripe Connect (seller onboarding, payouts, escrow holds)
- Email: Resend (transactional notifications + newsletters + drip campaigns)
- Images: Cloudinary
- Caching/Rate Limiting: Upstash Redis
- Phone Verification: Twilio Verify
- Error Monitoring: Sentry
- Logging: Winston
- Validation: Zod
- Encryption: AES-256-GCM for sensitive field-level data
- UI: React 18, Tailwind CSS, Lucide React icons, custom design system
- Testing: Vitest, Playwright, Testing Library
- Deployment: Replit Autoscale
- Infrastructure: Custom domain (bridleway.co), managed PostgreSQL, Cloudinary CDN
Building a website for a small business? I also build professional WordPress and Webflow sites for small businesses, starting at $1,000. If you or someone you know needs a site, check out my services or get in touch.
Paul Mulligan
Freelance Web Developer
Paul Mulligan is a freelance web developer based in Baltimore, MD with 10+ years of experience building WordPress and Webflow sites for small businesses. He focuses on clean design, fast performance, and real results.
Support My Open Source Work
I build free, open-source developer tools like Flavian and Aurelius. If you find my work helpful, consider supporting me on Patreon.
Support on PatreonRelated Articles
Building Bridleway: A Horse Marketplace Case Study
Read ArticleChoosing the Right Platform: WordPress, Webflow, or Custom
Read ArticleSEO Strategies for Small Businesses
Read ArticleReady to Transform Your Business's Website?
Let's discuss how I can create a website that attracts and converts more customers.
Get a Free Consultation