Skip to main content

Processing Application gate cutover

The gate is disabled by default. Deploying the capture, access, and cutover changes does not block any operator. Enable one location only after completing the sequence below.

What enabling blocks

Submitting a contact to processing requires a captured Processing Application or an active grandfather override. This covers every processing type, including term loans — submitting term loans to 7 Figures is a handoff, so it carries the client’s own attested answers like any other handoff. Term loans were exempt until Jul 28 2026. Generating term-loan offers through Engine is not affected: pulling rates is not a handoff, so operators can still generate offers while an application is outstanding. The funding agreement is a separate gate that is always on and was never scoped by processing type. A contact needs both a completed agreement carrying a rate and an application before any processing submit succeeds.

What the operator sees

Enabling changes the funding-workflow checklist between the agreement send and the review:
  • Send client agreement & application replaces “Send client agreement”. The application link is a custom value inside the agreement template, so one send delivers both.
  • Once the agreement is out, that row nests a Waiting on client cluster with two rows — Awaiting signed agreement and Awaiting processing application — each carrying its own resend button. The rows are derived from CRM state and have no checkbox: the authoritative gate is the Convex check on the captured application, and a tickable row could claim a file was ready when a submit would still be rejected. Resend Application lives here rather than on the review step, so the cluster stays visible while either artifact is outstanding — even if the operator ticked the review early — and disappears only once both have landed and the review is cleared.
  • Review agreement & processing application replaces “Review signed agreement” and covers both artifacts in one review call. Unlike the gate-off agreement review, it does not auto-complete when the paperwork lands — the operator clears it by hand once both artifacts are in.
  • Its checkbox is disabled until both artifacts arrive, with a tooltip naming what is still missing. You cannot review paperwork that has not shown up, and ticking it early used to march the checklist onto a submit the server gate rejects. Un-ticking stays available, so a file whose review completed before the gate was switched on is never frozen. This is a UI guardrail only — the authoritative check is the Convex gate at submit time.
  • While the review is outstanding, the pinned banner names the missing artifact and offers Message Client instead of prompting a review of paperwork that has not arrived.
  • On term-loan files the offers step stays reachable during the wait, because pulling rates from Engine is not a hand-off. Only Generate Offers is offered early; Submit Term Loans to 7 Figures Funding is withheld until the review is genuinely current, since that is the hand-off the gate exists to hold back.
If an application is genuinely in hand but never synced, the documented override is grandfathering the contact, which returns the whole page to gate-off behavior — not ticking through the checklist. Gate-off locations see none of this; the checklist is unchanged. Established funding is exempt. It is a referral to a lending partner: the file never reaches 7 Figures, so the application it would collect is never read and the agreement isn’t required either. Established cards therefore render the plain gate-off spine — no relabelled send step, no waiting rows, no banner wait, and a freely tickable review — so the path is never parked on paperwork it has no use for. The exemption is display-only: completedWorkflowSteps is shared across tracks, so on a dual-track file the creative card still enforces everything above. An application may still be collected on an established file if the CRM template sends the link; stopping that is a template change, not an app change.

Preconditions

  1. The location installation has Forms read access and has been re-authorized if the scope was added after installation.
  2. The location has exactly one form named FM Processing Application.
  3. The form-submission workflow sends the contact ID, location ID, and form ID to the Processing Application webhook. Send the exact submission ID when the workflow exposes it. When it does not, the webhook queries the Forms API for submissions by that exact contact on that form and excludes submissions already captured in Convex. Exactly one uncaptured submission within the preceding 10 minutes is recorded; any uncaptured submission older than that window, or multiple recent uncaptured matches, fail closed with an ambiguity error so a delayed event can never record a different submission. Zero matches are retried. When every candidate is already captured (a duplicate or delayed delivery), recording dedupes against the existing snapshot instead of writing a new one. A successfully submitted, non-empty snapshot is the completion evidence.
  4. A contact-tag workflow listens for fm-resend-processing-application and sends the client the Processing Application message using the Processing Application Form Link custom value.
  5. The funding-agreement template includes the same custom value where the application link should appear. The app creates or refreshes this value before sending the template.
  6. GHL_PROCESSING_APPLICATION_WEBHOOK_SECRET is configured on the app. The workflow Authorization header uses the per-location HMAC token provisioned from that secret, not one bearer token shared by every location.
  7. Every contact already at or beyond the processing handoff has been reviewed. Generate the candidate list with
    which replays the gates in the order the submit path runs them. Do not export by pipeline stage: the agreement gate runs first, so a contact in the right stage without a completed, rated agreement needs no override, while one in an unexpected stage with a signed agreement does. The script reports contacts still in flight separately from those whose processing has already finished, because the latter only meet the gate on a new submit. Processing here spans every type: a contact whose cards are done but whose term-loan handoff is still ahead counts as in flight. A third bucket, needs manual review, holds contacts whose submission history exceeded the per-contact scan limit with nothing open observed: the review cannot tell whether processing is still open, so those contacts are left out of the recommended override list (and out of the --out file’s recommendedContactIds) and listed under needsManualReviewContactIds instead. Check each one in the CRM and grandfather it only if it is genuinely still in flight. Activation reconciles the reviewed list against active overrides.

Check configuration

From an authenticated admin browser session:
Do not continue unless inspection.formId is populated, inspection.webhookTokenConfigured is true, and inspection.error is absent.

Grandfather in-flight contacts

Create one explicit override for each reviewed in-flight contact:
Overrides are idempotent and remain separate from captured application data. They record the verified admin identity and reason, are bound to the currently verified form, and expire after 90 days. Recreate an expired override only after a new review. Overrides created before form/expiry scoping remain valid only for 90 days from their original creation time. Re-grandfather those contacts to write the explicit form and expiry fields before the compatibility window closes. Activation reconciles at most 1000 active overrides in one call and rejects a list that does not match them exactly. A location with more in-flight contacts than that cannot be activated until the population is narrowed.

Prove the capture workflow

Submit the live Processing Application once for a designated test contact and confirm that its snapshot appears in the Application tab. Activation requires a successful authenticated capture for the verified location and form within the preceding 24 hours; a confirmation checkbox alone is not sufficient. When the workflow omits the submission ID, use a contact that has no earlier uncaptured submission on this form, and submit exactly once. A contact that already carries an uncaptured submission fails closed on every later attempt, and no retry clears it; start over with a new contact. Getting a genuinely new contact takes more than a new email address. The form widget stores the contact id in the browser under localStorage._ud, so a second submission from the same browser updates that same contact no matter what email and phone you type, leaving it with two uncaptured submissions and failing closed. Submit the retry from a fresh browser profile or a private window, or clear site data for the form’s domain first.

Troubleshoot a failed capture

The GHL workflow execution log shows the webhook response body. Read it before changing any configuration:
  • 400 Invalid payload — the body must omit submissionId entirely when the workflow cannot supply one. An empty string is rejected.
  • 401 Valid session required — the request never reached the route. The path must be exempt from the session middleware (lib/proxy/isExemptPath.ts).
  • 409 ambiguous_submission — the contact has more than one uncaptured submission, or an uncaptured one older than the fallback window.
  • 409 configuration_error — Forms read access is missing, or the location does not have exactly one FM Processing Application form.
  • 422 invalid_submission — the submission could not be turned into a field snapshot. The logged message names the offending key.

Enable

After the workflow and grandfather list have both been checked:
Activation bypasses the form-ID cache and re-checks live form access plus the server webhook configuration. The stored gate state records the resolved form ID, confirmation timestamps, and verified admin identity. Enable and disable transitions also append immutable audit events instead of relying only on the current settings row.

Smoke checks

Verify all three paths before widening rollout:
  • A newly captured application can submit to Processing.
  • A grandfathered in-flight contact can submit to Processing.
  • An expired override or an override from a replaced form does not bypass the gate.
  • A contact with neither a captured application nor an override is blocked with the Processing Application requirement.
  • Resend Application, on the Awaiting processing application row, removes and re-adds the workflow tag and produces a new client message with a working form link.
  • The review step stays open after both artifacts land and only clears when the operator checks it.
  • Operators can read the captured snapshot and processors can edit only the audited working copy from the Application tab.

Roll back

Disable enforcement without deleting snapshots or audit records: