v1.0

Analyzing Bank Statements

How to use the SmartScrub analysis API for bank statement classification

This guide covers both analysis modes — PDF upload and JSON transactions — and how to interpret the results for MCA underwriting decisions.

Choosing a Mode

ModeWhen to useResponse
PDF uploadYou have raw bank statement PDFsAlways async (202)
JSON transactionsYou’ve already parsed the transactionsSync if ≤500 txns, async if >500

Mode 1: PDF Upload

Upload PDF bank statements and let SmartScrub handle parsing and classification.

const form = new FormData();
form.append('files', fs.createReadStream('jan-2026.pdf'));
form.append('files', fs.createReadStream('feb-2026.pdf'));
form.append('files', fs.createReadStream('mar-2026.pdf'));
form.append('merchantId', 'merch_123');
form.append('dealId', 'deal_456');

const res = await fetch('https://api.smartmca.com/api/public/v1/analysis/bank-statements', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${API_KEY}` },
  body: form,
});

const { data } = await res.json(); // { id, status: "pending", mode: "pdf" }
const analysisId = data.id;

Poll for Results

async function pollAnalysis(analysisId) {
  while (true) {
    const res = await fetch(
      `https://api.smartmca.com/api/public/v1/analysis/${analysisId}`,
      { headers: { 'Authorization': `Bearer ${API_KEY}` } }
    );
    const { data } = await res.json();

    if (data.status === 'completed') return data;
    if (data.status === 'failed') throw new Error(data.error);

    // Wait 5 seconds before next poll
    await new Promise(r => setTimeout(r, 5000));
  }
}

const result = await pollAnalysis(analysisId);

Mode 2: JSON Transactions

If you’ve already extracted transactions (from your own parser, a banking API, or Plaid), submit them directly:

const res = await fetch('https://api.smartmca.com/api/public/v1/analysis/transactions', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    merchantId: 'merch_123',
    transactions: parsedTransactions.map(t => ({
      date: t.date,           // "2026-01-15"
      description: t.memo,     // "ACH DEBIT - FUNDER CO"
      amount: Math.abs(t.amount),
      type: t.amount > 0 ? 'credit' : 'debit',
    })),
  }),
});

const { data } = await res.json();

if (data.status === 'completed') {
  // Sync response — results are already included
  processResults(data);
} else {
  // Async — poll for results
  const result = await pollAnalysis(data.id);
  processResults(result);
}

Interpreting Results

MCA Positions

The mcaPositions array identifies existing MCA advances detected in the bank statements:

for (const position of result.mcaPositions) {
  console.log(`Funder: ${position.funderName}`);
  console.log(`  Frequency: ${position.frequency}`);
  console.log(`  Avg payment: $${position.avgPayment.toFixed(2)}`);
  console.log(`  Total paid: $${position.totalPayments.toFixed(2)}`);
  console.log(`  Confidence: ${(position.confidence * 100).toFixed(0)}%`);
}

Use this to assess:

  • Stacking risk — How many active MCA positions exist?
  • Payment burden — What percentage of revenue goes to MCA repayments?
  • Funder exposure — Which funders already have positions?

Classification Breakdown

The classifications object groups transactions into buckets:

const { classifications } = result;
const totalRevenue = classifications.revenue.total;
const totalMCA = classifications.mcaRepayment.total;
const mcaBurden = (totalMCA / totalRevenue * 100).toFixed(1);

console.log(`Revenue: $${totalRevenue}`);
console.log(`MCA repayments: $${totalMCA} (${mcaBurden}% of revenue)`);
console.log(`Operating expenses: $${classifications.operatingExpense.total}`);

Per-Transaction Detail

Each transaction includes its classification and confidence:

// Find high-confidence revenue transactions
const verifiedRevenue = result.transactions
  .filter(t => t.classification === 'businessRevenue' && t.confidence > 0.8);

// Find NSF/overdraft events
const nsfEvents = result.transactions
  .filter(t => t.classification === 'nsfFee' || t.classification === 'bankFee');

console.log(`Verified revenue transactions: ${verifiedRevenue.length}`);
console.log(`NSF/overdraft events: ${nsfEvents.length}`);

Deal-Scoped Analysis

When you provide a dealId, the analysis is linked to the deal. You can then access it through the underwriting endpoints:

// Get all underwriting results for a deal (includes SmartScrub + other tools)
const results = await fetch(
  `https://api.smartmca.com/api/public/v1/deals/${dealId}/underwriting/results`,
  { headers: { 'Authorization': `Bearer ${API_KEY}` } }
).then(r => r.json());

Cleanup

Delete analysis data when no longer needed:

await fetch(`https://api.smartmca.com/api/public/v1/analysis/${analysisId}`, {
  method: 'DELETE',
  headers: { 'Authorization': `Bearer ${API_KEY}` },
});