> ## 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.

# Lexington v1 one way

# Lexington Capital — V1 One-Way Handoff (FM-2103 / FM-2104)

**Status:** Prep. Types, fixtures, and tests are in the repo; no new schema, no new UI,
no network calls. Nothing here changes what operators see today.

**Code:** `lib/directLenders/lexingtonV1.ts` · fixtures `tests/fixtures/lexingtonV1Handoff.ts`
· tests `tests/lexingtonV1Handoff.test.ts`.

**Builds on:** `docs/direct-lenders/direct-lenders-v1-plan.md` (Phase 1 shipped in PR #1343,
lender registrations and the reworked submit dialog in PR #1425).

***

## External dependency (blocks the payload, not the manual flow)

> **Referring-party key — owed by Ty Talbot / Kevin Hill.**
> Lexington has not told us which field on a submitted deal identifies the referring ISO
> (the operator). Until they do, the handoff record carries a `pending_external` marker
> (`LEXINGTON_REFERRING_PARTY_KEY`) in place of the real key. **Do not guess a field name.**
> `tests/lexingtonV1Handoff.test.ts` fails if anyone fills it in with `isoId`, `referrerId`,
> `affiliateId`, or similar before Lexington confirms.

What we know: the ISO signup link in the catalog carries an `isoid` query parameter that
points at the Funding Machine parent ISO account. That parameter is for **signup**. Whether
the same identifier, a per-operator child id, or something else attributes a **deal** is
exactly the open question. Ask Lexington for both the key name and where it goes (query
string on the application link, a form field, or a field in their portal).

The manual V1 flow does not wait on this: the operator submits through their own Lexington
ISO account, so attribution is implicit in that login. The key matters the moment FM
produces any structured payload on the operator's behalf.

***

## FM-2103 — Integration method

| Question | V1 answer | In code |
| - | - | - |
| API or operational? | **Operational.** One-time ISO signup via Lexington's public link, then Lexington emails submission instructions. No API. | `LEXINGTON_V1_INTEGRATION.method = 'operational'` |
| Direction | **One-way.** FM → Lexington. Nothing flows back into FM. | `direction = 'one_way'` |
| Where status lives | **Lexington's lender portal.** The operator reads approvals, declines, and funding there. | `statusSource = 'lender_portal'` |
| Callbacks / webhooks / polling | **None in V1.** Two-way sync is a later version and needs its own ticket. | `callbacks = 'none'` |

The module is guarded against drift: a test fails if `lexingtonV1.ts` gains a `fetch` call or
a Convex import.

***

## FM-2104 — Required fields, documents, eligibility, response handling

### Required fields (FM side)

These are the fields FM must hold before a file can be handed to Lexington. They are the
inputs to `evaluateLexingtonV1Readiness` and map to what the submit dialog already collects.

| Field | Source today | Required? |
| - | - | - |
| `locationId` | Session | Yes. Tenant boundary. |
| `contactId` | Contact record | Yes. |
| `products` | `selectedFundingTypes` on the file | Yes. Must include `mca` or `bloc` after legacy `sba` → BLOC normalization. |
| `revenue.questionnaireBand` | Business questionnaire `monthlyRevenue` (legacy files) | One of the three revenue inputs must resolve. |
| `revenue.contactBand` | Contact `averageMonthlyRevenue` | Only bands wholly on one side of \$75k resolve. |
| `revenue.operatorAnswer` | Inline question in the submit dialog | Fallback when neither band settles it. |
| `registration.isoSignedUp` | `lenderRegistrations` row for this Location | Yes. |
| `registration.applicationUrl` | Same row; https-only, from the operator's Lexington portal | Yes. |
| `clientEmail` | Contact email | No. Missing email is a warning; the operator copies the link. |
| `dealPackageConfirmed` | Operator attestation checkbox | Yes. FM cannot observe the send. |

**Not FM's fields:** owner name, entity details, requested amount, use of funds, and every
other application question. Lexington collects those from the client through the
application link (Kevin, 2026-07-29). FM does not model or store them.

### Documents

Last **4 months of business bank statements**, sourced from the client. Read from the
catalog entry (`ESTABLISHED_LENDERS` → `lexington_capital.requiredDocs`) so there is one
definition. No signed application; the Established workflow does not use one.

### Eligibility

A file is ready for Lexington when all of the following hold. Each failure is a named
blocker so the dialog can show the operator exactly what is missing.

| Blocker | Rule |
| - | - |
| `product_not_mca_or_bloc` | At least one selected product is MCA or BLOC after the same legacy `sba` → BLOC mapping the lender resolver uses. |
| `revenue_routing_unresolved` | Questionnaire band, then contact band, then operator answer must resolve the \$75k split. No guessing. |
| `revenue_routes_to_sandbar` | Resolved side must be \*\*under $75k/mo**. $75k and up goes to Sandbar Fund. |
| `iso_registration_incomplete` | Location has attested ISO signup with Lexington. |
| `application_url_missing` | Location has saved a valid `https:` application link from its Lexington portal (http / malformed rejected). |
| `deal_package_not_confirmed` | Operator attested the bank statements went out per Lexington's instructions. |

Warning (does not block): `no_client_email_copy_link_manually`.

The exact-$75k boundary follows the existing band edge (`75_150k` starts at $75k and routes
to Sandbar). Still awaiting Leo / Kevin confirmation per the V1 plan.

### Response handling (one-way)

| Event | V1 handling | In code |
| - | - | - |
| Submission acknowledged | FM records only the operator's "Mark Submitted" attestation. | `acknowledgement = 'operator_attestation'` |
| Status changes | Operator reads them in Lexington's portal and updates the file by hand. | `statusUpdates = 'lender_portal_manual'` |
| Approve / decline | Not sent to FM. | `decision = 'not_received_by_fm'` |
| Funded / commission | Logged through the existing `record_payment_received` workflow step. | `funded = 'record_payment_received_step'` |
| Callbacks | Out of scope for V1. | `callbacks = 'out_of_scope_v1'` |

***

## Handoff record

`buildLexingtonV1Handoff(input)` returns either `{ ok: false, readiness }` with the blockers,
or `{ ok: true, handoff }` where `handoff` is:

```ts theme={null}
{
  integration: LEXINGTON_V1_INTEGRATION,
  locationId, contactId,
  products: ['mca' | 'bloc', ...],
  routing: 'under_75k',
  applicationUrl,                       // operator's Lexington portal link
  dealPackage: [{ type: 'bank_statement', label: 'Business bank statements', detail: 'Last 4 months' }],
  referringParty: LEXINGTON_REFERRING_PARTY_KEY,   // pending_external — see top of doc
  responseHandling: LEXINGTON_V1_RESPONSE_HANDLING,
}
```

Nothing persists this record yet. When Lexington names the referring-party key, replace the
marker with the real field, update the fixture, and let the guard test tell you what else
moved.

***

## Out of scope for this prep

* Inventing the referring-party / attribution field.
* Callbacks, webhooks, polling, or any two-way status sync.
* New Convex tables (`lenderSubmissions`, events) — still Phase 2 of the V1 plan and gated.
* Billing, fees, or commissions math.
* Real submissions to Lexington. Fixtures are synthetic.

## Next steps (not started)

1. Ty / Kevin: get the referring-party key name and placement from Lexington.
2. Confirm the exact-\$75k boundary with Leo / Kevin.
3. Decide whether the submit dialog should call `evaluateLexingtonV1Readiness` to show
   named blockers, replacing its inline gating. Small, non-gated change.


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