ReconcileIQ API Documentation

The ReconcileIQ API provides automated bank reconciliation capabilities for accounting software, financial platforms, and enterprise systems. This documentation offers detailed technical specifications for implementing the API.

Base URL

https://api.bankreconciler.app

Key Features

  • Automated bank statement reconciliation with bookkeeping records
  • Intelligent column mapping and format detection
  • Secure client-side data encryption
  • Real-time progress updates via WebSockets
  • High-performance processing with C++ backend engine

Authentication

All API requests (except registration and login) require authentication using JWT tokens.

POST /api/register

Request Body

{
  "email": "[email protected]",
  "password": "SecureP@ssw0rd"
}

Response 201 Created

{
  "message": "Registration successful. Please check your email for a verification link to activate your account.",
  "user": {
    "id": "user-uuid",
    "email": "[email protected]",
    "tier": "free"
  }
}

Note: Passwords must be at least 8 characters and include uppercase letters and numbers/special characters. Email verification is required before login.

POST /api/login

Request Body

{
  "email": "[email protected]",
  "password": "SecureP@ssw0rd"
}

Response 200 OK

{
  "message": "Login successful.",
  "user": {
    "id": "user-uuid",
    "email": "[email protected]", 
    "tier": "api_global"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Note: The JWT token expires after 24 hours. Store this token securely and include it in all subsequent API requests. For security, don't store tokens in localStorage in browser environments; prefer HTTP-only cookies.

Data Encryption

All CSV data must be encrypted client-side before transmission.

Encryption Process

  1. Generate AES-GCM 256-bit key
  2. Encrypt CSV data with this key
  3. Send encrypted data and key with API request

JavaScript Implementation

// Generate encryption key
const generateKey = async () => {
  return await window.crypto.subtle.generateKey(
    {
      name: "AES-GCM",
      length: 256
    },
    true,
    ["encrypt", "decrypt"]
  );
};

// Encrypt CSV data
const encryptData = async (data, key) => {
  const iv = window.crypto.getRandomValues(new Uint8Array(12));
  const encoder = new TextEncoder();
  const encodedData = encoder.encode(data);

  const encryptedContent = await window.crypto.subtle.encrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    key,
    encodedData
  );

  const encryptedArray = new Uint8Array(iv.length + encryptedContent.byteLength);
  encryptedArray.set(iv);
  encryptedArray.set(new Uint8Array(encryptedContent), iv.length);

  return {
    encrypted: encryptedArray,
    key: await window.crypto.subtle.exportKey("raw", key)
  };
};

// Convert ArrayBuffer to Base64
const arrayBufferToBase64 = (buffer) => {
  const bytes = new Uint8Array(buffer);
  let binary = '';
  for (let i = 0; i < bytes.length; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return btoa(binary);
};

Reconciliation Process

The reconciliation process happens in two API calls: initial mapping and final processing.

1. Initial Column Mapping Request

POST /

Headers

Authorization: Bearer your_jwt_token
Content-Type: application/json

Request Body

{
  "bankCsv": "base64_encoded_encrypted_csv",
  "bookCsv": "base64_encoded_encrypted_csv",
  "key": "encryption_key_base64",
  "clientId": "unique_client_identifier"
}

Response 200 OK

{
  "bank": {
    "headers": ["Date", "Description", "Debit", "Credit", "Balance"],
    "mapping": {
      "Date": {
        "columnIndex": 0,
        "columnName": "Date"
      },
      "Description": {
        "columnIndex": 1,
        "columnName": "Description"
      },
      "Money Out": {
        "columnIndex": 2,
        "columnName": "Debit"
      },
      "Money In": {
        "columnIndex": 3,
        "columnName": "Credit"
      }
    },
    "sampleRows": [
      ["2024-01-01", "Opening Balance", "", "1000.00", "1000.00"],
      ["2024-01-15", "Rent Payment", "500.00", "", "500.00"]
    ]
  },
  "book": {
    "headers": ["Transaction Date", "Narrative", "Amount", "Type"],
    "mapping": {
      "Date": {
        "columnIndex": 0,
        "columnName": "Transaction Date"
      },
      "Description": {
        "columnIndex": 1,
        "columnName": "Narrative"
      },
      "Single Amount": {
        "columnIndex": 2,
        "columnName": "Amount"
      }
    },
    "sampleRows": [
      ["2024-01-01", "Opening Balance", "1000.00", "Deposit"],
      ["2024-01-15", "Rent Payment", "-500.00", "Withdrawal"]
    ]
  }
}

Note: The clientId must be unique per reconciliation session and used in subsequent requests. The system automatically detects column mappings based on common naming patterns.

2. Confirm Column Mapping and Process

POST /api/reconciliation/approve-mapping

Headers

Authorization: Bearer your_jwt_token
Content-Type: application/json

Request Body

{
  "clientId": "same_client_id_as_initial_request",
  "approvedMapping": {
    "bank": {
      "mapping": {
        "Date": {
          "columnIndex": 0,
          "columnName": "Date"
        },
        "Description": {
          "columnIndex": 1,
          "columnName": "Description"
        },
        "Money Out": {
          "columnIndex": 2,
          "columnName": "Debit"
        },
        "Money In": {
          "columnIndex": 3,
          "columnName": "Credit"
        }
      },
      "headers": ["Date", "Description", "Debit", "Credit", "Balance"]
    },
    "book": {
      "mapping": {
        "Date": {
          "columnIndex": 0,
          "columnName": "Transaction Date"
        },
        "Description": {
          "columnIndex": 1,
          "columnName": "Narrative"
        },
        "Single Amount": {
          "columnIndex": 2,
          "columnName": "Amount" 
        }
      },
      "headers": ["Transaction Date", "Narrative", "Amount", "Type"]
    }
  }
}

Response 200 OK

{
  "missingFromBooks": [
    {
      "Date": "2024-02-15",
      "Amount": "105.99",
      "Description": "Supplier Payment"
    },
    {
      "Date": "2024-02-21",
      "Amount": "-32.50",
      "Description": "Refund Received"
    }
  ],
  "removeFromBooks": [
    {
      "Date": "2024-01-30",
      "Amount": "250.00",
      "Description": "Transfer to Savings"
    }
  ]
}

Note: This step actually processes the reconciliation and deducts credits from your account. Transaction counts from the bank statement determine credit usage.

WebSocket Progress Updates

For large files, you can monitor processing progress using WebSockets.

Connection

const ws = new WebSocket(`wss://api.bankreconciler.app/ws?clientId=${clientId}&token=${token}`);

Parameters

  • clientId: Same ID used in reconciliation requests
  • token: Your JWT authentication token

Message Format

{
  "percentage": 45,
  "status": "Processing transactions..."
}

Example Implementation

// Connect to WebSocket
const ws = new WebSocket(`wss://api.bankreconciler.app/ws?clientId=${clientId}&token=${token}`);

ws.onopen = () => {
  console.log('WebSocket connected');
  // Send an initial ping
  ws.send(JSON.stringify({ type: 'ping' }));
};

ws.onmessage = (event) => {
  try {
    const data = JSON.parse(event.data);
    
    if (data.percentage !== undefined) {
      // Update progress UI
      updateProgressBar(data.percentage);
      updateProgressText(data.status || `Processing... ${Math.round(data.percentage)}%`);
    }
  } catch (err) {
    console.error('Failed to parse WebSocket message:', err);
  }
};

// Send ping every 30 seconds to keep connection alive
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }));
  }
}, 30000);

Note: The WebSocket sends progress updates during both mapping and final reconciliation. The connection closes automatically when processing completes.

Matching Algorithm Details

The ReconcileIQ engine uses several techniques to match transactions:

Date Matching

  • Exact date matches are prioritized
  • Date window tolerance of ±3 days for near matches
  • Automatic handling of different date formats (DD/MM/YYYY, MM/DD/YYYY, etc.)

Amount Matching

  • Exact amount matches within 0.001 tolerance
  • Proper handling of different currency formats
  • Smart detection of positive/negative values

Description Matching

  • Basic string comparison
  • Not used as primary matching criterion due to formatting inconsistencies

Note: Our bidirectional matching algorithm first attempts to match each bank transaction to bookkeeping records, then matches each bookkeeping record to bank transactions. This ensures all discrepancies are found in a single pass.

Error Handling

Common Error Codes

HTTP Code Description Possible Causes
400 Bad Request Missing fields, invalid data format
401 Unauthorized Missing or expired token
403 Forbidden Insufficient permissions or unverified email
404 Not Found Resource does not exist
429 Too Many Requests Rate limit exceeded
500 Server Error Internal processing error

Error Response Format

{
  "error": "Error message",
  "type": "ValidationError"
}

Error Types

  • ValidationError: Input data failed validation
  • SecurityError: Security-related issue
  • ProcessingError: Error during reconciliation processing
  • TimeoutError: Request took too long to process

Subscription Management

Get Subscription Status

GET /api/subscription/status

Headers

Authorization: Bearer your_jwt_token

Response 200 OK

{
  "plan_name": "api_global",
  "display_tier": "api_global",
  "monthly_credits": 1000000,
  "credits_used": 250000,
  "bonus_credits": 50000,
  "renewal_date": "2024-04-01T00:00:00.000Z",
  "credits_remaining": 750000,
  "tier": "api_global",
  "payment_status": "active",
  "last_payment_date": "2024-03-01T00:00:00.000Z",
  "next_payment_date": "2024-04-01T00:00:00.000Z",
  "billing_cycle": "monthly",
  "months_duration": 1
}

Add Bonus Credits

POST /api/subscription/add-bonus

Headers

Authorization: Bearer your_jwt_token
Content-Type: application/json

Request Body

{
  "bonus_amount": 50000
}

Response 200 OK

{
  "message": "Bonus credits added successfully"
}

Note: This endpoint is typically used for administrative purposes and may require special permissions.

CSV Format Requirements

  • Maximum file size: 10MB
  • Maximum rows: 400,000
  • Required columns: Date, Description, and Amount (or Credit/Debit)
  • Date formats supported: All standard formats
  • Amount formats: Standard currency formats with or without symbols

File Sample

Bank statement CSV:

Date,Description,Debit,Credit,Balance
2024-01-01,"Opening Balance",,1000.00,1000.00
2024-01-15,"Rent Payment",500.00,,500.00
2024-01-20,"Salary",,2000.00,2500.00
2024-01-25,"Groceries",150.00,,2350.00

Bookkeeping CSV:

Transaction Date,Narrative,Amount,Type
2024-01-01,"Opening Balance",1000.00,"Deposit"
2024-01-15,"Rent Payment",-500.00,"Withdrawal"
2024-01-25,"Groceries",-150.00,"Withdrawal"
2024-01-30,"Transfer to Savings",-250.00,"Transfer"

Rate Limits

Plan Requests per Minute Concurrent Requests
API Startup 100 5
API Growth 300 15
API Enterprise Custom Custom

Note: Rate limits apply to all endpoints. Exceeding the limit will result in a 429 Too Many Requests response.

Implementation Examples

Complete Reconciliation Flow

// Step 1: Login and get token
async function login(email, password) {
  const response = await fetch('https://api.bankreconciler.app/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });
  
  const data = await response.json();
  if (!response.ok) throw new Error(data.error || 'Login failed');
  
  return data.token;
}

// Step 2: Process reconciliation
async function reconcile(bankCsvContent, bookCsvContent, token) {
  // Generate client ID
  const clientId = Date.now().toString(36) + Math.random().toString(36).substring(2);
  
  // Encrypt data
  const key = await generateKey();
  const encryptedBank = await encryptData(bankCsvContent, key);
  const encryptedBook = await encryptData(bookCsvContent, key);
  
  const payload = {
    bankCsv: arrayBufferToBase64(encryptedBank.encrypted),
    bookCsv: arrayBufferToBase64(encryptedBook.encrypted),
    key: arrayBufferToBase64(encryptedBank.key),
    clientId
  };
  
  // Set up WebSocket for progress updates
  const ws = new WebSocket(`wss://api.bankreconciler.app/ws?clientId=${clientId}&token=${token}`);
  
  ws.onmessage = (event) => {
    try {
      const data = JSON.parse(event.data);
      if (data.percentage !== undefined) {
        updateProgressUI(data.percentage, data.status);
      }
    } catch (err) {
      console.error('Failed to parse WebSocket message:', err);
    }
  };
  
  // Step 2a: Initial mapping request
  const mappingResponse = await fetch('https://api.bankreconciler.app/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify(payload)
  });
  
  const mappingSuggestion = await mappingResponse.json();
  if (!mappingResponse.ok) throw new Error(mappingSuggestion.error || 'Mapping failed');
  
  // Step 2b: Let user review mapping or automatically proceed
  const approvedMapping = mappingSuggestion; // Or modify based on user input
  
  // Step 2c: Send approved mapping
  const finalResponse = await fetch('https://api.bankreconciler.app/api/reconciliation/approve-mapping', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify({
      clientId,
      approvedMapping
    })
  });
  
  const result = await finalResponse.json();
  if (!finalResponse.ok) throw new Error(result.error || 'Reconciliation failed');
  
  // Close WebSocket
  ws.close();
  
  return result;
}

// Sample usage
async function main() {
  try {
    const token = await login('[email protected]', 'SecureP@ssw0rd');
    const bankCsv = await fetchBankStatement(); // Your implementation
    const bookCsv = await fetchBookkeepingData(); // Your implementation
    
    const result = await reconcile(bankCsv, bookCsv, token);
    
    // Process results
    console.log(`Found ${result.missingFromBooks.length} missing transactions`);
    console.log(`Found ${result.removeFromBooks.length} extra transactions`);
    
    // Display results to user
    displayResults(result);
  } catch (error) {
    console.error('Reconciliation failed:', error);
  }
}

Best Practices

Security

  • Store JWT tokens securely
  • Encrypt sensitive data client-side
  • Use HTTPS for all communications
  • Implement proper error handling

Performance

  • Pre-process large files into smaller chunks
  • Use WebSockets for progress monitoring
  • Implement retry logic with exponential backoff

User Experience

  • Show clear progress indicators for long-running operations
  • Provide intuitive mapping interface
  • Display reconciliation results in a user-friendly format

Error Handling

  • Implement proper error handling for all API calls
  • Provide meaningful error messages to users
  • Log all API interactions for troubleshooting

Support & Contact

For API support inquiries and custom integration requests: