nelvori

CRM Health

A read-only look at location hygiene for a small GoHighLevel agency. Suggestions stay suggestions.

Example dataChecking connection

Example data. Harbor Studio, Lumen Dental, Empty Annex, and Partial Import are fictional. They are not a Cadra account or any live GoHighLevel location.

Harbor Studio (example)
Example Harbor Group · example · 6 Nov 2026, 16:00 UTC
37

Provisional score

37 / 100

4 measured · 0 partial · 0 missing · 1 not measured. Weight used 90 of 100.

  • Stage duration: 5 records could not be scored
  • Unowned contacts: 1 record could not be scored
  • Exact duplicates: 1 record could not be scored
  • Unused custom fields: not measured

Provisional. 90 of 100 weight produced a number. Excluded: Unused custom fields. Missing ids, invalid stage timestamps, and unmeasured custom-field usage cannot become a clean score.

Prioritized fixes
Suggestion only. Nothing here assigns an owner, merges a contact, edits a tag, moves a deal, or sends a message.

Stage duration

20 / 100Weight 30

2 open opportunities are at 30+ days in stage, 2 at 14+ days. 5 records could not be timed. This is stage duration, not inactivity.

Alerts stay on this page. Nothing is emailed, Slacked, or scheduled.

  • Harbor civic site has been in stage 40 days

    alert

    Open opportunity, 40 days since lastStageChangeAt (threshold 30). This is a stage-duration alert, not a judgment that the deal is inactive. Suggestion only.

    New business · Proposal · Avery Chen

  • North pier workshop has been in stage 18 days

    watch

    Open opportunity, 18 days since lastStageChangeAt (threshold 14). This is a stage-duration alert, not a judgment that the deal is inactive. Suggestion only.

    New business · Contract · Jordan Hale

  • Blair renewal has been in stage 91 days

    alert

    Open opportunity, 91 days since lastStageChangeAt (threshold 30). This is a stage-duration alert, not a judgment that the deal is inactive. Suggestion only.

    Renewals · Annual review · Unassigned

  • Proposal with no stage time has no usable stage timestamp

    Data gap

    Stage duration uses lastStageChangeAt only. Created, updated, and last-action times were not used, and this deal was not called fresh.

    New business · Proposal · Avery Chen

  • Quote with a broken timestamp has no usable stage timestamp

    Data gap

    Stage duration uses lastStageChangeAt only. Created, updated, and last-action times were not used, and this deal was not called fresh.

    Renewals · Quote · Jordan Hale

  • Opportunity has no id

    Data gap

    This record was excluded from stage-duration scoring. A missing id is not treated as a fresh or healthy deal.

    New business

  • Future stage clock has a future stage timestamp

    Data gap

    A stage timestamp after the score clock cannot be turned into days in stage. It was not counted as fresh.

    New business · Intro · Avery Chen

  • Opportunity status is not usable

    Data gap

    Only status open is scored. Won, lost, and abandoned are closed. Any other status is unknown and was not assumed open or inactive.

    New business · Proposal · Jordan Hale

  • Fourteen-day proposal has been in stage 14 days

    watch

    Open opportunity, 14 days since lastStageChangeAt (threshold 14). This is a stage-duration alert, not a judgment that the deal is inactive. Suggestion only.

    New business · Proposal · Sam Okonkwo

GoHighLevel connection
Checking whether a server-side connection exists.
Billing
Disabled. No Stripe product, price, or checkout exists in this MVP.

Marketplace publishing, charges, and any write into GoHighLevel are outside this build. An earlier $39–$79 range was a planning assumption, not an offer.

How the provisional score is calculated

Model v0.1.0. Weights: stage duration 30, unowned contacts 25, exact duplicates 20, tag families 15, unused fields 10.

Stage duration: round(clamp(100 * (1 - (alerts + 0.5 * watches) / known) * known / (known + gaps), 0, 100)). Uses lastStageChangeAt on open opportunities only.

Unowned: round(100 * owned / (owned + unowned + unknownOwners + missingIds)).

Duplicates: round(100 * (1 - unresolved / contacts)).

Tag families: round(100 * (1 - candidateTags / tags)).

Unused fields: round(100 * (1 - unused / fieldsWithId)), and only after a complete catalog plus a complete usage census. Otherwise the check is not measured.

The overall number is the weighted average of checks that produced a number. It is authoritative only when every check is measured and there are no data gaps. Scored as of 6 Nov 2026, 16:00 UTC.