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

# Documents and bank statements

> Collect documents from merchants by email, text or link, upload from your own systems, and read what the bank statements say.

Documents live on the deal. They get there three ways: your team uploads them in the app, the merchant uploads them through the portal, or your own systems push them in. Bank statements are the ones that matter most. Once analysed, they give you bank-verified revenue, NSFs, negative days and existing positions, and lender matching switches from the stated revenue to the verified figure.

## Collecting documents from the merchant

Ask for exactly what you need, the way the app's **Request documents** button does. A request lists the items (the last three months of bank statements, a voided check, a driver's licence) and reaches the merchant by email, by text, or as a link you deliver yourself. The merchant opens a secure upload page, and whatever they upload lands on the deal and is matched to the item you asked for, so the request itself shows what is still outstanding: pending, partly received, complete. Requests expire after 30 days unless you say otherwise.

The merchant portal goes further: one link where the merchant sees the offers you have shared, uploads documents at any time and comes back for more. Portal links are good for 24 hours, can be sent by email or text, and the merchant can ask the portal for a fresh one.

Emails go through the workspace's connected mailbox; texts go through the CRM conversation when the contact is linked to it. When neither is available the request is still created and you get the link back to deliver however you like.

## Uploading from your own systems

Any file your systems already hold, an application from your website, statements from a bank-data provider, a signed contract, can be pushed straight onto a deal. Each file carries a type (application, bank statement, tax return, voided check, contract, driver's licence, ACH authorisation, business licence, proof of ownership, utility bill or other) and becomes visible in the app the moment the upload is confirmed. Bank statements also update the deal's statement coverage: which months are on file for which account, and which are still missing.

## What statement analysis gives you

Once a deal's bank statements are analysed you can read, per month and in summary:

* **Bank-verified revenue**: true deposits, with transfers and advance credits taken out where the parser identified them. This is the figure lender matching uses instead of stated revenue.
* **Balances**: average daily balance and ending balance, month by month.
* **NSFs and negative days**: returned items and days the account closed below zero, both of which feed the computed paper grade and many lender gates.
* **Positions**: existing advances found from recurring debits to known funders, alongside any entered by hand, with payment amount, frequency and balance.
* **Trend**: whether deposits are going up, flat or down across the analysed period.
* **Coverage**: whether the months the workspace requires (three full months plus month-to-date, by default) are on file for every account, and exactly which statements are still missing.

Analysis is started from the deal's Underwrite tab in the app rather than automatically on upload, because it is billed per file. When it finishes, a notification fires and the figures are ready to read.

## Knowing who shopped a deal

Every package you email to a lender carries a short document reference, printed in light grey at the bottom of each page (for example `Ref K7M-3QX`) and stored inside the PDF file. Each lender receives a different reference for the same deal. The reference is added to the files as they leave; NextLevel MCA keeps no extra copies of your documents.

If a deal comes back to you from a funder you never sent it to, look at the footer of the application or the bank statements. Type the reference into the search box on the Deals page and you'll see which lender received that package and when. The same reference is shown next to each lender on the deal's Submissions tab.

## For developers

Reading needs `documents:read`; uploading and requesting need `documents:write`. The API never proxies file bytes: uploads and downloads go straight to storage through short-lived signed URLs.

| What | Endpoint |
| - | - |
| Start an upload | `POST /v1/deals/{dealId}/documents` with `name`, `type`, `mime_type`, optional `file_size` → `document_id`, `upload_url`, `upload_method: "PUT"`, `headers`, `expires_at` (about two hours) |
| Send the bytes | `PUT` the file to `upload_url` with exactly the returned `headers` and no `Authorization` header; `Content-Type` must match `mime_type` |
| Confirm | `POST /v1/documents/{id}/finalize` (optional `file_size`): makes the document visible, fires `document.uploaded`, recomputes coverage for bank statements. `409` with `code: "upload_not_found"` if nothing was uploaded yet; safe to repeat |
| List and read | `GET /v1/deals/{dealId}/documents` (`?type=` filter); `GET /v1/documents/{id}` adds `download_url`, valid for 15 minutes (`download_url_expires_at`). Fetch a fresh one each time |
| Request from the merchant | `POST /v1/deals/{dealId}/document-requests` with `items[]` (`type`, optional `label`), `channel` (`email`, `sms` or `link_only`), optional `person_id` (defaults to the primary owner, then the primary contact), `message`, `expires_in_days` (1 to 90, default 30) |
| Portal link | `POST /v1/deals/{dealId}/portal-link` with `send_via` (`email`, `sms` or `none`); needs `portal:send` |
| Statement analysis | `GET /v1/deals/{dealId}/statement-analysis` |

**Types.** Document `type` uses hyphens (`application`, `bank-statement`, `tax-return`, `voided-check`, `contract`, `drivers-license`, `ach-authorization`, `business-license`, `proof-of-ownership`, `utility-bill`, `other`); request `items[].type` uses underscores (`bank_statement`, `voided_check`, `drivers_license`, `business_license`, `tax_returns`, `proof_of_ownership`, `utility_bill`).

**Document requests.** `channel: "email"` answers `409` with `code: "no_email_sender"` when no mailbox is connected; an `sms` that cannot be sent still creates the request and returns `sent: false`, `delivery: "link_only"` and `meta.warning`. `delivery` reports what actually happened. Received files set `items[].status` to `received` with `received_document_id`, and the request's `status` moves `pending` → `partial` → `complete`. Document requests and portal links send messages, so pass an `Idempotency-Key`.

**Statement analysis.** `document.processed` fires per file and `statement.analyzed` when the deal's analysis is complete; then `GET /v1/deals/{dealId}/statement-analysis` returns:

| Field | What it holds |
| - | - |
| `status` | `not_analyzed` (empty figures, not an error), `pending`, `processing`, `completed` or `failed`, with `analyzed_at` |
| `months[]` | `month`, `deposits`, `deposit_count`, `withdrawals`, `mca_debits`, `avg_daily_balance`, `ending_balance`, `nsf_count`, `negative_days`, `statement_count` |
| `summary` | `avg_monthly_deposits` (the bank-verified revenue), `avg_daily_balance`, `nsf_count`, `negative_days`, `revenue_trend` (`up` or `down` at ±10% between the later and earlier half of the period, otherwise `flat`; `null` under two months), `months_analyzed` |
| `positions[]` | `funder_name`, `payment_amount`, `payment_frequency`, `current_balance`, `original_amount`, `position_number`, `detected` (found in the statements rather than entered by hand) |
| `coverage` | `status` (`complete`, `missing`, `needs_analysis`, `no_statements`), `required_months`, `mtd_required`, `complete`, `summary`, `missing[]` (`bank_name`, `account_last_four`, `month`, `label`), `unanalyzed_documents` |
| `accounts[]` | `bank_name`, `account_last_four`, `months_covered`, `months_missing` |

Exact types are on the endpoint's page in the **API reference** tab.

**Document references.** A submission carries `trace_code` once its package has been emailed. `GET /v1/submissions/by-reference/{code}` resolves a reference to the submission it was sent under (lender, deal, when), and the MCP `search` tool does the same when the query is a reference. Packages sent through a lender's API integration are not stamped.


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