This page captures everything we need to review before building: the full revised workflow, each new mechanic drawn out as its own flow, the condo white-label pilot scope, and what we reuse, build, and defer. It supersedes the earlier connection-unlock flow.
Start with the condominium white-label wedge. We hand-pick a few pros who already work in one condo, onboard them ourselves, and let a handful of that building's tenants request jobs and pay through the platform. One building, a closed loop, real money — the cheapest way to prove the whole flow end-to-end before we automate anything.
A building is a ready-made dense cluster with a trusted brand (the administration) and pros who are already on-site. It collapses the two hardest cold-start problems — supply and demand density — into a single, warm, bounded environment we can support by hand.
The full loop for one job, with the NEW mechanics highlighted. Each new step gets its own section below.
flowchart TD A1["Tenant requests a job
(building-branded portal)"]:::cust --> A2["Chat intake → brief + photos"]:::cust A2 --> S1{"Pro sourcing"}:::new S1 -->|"1 · in-platform pros (priority)"| OUT["Outreach to matched pros"]:::admin S1 -->|"2 · fallback: Google API"| OUT S1 -->|"3 · last resort: admin manual"| OUT OUT --> E["Pros submit free initial estimates"]:::pro E --> CMP["Tenant compares & selects a pro"]:::cust CMP --> ACC["Pro accepts the connection"]:::pro ACC --> COMMS["Platform-intermediated chat opens
(no contact details shared)"]:::new COMMS --> FQ["Pro sends firm quote → tenant accepts
(price locked, before dispatch)"]:::new FQ --> ARR["Pro travels & arrives"]:::pro ARR --> VER["On-site scope confirmation
reality matches the brief?"]:::new VER -->|"scope changed → no agreement"| CX["Clean cancel · trip fee by fault"]:::sys VER -->|"confirmed / re-quote accepted"| PAY["Tenant pays now (card on file)
captured via ONVO"]:::new PAY --> WORK["Pro does the work"]:::pro WORK --> HOLD["24-hour refund window
customer protection"]:::new HOLD --> REL["Window closes · payment final
(pro settled, minus fee)"]:::sys REL --> REV["Two-sided reviews"]:::sys REV --> Q{"Both positive?"}:::sys Q -->|"yes + both agree"| CAD["Set a recurring cadence
(card on file)"]:::new Q -->|"no"| DONE(["Done"]):::sys classDef cust fill:#EAF6F2,stroke:#0F7458,color:#08382C; classDef pro fill:#FDF6EA,stroke:#C57D17,color:#7a4d09; classDef admin fill:#EBE2D3,stroke:#6B7B77,color:#1B2A28; classDef sys fill:#ffffff,stroke:#1C8F73,color:#1B2A28; classDef new fill:#EFE9FB,stroke:#7C5CBF,color:#3f2a73;
When a job comes in, we prioritise pros already on the platform; only if none fit do we reach outward.
flowchart TD
J["New job brief"]:::sys --> P1{"In-platform pros match
category + building/area?"}:::new
P1 -->|"yes"| USE["Use in-platform pros
(priority)"]:::new
P1 -->|"none"| P2{"Google Places API
find local candidates"}:::new
P2 -->|"found"| IMP["Import as candidate pros"]:::admin
P2 -->|"none / low quality"| P3["Admin manual review
(hand-source)"]:::admin
USE --> OUT["Outreach → estimates"]:::sys
IMP --> OUT
P3 --> OUT
classDef sys fill:#ffffff,stroke:#1C8F73,color:#1B2A28;
classDef admin fill:#EBE2D3,stroke:#6B7B77,color:#1B2A28;
classDef new fill:#EFE9FB,stroke:#7C5CBF,color:#3f2a73;
We no longer unlock phone/email on connection. All tenant↔pro communication runs through the platform.
sequenceDiagram
actor T as Tenant
participant TT as True Trade
actor P as Pro
T->>TT: Message (scheduling, access notes)
TT->>P: Relayed — no phone / email shared
P->>TT: Reply
TT->>T: Relayed
Note over TT: Full thread retained for safety & dispute evidence
We bind the price before the pro travels. After selection the pro turns the estimate into a firm quote the tenant accepts; on arrival the step is a quick scope check, not a renegotiation — so a pro never drives out on an un-agreed price.
flowchart TD SEL["Tenant selects pro"]:::cust --> FQ["Pro sends firm quote
(pre-dispatch)"]:::new FQ --> ACK["Tenant accepts → price locked"]:::new ACK --> ARR["Pro travels & arrives"]:::pro ARR --> CHK{"Scope matches
the brief?"}:::new CHK -->|"yes"| GO["✅ Confirm → payment captured → work begins"]:::new CHK -->|"no · evidenced
scope change"| REP["Pro re-prices with photo"]:::new REP -->|"tenant accepts"| GO REP -->|"declines · good faith"| WV["Clean cancel · trip fee waived / split"]:::sys CHK -.->|"tenant backs out
at locked price"| FEE["Trip fee → pro"]:::new classDef cust fill:#EAF6F2,stroke:#0F7458,color:#08382C; classDef pro fill:#FDF6EA,stroke:#C57D17,color:#7a4d09; classDef sys fill:#ffffff,stroke:#1C8F73,color:#1B2A28; classDef new fill:#EFE9FB,stroke:#7C5CBF,color:#3f2a73;
firmQuote at selection. On arrival the pro taps "scope matches → start" (captures payment) or "scope changed → re-quote" (photo required, tenant re-accepts).The tenant pays the moment work starts (on cost agreement) and has 24 hours to flag a problem — a protection window backed by a refund. Same safety for both sides; for the pilot it's built as an immediate charge + refund window rather than a literal held payout.
stateDiagram-v2
[*] --> awaiting_confirmation: pro on site (price already locked)
awaiting_confirmation --> cancelled: scope mismatch · trip fee by fault
awaiting_confirmation --> captured: scope confirmed → tenant charged, pro settled
captured --> refund_window: work begins (24h window)
refund_window --> final: +24h, no dispute → settled, window closes
refund_window --> disputed: tenant flags an issue in-window
disputed --> final: admin resolves for pro
disputed --> refunded: admin resolves for tenant → refund
final --> [*]
refunded --> [*]
cancelled --> [*]
After the job, both rate each other. Only if both reviews are positive — and both opt in — does the job become a standing cadence.
flowchart LR
REL["Payment final"]:::sys --> R1["Tenant reviews pro"]:::cust
REL --> R2["Pro reviews tenant"]:::pro
R1 --> Q{"Both positive?"}:::sys
R2 --> Q
Q -->|"no"| END(["Done — one-off"]):::sys
Q -->|"yes"| OFFER["Offer a recurring cadence"]:::new
OFFER -->|"both agree"| PLAN["Standing plan
(card on file, auto-scheduled)"]:::new
OFFER -->|"either declines"| END
classDef sys fill:#ffffff,stroke:#1C8F73,color:#1B2A28;
classDef cust fill:#EAF6F2,stroke:#0F7458,color:#08382C;
classDef pro fill:#FDF6EA,stroke:#C57D17,color:#7a4d09;
classDef new fill:#EFE9FB,stroke:#7C5CBF,color:#3f2a73;
At pilot scale we do a lot by hand on purpose. This is what each role does, and what's automated vs. a human (us) in the loop.
| Role | In the MVP they… | Mode |
|---|---|---|
| Tenant (customer) | Request a job, compare, select, chat in-app, verify cost on-site, pay, review, opt into cadence. | Self-serve |
| Pro | Get onboarded by us, receive outreach, estimate, accept, chat, confirm cost on-site, do the work, get paid +24h, review. | Hand-onboarded then self-serve |
| Admin (us) | Seed pros, qualify jobs, run sourcing fallback by hand, monitor threads, mediate disputes, release/verify payouts. | Hands-on |
| Building (condo admin) | Lend the brand, greenlight the pilot, point tenants to the portal. Sees privacy-safe activity only. | Partner |
Much of the loop already exists as a demo. The MVP is mostly the new mechanics + real payments on top.
| Capability | Status | Notes |
|---|---|---|
| Chat intake → brief + photos | Reuse | Exists; add building-branded entry. |
| Building-branded portal (white-label) | Reuse | Org/building model + /b/:slug exist. |
| Compare → select → pro accepts | Reuse | Exists in demo. |
| Two-sided reviews + recurring plans | Reuse | Add the mutual-positive + opt-in gate. |
| Admin console (jobs, pros, connections) | Reuse | Add sourcing-fallback + payout controls. |
| Pro sourcing priority + fallback | Build | In-platform live; Google + manual stubbed. |
| Intermediated comms thread | Build | In-app thread per connection; no SMS yet. |
| Firm quote (pre-dispatch) + on-site scope confirm | Build | Lock quote at selection; scope check triggers payment; trip-fee backstop. |
| Real payments + 24h refund window | Build | Card on file; immediate capture + refund window. |
| Pro payout onboarding | Build | Processor sub-account / KYC, done with us. |
| Dispute handling (in 24h window) | Build | Minimal: freeze + admin decides. |
| Google Places integration (live) | Defer | Stub now, build when supply needs it. |
| SMS / phone masking | Defer | In-app messaging is enough for pilot. |
| Self-serve pro onboarding | Defer | White-glove by hand at pilot scale. |
| Native apps · multi-building · automation | Defer | Mobile web; one building; humans in loop. |
What the new mechanics add on top of the existing entities (Job, Pro, Connection, Estimate, Review, RecurringPlan, Organization, Home).
| New / changed | Shape |
|---|---|
| Job.sourcing | source: in_platform | google | admin_manual + sourcing status, so we can see how each job was filled. |
| MessageThread (new) | One per Connection. messages[] (sender, body, ts), visible to tenant, pro, admin. |
| Connection.quote (new) | firmQuote, quoteLockedAt (pre-dispatch), scopeConfirmedAt, reprice{amount, photo}, status: quoted|locked|confirmed|repriced|cancelled, tripFee{amount, owedBy}. |
| Payment (new) | amount, fee, state: captured|refund_window|refunded|disputed, capturedAt, refundWindowUntil (+24h), processorRef. |
| Pro.payoutAccount (new) | Processor sub-account id + KYC/onboarding status (can they receive payouts yet?). |
| RecurringPlan.gate | Require bothReviewsPositive && bothOptedIn before a plan can be created. |
| Dispute (new, minimal) | connectionId, raisedBy, reason, resolution: upheld|refunded, decidedBy. |
A suggested order — each milestone is independently demoable, so we can review as we go.