Every quote, booking and move, planned before a single node is placed.
Six n8n workflows and three helpers for Trusted Mover, a moving company in Austin, TX. Claude reads and drafts, Supabase holds the jobs, Google Sheets holds the schedule, Stripe takes the money, and a person approves anything a client sees.
- n8n
- Supabase
- Claude
- Gmail
- Google Sheets
- Cal.com
- Telegram
- Stripe
Before anything is built
The outcome, the entry points and every way a job ends.
Outcome
Every quote is answered within 5 minutes, and nothing a client sees goes out without a person saying yes.
Entry points
Eight entry points: a web form, Cal.com, Stripe, two schedules, a sheet edit, a change form and n8n’s own error trigger.
Every way a job ends
- Closed
- Lost
- Cancelled
- Claim open
Out of scope
- Payroll
- Truck GPS
- Accounting exports
Naming convention
One vocabulary for workflows, nodes, links, keys and fields.
Workflows
TM-[number] · [Trigger] → [Outcome] TM-01 · Quote request → Qualified lead TM-H02 · Notify ops
The number is the build order. Helpers get an H.
Nodes
[Verb] [object] Find contact Draft estimate Log the quote
Default names like “HTTP Request1” never survive a build.
Webhook paths
/tm/[source] /tm/quote /tm/change /tm/stripe
One path per source, all lowercase.
Credentials
TM · [Service] ([mode]) TM · Stripe (live) TM · Stripe (test)
Test and live keys never share a name.
Supabase fields
snake_case, _at for times, _usd for money deposit_paid_at estimate_usd
The suffix tells you the unit.
Sheets tabs
Title Case, one job per tab Crew schedule Rate card
Workflows read columns by header name, never by position.
Where the data lives
Three tables, seven stages and five sheet tabs.
contacts
id name email phone created_at
One row per person, matched on email or phone.
jobs
id contact_id stage move_date size from_zip to_zip estimate_usd deposit_paid_at crew nudges_sent source
One row per move.
events
job_id type payload at
Every workflow writes one, so the next run knows what already happened.
Job stages
| Sheets tab | What it holds |
|---|---|
| Quote log | Every request, as it arrived |
| Rate card | Hourly rates, truck fees and materials |
| Crew schedule | One row per booked move. The crew edits it. |
| Test matrix | The test rows below, with results |
| Errors | One row per failed run |
What starts each workflow
Every entry point, and the workflow it runs.
Quote request → Qualified lead
14 nodes, 2 decisions.
Outcome
Every quote becomes a job, or one clear question, within 5 minutes.
Trigger
The quote form on the site posts to /tm/quote.
Exits
- Merged into an open job
- Waiting on one answer
- Survey link sent
- Quote form (Starts it, POST /tm/quote), then Normalize fields
- Normalize fields (Step, Edit Fields), then Find contact
- Find contact (Step, Supabase), then Duplicate in 24 h?
- Duplicate in 24 h? (Decides, If), then Extract move details if no; Merge into open job if yes
- Merge into open job (Ends, Supabase)
- Extract move details (Claude, Claude), then Anything missing?
- Anything missing? (Decides, If), then Create job if no; Draft one question if yes
- Draft one question (Claude, Claude), then Ask the client
- Ask the client (Step, Gmail), then Stage 0 · needs info
- Stage 0 · needs info (Ends, Supabase)
- Create job (Step, Supabase), then Log the quote
- Log the quote (Step, Google Sheets), then Notify ops
- Notify ops (Helper, TM-H02), then Send survey link
- Send survey link (Ends, Gmail)
Survey → Estimate sent
15 nodes, 2 decisions.
Outcome
Every survey ends with an approved estimate and a deposit link, the same day.
Trigger
Cal.com posts BOOKING_CREATED to /tm/survey.
Exits
- Estimate sent
- Sent back to the owner
- No-show, rebook link sent
- Survey booked (Starts it, Cal.com), then Stage 1
- Stage 1 (Step, Supabase), then Business hours
- Business hours (Helper, TM-H03), then Until 24 h before
- Until 24 h before (Waits, Wait), then Reminder
- Reminder (Step, Gmail), then Until the survey ends
- Until the survey ends (Waits, Wait), then Survey done?
- Survey done? (Decides, If), then Price from rate card if yes; Rebook link if no
- Rebook link (Ends, Gmail)
- Price from rate card (Step, Google Sheets), then Draft estimate
- Draft estimate (Claude, Claude), then Owner approves
- Owner approves (Person approves, Wait on form), then Approved?
- Approved? (Decides, If), then Deposit link if yes; Back to owner if no
- Back to owner (Ends, Telegram)
- Deposit link (Step, Stripe), then Send estimate
- Send estimate (Ends, Gmail)
Deposit paid → Move booked
13 nodes, 1 decision.
Outcome
A paid deposit books the move, and an unpaid estimate never sits forgotten.
Trigger
Stripe checkout.session.completed, plus an hourly sweep.
Exits
- Booked
- Nudged
- Lost after two nudges
- Deposit paid (Starts it, Stripe), then Verify signature
- Verify signature (Step, Code), then Find job
- Find job (Step, Supabase), then Stage 3 · Booked
- Stage 3 · Booked (Step, Supabase), then Add to crew schedule
- Add to crew schedule (Step, Google Sheets), then Confirmation
- Confirmation (Step, Gmail), then Notify ops
- Notify ops (Helper, TM-H02)
- Every hour (Starts it, Schedule), then Unpaid estimates
- Unpaid estimates (Step, Supabase), then Nudges sent?
- Nudges sent? (Decides, Switch), then Nudge 1 if 0; Nudge 2 if 1; Stage · Lost if 2
- Nudge 1 (Ends, Gmail)
- Nudge 2 (Ends, Gmail)
- Stage · Lost (Ends, Supabase)
Move week → Crew dispatched
9 nodes, 2 decisions.
Outcome
Every move has a crew, and a client who knows when they arrive.
Trigger
Schedule, every day at 07:00 Central.
Exits
- Packing guide sent
- Crew confirmed
- Ops alerted
- Arrival window sent
- Every day, 07:00 (Starts it, Schedule), then Upcoming moves
- Upcoming moves (Step, Supabase), then Days out?
- Days out? (Decides, Switch), then Stage 4 · Move week if 7; Crew assigned? if 2; Arrival window if 1
- Stage 4 · Move week (Step, Supabase), then Packing guide
- Packing guide (Ends, Gmail)
- Crew assigned? (Decides, If), then Crew confirmation if yes; Alert ops if no
- Crew confirmation (Ends, Gmail)
- Alert ops (Helper, TM-H02)
- Arrival window (Ends, Gmail)
Move done → Paid + reviewed
14 nodes, 1 decision.
Outcome
Every finished move is paid, and every reply lands with the right person.
Trigger
The crew sets status to Done on the Crew schedule sheet.
Exits
- Closed with a review link
- Claim open
- Owner follow-up
- Crew marks done (Starts it, Google Sheets), then Stage 5 · Moved
- Stage 5 · Moved (Step, Supabase), then Final invoice
- Final invoice (Step, Stripe), then Until paid
- Until paid (Waits, Wait), then 1 day
- 1 day (Waits, Wait), then Feedback question
- Feedback question (Step, Gmail), then Until reply
- Until reply (Waits, Wait), then Sort the reply
- Sort the reply (Claude, Claude), then Kind?
- Kind? (Decides, Switch), then Review link if praise; Open a claim if damage; Task for the owner if complaint
- Review link (Step, Gmail), then Stage 6 · Closed
- Stage 6 · Closed (Ends, Supabase)
- Open a claim (Step, Supabase), then Alert ops
- Alert ops (Helper, TM-H02)
- Task for the owner (Ends, Telegram)
Cancel or reschedule
11 nodes, 2 decisions.
Outcome
Changes are handled the same day, with refunds that follow the policy exactly.
Trigger
The reschedule form posts to /tm/change.
Exits
- New date confirmed
- Three dates offered
- Cancelled and refunded
- Change request (Starts it, POST /tm/change), then Find job
- Find job (Step, Supabase), then Type?
- Type? (Decides, Switch), then Check crew calendar if reschedule; Refund by policy if cancel
- Check crew calendar (Step, Google Sheets), then Slot free?
- Slot free? (Decides, If), then Move the date if yes; Offer 3 dates if no
- Move the date (Ends, Supabase + Sheets)
- Offer 3 dates (Ends, Gmail)
- Refund by policy (Step, Code), then Stripe refund
- Stripe refund (Step, Stripe), then Stage · Cancelled
- Stage · Cancelled (Step, Supabase), then Confirm
- Confirm (Ends, Gmail)
Helpers
Three small workflows the others call.
TM-H01 · Error handler
Every failure is logged and reaches ops with a link to the run.
- Any failure (Starts it, Error Trigger), then Log it
- Log it (Step, Google Sheets), then Tell ops
- Tell ops (Ends, Telegram)
TM-H02 · Notify ops
One place formats every ops message.
- Called by a workflow (Starts it, Execute Workflow Trigger), then Format
- Format (Step, Edit Fields), then Send
- Send (Ends, Telegram)
TM-H03 · Business-hours gate
Nobody gets a message at 2 am.
- Called by a workflow (Starts it, Execute Workflow Trigger), then Next allowed time
- Next allowed time (Step, Code), then Wait until then
- Wait until then (Waits, Wait)
Test matrix
One row per branch. Tick each one as it passes.
| Passed | ID | Workflow | Branch | Test | Expected |
|---|---|---|---|---|---|
t01 | TM-01 | New quote | A fresh email with every field filled | Job at stage 0, a Quote log row, survey link sent | |
t02 | TM-01 | Duplicate | The same email again within 24 h | Notes merged into the open job, no second job | |
t03 | TM-01 | Missing detail | No move date | One question sent, stage 0 needs info | |
t04 | TM-01 | Bad JSON from Claude | Force a parser error | TM-H01 logs it and pings Telegram | |
t05 | TM-02 | Reminder | Survey booked for Tuesday 10:00 | Reminder arrives Monday 10:00 CT | |
t06 | TM-02 | No-show | No inventory form within 3 h | One rebook link sent | |
t07 | TM-02 | Owner approves | Approve the draft | Estimate and 20% deposit link sent, stage 2 | |
t08 | TM-02 | Owner edits | Send it back with a note | Note on Telegram, nothing sent to the client | |
t09 | TM-03 | Deposit paid | Stripe test card 4242 4242 4242 4242 | Stage 3, a crew schedule row, confirmation sent | |
t10 | TM-03 | Replayed event | Resend the same Stripe event | Still one booking and one email | |
t11 | TM-03 | Unsigned call | POST without Stripe-Signature | Rejected with 400, nothing changes | |
t12 | TM-03 | Unpaid for 72 h | An estimate sent 73 h ago | Nudge 1, then nudge 2, then Lost | |
t13 | TM-04 | Seven days out | Move date is today + 7 | Stage 4, packing guide sent | |
t14 | TM-04 | No crew | Move in 2 days, crew blank | Urgent Telegram alert | |
t15 | TM-04 | Day before | Move date is tomorrow | Arrival window email | |
t16 | TM-05 | Paid and happy | Pay the invoice, reply “Great crew” | Review link sent, stage 6 | |
t17 | TM-05 | Damage | Reply “They scratched the table” | Claim event and urgent alert, no review link | |
t18 | TM-06 | Reschedule, slot free | A new date with a free crew | Date moved, schedule updated, confirmation sent | |
t19 | TM-06 | Cancel 10 days out | Cancel a move 10 days away | Half the deposit refunded, stage Cancelled | |
t20 | TM-06 | Cancel 3 days out | Cancel a move 3 days away | No refund, still confirmed |
Build order
Five phases, in the order they are built.
1 · Foundation
- Create the Supabase tables: contacts, jobs, events
- Add the credentials: TM · Supabase, TM · Stripe (test), TM · Gmail, TM · Telegram, TM · Anthropic
- Create the Sheets tabs: Quote log, Rate card, Crew schedule, Test matrix, Errors
- Build TM-H01, TM-H02 and TM-H03, and set TM-H01 as the error workflow
2 · Intake
- Build TM-01 and point the site’s quote form at /tm/quote
- Build TM-02 with the Cal.com webhook and the approval form
- Run tests t01–t08
3 · Money
- Build TM-03 with the Stripe webhook and the hourly sweep
- Build TM-05 with the invoice and the feedback steps
- Run tests t09–t12, t16 and t17
4 · Operations
- Build TM-04
- Build TM-06 with the refund policy
- Run tests t13–t15 and t18–t20
5 · Test and hand over
- Switch Stripe to live keys
- Run the whole matrix again with a real card and a real refund
- Walk the owner through the handover page
- Turn every workflow on
Handover
What the owner keeps.
What it does
- Answers every quote within 5 minutes, or asks one question.
- Books surveys and drafts estimates for you to approve.
- Takes deposits, fills the crew calendar and chases unpaid estimates.
- Reminds clients and crews before every move.
- Invoices, asks for feedback and sends damage reports to you.
What breaks it
- Renaming or moving a column in the Crew schedule sheet.
- An expired or rotated Stripe key.
- Changing the Cal.com event type or its questions.
- Turning a workflow off without turning off its schedule.
Who to call
Justine Acosta, who built it. Errors already reach the ops Telegram group with a link to the run.
Where the logs are
Every failure is a row in the Errors tab and a Telegram message. Every job change is a row in the Supabase events table.