Generating a Transaction-Level Balance Timeline Using Fiskil's Banking APIs

Overview

Fiskil’s API does not return historical balances directly—but you can reconstruct them by starting with the current balance and working backward through transaction history.

This guide shows how to generate a running balance after each transaction, using:

  • /v1/banking/balances – to retrieve the current balance
  • /v1/banking/transactions – to retrieve all posted transactions

This method is accurate to each individual transaction, not just by day.

Use Cases

  • Treasury platforms tracking fund movements over time
  • Accounting platforms syncing general ledger and balances
  • Reconciliation tools verifying balances after each bank movement
  • Any application requiring true ledger-style balance updates

How It Works

To reconstruct a running balance timeline:

  1. Fetch the current balance.
  2. Fetch all POSTED transactions sorted by posting_date_time descending.
  3. Walk backward, subtracting the amount of each transaction from the current balance.
  4. Store the balance after each transaction, paired with that transaction’s metadata.

This produces an audit-accurate sequence of balance changes.

Example output:

[
  {
    "transaction_id": "txn-1234",
    "posting_date_time": "2024-09-15T14:30:00Z",
    "amount": "-50.00",
    "balance_after": "950.00"
  },
  {
    "transaction_id": "txn-1233",
    "posting_date_time": "2024-09-15T10:00:00Z",
    "amount": "-100.00",
    "balance_after": "1000.00"
  }
]

Step-by-Step Guide

Step 1: Fetch the Current Balance

Use the /v1/banking/balances endpoint:

GET /v1/banking/balances?end_user_id={end_user_id}&account_id={account_id}

This returns the most recent known balance for the account.

Example response:

{
  "balances": [
    {
      "account_id": "123456789",
      "current_balance": "-250.00",
      "currency": "AUD"
    }
  ]
}

Step 2: Fetch All Transactions

Use the /v1/banking/transactions endpoint with a from date:

GET /v1/banking/transactions?end_user_id={end_user_id}&account_id={account_id}&from=2024-09-02&status=POSTED

Retrieve all POSTED transactions and sort them in descending order by posting_date_time.

JavaScript Example: Generate Historical Balances

const fetch = require('node-fetch');
 
const ACCESS_TOKEN = 'YOUR_ACCESS_TOKEN';
const END_USER_ID = 'your_end_user_id';
const ACCOUNT_ID = 'your_account_id';
 
const BASE_URL = 'https://api.fiskil.com/v1';
const HEADERS = {
  'Authorization': `Bearer ${ACCESS_TOKEN}`,
  'Accept': 'application/json; charset=UTF-8',
  'Content-Type': 'application/json; charset=UTF-8'
};
 
// Fetch JSON helper
async function fetchJSON(url) {
  const response = await fetch(url, { headers: HEADERS });
  if (!response.ok) throw new Error(`HTTP ${response.status} - ${response.statusText}`);
  return response.json();
}
 
// Step 1: Get current balance
async function getCurrentBalance() {
  const url = `${BASE_URL}/banking/balances?end_user_id=${END_USER_ID}&account_id=${ACCOUNT_ID}`;
  const data = await fetchJSON(url);
  return parseFloat(data.balances?.[0]?.current_balance || '0');
}
 
// Step 2: Get all posted transactions
async function getAllPostedTransactions() {
  let transactions = [];
  let nextPage = null;
 
  do {
    const params = new URLSearchParams({
      end_user_id: END_USER_ID,
      account_id: ACCOUNT_ID,
      status: 'POSTED',
      'page[size]': 100
    });
    if (nextPage) params.set('page[after]', nextPage);
 
    const url = `${BASE_URL}/banking/transactions?${params.toString()}`;
    const data = await fetchJSON(url);
 
    transactions.push(...(data.transactions || []));
    const match = data.links?.next?.match(/page\[after\]=([^&]+)/);
    nextPage = match?.[1] || null;
  } while (nextPage);
 
  // Sort transactions descending by posting_date_time
  return transactions.sort((a, b) =>
    new Date(b.posting_date_time) - new Date(a.posting_date_time)
  );
}
 
// Step 3: Build per-transaction balance timeline
async function getTransactionBalanceTimeline() {
  const transactions = await getAllPostedTransactions();
  let balance = await getCurrentBalance();
 
  const timeline = transactions.map(tx => {
    const txAmount = parseFloat(tx.amount);
    const entry = {
      transaction_id: tx.transaction_id,
      posting_date_time: tx.posting_date_time,
      amount: tx.amount,
      balance_after: balance.toFixed(2)
    };
    balance -= txAmount;
    return entry;
  });
 
  return timeline;
}
 
// Run and display the timeline
getTransactionBalanceTimeline()
  .then(timeline => {
    console.log("Transaction-level balance timeline:");
    console.table(timeline);
  })
  .catch(console.error);
 

Additional notes

  • Per-transaction precision
    Each balance entry corresponds directly to a specific posted transaction, providing an accurate ledger-style timeline.

  • Extendable structure
    You can enrich each timeline entry with additional transaction fields such as:

    • description
    • merchant_name
    • category.primary_category
    • reference

    This can make the balance timeline more informative for audit trails or UIs.

  • Derive daily or monthly snapshots
    If needed, you can group transactions and extract the balance_after of the last transaction per day or per month to generate high-level trends.

  • Timezone handling
    All posting_date_time values from the Fiskil API are in UTC. If you're displaying balances in a user-facing product, make sure to adjust for the user's local timezone where appropriate.

  • Currency consideration
    This implementation assumes a single currency per account. If the account includes multi-currency purses, you'll need to run this logic per currency.

  • Performance
    For high-volume accounts, consider caching results or running this logic as a background job if the full ledger is long. You can also limit by date using the from/to parameters on /transactions to reduce API load.

Was this page helpful?