Concepts
A book is a container for one deal one or more documents that get parsed together. Books have a parse_status that progresses through:
Documents can be bank statements, credit reports, tax forms, or loan applications. The book API returns different output shapes depending on which document types were parsed.
Submit a book
Multipart form upload. Pathway creates the book, uploads the files to S3, and starts parsing in the background. Returns a book_id immediately.
One or more PDF files. Pathway auto-classifies each file as bank statement, credit report, tax form, or loan application.
If provided, Pathway will POST to this URL when parsing completes. If omitted, poll GET /books/:id.
import requests
API_BASE = "https://api.lendpathway.com/api"
TOKEN = "pat_your_token_here"
headers = {"Authorization": f"Bearer {TOKEN}"}
with open("statement.pdf", "rb") as f:
r = requests.post(
f"{API_BASE}/submit-book",
headers=headers,
files={"files": ("statement.pdf", f, "application/pdf")},
data={
"book_name": "Q3 Statements",
"webhook_url": "https://your-server.com/webhook"
}
)
print(r.json())
# {"book_id": "...", "status": "processing", "message": "Processing started"}
Submit response
{
"book_id": "99cc93e6-1f3f-42b1-9fe4-ba5a95be9c78",
"status": "processing",
"message": "Processing started"
}
Webhook payload (POST to your webhook_url when done)
{
"book_id": "99cc93e6-1f3f-42b1-9fe4-ba5a95be9c78",
"status": "completed",
"book_url": "https://app.lendpathway.com/books/99cc93e6-1f3f-42b1-9fe4-ba5a95be9c78"
}
Get a book
Returns the Book object parse status, name, timestamps, and book_meta.
r = requests.get(f"{API_BASE}/books/{book_id}", headers=headers)
book = r.json()
print(book["parse_status"]) # "completed"
print(book["parse_status_message"]) # "Processed 3 bank_statements"
Book fields
| Field | Type | Description |
|---|
id | string | UUID |
parse_status | string | new, processing, completed, failed, cancelled |
parse_status_message | string | Human-readable status message, e.g. “Processed 4 bank_statements” |
name | string | Book name |
description | string | |
book_tag | string | Optional label/tag for the book |
is_starred | bool | |
created_at | string | ISO timestamp |
last_parsed_at | string | ISO timestamp of last parse |
salesforce_opportunity_id | string | Set if the book was imported from Salesforce |
book_meta | object | Raw parser output (see below) |
book_meta keys
| Key | Present when | Description |
|---|
parser_v2_mca_result | Bank statements parsed | Raw HolyMCAResult full account ledgers, transactions, positions, business info |
parser_v2_credit_report | Credit report parsed | CRMeta bureaus, accounts, FICO scores |
parser_v2_tax_forms | Tax forms parsed | Raw tax extraction result |
parser_v2_loan_application | Loan application parsed | Extracted loan app fields |
excluded_document_ids | Always | List of document UUIDs excluded from analytics |
excluded_account_ids | Always | List of account IDs excluded from analytics |
excluded_position_ids | Always | List of position IDs excluded from revenue |
revenue_exclusion_tags | Always | List of tags excluded from true revenue computation |
For computed analytics, use /analytics and /tax-analytics rather than reading raw book_meta.
Bank statement analytics
Returns BookAnalytics computed metrics across all bank statements in the book. Applies your org’s exclusion config (excluded documents, excluded accounts, excluded positions, revenue exclusion tags) before computing.
This is the primary endpoint for underwriting data.
r = requests.get(f"{API_BASE}/books/{book_id}/analytics", headers=headers)
a = r.json()
Top-level fields
| Field | Type | Description |
|---|
total_deposits | float | Sum of all credits across all accounts and months |
total_withdrawals | float | Sum of all debits |
true_revenue | float | Credits excluding loans, transfers, NSF, owner draws, and other non-revenue tags |
average_daily_balance | float | Weighted average daily balance across the period |
days_negative_balance | int | Total days with a negative ending balance |
debt_to_income_ratio | float | (total_loan_payments / true_revenue) × 100 |
opening_balance | float | Balance at the start of the earliest statement |
closing_balance | float | Balance at the end of the most recent statement |
num_deposits | int | Count of deposit transactions |
num_withdrawals | int | Count of withdrawal transactions |
total_loan_disbursements | float | Total money in tagged as loan disbursements |
total_loan_payments | float | Total money out tagged as loan payments |
num_mca_positions | int | Number of detected MCA positions |
statements[] per-document breakdown
All documents appear in this array, including excluded ones. Excluded documents will still show their statement_period and document_name, but their metrics are zeroed. Filter by excluded_document_ids from book_meta if you want to exclude them.
Each statement has document_id, document_name, statement_start_date, statement_end_date, statement_period, and an accounts[] array. Each account has:
| Field | Type | Description |
|---|
account_id | int | 0 = combined aggregate for this statement; 1+ = individual accounts |
account_name | string | Account name from the bank |
starting_balance | float | |
ending_balance | float | |
total_deposits | float | |
total_withdrawals | float | |
loan_disbursements | float | |
loan_payments | float | |
average_daily_balance | float | |
days_negative_balance | int | |
true_revenue | float | |
debt_to_income_ratio | float | |
is_reconciled | bool | Whether the parser’s transaction sum matches the bank-reported ending balance |
discrepancy | float | Difference between computed and bank-reported ending balance |
loan_summary[] loan type rollup
| Field | Type | Description |
|---|
loan_type | string | One of the loan tag values |
total_disbursements | float | |
total_payments | float | |
disbursement_count | int | |
payment_count | int | |
is_excluded | bool | True if this loan type is excluded from revenue computation |
average_statement_metrics average row across all statements
An AccountStatementMetrics object representing the per-statement average. Same fields as the per-account table above. Used for the Average row in CSV exports.
positions[] detected debt positions
Each position is a group of transactions the parser identified as belonging to the same loan or MCA.
| Field | Type | Description |
|---|
position_id | string | |
name | string | Display name (e.g. “ONDECK CAPITAL”) |
loan_type | string | merchant_cash_advance, bank_loan, factoring, credit, lease, etc. |
total_disbursements | float | Money received from this lender |
total_payments | float | Money paid to this lender |
disbursement_count | int | |
payment_count | int | |
first_disbursement_date | string | YYYY-MM-DD |
last_payment_date | string | YYYY-MM-DD |
avg_payment | float | Average payment size |
is_active | bool | Still making payments at end of statement period |
is_excluded | bool | True if excluded from revenue calculation |
returns_count | int | Reversal-tagged credits within this position |
returns_total | float | |
funder_title | string | Matched funder name from your org’s funder directory |
funder_link | string | Funder website |
funder_contact | string | |
funder_email | string | |
payment_schedules | array | Detected payment schedule objects each has frequency (daily, weekly, monthly, irregular), payment_dates[], and next_expected_date |
merged_accounts transaction-level data
Keyed by account number. Each account has a transactions[] array. Each transaction:
| Field | Type | Description |
|---|
transaction_id | int | |
transaction_date | string | YYYY-MM-DD |
description | string | Raw description from the bank |
amount | float | Always positive |
transaction_type | string | credit or debit |
ledger_balance | float | Running balance after this transaction |
tag | string[] | Classification tags see tags reference below |
position | object | If this transaction belongs to a debt position, contains position_id, position_name, loan_type, funder_title |
Transaction tags
Tags classify what a transaction represents. Revenue computation excludes all tags in revenue_exclusion_tags.
| Tag | Meaning |
|---|
true_revenue | Counts toward revenue (synthetic tag added post-classification) |
merchant_cash_advance | MCA disbursement or payment |
bank_loan | Bank loan |
factoring | Factoring advance or payback |
credit | Credit card payment |
lease | Equipment/vehicle lease |
internal_transfer | Transfer between accounts owned by the same entity |
owner_transaction | Owner draw or owner contribution |
nsf_overdraft | NSF fee or overdraft fee |
bank_fee | General bank fee |
payment_processor | Payment processor deposit (Stripe, Square, etc.) |
reversal | Transaction reversal |
untagged | Could not be classified |
Tag-based aggregate fields on BookAnalytics:
| Field | Description |
|---|
nsf_total | Total NSF fee amount |
num_nsf | Count |
overdraft_total | Total overdraft fee amount |
num_overdraft | Count |
owner_transaction_total | Total owner draw/contribution amount |
num_owner_transaction | Count |
internal_transfer_total | Total internal transfer amount |
num_internal_transfer | Count |
bank_fee_total | Total bank fee amount |
num_bank_fee | Count |
payment_processor_total | Total payment processor credits |
num_payment_processor | Count |
reversal_total | Total reversal amount |
num_reversal | Count |
stop_payment_total | Total stop payments |
num_stop_payment | Count |
The nsf_overdraft tag is a single tag applied to transactions. The aggregate splits into nsf_total and overdraft_total at the metrics level.
Bank statement detailed view
GET /books/:id/statements
Account-centric view of the same parsed data. Structured as a list of accounts, each with their full statement history. Useful when you need transaction-level data, daily balance maps, or per-lender activity broken down by month.
Two key differences from /analytics:
- Organized by account first, then statements under each account (vs
/analytics which groups by document first)
- Does not apply org-level exclusions — all documents and accounts are included
- Includes
daily_balances (a full {"YYYY-MM-DD": balance} map for every day in the period) and funders[] (per-lender activity within that statement)
r = requests.get(f"{API_BASE}/books/{book_id}/statements", headers=headers)
accounts = r.json()
for account in accounts:
print(f"\n{account['account_name']} ({account['bank_name']})")
print(f" {account['business_name']}")
for stmt in account["statements"]:
print(f" {stmt['statement_start_date']} to {stmt['statement_end_date']}")
print(f" Credits: ${stmt['sum_credits']:,.2f} Debits: ${stmt['sum_debits']:,.2f}")
print(f" Revenue: ${stmt['revenue_credits']:,.2f} DTI: {stmt.get('debt_to_income_ratio', 'N/A')}%")
print(f" Transactions: {len(stmt['transactions'])}")
if stmt["funders"]:
for f in stmt["funders"]:
print(f" Funder: {f['name']} ({f['loan_type']}) "
f"in=${f['total_disbursements']:,.2f} out=${f['total_payments']:,.2f} "
f"freq={f.get('payment_frequency', 'N/A')}")
SimpleAccount fields
| Field | Type | Description |
|---|
account_id | int | |
account_name | string | |
account_number | string | |
bank_name | string | |
routing_number | string | |
account_type | string | e.g. checking, savings |
business_name | string | Business name extracted from the statement |
business_address | object | street_address, city, state_province, postal_code, country |
statements[] | array | Per-statement data for this account, sorted by date ascending |
SimpleStatement fields
| Field | Type | Description |
|---|
document_id | string | |
document_name | string | |
statement_start_date | string | YYYY-MM-DD |
statement_end_date | string | YYYY-MM-DD |
starting_balance | float | |
ending_balance | float | |
min_balance | float | Lowest daily balance in the period |
max_balance | float | Highest daily balance in the period |
sum_credits | float | Total deposits |
sum_debits | float | Total withdrawals |
net_deposits | float | sum_credits - sum_debits |
num_deposits | int | |
num_withdrawals | int | |
average_daily_balance | float | |
days_negative_balance | int | |
days_in_period | int | |
revenue_credits | float | True revenue for this account in this statement |
loan_disbursements | float | Total loan disbursements received |
total_mca_disbursements | float | MCA-specific disbursements only |
loan_payments | float | Total loan payments made |
debt_to_income_ratio | float | |
transactions[] | array | All transactions for this account in this statement period. Each has transaction_id, transaction_date, description, amount, transaction_type, ledger_balance, tag[] |
daily_balances | object | {"YYYY-MM-DD": balance} for every calendar day in the period |
funders[] | array | Per-lender activity in this statement period (see below) |
SimpleFunder fields (inside funders[])
Each entry represents one detected loan or MCA position and its activity within this specific statement month.
| Field | Type | Description |
|---|
position_id | string | |
name | string | |
loan_type | string | |
funder_title | string | Matched funder from your org’s funder directory |
funder_link | string | |
funder_contact | string | |
funder_email | string | |
transaction_count | int | Total transactions (disbursements + payments) in this month |
total_disbursements | float | Money received from this lender in this month |
total_payments | float | Money paid to this lender in this month |
funded_date | string | Date of first disbursement in this month |
first_payment_date | string | |
last_payment_date | string | |
payment_count | int | Number of payment transactions |
avg_payment_amount | float | Average payment size |
payment_frequency | string | daily, weekly, monthly, irregular. Null if fewer than 3 payments in the period |
Credit report data
Credit report data lives in book_meta from GET /books/:id. Access it at book["book_meta"]["parser_v2_credit_report"].
r = requests.get(f"{API_BASE}/books/{book_id}", headers=headers)
book = r.json()
cr = book["book_meta"]["parser_v2_credit_report"] # CRMeta shape
CRMeta shape (inside book_meta.parser_v2_credit_report):
{
"primary_entity": {
"full_name": "John Smith",
"date_of_birth": "1985-03-14",
"address": [
{
"address_line_1": "123 Main St",
"city": "New York",
"state": "NY",
"zip_code": "10001",
"is_primary": true
}
]
},
"credit_report_body": [
{
"credit_bureau": { "name": "experian", "display_name": "Experian" },
"fico_score": { "score": 720, "date_of_score": "2025-06-01" },
"underwritten_accounts": [
{
"account_name": "CHASE SAPPHIRE",
"account_number": "xxxx1234",
"is_open": true,
"account_type_normalized": "revolving",
"account_status_normalized": "current",
"collateral_type": null,
"recent_balance": 2400.00,
"credit_limit": 15000.00,
"monthly_payment": 45.00,
"date_opened": "2018-04-01"
}
]
}
]
}
Account status values: current, delinquent, charged_off, collection, bankruptcy, foreclosure, repossession, settled, closed, unknown
Account type values: revolving, installment, mortgage, charge, other
Collateral type values: unsecured, real_estate, vehicle, cash_deposit, education
GET /books/:id/tax-analytics
Returns TaxCashFlowAnalysis Fannie Mae 1084-style qualifying income computation from parsed 1040, Schedule C, 1065, and 1120S forms.
r = requests.get(f"{API_BASE}/books/{book_id}/tax-analytics", headers=headers)
tax = r.json()
for year in tax["years"]:
print(f"{year['tax_year']}: ${year['total_qualifying_income']:,.2f}")
TaxCashFlowAnalysis fields
| Field | Type | Description |
|---|
years[] | array | One entry per tax year |
most_recent_year | int | |
avg_qualifying_income | float | Average qualifying income across all years |
TaxYearAnalysis per year
| Field | Type | Description |
|---|
tax_year | int | |
total_qualifying_income | float | Sum of all income sources for this year |
total_wages | float | W-2 wages |
total_schedule_c | float | Self-employment income |
total_partnership | float | Partnership K-1 income |
total_s_corp | float | S-corp K-1 income |
total_depreciation_addback | float | Depreciation added back to income |
reported_agi | float | AGI from line 11 of 1040 |
wage_sources[] | array | Per W-2 income source detail |
schedule_c_sources[] | array | Per Schedule C income source detail |
partnership_sources[] | array | Per K-1 (partnership) income source |
s_corp_sources[] | array | Per K-1 (S-corp) income source |
warnings[] | array | Reconciliation warnings (missing K-1, missing return, income mismatch) |
Export as CSV
GET /books/:id/csv-export
Downloads the bank statement UW table as a CSV. Applies org-level exclusions (same as /analytics).
table_format
string
default:"month_as_row"
month_as_row one row per statement period per account (default).
month_as_col metrics as rows, months as columns (pivoted). Uses the combined account (account_id=0) per statement.
# Default
curl "https://api.lendpathway.com/api/books/{book_id}/csv-export" \
-H "Authorization: Bearer pat_xxx" \
-o uw.csv
# Pivoted
curl "https://api.lendpathway.com/api/books/{book_id}/csv-export?table_format=month_as_col" \
-H "Authorization: Bearer pat_xxx" \
-o uw_pivoted.csv
Columns: Period, Document, Account, Starting Balance, Deposits, Deposit Count, Withdrawals, Withdrawal Count, Ending Balance, Loan In, Loan Out, Avg Daily Balance, Days Negative, True Revenue, DTI %
Includes NET and Average summary rows.
Export as Excel
GET /books/:id/spreadsheet-export?sheet_type=mca
Returns an .xlsx file using your org’s configured spreadsheet template.
mca bank statements. vlad credit reports. tax tax forms.
curl "https://api.lendpathway.com/api/books/{book_id}/spreadsheet-export?sheet_type=mca" \
-H "Authorization: Bearer pat_xxx" \
-o statements.xlsx