Cofounder · Live on the Jobber marketplace
Patcher
When a customer cancels, the empty slot costs a service business real money. Patcher watches the calendar and fills the gap before it becomes lost revenue.

01 · The elevator pitch
What it is
When a customer cancels on a service business, the empty slot costs more than one visit: the tech still drives around it and the day still gets paid for. Patcher plugs into the scheduling tools these businesses already use, spots the gap the moment it opens, finds which jobs can safely move up, and either texts the best fit itself or drafts the proposal for a one-tap Send.
I'm the cofounder and the engineer: the scheduling engine, the integration layer, the API, and the frontend are all mine, end to end. It's live, approved on the Jobber marketplace.
02 · The core
The scheduling engine
Moving a job into a gap changes the drive before and after the slot, the slack left in the day, and which customers you're comfortable nudging. So the engine spends the expensive math only on candidates that earn it: cheap in-memory filters cut the field, a Haversine travel proxy pre-ranks the survivors, and only the top of that queue gets real Google Distance Matrix calls. The proxy runs the same detour and slack math as the verified pass, just on approximate inputs, so the two can never disagree.
Candidates aren't crowned by a score anymore. Each one passes hard yes-or-no checks: does the job physically fit, is the added drive inside the business's limit, can the customer be reached, is a technician swap allowed. Managers see the facts they'd check themselves, plus a plain-English reason when a candidate isn't ready. A fit score still exists under the hood; it just no longer decides who gets texted. And while a dispatcher reads one gap, a background worker pre-builds the next few, saving roughly 800ms per click.
Gap detected · Tue 9:40 AM · 2h 30m open
Proposal ready · May 27 → May 22 · 5 days earlier
03 · The connections
The integration layer
Patcher meets each platform where it lives. Three scheduling adapters (Jobber over OAuth, MoeGo and Housecall Pro over API keys) sit behind one interface, so the engine never knows whose calendar it's reading. Google, Twilio, and Slack are utilities a layer down: calendar and maps, SMS delivery, notifications.
The adapters are honest about platform differences. Moving an appointment is one atomic call on Housecall Pro, two separate mutations on Jobber, and on MoeGo, which has no reassign endpoint, a create-then-cancel sequence in that order so a failed create leaves the original untouched. Writebacks are treated as proposals, not facts: every adapter parses what the provider actually did, and a verifier confirms the move really happened before Patcher believes it. Each adapter also ships a fixture twin so the core flows run deterministically in tests.
- JobberScheduling · OAuth
- MoeGoScheduling · API key
- Housecall ProScheduling · API key
- GoogleCalendar & maps
- TwilioSMS delivery
- SlackNotifications
04 · The plumbing
Async infrastructure
Webhooks arrive badly: in bursts, out of order, more than once. They get their own front door, a second small FastAPI app that validates the signature, writes a row, and returns 200 in milliseconds, so a webhook storm can never starve real users. Payloads are hashed for dedupe and the downstream writes are idempotent, so a duplicate delivery collapses into a no-op.
Five gap-detection triggers used to race each other. Now every trigger feeds one durable queue in Postgres (no Redis, no Celery): workers claim withFOR UPDATE SKIP LOCKED, priorities are integers the SQL sorts on, and pending requests for the same integration merge at claim time. A per-integration lease, one conditional UPDATE with a five-minute TTL, guarantees two detection runs can never overlap.
05 · The last mile
The proposal flow
Filling the gap runs in one of two gears. On autopilot, the engine sends the proposal itself the moment a gap clears its checks. In approve-first, it does the same work and stops one tap short: the drafted proposal waits in a tray until a human hits Send. Pacing is decided by urgency: an opening starting soon goes to every eligible candidate at once, and a farther-out one is offered one candidate at a time, auto-advancing on declines.
The SMS is deliberately dumb: replies never change proposal state, and accepting happens only through a tokenized link, never by parsing "YES". Guardrails wrap the rest: a consent disclosure fires exactly once per customer, quiet hours are quiet, someone just served isn't re-contacted, and a decline pauses that customer until after their next appointment. A separate reschedule link lets a customer browse eligible slots and pick their own time, powered by the same travel engine.
06 · War stories
Production stories
The webhook outage
Under burst webhook load, the API wedged. The sync database client held one HTTP/2 connection for the life of the process; when the pooler recycled its backend, the client never noticed the dead socket, and every request failed until a restart. The fix: hot paths moved to an async client, and the worst endpoints (one made 30-plus sequential round trips) moved to hand-written SQL and single JOINs.
The page that made 300 API calls
The customer reschedule page checked travel feasibility for every visible gap, so one page load could fan out to 300-plus Distance Matrix calls and stall for 30 seconds. The fix was honesty about limits: cap the sweep, narrow the window, and hand the customer a page of solid options fast.
07 · The product
Screenshots
Real product screenshots are on deck. Until they land, the live thing is at patcher.com ↗.
08 · The toolbox
Stack
- Frontend
- React, TypeScript, Vite, Tailwind
- API
- Python 3.12, FastAPI, Pydantic
- Data
- PostgreSQL on Supabase (PostgREST, direct asyncpg on hot paths)
- Messaging
- Twilio SMS, A2P-registered
- Infra
- Docker, Render, Vercel, GitHub Actions
Three services run in production: the API, a webhook app, and a background worker. The schema moves fast on rails: 251 migrations in eight months, each CI-gated and deployed automatically on merge.
Postgame
See it live
Patcher is in production today, filling real schedules for real businesses.