Work · Audit & diagnostic · High-ticket education business
CRM Systems Audit & Cleanup
Years of CRM data, and no way to prove what earned it
A data-rich CRM that nobody trusted — 22.6k contacts, 17 pipelines, 263 uncontrolled tags — audited end-to-end and turned into a decision-ready cleanup and attribution plan, executed without losing a single record.
- GoHighLevel
- Stripe
- Audit
- Attribution
- Role · sole designer & builder
- 8 min
- contacts / opportunities audited
- 22.6k / 8.6kScope
- workflows documented
- 172Scope
- records lost in cleanup
- 0Scope
- System size
- System flow · 9 stages · 44 components reconstructed from the build record
The situation
A high-ticket education business ran everything through one CRM. Enquiries, applications, event registrations, enrolments and payments all landed in the same account, and years of activity had accumulated in it.
The owner had one question, and it was not an exotic one. Which sources, events and reps actually produce revenue that has been collected? Nobody could answer it. Not the team, not the reports, and not the CRM itself.
The account was rich in data and poor in trust. The engagement began as a read-only teardown, before anyone agreed to change a field.
What was broken
- Lead source lived in four competing places: native attribution, a free-text "how did you hear about us" box, UTM fields and tags. Any two of them could disagree about the same person.
- 17 pipelines carried 119 stages. Five were near-identical per-rep boards differing only in whose name was on them, two were reports in disguise, and others were year-on-year duplicates. A deal's status depended on which board it landed on.
- Of 263 tags, roughly 59% were one-per-event date stamps created fresh for every event, in at least four inconsistent formats, some without a year, some with typos.
- Revenue was split across payments, invoices, opportunity fields and contact fields, so "what did we collect" had four possible answers.
- Show and no-show were never captured, and neither was checkout-start, so the funnel had no attendance step at all. The ad pixel id and access token were both empty.
- 172 workflows ran the business, and nobody held a map of what any of them did.
Constraints
- The audit phase had to be structurally incapable of changing the account it measured. Ten written rules governed it: no deletes, no financial changes, no workflow publish or edit, no pipeline restructuring, no mass update without an approved dry run.
- The public API exposes no workflow internals. Through that rail, 172 automations are names and statuses, nothing more.
- Some domains could not be read with the token available. Snapshots and the SaaS API need an agency-scoped token, store and proposals refuse on scope, smart lists sit behind a POST a read-only export declines to call, and courses and memberships have no public API at all.
- Cleanup was blocked on business decisions rather than engineering, so nothing moved until the owner made them.
- Field creation was blocked pending a token with location access, so part of the model shipped as a build spec rather than built fields.
FIG 01 · System Flow
How the system actually runs
Every component below is reconstructed from the project’s own build records. Nothing is added to make it look bigger.
- components
- 44
- components
- stages
- 9
- stages
- decisions & guards
- 6
- decisions & guards
- AI
- 0
- AI
- human handoffs
- 7
- human handoffs
- platforms
- 5
- platforms
- Stage 1, Engagement frame: Make “read-only until signed” structural rather than a promise.
- Written safety rules (Guard, Engagement doc). Ten hard rules govern the engagement: no deletes, no financial changes, no workflow publish or edit, no pipeline restructuring, no mass update without an approved dry run. Connects to Read-only by default; Decision & approval log.
- Read-only by default (Guard, API client). Default to read actions; any non-GET is previewed as a dry run first, and high-risk actions need an explicit confirm plus a matching approval entry. Connects to Public API rail when read actions only; Internal builder rail when read-only browser token.
- Decision & approval log (Human, Engagement doc). Decision, risk and approval logs carried through the whole engagement. Connects to Execute approved only when matching approval entry.
- Stage 2, Read-only capture: Pull every domain of the live account into a preserved local corpus without touching it.
- Live CRM account (Source, GoHighLevel). One sub-account carrying contacts, opportunities, pipelines, conversations, workflows, fields, tags, forms, calendars, products, payments, funnels and users. Connects to Public API rail; Internal builder rail when internals absent from public API; Live reread validation when live reread, 13 days later.
- Public API rail (API / service, REST via MCP). Human-paced read-only pulls across roughly 23 domains, from contacts and opportunities through to media, social and the location profile. Connects to Preserved raw corpus; Named capture gaps when 401 · scope · read-only refusal.
- Internal builder rail (API / service, Workflow builder API). Workflow bodies, triggers and sticky notes for all 172 workflows, captured with a read-only browser token taken from the logged-in session. Connects to Preserved raw corpus.
- Preserved raw corpus (Data store, Local files). 24 per-domain raw JSON files plus a full-corpus workflow capture, exploded into one directory per workflow holding its body, triggers and sticky notes. Connects to Per-domain reports; Workflow doc generator.
- Named capture gaps (Staff alert, Audit index). A published table of what could NOT be seen — snapshots and the SaaS API need an agency-scoped token, store and proposals refuse on scope, smart lists exist only behind a POST a read-only export declines to call, courses and memberships have no public API at all. Connects to Audit findings summary when state what could not be seen.
- Stage 3, Domain inventory: Turn raw captures into counted, per-domain reports — the denominator for every later decision.
- Per-domain reports (Report, Markdown). One numbered report per domain, each stating its authoritative total, its breakdown and its own API limitations. Connects to Contacts & opportunities; Pipelines & stages; Custom fields & tags; Money & touchpoints; Spec vs reality map when the reality side.
- Contacts & opportunities (CRM record, GoHighLevel). 22,616 contacts and 8,608 opportunities, counted unfiltered across all statuses. Connects to Audit findings summary.
- Pipelines & stages (CRM record, GoHighLevel). 17 pipelines carrying 119 stages between them. Connects to Pipeline cleanup list.
- Custom fields & tags (CRM record, GoHighLevel). 233 custom fields (202 on the contact, 10 on the opportunity, 21 on custom objects) and 263 tags. Connects to Attribution blockers when four competing source stores.
- Money & touchpoints (Data store, GoHighLevel + Stripe). Around 1,211 payment transactions, alongside 31 products, 50 forms, 21 calendars and 21,879 conversations. Connects to Attribution blockers when revenue split four ways.
- Attribution blockers (Staff alert, Audit finding). Lead source split across native attribution, a free-text “how did you hear about us” box, UTM fields and tags; the ad pixel id and access token both empty; show/no-show and checkout-start never captured. Connects to Spec vs reality map; Audit findings summary.
- Stage 4, Automation teardown: Recover the step graph the public API cannot see, then classify it by business function.
- Workflow doc generator (Scheduled job, Python script). A deterministic, re-runnable generator that reads the full-corpus capture and emits one markdown document per workflow plus an aggregate index, accumulating the frequency tables as it goes. Connects to 172 workflows documented.
- 172 workflows documented (Data store, Markdown + CSV). Every workflow with its trigger set, its ordered step graph and its sticky notes, plus a machine-readable inventory and the computed corpus totals. Connects to Classified by function; Workflow risk list when published, or referenced by one; Live reread validation when captured doc vs live.
- Classified by function (Report, Markdown + CSV). Every workflow classified by the operation it performs rather than by its name — event registration, event check-in, graduation, sales pipeline, application intake, purchase tagging, nurture, and so on down to abandoned checkout. Connects to Six domain reviews; Disposition plan.
- Six domain reviews (Report, Markdown). Written reviews of six workflow families — sales and leads, events and attendance, student onboarding, payments and products, data-admin-comms, and templates/legacy — deliberately overlapping where one workflow serves two processes. Connects to Workflow risk list; Proposed dashboard pages when workflow health page.
- Workflow risk list (Staff alert, Markdown). Which workflows are dangerous to touch: published and active, duplicates, copies, no-trigger and manual ones, and drafts still referenced by published workflows. Connects to Disposition plan; Proposed cleanup list.
- Live reread validation (Guard, Markdown). A later live reread of the workflows plus separate content-integrity and recording-accuracy validations, checking whether the captured documentation still reflects the live account. Connects to Disposition plan when documentation confirmed accurate; Proposed dashboard pages when cleanup exceptions page.
- Disposition plan (Report, Markdown + CSV). A per-workflow disposition across the whole corpus, plus a duplicate/junk/draft shortlist and a per-event tag automation dependency map. Connects to Decision & approval sheet.
- Stage 5, Spec vs reality: Diff the intended attribution model against what the account actually contains.
- The written spec (Source, Client working guide). The client’s pre-existing field-map and source-link guides — the intended attribution model, on paper. Connects to Spec vs reality map when the spec side.
- Spec vs reality map (Report, Markdown). A field-level diff between the written single-source-of-truth spec and the real account, carrying its own “what still needs eyes” list. Connects to Field cleanup map; Source taxonomy map when source fields; Six reporting fields when named fields do not exist; Audit findings summary.
- Field cleanup map (Report, Markdown). A per-field disposition across all 233 custom fields, deciding which are load-bearing and which are noise. Connects to Proposed cleanup list; Six reporting fields.
- Source taxonomy map (Report, Markdown + CSV). Every current lead-source value in the account mapped onto a proposed normalised category set, with the current-source map kept as a CSV. Connects to Source taxonomy locked.
- Pipeline cleanup list (Report, Markdown). The 17-pipeline, 119-stage estate assessed board by board, with the per-rep duplicates, the reports-in-disguise and the year-on-year copies each given a disposition. Connects to Audit findings summary; Proposed cleanup list.
- Stage 6, Decision package: Convert findings into decisions a non-technical owner can read, answer and sign.
- Audit findings summary (Report, Client doc). A plain-English scale table, six findings, and a five-part pass bar: for any flagship-program buyer, show original source, closer, show status, close status and collected revenue in one place. Connects to Decision & approval sheet.
- Decision & approval sheet (Human, Client doc). The remaining questions that need an owner’s answer, each with a recommended default where one can safely be recommended. Connects to Owner decisions captured when owner answers.
- Proposed cleanup list (Report, Client doc). The specific, prioritised tidy-ups proposed for approval, gated behind an in-depth account audit and an explicit pre-cleanup readiness audit. Connects to Owner decisions captured when owner approves items.
- Owner decisions captured (Human, Audit package). Recorded answers: retire the legacy label, remove the per-rep boards provided no contact is deleted, extend the source taxonomy, treat revenue collected as cash actually received, have advisors set appointment status, and replace endless per-event date tags with structured event records. Connects to Decision & approval log; Read-only scan first when an item is approved; Source taxonomy locked when taxonomy approved; Cash-basis revenue rule when many buyers on instalments; Show status defined when advisors will set call status; Two sibling builds when two future builds named.
- Stage 7, Safety-first cleanup: Execute only approved, reversible changes — each scanned, previewed and reconciled.
- Read-only scan first (Guard, Script). A live read-only snapshot and a reference scan run before each cleanup wave — a field and tag reference scan, a product dependency check — establishing what still points at the thing about to change. Connects to Dry-run preview when dependencies known.
- Dry-run preview (Guard, Script + CSV). Every bulk or structural change previewed as a CSV or markdown packet first — pipeline sample copy, pipeline delete, product archive, name merge, unledgered pipeline review. Connects to Execute approved only when previewed AND approval logged.
- Execute approved only (Scheduled job, API writes). Only items that were approved, in scope, and supported by the token were executed at all. Connects to Rename, never delete when item is a retirement; Scope-blocked write when refused on token scope; Post-change reconciliation.
- Rename, never delete (Outcome, CRM). A duplicate product was renamed to a “legacy — do not use” name and left non-purchasable rather than deleted; ten whitespace-damaged field display names were corrected with ids, keys and data types unchanged. Connects to Post-change reconciliation.
- Scope-blocked write (Staff alert, API). A settings write was refused by the platform for insufficient token scope; an immediate readback proved the setting unchanged, the owner then made the change in the UI, and a later live readback confirmed the new value. Connects to Post-change reconciliation when owner does it in the UI, then re-read.
- Post-change reconciliation (Report, Markdown). A reconciliation document after every wave, with a fresh live readback proving each change landed — and naming the endpoints that were never called. Connects to Read-only scan first when next cleanup wave; Clean reporting entities when account state known-good.
- Stage 8, Attribution model: Lock the reporting semantics, then express them as buildable schema.
- Source taxonomy locked (Outcome, Reporting spec). An 11-value Original Source Category list for rollups, plus an Original Source Detail field holding the exact proof — the campaign, event, form or referrer — owned at contact level. Connects to Six reporting fields.
- Cash-basis revenue rule (Outcome, Reporting spec). Revenue Collected means cumulative payments actually received to date — explicitly not the contracted total, which is tracked separately as deal value. Connects to Six reporting fields.
- Show status defined (Outcome, Reporting spec). A single-option Show Status — scheduled, showed, no-show, rescheduled, cancelled — set at booking and updated after the appointment, owned at opportunity level. Connects to Six reporting fields.
- Six reporting fields (Report, Build spec). A build spec for six fields — source category and source detail on the contact; show status, closer name, close status and revenue collected on the opportunity — each with type, options, owning object and a readiness flag. Connects to Frozen decisions when product, coupon or token unresolved; Clean reporting entities.
- Frozen decisions (Guard, Build spec). Two of the six fields were deliberately left unbuilt — the canonical checkout product was one of three protected candidates and a coupon authority was unresolved — and field creation itself was blocked pending a token with location access. Connects to Clean reporting entities when frozen fields carried as known gaps.
- Stage 9, Reporting blueprint: Define the clean entities and pages the messy CRM must be transformed into.
- Clean reporting entities (Data store, Dashboard design). The dashboard syncs raw CRM data and then transforms it into clean reporting concepts — source category and detail, first landing URL and UTMs, show status, closer, close status, revenue collected, revenue confidence, data quality, the product journey and sales performance by owner. Connects to Proposed dashboard pages.
- Proposed dashboard pages (Report, Dashboard design). Executive overview, product funnel, source attribution, sales team performance, revenue verification, commission verification, pipeline cleanup exceptions, workflow health, and an entry-workshop performance page. Connects to Two sibling builds.
- Two sibling builds (Outcome, Roadmap). The future-items list names exactly the two systems that were built next: an ads analytics dashboard, and event QR check-in written back to the CRM for both event formats.
A buyer of the flagship programme is opened in the CRM and the owner asks: where did they come from, who closed them, and how much did we actually collect?
0/15A buyer of the flagship programme is opened in the CRM and the owner asks: where did they come from, who closed them, and how much did we actually collect?
Read this system as text
Stage 01
Engagement frame
Make “read-only until signed” structural rather than a promise.
- Written safety rules · Guard · Engagement doc — Ten hard rules govern the engagement: no deletes, no financial changes, no workflow publish or edit, no pipeline restructuring, no mass update without an approved dry run. → Read-only by default, Decision & approval log
- Read-only by default · Guard · API client — Default to read actions; any non-GET is previewed as a dry run first, and high-risk actions need an explicit confirm plus a matching approval entry. → Public API rail (read actions only), Internal builder rail (read-only browser token)
- Decision & approval log · Human · Engagement doc — Decision, risk and approval logs carried through the whole engagement. → Execute approved only (matching approval entry)
Stage 02
Read-only capture
Pull every domain of the live account into a preserved local corpus without touching it.
- Live CRM account · Source · GoHighLevel — One sub-account carrying contacts, opportunities, pipelines, conversations, workflows, fields, tags, forms, calendars, products, payments, funnels and users. → Public API rail, Internal builder rail (internals absent from public API), Live reread validation (live reread, 13 days later)
- Public API rail · API / service · REST via MCP — Human-paced read-only pulls across roughly 23 domains, from contacts and opportunities through to media, social and the location profile. → Preserved raw corpus, Named capture gaps (401 · scope · read-only refusal)
- Internal builder rail · API / service · Workflow builder API — Workflow bodies, triggers and sticky notes for all 172 workflows, captured with a read-only browser token taken from the logged-in session. → Preserved raw corpus
- Preserved raw corpus · Data store · Local files — 24 per-domain raw JSON files plus a full-corpus workflow capture, exploded into one directory per workflow holding its body, triggers and sticky notes. → Per-domain reports, Workflow doc generator
- Named capture gaps · Staff alert · Audit index — A published table of what could NOT be seen — snapshots and the SaaS API need an agency-scoped token, store and proposals refuse on scope, smart lists exist only behind a POST a read-only export declines to call, courses and memberships have no public API at all. → Audit findings summary (state what could not be seen)
Stage 03
Domain inventory
Turn raw captures into counted, per-domain reports — the denominator for every later decision.
- Per-domain reports · Report · Markdown — One numbered report per domain, each stating its authoritative total, its breakdown and its own API limitations. → Contacts & opportunities, Pipelines & stages, Custom fields & tags, Money & touchpoints, Spec vs reality map (the reality side)
- Contacts & opportunities · CRM record · GoHighLevel — 22,616 contacts and 8,608 opportunities, counted unfiltered across all statuses. → Audit findings summary
- Pipelines & stages · CRM record · GoHighLevel — 17 pipelines carrying 119 stages between them. → Pipeline cleanup list
- Custom fields & tags · CRM record · GoHighLevel — 233 custom fields (202 on the contact, 10 on the opportunity, 21 on custom objects) and 263 tags. → Attribution blockers (four competing source stores)
- Money & touchpoints · Data store · GoHighLevel + Stripe — Around 1,211 payment transactions, alongside 31 products, 50 forms, 21 calendars and 21,879 conversations. → Attribution blockers (revenue split four ways)
- Attribution blockers · Staff alert · Audit finding — Lead source split across native attribution, a free-text “how did you hear about us” box, UTM fields and tags; the ad pixel id and access token both empty; show/no-show and checkout-start never captured. → Spec vs reality map, Audit findings summary
Stage 04
Automation teardown
Recover the step graph the public API cannot see, then classify it by business function.
- Workflow doc generator · Scheduled job · Python script — A deterministic, re-runnable generator that reads the full-corpus capture and emits one markdown document per workflow plus an aggregate index, accumulating the frequency tables as it goes. → 172 workflows documented
- 172 workflows documented · Data store · Markdown + CSV — Every workflow with its trigger set, its ordered step graph and its sticky notes, plus a machine-readable inventory and the computed corpus totals. → Classified by function, Workflow risk list (published, or referenced by one), Live reread validation (captured doc vs live)
- Classified by function · Report · Markdown + CSV — Every workflow classified by the operation it performs rather than by its name — event registration, event check-in, graduation, sales pipeline, application intake, purchase tagging, nurture, and so on down to abandoned checkout. → Six domain reviews, Disposition plan
- Six domain reviews · Report · Markdown — Written reviews of six workflow families — sales and leads, events and attendance, student onboarding, payments and products, data-admin-comms, and templates/legacy — deliberately overlapping where one workflow serves two processes. → Workflow risk list, Proposed dashboard pages (workflow health page)
- Workflow risk list · Staff alert · Markdown — Which workflows are dangerous to touch: published and active, duplicates, copies, no-trigger and manual ones, and drafts still referenced by published workflows. → Disposition plan, Proposed cleanup list
- Live reread validation · Guard · Markdown — A later live reread of the workflows plus separate content-integrity and recording-accuracy validations, checking whether the captured documentation still reflects the live account. → Disposition plan (documentation confirmed accurate), Proposed dashboard pages (cleanup exceptions page)
- Disposition plan · Report · Markdown + CSV — A per-workflow disposition across the whole corpus, plus a duplicate/junk/draft shortlist and a per-event tag automation dependency map. → Decision & approval sheet
Stage 05
Spec vs reality
Diff the intended attribution model against what the account actually contains.
- The written spec · Source · Client working guide — The client’s pre-existing field-map and source-link guides — the intended attribution model, on paper. → Spec vs reality map (the spec side)
- Spec vs reality map · Report · Markdown — A field-level diff between the written single-source-of-truth spec and the real account, carrying its own “what still needs eyes” list. → Field cleanup map, Source taxonomy map (source fields), Six reporting fields (named fields do not exist), Audit findings summary
- Field cleanup map · Report · Markdown — A per-field disposition across all 233 custom fields, deciding which are load-bearing and which are noise. → Proposed cleanup list, Six reporting fields
- Source taxonomy map · Report · Markdown + CSV — Every current lead-source value in the account mapped onto a proposed normalised category set, with the current-source map kept as a CSV. → Source taxonomy locked
- Pipeline cleanup list · Report · Markdown — The 17-pipeline, 119-stage estate assessed board by board, with the per-rep duplicates, the reports-in-disguise and the year-on-year copies each given a disposition. → Audit findings summary, Proposed cleanup list
Stage 06
Decision package
Convert findings into decisions a non-technical owner can read, answer and sign.
- Audit findings summary · Report · Client doc — A plain-English scale table, six findings, and a five-part pass bar: for any flagship-program buyer, show original source, closer, show status, close status and collected revenue in one place. → Decision & approval sheet
- Decision & approval sheet · Human · Client doc — The remaining questions that need an owner’s answer, each with a recommended default where one can safely be recommended. → Owner decisions captured (owner answers)
- Proposed cleanup list · Report · Client doc — The specific, prioritised tidy-ups proposed for approval, gated behind an in-depth account audit and an explicit pre-cleanup readiness audit. → Owner decisions captured (owner approves items)
- Owner decisions captured · Human · Audit package — Recorded answers: retire the legacy label, remove the per-rep boards provided no contact is deleted, extend the source taxonomy, treat revenue collected as cash actually received, have advisors set appointment status, and replace endless per-event date tags with structured event records. → Decision & approval log, Read-only scan first (an item is approved), Source taxonomy locked (taxonomy approved), Cash-basis revenue rule (many buyers on instalments), Show status defined (advisors will set call status), Two sibling builds (two future builds named)
Stage 07
Safety-first cleanup
Execute only approved, reversible changes — each scanned, previewed and reconciled.
- Read-only scan first · Guard · Script — A live read-only snapshot and a reference scan run before each cleanup wave — a field and tag reference scan, a product dependency check — establishing what still points at the thing about to change. → Dry-run preview (dependencies known)
- Dry-run preview · Guard · Script + CSV — Every bulk or structural change previewed as a CSV or markdown packet first — pipeline sample copy, pipeline delete, product archive, name merge, unledgered pipeline review. → Execute approved only (previewed AND approval logged)
- Execute approved only · Scheduled job · API writes — Only items that were approved, in scope, and supported by the token were executed at all. → Rename, never delete (item is a retirement), Scope-blocked write (refused on token scope), Post-change reconciliation
- Rename, never delete · Outcome · CRM — A duplicate product was renamed to a “legacy — do not use” name and left non-purchasable rather than deleted; ten whitespace-damaged field display names were corrected with ids, keys and data types unchanged. → Post-change reconciliation
- Scope-blocked write · Staff alert · API — A settings write was refused by the platform for insufficient token scope; an immediate readback proved the setting unchanged, the owner then made the change in the UI, and a later live readback confirmed the new value. → Post-change reconciliation (owner does it in the UI, then re-read)
- Post-change reconciliation · Report · Markdown — A reconciliation document after every wave, with a fresh live readback proving each change landed — and naming the endpoints that were never called. → Read-only scan first (next cleanup wave), Clean reporting entities (account state known-good)
Stage 08
Attribution model
Lock the reporting semantics, then express them as buildable schema.
- Source taxonomy locked · Outcome · Reporting spec — An 11-value Original Source Category list for rollups, plus an Original Source Detail field holding the exact proof — the campaign, event, form or referrer — owned at contact level. → Six reporting fields
- Cash-basis revenue rule · Outcome · Reporting spec — Revenue Collected means cumulative payments actually received to date — explicitly not the contracted total, which is tracked separately as deal value. → Six reporting fields
- Show status defined · Outcome · Reporting spec — A single-option Show Status — scheduled, showed, no-show, rescheduled, cancelled — set at booking and updated after the appointment, owned at opportunity level. → Six reporting fields
- Six reporting fields · Report · Build spec — A build spec for six fields — source category and source detail on the contact; show status, closer name, close status and revenue collected on the opportunity — each with type, options, owning object and a readiness flag. → Frozen decisions (product, coupon or token unresolved), Clean reporting entities
- Frozen decisions · Guard · Build spec — Two of the six fields were deliberately left unbuilt — the canonical checkout product was one of three protected candidates and a coupon authority was unresolved — and field creation itself was blocked pending a token with location access. → Clean reporting entities (frozen fields carried as known gaps)
Stage 09
Reporting blueprint
Define the clean entities and pages the messy CRM must be transformed into.
- Clean reporting entities · Data store · Dashboard design — The dashboard syncs raw CRM data and then transforms it into clean reporting concepts — source category and detail, first landing URL and UTMs, show status, closer, close status, revenue collected, revenue confidence, data quality, the product journey and sales performance by owner. → Proposed dashboard pages
- Proposed dashboard pages · Report · Dashboard design — Executive overview, product funnel, source attribution, sales team performance, revenue verification, commission verification, pipeline cleanup exceptions, workflow health, and an entry-workshop performance page. → Two sibling builds
- Two sibling builds · Outcome · Roadmap — The future-items list names exactly the two systems that were built next: an ads analytics dashboard, and event QR check-in written back to the CRM for both event formats.
Platforms · GoHighLevel · Stripe · Workflow builder API · Python · Markdown + CSV
What I found and fixed
Two capture rails, because one was not enough
I pulled roughly 23 domains through the public read-only rail, preserving raw responses verbatim so every published count could be re-derived rather than asserted. That rail cannot see inside a single automation. So workflow bodies, triggers and sticky notes for all 172 workflows came through the platform's internal builder rail instead, using a read-only browser token from the logged-in session.
The result was 24 per-domain raw JSON files plus a full-corpus workflow capture. Alongside the counts I published a table of what could not be seen, each gap with its cause and its remediation. Stating the blind spots in the same index as the findings is what makes the findings credible.
The inventory, counted rather than estimated
The audit covered 22,616 contacts and 8,608 opportunities, counted unfiltered across all statuses, over 17 pipelines and 119 stages. It covered 233 custom fields, 263 tags, around 1,211 payment transactions, 31 products, 50 forms, 21 calendars and 21,879 conversations. Each domain got a numbered report stating its total, its breakdown and its own API limitations.
The inventory also turned up things already working, which the findings summary says out loud. Payments flow cleanly, one calendar is properly tracked, and the platform's event and ticket objects already existed, unused.
172 workflows, documented by a generator
I did not hand-write the workflow documentation. I built a deterministic, re-runnable generator that reads the full-corpus capture and emits one markdown document per workflow plus an aggregate index. Hand documentation of 172 automations cannot be kept in sync, and its totals never reconcile. Computed totals do.
The corpus came to 76 published and 96 draft workflows, 1,872 steps and 217 triggers. Every workflow was then classified by the operation it performs rather than by its name, because a cleanup decision made on what someone once typed into a name field is not a decision at all.
Spec versus reality
The client already had field-map and source-link guides describing the intended attribution model. I diffed that written spec against the real account, field by field. The key finding of the whole audit came out of that diff: the model existed on paper only, and most of the named fields did not exist as dedicated fields anywhere in the account.
From there came a disposition for all 233 custom fields, a value-level map from every current lead-source value onto a normalised category set, and a board-by-board disposition for the 17-pipeline estate. Four competing source stores can only be merged through explicit value-level mapping, never through a rename.
A decision package, not a black box
The audit landed as three documents behind a one-page cover, opening with the plain statement that nothing in the account had been changed. Inside sat a scale table, six findings, and a five-part pass bar: for any flagship-programme buyer, show the original source, the closer, the show status, the close status and the collected revenue in one place. None of the five was reliably answerable that day, and the summary says so.
The owner's answers became the plan. Retire the legacy label, remove the per-rep boards provided no contact is deleted, extend the source taxonomy, treat revenue collected as cash received, have advisors set appointment status, and replace per-event date tags with structured event records.
The attribution model
The locked model uses an 11-value source category field for rollups and a source detail field holding the exact proof, the campaign, event, form or referrer. One category field and one detail field replace four competing stores without losing specificity. Revenue Collected means cumulative payments actually received to date, explicitly not the contracted total, which stays tracked separately as deal value. A single-option Show Status covers scheduled, showed, no-show, rescheduled and cancelled.
That became a build spec for six reporting fields, each with type, options, owning object and readiness flag, plus a nine-page dashboard blueprint. The transform layer is the point: it makes an untrustworthy CRM reportable without first making the CRM perfect.
The faults, one by one
- Some drafts are load-bearing, so "draft means safe to delete" was rejected outright. The risk list names the drafts still referenced by published workflows, alongside the duplicate, copy, no-trigger and manual ones.
- A settings write was refused for insufficient token scope. I recorded the refusal, re-read to prove the setting unchanged, handed the change to the owner to make in the UI, and confirmed the new value on a later live readback.
- Documentation of a live account decays. A re-pull 13 days after capture measured real drift: pipelines and stages unchanged, but products, fields, tags and exported opportunities all higher. That is why the blueprint carries a Workflow Health page and a Cleanup Exceptions page.
- A duplicate product could not be deleted without losing history, so it was renamed to a legacy name and left non-purchasable.
- Ten field display names were damaged by stray whitespace. They were corrected with ids, keys and data types untouched, so nothing that wrote to them broke.
- Two of the six reporting fields could not be defined honestly. The canonical checkout product was one of three protected candidates and a coupon authority was unresolved, so both were left uncreated and marked "do not default".
- Treating an opportunity value as collected revenue was forbidden by rule six. That rule forced the cash-basis definition once instalment buyers turned up in the data.
How I verified it
Every published count is re-derivable from the preserved raw corpus rather than asserted, and the workflow totals reconcile exactly because a generator computes them on each run.
Before each cleanup wave I ran a live read-only snapshot, a field and tag reference scan and a product dependency check, to establish what still pointed at the thing about to change. Every bulk or structural change was previewed as a CSV or markdown packet first, and only approved, in-scope, token-supported items executed at all.
After every wave came a reconciliation document with a fresh live readback proving each change landed, and naming the endpoints that were never called. No product delete, no custom-field delete, no tag delete. That list of uncalled endpoints is what zero records lost actually means here. The execution pass records the same in its own header: live deletes executed, none; workflow edits executed, none.
A later live reread of the workflows, plus content-integrity and recording-accuracy validations, confirmed the captured documentation still reflected the live account.
What changed
22.6k contacts and 8.6k opportunities are audited and accounted for. All 172 workflows are documented step by step, including the graphs the public API cannot see. Every pipeline and every field carries a written disposition, and the cleanup ran with zero records lost.
The owner has a decision-ready audit package instead of a black box: a findings summary, a decision-and-approval sheet, and a prioritised cleanup list. There is a defined revenue-attribution model where there was none, source to rep to verified revenue, with the dashboard blueprint to run it.
Before / after
- Before
- ✕ Lead source lived in four competing places
- ✕ Pipelines fragmented, tags multiplied into the hundreds
- ✕ Revenue split across payments, invoices, and opportunity fields
- ✕ No way to tell which sources or reps produced verified revenue
- After
- ✓ Every workflow, pipeline, and field documented
- ✓ Cleanup executed with zero data loss
- ✓ A decision-ready audit package instead of a black box
- ✓ A defined revenue-attribution model: source → rep → revenue
ScopeFigures on this page describe what was built, covered or verified — not a measured lift.
Related systems
Not a one-off.
High-ticket education business · sibling of CS-03
Ads & sales reporting dashboard
Same client — the attribution model designed here was built into that dashboard.
13purpose-built screens, live on the client’s domainScope
High-ticket education business · sibling of CS-03
Event ticketing & door check-in
Same client — the event revenue this audit couldn't trace, made countable.
48/57tickets scanned at the door — none counted by handScope
Aesthetics clinic · via agency
Live launch & fault triage
Both start by reading the live account before believing anything it reports.
5silent faults fixed in a "finished" buildScope
Have something close to this? Book a call. I’ll tell you if I’ve already built it.
One Manila business day to reply. All client work is under NDA; what you see here is redrawn architecture with real metrics.