Read the story

Architecture

Saves website leads and puts them on Monday.Replaces Tally and Zapier.

A patient's answers post from an iframe on our websites to forms-public, which saves them and their delivery jobs in one Firestore write. A private worker, woken at once by Cloud Tasks and every minute by Cloud Scheduler, puts the lead on Monday and sends the email copies.

1 min

longest a lead waits for the backup sweep when the instant wake fails

0

answers, names, emails or phones in any log line

14 days

from the first commit to Elmwood's contact form running on it, Oct 14

67 leads

backfilled on Monday with every UTM, the GCLID and the landing page

New York --:-- simulated

A new lead
Hybridge Forms A patient's answers post from an iframe on our websites to forms-public, which saves them and their delivery jobs in one Firestore write. A private worker, woken at once by Cloud Tasks and every minute by Cloud Scheduler, puts the lead on Monday and sends the email copies. reCAPTCHA sees a token, no answers Cloud Tasks send now, 1 attempt Cloud Scheduler sweep every minute OIDC Practice websites iframe from our origin forms-public Cloud Run, 1 kept warm Firestore answers, IP as a hash forms-worker private, holds the token Monday Leads item, UTMs, answers PDF Nightly checks 2 AM, rescues copies Fallback bucket if Firestore fails Forms dashboard IAP, reads audited Gmail copies, send only send one create due jobs new lead token wake sweep now POST /sweep if store fails moved in read, audited email copies

Hover a part to trace what it touches. Click it for its notes.

  • Built by me
  • Managed by Google
  • Outside system
  • Main data path
  • On demand
  • Credentials cross

When it breaks

What each failure does, and what it does not do.

  1. Firestore is down

    The inquiry goes to a private bucket, the patient still sees sent, and the 2 AM job moves it in.

    packages/core/src/submit.ts:164

  2. Both stores are down

    The server answers 503. The browser retries for about 15 seconds with the same id, then keeps the answers on the device with a Try again button.

    docs/security.md:59

  3. Monday refuses a lead

    The delivery retries after 1 minute, 10 minutes and 1 hour, then stops and alerts the developers. The inquiry was saved first, so nothing is lost.

    packages/core/src/delivery/jobs.ts:15

What I tuned

Small choices that keep it fast and cheap.

  1. Wake now, sweep as backup

    A Cloud Tasks wake sends a lead right after the save. The minute sweep stays as the safety net and the retry clock.

    packages/core/src/delivery/wake.ts:1

  2. Answers and jobs, one write

    The inquiry and its delivery jobs are created together, create only, so a retried send makes neither twice.

    packages/core/src/submit.ts:151

  3. One warm public instance

    forms-public keeps one instance running, so the first patient of the morning never waits on a cold start. It is $7.14 of the $8.62 measured in October.

    scripts/deploy.sh:794

Guards

Rules the code enforces, not rules people remember.

  1. Answers never reach logs

    The logger drops 18 field names, answers, email, phone, name and ip among them, and any value that is not plain text, a number or a boolean. Cloud Run request logs for both apps are excluded.

    packages/core/src/log.ts:24

  2. Public side cannot send

    forms-public holds neither the Monday token nor the right to send email. Only the private worker does, and only two accounts may call it.

    docs/security.md:52

  3. Browsers never reach data

    Firestore rules deny every read and write from a browser or phone. A patient's address is stored only as a salted hash.

    firestore.rules:8

What it costs

Measured from the Google Cloud billing export, October 1 to 28, net of credits. A partial month: the first cost landed October 6. One warm public instance is $7.14 of it.

$9in October

  • $7.74 Cloud Run, one warm instance plus requests
  • $0.51 Cloud Build
  • $0.23 Secret Manager
  • $0.14 Scheduler, Firestore, storage, registry