Skip to main content

Funding Machine subscriber opportunity sync

Target and activation boundary

Source agency: TY8NPnDY2A1uP5yZ8YKJ. Destination: My Funding Machine, ReoQmuh4ooNuHeE5stlj. These are pinned in code. All mirror tables are keyed by the destination locationId; source identity is a separate sourceLocationId. Only internal Convex maintenance functions and a disabled cron can access them. There is deliberately no browser API, operator-facing roster endpoint, or health dashboard in V1. Implementation approval covers the additive schema, scheduler, and code. This does not activate production. Deployment, production owner confirmations, pipeline/field creation, and enabling live writes need the named production authorization. Do not use a credit pull or a payment write to validate this sync. Read-only inspection on September 25, 2026 found:
  • Destination OAuth works for location, pipeline, field, opportunity, and payment subscription reads. Use its own installation token, not the agency token.
  • Existing pipelines: 3. FM Client Onboarding, Onboarding, and Retention. None is a dedicated subscriber pipeline. Leave them unchanged.
  • Multiple opportunities per contact are already enabled.
  • The eight sync fields below do not exist yet.
  • The refreshed agency list contained 220 records, 95 Active (the earlier export had 93). Source counts are observations, not a fixed enrollment manifest.
No live configuration or subscriber records were written during implementation.

Destination configuration

After the production configuration checkpoint, create Funding Machine Subscribers with exactly these stage names: Active, Trialing, Past Due, Paused, Canceled. Use the CRM pipeline UI; the installed official SDK exposes pipeline reads but no pipeline creation method. Runtime lookup resolves the unique pipeline and stage IDs within the pinned destination. Create these opportunity custom fields in that destination. Exact names and types are validated on every run. Do not reuse the existing Product Location ID field from other workflows. The settings check requires allowDuplicateOpportunity=true because one owner may have multiple businesses. It does not change that setting automatically. Review existing workflow triggers for these new records before activation. The sync never sends an email/message or enrolls a contact in a workflow itself.

Read-only preview and owner confirmation

Run commands against the intended deployment. Commands below omit --prod on purpose. Only add it for explicitly authorized production operations. All new functions are internal; deploying them is a separate step from checking out code.
The first command discovers the whole agency and configuration. The second adds live details for up to ten chosen source IDs: roster, owner, contact matches, existing opportunity, proposed owned fields, and exceptions. Repeat batches for the intended rollout. Preview is entirely read-only, including OAuth: it will report a stale/missing agency or destination credential instead of rotating it. Normal existing token-refresh operations can restore those prerequisites. After a human identifies the main owner, record the user ID from that source roster, plus an attributable confirmation note. Never guess from the first admin or equate the billing contact with the main owner. Confirmation is blocked while a run is active so it cannot race a worker.
Pass userId:null with a new note to revoke a confirmation. Re-run the preview after confirmations. Main-owner changes that need a different destination primary contact remain exceptions until staff relink the same opportunity. The documented Update Opportunity API has no contactId update field.

Enable and inspect

After reviewing the preview and obtaining production activation approval, set SUBSCRIBER_SYNC_ENABLED=true in the intended Convex deployment. This setting is not a Next.js setting. Missing or any other value keeps the scheduler disabled.
start defaults to preview unless dryRun:false is explicit, and live mode still requires the deployment switch. recentRuns reports cursor/queue length and synced/needsReview/skipped counts. state shows mapped opportunity IDs, last attempt, last full sync, and per-account issues. roster includes its observation time. Restrict these outputs to staff because they contain customer information. First observe an authorized run and inspect destination readbacks before describing the automation as live. New enrollment is Active-only. Once managed, all five supported stages reconcile, including reactivation. Opportunity lifecycle status is preserved; new opportunities start open. The next scheduled discovery runs every six hours. A crashed worker’s run can resume through start after its 15-minute lease expires, which exceeds Convex’s 10-minute action lifetime. Concurrent starts reuse the one active destination run. Only one worker owns it at a time; creates have durable intents written before the external request.

Failure handling and recovery

  • Incomplete/changing source pagination stops discovery before queuing writes. Discovery failures are visible in Convex logs. Unknown source statuses and missing listings are per-account exceptions, never inferred cancellations.
  • Watchlist IDs q3gEsVFOc126Rln8a3FL, Exq0dCONAQ7BcjcFzFTs, and lVHjDVx6cf3pi9R30VNd were visible in CRM Active but absent from the earlier API export. Discovery retains them even before they have a mapping. If they appear later, normal source checks apply. Clear Trac’s Setup Pending is not proof of an active billable subscription.
  • A failed roster read preserves the last roster and count. A missing owner blocks new opportunity creation. For an existing opportunity, known stage/name and billing fields may update while owner review remains visible.
  • Exact priceId matching is required. V2 prices are dollars; V1 prices are cents. Missing units, unknown currency/interval, or historical prices absent from the catalog remain exceptions. No guessed fallback price is written.
  • The SaaS API does not provide billing dates/contact snapshots. V1 reads the exact payment subscription under the destination altId, verifies its ID and tenant, and reads its billing contact separately. Unavailable payment details preserve existing fields, keep the last full sync timestamp unchanged, and flag the account. The next date is the payment subscription period end unless it is scheduled not to renew. Unsupported payment shapes remain exceptions.
  • A missing/moved/mismatched mapped opportunity is never recreated or moved back automatically. Duplicate source IDs or duplicate exact-email contacts stop that account for review. A provider search failure is never an empty set.
  • Create timeout/crash recovery searches the full destination inventory for the source ID, including other pipelines, and adopts only a unique matching opportunity in the subscriber pipeline. Pending contact creates similarly reconcile by exact destination email. If absent, the durable intent stays blocked. Inspect provider records/logs before a separately authorized manual repair; there is deliberately no blind retry/reset command.
  • Primary-contact changes are a manual relink on the existing opportunity. Updating a source owner’s email does not silently overwrite a destination contact or create a replacement opportunity.
  • Only sync-owned custom-field IDs are sent on updates. Notes, assignments, status, followers, monetary value, and unrelated fields are omitted.

Disable and resume

Set SUBSCRIBER_SYNC_ENABLED=false to stop new workers. An in-flight worker may finish. A scheduled next worker closes the run as stopped. For an abandoned run, after its lease expires:
This does not delete opportunities, contacts, mappings, rosters, or create intents. To resume, restore the switch after reviewing a new preview, then call start with dryRun:false. Identity and unresolved-create safeguards survive stop/resume.

Primary API references

The installed official SDK declarations and live read-only payloads were also checked for pagination, SaaS plan units, destination settings, and payment subscription response shape. No live create/update endpoint was exercised.