Skip to content

File Upload Guide

Learn how to upload financial documents to Laminr for processing.

Overview

Files are uploaded to packages. A package is a container that groups related documents together (e.g., all documents for a single loan application).

Workflow:

  1. Create a package
  2. Upload one or more files to the package
  3. Monitor processing status
  4. Retrieve results

See the Getting Started guide for a complete walkthrough.

Supported File Types

Laminr accepts various financial document types:

  • Bank statements: PDF, PNG, JPEG
  • Pay stubs: PDF, PNG, JPEG
  • Tax returns: PDF
  • W-2 forms: PDF, PNG, JPEG

File requirements:

  • Maximum file size: 1 GB per file
  • Recommended: 300 DPI or higher for scanned documents
  • PDF format preferred for best results

Best Quality

For best extraction accuracy, use high-quality PDFs generated directly from financial institutions rather than scanned documents.

Upload Files to a Package

File uploads use a three-step process with presigned URLs for secure, direct-to-storage uploads:

Step 1: Get a Presigned Upload URL

Endpoint:

POST /api/v1/files

Request:

curl -X POST https://api.laminr.ai/api/v1/files \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_name": "bank_statement.pdf"}'

Parameters:

Parameter Type Required Description
file_name string Yes The name of the file you want to upload

Response:

{
  "upload_url": "https://storage.example.com/presigned-url-with-credentials...",
  "uri": "tenants/123/files/1699123456-789-bank-statement-pdf"
}

The upload_url is a temporary presigned URL that allows you to upload directly to cloud storage. The uri is the permanent identifier for the file that you'll use in Step 3.

Step 2: Upload File to Presigned URL

Upload your file directly to the presigned URL using HTTP PUT:

curl -X PUT "https://storage.example.com/presigned-url-with-credentials..." \
  --upload-file /path/to/bank_statement.pdf \
  -H "Content-Type: application/pdf"

Direct Upload

This upload goes directly to cloud storage (not through the Laminr API), which provides better performance and reliability for large files.

Step 3: Create File Record in Package

After uploading, create the file record in your package:

Endpoint:

POST /api/v1/packages/{package_id}/files

Request:

curl -X POST https://api.laminr.ai/api/v1/packages/LP-2025-001/files \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "uri": "tenants/123/files/1699123456-789-bank-statement-pdf",
    "file_name": "bank_statement.pdf"
  }'

Parameters:

Parameter Type Required Description
uri string Yes The URI returned from Step 1
file_name string Yes The name of the file

Response:

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "loan_package_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "file_name": "bank_statement.pdf",
  "file_extension": "pdf",
  "file_size": 245678,
  "file_md5_hash": "abc123def456...",
  "file_content_type": "application/pdf",
  "file_created_at": "2025-11-05T10:05:00.000000+00:00",
  "status": "queued",
  "created_at": "2025-11-05T10:05:00.000000+00:00",
  "updated_at": "2025-11-05T10:05:00.000000+00:00",
  "progress": null
}

Upload Multiple Files

You can upload multiple files to the same package. Each file is processed independently. Repeat the three-step process for each file:

# File 1
curl -X POST https://api.laminr.ai/api/v1/files \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_name": "bank_statement_jan.pdf"}'
# Returns upload_url and uri
curl -X PUT "<upload_url>" --upload-file bank_statement_jan.pdf
curl -X POST https://api.laminr.ai/api/v1/packages/LP-2025-001/files \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"uri": "<uri>", "file_name": "bank_statement_jan.pdf"}'

# File 2
curl -X POST https://api.laminr.ai/api/v1/files \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_name": "bank_statement_feb.pdf"}'
# Returns upload_url and uri
curl -X PUT "<upload_url>" --upload-file bank_statement_feb.pdf
curl -X POST https://api.laminr.ai/api/v1/packages/LP-2025-001/files \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"uri": "<uri>", "file_name": "bank_statement_feb.pdf"}'

Check Processing Status

After uploading, files go through several processing stages:

Status Description
queued File has been uploaded and is waiting to be processed
processing Document is being analyzed and data extracted
processed Processing finished successfully
failed Processing failed (see error details)

Check Package Status

Get the overall status of all files in a package:

curl https://api.laminr.ai/api/v1/packages/LP-2025-001 \
  -H "x-api-key: YOUR_API_KEY"

Response:

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "tenant": {
    "id": "tenant_123",
    "name": "Your Company"
  },
  "title": "John Doe Application",
  "public_id": "LP-2025-001",
  "created_at": "2025-11-05T10:00:00.000000+00:00",
  "updated_at": "2025-11-05T10:05:30.000000+00:00",
  "status": "Processing",
  "created_by": {
    "id": "user_123",
    "email": "you@example.com"
  },
  "progress": 0.5,
  "under_review": false
}

To see individual file statuses, list the files in the package:

curl https://api.laminr.ai/api/v1/packages/LP-2025-001/files \
  -H "x-api-key: YOUR_API_KEY"

The package status will be: - "Processing": Files are still being processed - "Processed": All files have been processed successfully - "Under Review": Package is under manual review

The progress field (0.0 to 1.0) indicates overall package processing progress.

Error Handling

Upload Errors

Common upload errors:

{
  "error": {
    "code": "invalid_file_type",
    "message": "File type not supported. Accepted types: PDF, PNG, JPEG"
  }
}

Common error codes: - invalid_file_type: Unsupported file format - file_too_large: File exceeds 10 MB limit - invalid_package: Package ID not found - rate_limit_exceeded: Too many requests

Processing Errors

If a file fails to process, check the package details for error information:

{
  "id": "file_xyz789",
  "filename": "bank_statement.pdf",
  "status": "failed",
  "error": {
    "code": "extraction_failed",
    "message": "Unable to extract data from document",
    "details": "Document quality too low for reliable extraction"
  }
}

Best Practices

File Quality

  • Use PDF format when possible
  • Ensure text is readable (not too blurry or low resolution)
  • Avoid heavily compressed images
  • For scans, use at least 300 DPI

Batch Uploads

Upload files concurrently to save time:

import asyncio
import aiohttp

async def upload_file(session, package_id, filepath, api_key):
    headers = {'x-api-key': api_key}

    # Step 1: Get presigned URL
    async with session.post(
        'https://api.laminr.ai/api/v1/files',
        json={'file_name': filepath},
        headers=headers
    ) as response:
        data = await response.json()
        upload_url = data['upload_url']
        uri = data['uri']

    # Step 2: Upload file to presigned URL
    with open(filepath, 'rb') as f:
        file_data = f.read()
    async with session.put(upload_url, data=file_data) as response:
        await response.read()

    # Step 3: Create file record in package
    async with session.post(
        f'https://api.laminr.ai/api/v1/packages/{package_id}/files',
        json={'uri': uri, 'file_name': filepath},
        headers=headers
    ) as response:
        return await response.json()

async def batch_upload(package_id, filepaths, api_key):
    async with aiohttp.ClientSession() as session:
        tasks = [upload_file(session, package_id, fp, api_key) for fp in filepaths]
        return await asyncio.gather(*tasks)

# Upload 3 files concurrently
API_KEY = 'your_api_key_here'
files = ['statement1.pdf', 'statement2.pdf', 'paystub.pdf']
results = asyncio.run(batch_upload('LP-2025-001', files, API_KEY))

Monitoring Progress

Poll the package status endpoint to monitor processing:

import time
import requests

def wait_for_completion(package_id):
    headers = {'x-api-key': API_KEY}

    while True:
        response = requests.get(
            f'https://api.laminr.ai/api/v1/packages/{package_id}',
            headers=headers
        )
        package = response.json()

        if package['status'] == 'Processed':
            print("Processing complete!")
            return package
        elif package['status'] == 'Failed':
            print("Processing failed!")
            return package

        # Show progress (0.0 to 1.0)
        progress = package['progress']
        print(f"Progress: {progress * 100:.0f}%")

        time.sleep(5)  # Wait 5 seconds before checking again

result = wait_for_completion('LP-2025-001')

Supported Document Types

Bank Statements

  • Extract transactions, balances, account information
  • Supports most major US banks
  • Best results with statements covering 2-3 months

Pay Stubs

  • Extract income, deductions, YTD totals
  • Supports standard pay stub formats
  • Include multiple pay periods for better accuracy

Tax Returns

  • Extract income, deductions, and tax information
  • Supports Form 1040 and common schedules
  • W-2 forms can be uploaded separately

Next Steps

Support

Need help? Contact us at support@laminr.ai