KOD
← Back to hub
DRAFTA working draft — not yet checked against reality by a person. Read it, but confirm before you rely on it.

GHL Sync + David Handoff + Surplus/Redemption

GHL bridge — the sync + handoff flow from KOD into GoHighLevel. · tap to zoom & pan

PURPOSE

Keep the KOD app and GoHighLevel in sync every day so a lead's badge and stage in the app drives the exact same sequence GHL is sending, and every GHL result (a reply, a booked call, an opt-out, a completed call) flows back into the app as a disposition without anyone re-typing it. This also covers the post-sale product: when a sale happens before it can be stopped, the surplus-funds or redemption-rights recovery play keeps the lead alive as revenue instead of closing it out dead.

WHEN TO USE

  • Every day, to confirm new leads pushed into GHL landed in the correct workflow and pipeline stage
  • Any time a disposition happens in GHL (a reply, a booked appointment, an opt-out, a call outcome) and needs to show up back in the KOD app
  • A lead's sale has already happened and needs to be checked for surplus funds or redemption rights instead of being marked dead
  • David (GHL builder) delivers a new batch of workflow IDs, pipeline IDs, or field IDs that need to be plugged in

PREREQUISITES

  • GHL_ENABLED is on and the Private Integration Token, Location ID, and workflow IDs from David's handoff sheet are already configured
  • The KOD Lead ID custom field (kod_lead_id) exists in GHL under the KOD Sync folder and is never edited by hand
  • The webhook receiver at /api/integrations/ghl/webhook is live and the x-ghl-secret header value matches what is configured in GHL's Result Sync workflow
  • A2P 10DLC registration is approved before any SMS workflow goes live, unregistered texting is blocked by carriers

STEPS

  1. When a KOD lead is created or enriched, the app calls upsertGhlContact to push it into GHL as a Contact carrying the hidden kod_lead_id field. Confirm the contact landed correctly, this is the bridge the whole sync depends on.
  2. The app then calls addContactToWorkflow to enroll the contact in the matching pf:* sequence's GHL workflow. Match each KOD sequence to its GHL workflow by name exactly: pf:pre_nod to PF — Pre-NOD Seed, pf:nod to PF — NOD Speed-to-Lead, pf:middle to PF — Mid-Runway, pf:last10 to PF — Last 10 Days, pf:past_sale to PF — Post-Sale, pf:gatekeeper to PF — Gatekeeper, pf:expired to PF — Expired Blue Ocean, pf:long_term to PF — Long-Term, pf:doubt_bk/pf:doubt_mod/pf:doubt_reinstate to their matching Doubt-Cast workflows, pf:probate_heir to PF — Probate Heir, pf:divorce_stage1/pf:divorce_stage2 to the Divorce Stage workflows, pf:surplus to PF — Surplus Recovery, pf:redemption to PF — Redemption, and pf:tax to PF — Tax Delinquent. Remember the core stage sequences each carry Gold/Silver/Bronze tier variants because the badge controls how hard GHL works the lead, same as it does inside the KOD app.
  3. Confirm the lead's card sits on the correct pipeline. KOD Acquisition is the main seller board (New Lead through Closed). KOD Surplus Recovery is the separate post-auction board (Auction Passed through Recovered). KOD Disposition tracks David's cash-buyer side (Buyer Inquiry through Funded). Do not put re-default, probate, divorce, or tax leads on their own boards, they stay on Acquisition and are worked by their own sequence.
  4. Remember the pipeline stage and the foreclosure stage are two different things. The pipeline stage (New Lead, Contacted, Appointment Set, and so on) tracks deal progress for reporting. The foreclosure_stage custom field (Pre-NOD, NOD, Mid, Last 10, Post-Sale, Expired) is what actually drives which sequence fires, not the board position. A lead can sit at pipeline stage Contacted while its foreclosure stage is Last 10.
  5. When GHL logs a result, whether a reply, a booked appointment, an opt-out, or a completed call, its Result Sync workflow fires a Custom Webhook to /api/integrations/ghl/webhook, authenticated by the x-ghl-secret header, carrying the kod_lead_id in the body. Confirm this webhook posts and that the app writes it as a crm_dispositions row.
  6. That disposition row runs through the same resolveEnrollments logic the app already uses for its own dispositions: throttle up or down, switch campaigns, arm or bail the gatekeeper loop, or finish the sequence on a positive outcome. Map GHL outcomes to KOD dispositions exactly: GHL "no answer" or "voicemail" maps to KOD no_answer or left_voicemail (bumps the owner-dark counter, may arm pf:gatekeeper); GHL "connected" or "appointment booked" maps to KOD connected or meeting_booked (resets the dark counter, throttles down); a GHL tag showing the seller is pursuing a loan mod, bankruptcy, or reinstatement maps to pursuing_mod/pursuing_bk/pursuing_reinstate (switches to the doubt-cast campaign); a GHL tag showing bankruptcy filed, mod approved, or reinstated maps to bk_filed/mod_approved/reinstated (switches to pf:long_term); and GHL "do not contact" maps to KOD do_not_contact (writes a suppression and bails every active enrollment).
  7. Enforce the DNC rule exactly: a number marked plain DNC still imports into GHL but with SMS Do-Not-Disturb turned on, so the auto-texts skip it while Jason can still dial it by hand. A number flagged litigator is not imported at all, it stays fully suppressed on both sides.
  8. When a lead's sale goes through at auction, move its card from KOD Acquisition to KOD Surplus Recovery rather than marking it Lost. Confirm the sale price against the loan balance. If the sale price is higher, the overage belongs to the former owner, advance the card through Auction Passed, Surplus Confirmed, Agreement Signed (Services Agreement, 30% contingency fee), Filed (Assignment of Surplus Funds and Letter of Direction with the trustee or court), and Recovered. Expect a 30 to 90 day timeline.
  9. In a redemption state (Kansas 3 to 12 months, Michigan 6 months), also route the lead through the Redemption sequence on the Surplus Recovery board rather than closing it as dead, the former owner may still reclaim the property within that window.
  10. When David delivers a new or updated handoff sheet (Location ID, Private Integration Token, Calendar ID, the KOD Lead ID field ID, the 17 workflow IDs, and the pipeline and stage IDs), plug the values into the app's environment configuration and run one test lead end to end: push a contact, confirm the correct workflow fires, confirm a test reply comes back through the webhook, and confirm the app records it as a disposition before trusting the sync for real leads.

VERIFICATION

  • A newly ingested lead shows up in GHL as a Contact carrying the correct kod_lead_id and sits in the correct workflow for its badge and stage
  • A GHL disposition (reply, booking, opt-out, call result) shows up in the KOD app as a crm_dispositions row within the expected delay, not lost silently
  • No plain-DNC number is receiving auto-texts, and no litigator-flagged number was ever imported
  • Every lead whose sale went through at auction sits on the Surplus Recovery board, not marked Lost, unless surplus and redemption were both explicitly checked and ruled out
  • The pipeline stage and the foreclosure_stage field are not being confused with each other when troubleshooting why a sequence did or didn't fire

TROUBLESHOOTING

  • Problem: A lead was pushed to GHL but the wrong sequence fired. Fix: Check the badge tier variant, the core stage sequences each have Gold/Silver/Bronze versions and the app defaults to Silver unless the tier-specific workflow ID was also configured. Confirm the right workflow ID is wired for that badge.
  • Problem: GHL results aren't showing up in the app. Fix: Confirm the Result Sync workflow's Custom Webhook action still has the correct URL and the x-ghl-secret header value. A rotated secret or a changed URL on either side breaks the sync silently.
  • Problem: SMS isn't sending at all. Fix: Confirm A2P 10DLC registration is actually approved, not just started, unregistered texting is blocked by carriers regardless of how the workflow is built.
  • Problem: A lead whose sale already happened is sitting marked Lost. Fix: Re-open it and run Step 8 and Step 9 regardless of how long ago it closed out, surplus and redemption checks are not blocked by a stale CRM status.
  • Problem: Unsure which board a re-default, probate, divorce, or tax lead belongs on. Fix: All of them stay on KOD Acquisition. They are distinguished by Lead Source or Deal Type and worked by their own sequence, not by a separate board.

Linked resources

No linked Google Doc or Sheet yet — these are generated when this SOP is pushed to Google (npm run push-to-google).