> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myfundingmachine.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Partner referral sync

# CRM-confirmed partner referrals

The customer-list API returns leads before commissions exist. Read
`GET /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.

Each `partnerReferralSyncs` 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, Location `ts3Kmj7oyUTjwvx6mKwn`.
First partner: Christinya Romero, affiliate `6a943c2c7e7772057dcae5e4`.

1. Set the production Convex environment value
   `GHL_PARTNER_REFERRAL_SYNC_LOCATION_IDS=ts3Kmj7oyUTjwvx6mKwn` **before**
   deploying. The restriction is checked at enrollment and each worker claim.

2. Deploy the schema/backend first, then the compatible frontend. Do not
   enable or change the separate affiliate-cache cron as part of this rollout.

3. Enqueue this partner through the internal mutation
   `prospecting/affiliateCustomerSync:enqueue` with:

   ```json theme={null}
   {
     "locationId": "ts3Kmj7oyUTjwvx6mKwn",
     "affiliateIds": ["6a943c2c7e7772057dcae5e4"],
     "force": true
   }
   ```

4. Read the Location/affiliate job until it is `idle` with `lastCompletedAt`
   populated and no `lastError`. 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.

5. Confirm commissions and payouts did not change. Observe the next hourly
   run before expanding the Location allowlist.

Only these rollout steps write production. Local codegen/tests do not recover
the six live leads. CRM requests made by the new worker are read-only; the
worker writes the app's cache and schedules its own continuations.

## Pausing and diagnosis

To pause all customer relationship jobs, set
`GHL_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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.