CRM-confirmed partner referrals
The customer-list API returns leads before commissions exist. ReadGET /affiliate-manager/:locationId/affiliates/:affiliateId/customers with
Version: v3. The returned contactId is a CRM contact ID; _id is the
Affiliate Manager customer ID. meta.count is the full result count, including
on an empty page past the end (read-only verified on Whigham Group, 2026-09-09).
affiliateCustomerSync stores evidence in partnerReferralCustomers and
projects it into partnerReferralEdges with source: "crm". The old URL
reconciler cannot overwrite or clear CRM projections. Disabled/deleted records
are inactive; absence is reconciled only after all pages succeed with a stable
total and no repeated customer IDs. Conflicting active owners are ambiguous.
Money remains in the commission and payout caches.
Missing cached campaign membership no longer blocks the worker. It reads the
same-location affiliate from the CRM: a verified deletion or explicit empty
membership list completes an empty scan, while current memberships allow the
customer read to continue. Failed, malformed, or foreign-location responses
preserve the saved referrals for retry; cache absence alone never clears them.
September API cleanup
Campaign discovery now uses the location’s campaign-list API, including empty campaigns. Detail hydration remains necessary for referral templates. A failed campaign-list read retains the embedded-reference fallback and does not stamp a complete sync; a successful empty result never falls back. Commission status is preserved in the ledger. Only pending and approved rows contribute to unpaid balances; denied, canceled, and unknown rows remain visible in the recorded history. Report columns distinguish paid/unpaid amounts from the gross recorded commission total. The authenticated Marketplace enrollment adapter uses direct affiliate creation when there is no explicit staff workflow override. Hosted CRM forms and native workflows are unchanged. The direct path is tested with synthetic responses; an authorized test enrollment in a non-production CRM location is still needed before enabling its live use. Verify contact association, existing-email profile preservation, prior campaign memberships, and any campaign-triggered messages. Do not use the initial referral backfill as an enrollment test: it only reads existing CRM customer relationships. EachpartnerReferralSyncs row is an hourly job with durable offset, generation,
attempt, lease, next due time, completion timestamp and error. One action reads
one API page. Mutations fence stale workers and atomically save evidence,
projection and progress. The five-minute watchdog resumes expired work and
retries failures. The authorized affiliate-cache sync enrolls jobs; manual
Refresh requests an immediate run. No CRM workflow changes are required.
Initial production rollout (requires human authorization)
Target: Whigham Group, Locationts3Kmj7oyUTjwvx6mKwn.
First partner: Christinya Romero, affiliate 6a943c2c7e7772057dcae5e4.
-
Set the production Convex environment value
GHL_PARTNER_REFERRAL_SYNC_LOCATION_IDS=ts3Kmj7oyUTjwvx6mKwnbefore deploying. The restriction is checked at enrollment and each worker claim. - Deploy the schema/backend first, then the compatible frontend. Do not enable or change the separate affiliate-cache cron as part of this rollout.
-
Enqueue this partner through the internal mutation
prospecting/affiliateCustomerSync:enqueuewith: -
Read the Location/affiliate job until it is
idlewithlastCompletedAtpopulated and nolastError. Confirm the six known CRM leads are matched, visible in directory/detail/contact attribution, and included in a report preview. Do not send a report or trigger a credit pull to validate this. - Confirm commissions and payouts did not change. Observe the next hourly run before expanding the Location allowlist.
Pausing and diagnosis
To pause all customer relationship jobs, setGHL_PARTNER_REFERRAL_SYNC_LOCATION_IDS=__paused__. An empty value permits all
Locations that run affiliate-cache sync. A currently claimed page may finish;
subsequent claims obey the restriction. Keep the additive schema on rollback
so previously saved CRM projections remain readable and protected.
Inspect partnerReferralSyncs by (locationId, ghlAffiliateId) for progress or
the retained error. The record UI shows sync delays and assignment conflicts.
Invalid/missing IDs, unknown campaign membership, changed totals and malformed
responses fail closed. A repeated or inconsistent page restarts after five
minutes without clearing saved referrals.
Detail contacts paginate in groups of 100 by contact ID. Reports share that
reader, with an explicit 10,000-contact safety limit. Existing 500-commission
and directory edge scan limits remain bounded; directory counts display +
when incomplete. No limit is represented as an exact total.
