What I built and why payment comes firstA tentative hold, no Zoom link until money is verified, and a sweeper for abandoned slots
I sell a $400 one-hour Zoom strategy session. For a long time the mechanics behind it were the same mechanics most solo lawyers use: a payment link in one email, a scheduling conversation in another, and me manually connecting the two. That works until it does not. A client pays but never picks a time. A client picks a time but never pays. Two emails cross in the night and I am reconciling a calendar against a payment processor by hand, which is exactly the kind of clerical work a fixed-fee practice cannot afford to do badly.
So in July 2026 I built a scheduler that enforces one rule: payment first, always, structurally. The booking page shows real availability. When a visitor picks a slot, the system records a tentative hold, not a meeting. No Zoom link is minted, no calendar invite goes out, and no confirmation email is sent. The hold simply reserves the time and waits for money.
When the payment arrives and is verified, the system does everything at once: it confirms the booking, creates the Zoom meeting, writes the calendar event, and emails the client one clean confirmation with the meeting link. If the payment never arrives, a scheduled sweeper releases the hold after 48 hours so the slot goes back on the market.
The build itself was AI-assisted from the first line: Claude Code wrote the Cloudflare Worker, the database schema for holds and bookings, the reminder cron, and the booking front end, all under my direction. That part took hours, not weeks. The interesting part of this case study is everything that happened after the first version worked.
The auto-confirm chainWebhook, signature, amount gate, match, claim, mint, confirm
The heart of the system is the moment money meets calendar. When a client pays the $400 fee through PayPal, PayPal sends a webhook event to the scheduler. The worker then runs a chain of checks, in order, and every link in the chain is allowed to stop the process:
- Signature verification. The event is verified against PayPal's verification API before anything else happens. An unsigned or badly signed request is rejected and, if it keeps happening, an alert reaches me, rate-limited so a probe cannot flood my inbox.
- Amount and currency gate. The event must be a completed payment in USD within a dollar of the expected $400 fee. A partial payment, a refund echo, a different product purchased on the same account, none of those can confirm a session.
- Matching. The payment is matched to a pending hold using PayPal's verified payer email against the email the client entered when booking. More on why that exact choice matters below.
- Atomic claim. The matched hold is claimed in the database in a single atomic step, so two events arriving at once cannot both confirm the same booking.
- Fulfillment. Only then are the Zoom meeting, the calendar event, and the confirmation email created.
One concrete lesson from the first live week is worth sharing because it will bite anyone who wires PayPal hosted buttons to webhooks: hosted no-code buttons do not emit the event family you probably subscribed to. My webhook originally listened for the capture-completed event that the REST checkout flow sends. The hosted button fired a sale-completed event instead. The result was a webhook subscription that was configured, signed, and healthy, and heard nothing. The first real payment sat unmatched until the safety net, an owner alert on every unmatched payment, put it in front of me and I confirmed it manually. No money was lost, but "manually" is not the system I was building, so the handler now processes both event families.
The incident: a release email that read like a payment failureA duplicate hold, an honest-but-wrong automated email, and a scared paying client
In the first week of live operation, a marketplace founder booked a session and paid the $400 fee. What I did not see at the time: he had started a booking earlier in the day for one time slot, abandoned it, and then come back and completed a booking for a different slot. The system now held two tentative holds under his email, one abandoned and one paid.
The automation did exactly what it was written to do, which is the most dangerous kind of bug. The sweeper eventually released the abandoned hold and sent its standard notice: your reserved time has been released. To me, reading the code, that email was about the dead 3 p.m. hold. To a client who had just paid $400 for a 5 p.m. session, an email saying his time had been released read like one thing only: the payment failed and the session was cancelled.
He wrote back, worried, asking what had happened to his money. The session itself was never at risk, and no payment was lost. But a paying client spent part of his day believing his $400 might have evaporated, and he had to write to me to find out. In my book that is not a minor UX blemish. It is a client-communication failure delivered by software with my name on it, the same category of failure as a false intake confirmation, which regular readers will recognize from the intake pipeline incident.
That email was the trigger. The same day, I stopped treating the scheduler as finished and put it through the most useful process I know: adversarial review.
The adversarial process: one AI builds, another attacks, I judgeFour rounds, each ending in patches, until the auditor ran out of real findings
Here is the process, concretely. Claude Code, the AI agent that built the system, sat on one side. On the other side I ran a separate frontier model, GPT, with a different instruction: assume this system moves client money and client trust, assume the author is wrong, and find the ways it fails. Not style notes. Failures: race conditions, spoofable inputs, money-matching mistakes, misleading client-facing copy.
Then I did what I do with every redline that comes back from opposing counsel: I read every finding and made a judgment call on each one. Some findings were real and serious. Some were real but acceptable risks at my scale. A few were the auditor hallucinating confidence, which is itself a useful reminder of why neither model gets to grade its own work. Every accepted finding became a patch, written by the building agent, and then the patched system went back to the auditor for another round.
It took four rounds before the auditor's verdict came back as a clean go on every surface: the booking flow, the webhook handler, the sweeper, the reschedule path, and the upload channel. Each round found things the previous round did not, partly because each patch narrowed the noise and let the next audit look deeper.
Alongside the audits, the system gained an offline regression suite, sixteen scenarios covering the ugly cases: replayed webhooks, wrong amounts, two holds under one email, a confirmation that fails halfway through, a reschedule landing on a buffered slot. The suite runs before any deploy. An audit tells you the system is sound today; the tests keep tomorrow's edit from quietly unwinding it.
What the audits caughtAmount gates, idempotency, ambiguity-refusing matching, and honest notifications
The findings that mattered fall into four families, and every one of them maps onto a habit lawyers already have.
1. Amount gates: verify the consideration, not just the event. The first version treated "a completed payment arrived from this payer" as sufficient. The audit pointed out that a $5 payment is also a completed payment. The handler now insists on the right amount in the right currency before a booking can confirm, and it treats an "order approved" event as worthless for fulfillment, because approval is not settlement. Any lawyer who has ever chased a signed-but-unfunded settlement understands that distinction in their bones.
2. Idempotency: the same fact asserted twice is still one fact. PayPal retries webhooks, sometimes minutes later. Without protection, a retry could confirm the same booking twice, or worse, mint two Zoom meetings and email the client two different links. The hardened system deduplicates by transaction id, claims the booking with an atomic database update so concurrent events cannot both win, reuses the already-minted Zoom meeting if a retry arrives after a partial failure, and tears down any orphaned meeting if fulfillment fails midway. The sibling fix on the booking side: when the same email starts a second booking, the system reuses and refreshes the existing fresh hold instead of stacking a duplicate, which is the root cause of the incident above.
3. Payment matching that refuses to guess. This is my favorite finding, because the audit caught a subtle trust inversion. An early draft matched payments partly using a payer-email field that originated on the booking side, where the visitor typed it. That means the matching key was attacker-influenced: untrusted input steering where verified money lands. The fix inverts it: the match runs on PayPal's verified payer email against the stored booking email, and if that fails, a narrow fallback applies only when there is exactly one plausible pending hold in a recent window. If two candidates exist, the system does not pick the likelier one. It stops, holds the money-to-booking question open, and alerts me for a human decision. A system that guesses which client a payment belongs to is a system that will eventually deliver a confirmation, a meeting link, and an implied engagement to the wrong person. Lawyers do not guess about whose money is whose, and neither should their software.
4. Honest notifications, again. Booked clients can upload documents before their session through the booking's workroom page. The early copy said "Sergei is notified automatically" whenever the upload widget finished. The audit asked the right question: does the page actually know that? Now the server reports whether the file was stored and whether my alert was actually sent, and the page only claims automatic notification when both are true. Otherwise it tells the client, honestly, to email me the file. And the release email that started all of this is now duplicate-aware: if the person whose hold expired also has a live paid booking, the email says exactly that, in words a nervous client cannot misread.
Why lawyers already know how to do thisAdversarial review is not a software technique I learned; it is a legal habit I applied
Nothing in the process above is exotic engineering. It is the ordinary discipline of legal work, transplanted. When I draft a contract, the draft is not done when it reads well. It is done when I have attacked it as opposing counsel would and it survived. When I get a settlement agreement from the other side, I do not ask whether it sounds reasonable; I ask what it lets them do to my client in the worst case. Adversarial reading is the core professional skill.
The only new idea here is that frontier AI makes that discipline cheap enough to apply to code. One model builds. A different model, with different blind spots, attacks. The lawyer sits where the lawyer always sits: as the judge of which arguments are real. Four rounds of that cost me an evening. A payment-matching bug that confirmed a session for the wrong client would have cost far more, and not in dollars first.
Two boundaries worth restating. The AI here wrote and audited code; it did not give legal advice, and nothing about this build changes who is responsible for the practice: I am. And this write-up deliberately omits secrets, keys, webhook identifiers, admin endpoints, and internal code, because publishing a candid case study does not require publishing an attack surface.
This is the discipline I bring to AI implementation work for other firms: build with one AI, audit with another, judge as a lawyer, and never let client-facing software say anything a scared client could misread. The wider system this pipeline feeds, from the AI Legal Analyst to the client workrooms, is documented in the Small-Law AI Lab, and the client-facing rooms themselves are on display in the AI legal workrooms showroom.
See the system this pipeline feeds
The booking pipeline is one part of a working attorney-led stack: triage through the AI Legal Analyst, fixed-fee packages, and delivery in custom client workrooms.