GoodOps Voice Memory · how the service flows
Internal · for the team · Oct 2026

One module, four phases, and the information does the handoffs.

Two pictures. The first is what a client goes through, from first visit to retainer. The second is what feeds what: the website, the intake page, the call transcript and the proposal are one chain of data, so the proposal writes itself from what the client already told us.

01 · Service flow

Sell → Sign → Deliver → Grow

Two doors in: the module page, or a conversation where we suggest it and send the booking link. Either way the booking hands them the intake page on the spot and by email; they fill it before the call, on the call, or both, and the proposal with the blueprint follows within 30 minutes.

the wow moment gate the client approvesloops: three nudges, then park
02 · Information flow

The whole chain, step by step: who does what

Four lanes, read top to bottom. The client never fills in the same thing twice; the system carries every answer forward into the next document. A person reviews and approves at the three places that matter.

automatic, live today automatic, to build human reviews
Automatic today

Booking, confirmation and reminders. Intake page autosaves to a shared link, pre-fills from the booking URL, shows the operator live answers and who pressed Send. Call recorded and transcribed. The proposal page renders from one data object. Capture, outcomes and the hygiene digest run on their own once set up.

Automate next, in order

1. Cal.com webhook creates the client's intake link and sends E2. 2. Transcript → structured call notes (hours spent, tool verdicts, history size, where answers land). 3. Intake record + call notes fill the proposal data object. 4. Signature creates the Asana project and sends the access list.

The one thing a human always does

Reads the proposal before it goes out. The data fills it; the operator checks the verdicts and the price band, then presses send. Same at every gate: the system reports, the client approves.

03 · Where it all lives

One record per thing, in the system that owns it

The CRM owns people, organizations and the deal. A small database owns what the pages render from. Shared answers (people, tools, stack, consent) attach to the organization, not the intake, so a second module's intake opens pre-filled and several modules can share one discovery call. Module-specific answers (outcomes) attach to the intake. Asana owns the work, Stripe the money, the e-sign tool the agreement. Every system writes its id back to the CRM engagement, so the engagement is the one place that links everything.

CRM (HubSpot today) database (JSON documents) system of record elsewhere

Organization CRM · company

FieldTypeSet byExample
nametextenrichment; client can correct on the intakeNorthshore Studio
domain · websitetextbooking email → enrichmentnorthshore.ca
size_bandenumenrichment (LinkedIn / site); replaced by the stated counts once given11–50
fte · part_time · contractors · sitesintintake “Your team” step, pre-filled from the booking if known; drives seats and the price band18 · 4 · 6 · 1
industry · typetext · enumenrichmentagency · for-profit / non-profit / public
city · province · timezonetextenrichment; booking timezoneNorth Vancouver · BC · America/Vancouver
stackenumMX records: google / microsoft / mixed / unknowngoogle
detected_toolstext[]website tech + email headers, with confidenceGoogle Meet (0.9), HubSpot (0.8)
voice_stage1 · 2 · 3mini-diagnostic, else call2
lead_source · referred_byenum · contact →page / referral / outbound / eventreferral · Jordan Lee

Contact CRM · contact

FieldTypeSet byExample
first · last · email · phonetextbooking; intake “who else”; callDana Mori · dana@northshore.ca
titletextenrichment (LinkedIn), confirmed on callManaging Director
organization →company iddomain match
engagement_roleenum[]sponsor / decision maker / tools owner / first user / colleague namedsponsor, decision maker
sourceenumbooking / intake / call / enrichment / manualintake (named as “who else”)
recording_consent · opt_outbool · datehouse rules step; any time aftertrue
voice_enrolled_atdateset-up week
linkedinurlenrichment

Engagement CRM · deal · pipeline “Modules”

FieldTypeSet byExample
organization → · sponsor → · tools_owner →idsbooking; intake; call
module · tierenumbooking link or operator. tier is decided by the module count: 1 → single, 2–4 → combo, 5–11 → blueprint, 12 → 360voice-memory · single
stageenumeach step writes its own: booked · intake_started · call_done · proposal_sent · signed · delivering · live · retainer · parked · not_a_fitproposal_sent
booked_at · call_at · call_withdatetime · contact →Cal.comOct 8 10:00 · Kelso
intake_url · intake_sent_aturl · datetimewebhook; client presses Send
top_twotextintakerecap-10-min · ask-anyone
wants · haves · not_needed · unsureintintake (counts; detail lives in the DB)5 · 1 · 1 · 0
tools_count · dont_know_toolsint · boolintake6 · false
amount · currency · deposit_pctmoneyproposal14,500 · CAD · 30
proposal_url · proposal_sent_at · proposal_viewed_aturl · datetimeproposal step; page view beacon
agreement_id · signed_at · deposit_paid_atids · datetimee-sign + Stripe webhooks
start_week · project_iddate · Asana gidsigned stepOct 20
gate1_at · gate2_at · gate3_at · done_means_passdate · boolgate reports
nudge_count · parked_at · parked_reason · not_fit_reasonint · date · textnudge sequence; operator
next_module · retainer_started_atenum · datedone-means review; E8 reply

intakes DB

ColumnTypeNotes
id · slug · engagement_iduuid · text · fkslug is the URL; one intake per engagement, re-runs make a new row with kind = quarterly
module · schema_versiontext · intvoice-memory · 1. A new module ships a new catalog, same tables
kindenumpre-call / quarterly
status · sent_at · last_edited_atenum · datetimeopen / sent
prefilljsonbwhat the booking + enrichment put in: name, org, time, stack, stage
extrajsonbanything a future intake needs that isn’t a column yet

intake_desires DB · one row per outcome shown

ColumnTypeNotes
intake_id · desire_keyfk · textkey from desire_catalog, or null for custom
custom_texttext“in your own words” entries, verbatim
answerenumwant / not_needed / have / unsure / skipped
is_top_twobool
answered_by · answered_atenum · datetimeclient / operator / colleague — the “you” vs “Kelso” tag
call_notetextwhat was said about it on the call (“priority one, 3 h/week”)
go_live_week · verdictint · enumset by the proposal: build / keep / parked

org_tools DB · one row per tool or place, owned by the organization · intakes reference it

ColumnTypeNotes
org_id · tool_key · first_seen_in (intake_id)fk · text · fkkey from tool_catalog, or null. Shared across every module’s intake for this organization
custom_nametext“Something else” entries
placeenumonline / phone / room / on_person / written
who_usesenumeveryone / some / one / unknown
on_by_defaultenumyes / no / unknown
recordings_go_totextfrom the call: “stay in Fathom”, “Drive”, “nowhere”
source · answered_byenumintake / enrichment / call · client / operator / colleague
verdictenumactivate / optimize / migrate / delete / keep / not_needed (operator, on the call)
verdict_why · what_changestextbecome the two lines in the proposal’s four-state plan
access_needed · access_owner →text · contactbecome the access list (E6)

org_people DB · “who else” before they become contacts · owned by the organization

ColumnTypeNotes
org_id · name · role_text · first_seen_infk · text · fkexactly what the client typed: “Priya, our Google Workspace and the Zoom rooms”
email · phonetextoptional; often filled on the call
invited_at · completed_atdatetimethe two-minute tools step sent and finished
contact_id →fkset when promoted to a CRM contact with role tools owner
Also: intake_flags (intake_id, key, value) for things like dont_know_tools, too_small_hint, needs_it_on_call.

calls DB

ColumnTypeNotes
id · engagement_id · intake_idfk
started_at · duration_min · attendees →datetime · int · contactsfrom the calendar
transcript_ref · recording_reftextOtter / Meet ids; never copied into the CRM
why_nowtextQ1, verbatim-ish
hours_per_week · who_spends_itdecimal · textQ2 — the number the price leans on
first_outcomedesire_keyQ4
history_hours · history_placesint · text[]Q8 — sizes the migrate work
outputs_wantedtext[]Q9 — named products where answers must land
recording_language_today · exclusionstext · text[]Q10 — seeds the house rules
dropped_toolstext[]Q7
people_count · client_facing_count · meeting_typesint · int · text[]Q12 — price band + seats
three_yearstextthe closing question, goes on the proposal cover
parkeddesire_key[]explicit “not this”
extracted_by · reviewed_bytext · contactmodel version · the operator who checked it

proposals · agreements · projects · reviews DB

TableColumns
proposalsid · engagement_id · version · module · schema_version · data jsonb (the P object the page renders) · url · status (draft / sent / viewed / accepted / superseded) · price_cad · deposit_pct · sent_at · viewed_at · accepted_at · reviewed_by
agreementsid · engagement_id · proposal_id · provider · envelope_id · signer_contact_id · signed_at · deposit_invoice_id · invoice2_id · invoice3_id · amounts · payment_status
projectsid · engagement_id · asana_gid · template_version · start_week · gate1_at · gate1_pass · gate2_at · gate2_pass · gate3_at · gate3_pass · done_means jsonb (one row per test: name, result, measured_at)
reviewsid · engagement_id · kind (quarterly) · intake_id (the re-run) · now_true desire_key[] · newly_wanted desire_key[] · tools_changed text[] · next_module · reviewed_at
eventsid · engagement_id · type · payload jsonb · at — append-only log of every step (booking.created, intake.sent, proposal.viewed, gate.passed…). This is what writes the CRM stage, so the CRM is never the source of truth.

desire_catalog DB · per module

ColumnTypeNotes
key · module · schema_versiontextrecap-10-min · voice-memory · 1
title · frustrationtextthe two lines on the intake card
needstext[]brain, chat, auto, crm, tasks, calendar, email, notetaker — drives the “what you’ll need” list
default_go_live_week · sort · activeint · int · boolretire or reorder without touching old intakes

tool_catalog DB · shared across modules

ColumnTypeNotes
key · name · vendor · placetext · enumfathom · Fathom · Fathom · online
group · tiertext · enumNotetakers · common / long_tail
logo_asseturlthe file uploaded in the operator view
access_instructions · access_roletext“Upgrade to team plan; invite ops@goodops.io as admin” — pre-fills E6
can_record_by_default · exports_transcripts · apiboolthe facts behind activate / optimize / migrate
detect_signaturestext[]what enrichment looks for to pre-tick it
status · discontinued_atenum · dateLimitless Pendant: discontinued 2025-12
04 · Emails along the way

Eight emails, each with one job.

Short, plain, one link each. Signed by whoever runs the call. E2 and E4 are the two that carry the slice: the intake link and the proposal link. Everything else is a nudge or a note.

CodeTrigger · timingJobStops when
E1 · E1b · E1cInbound email with no booking · same day, +3 d, +7 dOne line with the booking link. The alt version recommends a single tool when it's clearly too small.Booked, or after E1c
E2Call booked · within 10 min (webhook)“You're booked for [time] with [Kelso]. Here's your page.” The intake link, pre-filled. “Fill what you know, skip the rest, forward to IT if you like.”—
E324 h before the callReminder with the meeting link and the intake link; notes what's still blank or says “no need to fill anything, we'll do it live.”—
E4Call ends · within 30 min“Here's the plan we talked through.” The proposal link: blueprint, roadmap, price, e-sign and deposit. “Anything that doesn't match what you said, tell me and I'll change it.”—
E5a · E5b · E5cUnsigned · +2 d, +5 d (start date held until …), +10 dNudge, offer a 10-minute call, then close with the door open.Signed, or after E5c (stage → parked)
E6Signed + deposit · within 1 hKickoff booking link and the access list, one two-minute line per tool with the owner's name. “Nothing is read before kickoff.”—
E7Weekly, Friday, from the Asana projectThree lines: this week, next week, one outcome now true. Need from you: one item or “nothing.”Project closes
E8Done-means review passedTwo ways forward: the retainer (daily checks, monthly review, new features, named contact; deposit credited) or the next module.—