# Cash flow forecasting with the Accounting API

A short-term cash flow forecast answers one question: how much cash will the business hold at the end of each of the next thirteen weeks. The pattern below assembles that forecast from the [Accounting API](/apis/accounting/reference) by combining a starting cash position with scheduled receivables, scheduled payables, and a recurring baseline derived from history. It is written for cash management, FP&A, lending, and treasury products that need a reproducible forecast across whichever accounting system the customer uses.

## One integration for every ledger

A thirteen-week forecast reads bank balances, open documents, payment history, and financial reports. Building that separately against QuickBooks, Xero, NetSuite, Sage Intacct, and Exact Online means five sets of auth, pagination, and schema mapping. The Accounting API gives you one model for all of them.

- Vendor bills and purchase invoices map to the same `Bill` resource on every connector, and their payments to the separate `BillPayment` resource at `/accounting/bill-payments`.
- Aged debtors and aged creditors share one report format, regardless of how the source system buckets them.
- `due_date`, `transaction_date`, `balance`, and `currency` mean the same thing everywhere, which keeps the bucket math connector-agnostic.
- Invoices, bills, payments, and journal entries accept `filter[updated_since]`, so the forecast can refresh incrementally.

## Resource mapping

Each row shows a forecast input, the endpoint that supplies it, and the object the connector exposes underneath.

| Forecast input | Unified endpoint | Typical downstream object |
| --- | --- | --- |
| Starting cash | [`GET /accounting/balance-sheet`](/apis/accounting/reference#operation/balanceSheetOne) | `BalanceSheet` report, `TrialBalance` |
| Cash account detail | [`GET /accounting/bank-accounts`](/apis/accounting/reference#operation/bankAccountsAll) | `Account`, `BankAccount`, `GLAccount` |
| Receivables schedule | [`GET /accounting/invoices`](/apis/accounting/reference#operation/invoicesAll), open items filtered client-side | `Invoice`, `SalesInvoice` |
| Receivables aging | [`GET /accounting/aged-debtors`](/apis/accounting/reference#operation/agedDebtorsOne) | `AgedReceivables`, `AR Aging Summary` |
| Payables schedule | [`GET /accounting/bills`](/apis/accounting/reference#operation/billsAll) with `filter[status]=unpaid` | `Bill`, `PurchaseInvoice`, `VendorBill` |
| Payables aging | [`GET /accounting/aged-creditors`](/apis/accounting/reference#operation/agedCreditorsOne) | `AgedPayables`, `AP Aging Summary` |
| Historical days-to-pay | [`GET /accounting/payments`](/apis/accounting/reference#operation/paymentsAll) | `Payment`, `CustomerPayment`, `Receipt` |
| Recurring baseline | [`GET /accounting/profit-and-loss`](/apis/accounting/reference#operation/profitAndLossOne), [`GET /accounting/journal-entries`](/apis/accounting/reference#operation/journalEntriesAll) | `ProfitAndLoss` report, `JournalEntry` |

## Walkthrough

The forecast is a table with a `Week 0` column for today's closing balance followed by thirteen projected weeks. Each row is a line item: opening balance, scheduled inflows, scheduled outflows, recurring inflows, recurring outflows, and closing balance.

### 1. Read the starting cash position

Pull a balance sheet dated today to anchor the forecast. The cash and bank lines under `assets` give the `Week 0` opening balance.

```http
GET /accounting/balance-sheet?filter[end_date]=2025-01-31 HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: cns_acme_treasury
x-apideck-service-id: quickbooks
```

To split the forecast by cash account (operating, payroll, FX sub-account), list bank accounts with [`GET /accounting/bank-accounts`](/apis/accounting/reference#operation/bankAccountsAll). Each record has `balance`, `currency`, `account_type`, and a `ledger_account` link back to the chart of accounts. `filter[account_type]` and `filter[status]` narrow the list, for example `filter[status]=active`.

```http
GET /accounting/bank-accounts?filter[status]=active HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: cns_acme_treasury
x-apideck-service-id: quickbooks
```

On connectors without bank account coverage, list [ledger accounts](/apis/accounting/reference#operation/ledgerAccountsAll) with `filter[classification]=asset`, keep the records whose `type` is `bank` (there is no type filter), and read `current_balance` where the connector returns it.

### 2. Schedule inflows from open invoices

For scheduled receivables, pull invoices and bucket the open ones by `due_date`. The invoices filter has no status parameter, so filter client-side: keep invoices with a `balance` above zero and drop any with `status` of `draft`, `void`, `deleted`, or `paid`. On the first run, page through the full list. After that, fetch only what changed with `filter[updated_since]` (step 8).

```http
GET /accounting/invoices?filter[updated_since]=2025-01-24T00:00:00Z&limit=200 HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: cns_acme_treasury
x-apideck-service-id: quickbooks
```

A representative item in the response looks like this. The forecast reads `due_date`, `balance`, `currency`, and `customer.id`.

```json
{
  "id": "inv_01H8X9Y2A3BCDE4F5G6H7J8K9L",
  "number": "INV-2025-0417",
  "customer": {
    "id": "cus_01H7T4V9MNQR2PS8KYZB3CDEF1",
    "display_name": "Northwind Logistics"
  },
  "invoice_date": "2024-12-20",
  "due_date": "2025-02-18",
  "currency": "USD",
  "total": 48250.0,
  "balance": 48250.0,
  "status": "unpaid"
}
```

Reference: [Invoices list](/apis/accounting/reference#operation/invoicesAll).

### 3. Cross-check receivables with aged debtors

The aged debtors report returns the downstream system's own aging buckets, which already account for partial payments and credit notes. Use it as a reconciliation check against the invoice-level sum.

```http
GET /accounting/aged-debtors?filter[report_as_of_date]=2025-01-31&filter[period_count]=4&filter[period_length]=30 HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: cns_acme_treasury
x-apideck-service-id: xero
```

The response groups `outstanding_balances` by customer, then by currency, with a `total_amount` per currency and per period. If the sum of open invoice balances for a customer disagrees with that total by more than a cent, trust the report and flag the customer's invoices for refresh. Reference: [Aged debtors](/apis/accounting/reference#operation/agedDebtorsOne).

### 4. Schedule outflows from open bills

Payables follow the same flow. The bills filter does support status, but takes one value per call, so request `filter[status]=unpaid` and `filter[status]=partially_paid` separately, bucket by `due_date`, and reconcile against aged creditors.

```http
GET /accounting/bills?filter[status]=unpaid&filter[updated_since]=2025-01-24T00:00:00Z HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: cns_acme_treasury
x-apideck-service-id: quickbooks
```

```json
{
  "id": "bill_01H9A2B4CDEF5G6H7J8K9LMNPQ",
  "supplier": {
    "id": "sup_01H8R3T5VWXY6Z7A8BCDEFGHJ2",
    "display_name": "Contoso Hosting"
  },
  "bill_date": "2025-01-05",
  "due_date": "2025-02-04",
  "currency": "USD",
  "total": 12480.75,
  "balance": 12480.75,
  "status": "unpaid"
}
```

Reference: [Bills list](/apis/accounting/reference#operation/billsAll), [Aged creditors](/apis/accounting/reference#operation/agedCreditorsOne).

### 5. Adjust each customer's due date with historical days-to-pay

Customers rarely pay on the due date. For every customer with at least six paid invoices in the trailing twelve months, compute median days-to-pay from the payments feed and shift the invoice's forecast bucket by that offset. For customers with sparse history, fall back to the median across the whole receivables book.

```http
GET /accounting/payments?filter[type]=accounts_receivable&filter[updated_since]=2024-01-31T00:00:00Z HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: cns_acme_treasury
x-apideck-service-id: quickbooks
```

Join each payment to its invoices through `allocations[]` entries whose `type` is `invoice` (the allocation `id` is the invoice ID), then subtract the invoice's `due_date` from the payment's `transaction_date`. A representative result of that calculation, stored on your side:

```json
{
  "customer_id": "cus_01H7T4V9MNQR2PS8KYZB3CDEF1",
  "paid_invoice_count": 14,
  "median_days_past_due": 9,
  "p75_days_past_due": 17
}
```

Apply the median to shift the invoice out of its contractual week. Store the p75 so the forecast can show a conservative scenario next to the base case. Reference: [Payments list](/apis/accounting/reference#operation/paymentsAll).

### 6. Add a recurring baseline from the P&L and journal entries

Open invoices and bills cover named, scheduled items. They miss payroll, rent debits, software subscriptions charged straight to a bank account, and revenue not yet invoiced. Backfill those from history.

The profit and loss report returns one report per date range, so request it once per month for the trailing twelve months to get a per-account monthly average. `filter[accounting_method]=cash` keeps accruals out where the connector supports it. Spread each monthly figure across the forecast weeks (7/30 of a month per week, or a calendar-aware spread for payroll).

```http
GET /accounting/profit-and-loss?filter[start_date]=2025-01-01&filter[end_date]=2025-01-31&filter[accounting_method]=cash HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: cns_acme_treasury
x-apideck-service-id: netsuite
```

For items that hit the bank without an invoice or bill (standing orders, loan repayments), look for repeating journal patterns: the same debit account, the same credit account, the same amount, on the same day of the month for three or more months. Pull the trailing year of journal entries with `filter[start_date]` and `filter[end_date]`. Connectors without date-range support reject those filters with `UnsupportedFiltersError`, so fall back to `filter[updated_since]` and filter on `posted_at` yourself.

```http
GET /accounting/journal-entries?filter[start_date]=2024-02-01&filter[end_date]=2025-01-31 HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: cns_acme_treasury
x-apideck-service-id: netsuite
```

For each journal, take the `line_items[].ledger_account.id` of the debit line and the credit line, group by those two IDs plus the amount rounded to two decimals, and keep groups that repeat three or more times. Project the next occurrence from the `posted_at` series. Reference: [Profit and loss](/apis/accounting/reference#operation/profitAndLossOne), [Journal entries list](/apis/accounting/reference#operation/journalEntriesAll).

### 7. Lay out the 13-week forecast

The forecast table has one row per cash account (or one consolidated row) and one column per week. Each cell is a signed decimal in the account's currency. This structure lives in your product; the API does not return it.

```json
{
  "forecast_id": "fcst_01HM3N5PQR7STV8WXY9ZABCDE2",
  "as_of": "2025-01-31",
  "currency": "USD",
  "weeks": [
    {
      "week_index": 0,
      "week_start": "2025-01-27",
      "opening_balance": 184320.5,
      "scheduled_inflows": 0.0,
      "scheduled_outflows": 0.0,
      "recurring_inflows": 0.0,
      "recurring_outflows": 0.0,
      "closing_balance": 184320.5
    },
    {
      "week_index": 1,
      "week_start": "2025-02-03",
      "opening_balance": 184320.5,
      "scheduled_inflows": 48250.0,
      "scheduled_outflows": 12480.75,
      "recurring_inflows": 7200.0,
      "recurring_outflows": 43500.0,
      "closing_balance": 183789.75
    }
  ]
}
```

Store the source record IDs that fed each cell so the UI can drill from a cell back to the originating invoice, bill, or recurring pattern.

### 8. Refresh incrementally with `updated_since`

A forecast refreshed daily does not need to re-fetch the full invoice book. Invoices, bills, payments, and journal entries accept `filter[updated_since]`. On each refresh:

1. Record the current time before the first call.
2. Call each of those list endpoints with `filter[updated_since]` set to the prior refresh's timestamp.
3. Upsert the changed records into local storage keyed by `id`. Invoices and bills that became paid come back in this set, which is how they drop out of the open schedule.
4. Rebuild the forecast from local storage.
5. Save the recorded time as the next refresh's cursor.

Aged debtors, aged creditors, the balance sheet, the profit and loss, and bank account balances have no `updated_since` filter, so fetch them again on each run.

### 9. Account for fiscal periods and currency

The P&L takes explicit `start_date` and `end_date` values, so month boundaries are yours to choose. A customer with a July year-end still reports calendar months if you ask for calendar months. To label the baseline in the customer's fiscal terms, read `fiscal_year_start_month` from [company info](/apis/accounting/reference#operation/companyInfoOne), or list periods with [`GET /accounting/fiscal-periods`](/apis/accounting/reference#operation/fiscalPeriodsAll) on connectors that support it.

The forecast lives in one presentation currency, but open invoices and bills can be in any currency the source system supports. Invoice and bill responses include `currency`, and many include `currency_rate`. Convert already-booked documents at their own rate. For scheduled items whose rate is only set at payment time, apply the current spot rate from your own FX source and show the FX exposure as a separate forecast line.

### 10. Where actuals end and assumptions begin

Keep the boundary explicit. Starting cash, open balances, aging reports, historical payments, and posted journals come from the API as actuals. Days-to-pay shifts, recurring-pattern thresholds, FX rates for unbooked items, and manual overrides are assumptions your product owns. Persist the two layers separately so a user can switch the assumptions off and see the actuals-only view.

## Connector-specific behavior

Not every connector exposes every input the forecast needs. The table below covers each connector in the catalog. Verify specifics in the live coverage matrix before relying on any one connector for a given input.

| Connector | Notes |
| --- | --- |
| `access-financials` | Invoices and payments only. Bills, journal entries, balance sheet, and P&L not in coverage. Forecast reduces to a receivables-only view. |
| `acumatica` | Invoices, bills, journal entries, and payments supported. Balance sheet and P&L not in coverage. Build the recurring baseline from journal entries alone. |
| `api-explorer` | Internal testing connector. Not for production forecasts. |
| `banqup` | Invoices and customers read-only; bills, payments, journal entries, and reports not in coverage. Not enough coverage for a forecast. |
| `campfire` | Invoices, bills, journal entries, balance sheet, and P&L in coverage; payments and bill payments are limited. Standard forecast works, but reconcile payment-driven days-to-pay against the limited payments feed. |
| `clearbooks-uk` | Invoices and bills read-only; journal entries, balance sheet, and P&L not in coverage. Forecast is limited to scheduled items; recurring baseline must come from external sources. |
| `coverage` | Metadata connector used for programmatic capability checks. Not a data source. |
| `digits` | Reports only (balance sheet, P&L, journal entries read-only). Invoices and bills not in coverage. Forecast collapses to a report-driven view with no scheduled line items. |
| `dualentry` | Invoices, bills, journal entries, and payments supported. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `exact-online` | Invoices, bills, journal entries, and payments read-write; balance sheet and P&L read-only. Standard forecast works. |
| `exact-online-nl` | Same coverage shape as `exact-online`. Standard forecast works. |
| `exact-online-uk` | Bills read-only; invoices and journal entries read-write; balance sheet and P&L read-only. Standard forecast works. |
| `freeagent` | Bills read-only; invoices and journal entries read-write; balance sheet and P&L read-only. Standard forecast works. |
| `freshbooks` | Invoices, bills, and payments read-write; balance sheet and P&L read-only; journal entries read-only. Standard forecast works. |
| `intuit-enterprise-suite` | Full stack. Standard forecast works with class and department dimensions available. |
| `kashflow` | Invoices read-only, bills not in coverage, journal entries read-only. Forecast is possible but outflow accuracy is limited. |
| `microsoft-dynamics-365-business-central` | Invoices, bills, bill payments, and journal entries supported. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `moneybird` | Invoices, bills, and journal entries supported. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `mrisoftware` | Bills and journal entries supported; invoices and payments not in coverage. Forecast reduces to a payables-only view. |
| `myob` | Invoices, bills, journal entries, payments supported. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `myob-acumatica` | Invoices, bills, and journal entries supported; payments not in coverage. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `netsuite` | Full stack with subsidiaries, departments, classes, and locations. Standard forecast works and can be sliced by any dimension. |
| `odoo` | Invoices, bills, journal entries, payments supported. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `overview` | Metadata connector. Not a data source. |
| `pennylane` | Invoices, bills, and journal entries read-write; payments, balance sheet, and P&L not in coverage. Recurring baseline comes from journal entries, and days-to-pay cannot be computed from Apideck payments on this connector. |
| `procountor-fi` | Invoices, bills, journal entries supported; payments read-only. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `quickbooks` | Full stack. Standard forecast works. |
| `rillet` | Invoices, bills, journal entries, and payments read-write; balance sheet and P&L read-only; bill payments limited. Standard forecast works. |
| `sage-business-cloud-accounting` | Invoices, bills, payments read-write; balance sheet and P&L read-only; journal entries read-only. Standard forecast works; recurring baseline leans on P&L patterns. |
| `sage-intacct` | Invoices, bills, journal entries, payments read-write; balance sheet and P&L read-only. Standard forecast works and can be sliced by dimension. |
| `sage-intacct-rest` | Invoices, bills, journal entries, payments supported. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `stripe` | Invoices and credit notes read-write; payments read-only; bills, journal entries, balance sheet, and P&L not in coverage. Useful as a signal for subscription inflows, not a standalone forecast source. |
| `visma-netvisor` | Invoices, bills, and payments read-write; journal entries read-only. Balance sheet and P&L not in coverage. Forecast works for scheduled items only. |
| `wave` | Invoices read-write; balance sheet and P&L read-only; bills, journal entries, and payments not in coverage. Forecast reduces to AR plus a report-driven baseline. |
| `workday` | Invoices, bills, journal entries, expenses, and payments supported; bill payments read-only. Balance sheet and P&L not in coverage. Build recurring baseline from journal entries. |
| `xero` | Invoices, bills, journal entries, payments read-write; balance sheet and P&L read-only; tracking categories read-only. Standard forecast works. |
| `yuki` | Invoices and journal entries read-write; bills read-only; payments, balance sheet, and P&L not in coverage. Days-to-pay cannot be computed from Apideck payments on this connector. |
| `zoho-books` | Invoices, bills, journal entries, payments read-write; balance sheet and P&L read-only. Standard forecast works. |

### QuickBooks

Starting cash per account comes from [`GET /accounting/bank-accounts`](/apis/accounting/reference#operation/bankAccountsAll) (`balance`), or from ledger accounts whose `type` is `bank` (`current_balance`). The balance sheet also works but reflects posted entries as of the report date.

### NetSuite

For OneWorld customers, invoices, bills, payments, and journal entries accept `filter[subsidiary_id]`, so build the schedules per subsidiary before consolidating. The balance sheet and P&L filters have no subsidiary parameter, so those reports cover the whole connection.

### Xero

Aged debtors and aged creditors return the exact buckets Xero displays in-app, including partial allocations. Prefer these over summing invoice balances when the two disagree.

### Sage Intacct

The unified P&L filters cover dates, `customer_id`, `location_id`, and `accounting_method`. Intacct reporting options outside that set, such as reporting books, would go through `pass_through`; check the connector's field mapping before relying on one.

### Microsoft Dynamics 365 Business Central

Balance sheet and P&L are not in Apideck's coverage for this connector. Derive the recurring baseline from [`GET /accounting/journal-entries`](/apis/accounting/reference#operation/journalEntriesAll), keeping lines whose `ledger_account.id` is a cash account (there is no account filter), or source it from your product's own data warehouse.

### Exact Online, Exact Online NL, Exact Online UK

Reporting endpoints are read-only across the three regional variants, and the UK variant exposes bills read-only. Reads work the same on all three; changes to bills for Exact Online UK have to be made in Exact itself.

### Stripe

Use Stripe as an upstream signal for subscription revenue. It does not cover bills, journal entries, or reports. For customers whose Stripe revenue is journaled into their main accounting system, pull from the main system to avoid double counting.

## Next steps

- [Mark invoices and bills as paid](/guides/mark-invoices-as-paid) once the forecasted payment lands.
- [Handling bills and expenses](/guides/expenses-bills) for the write side of the payables ledger.
- [Accounting API reference](/apis/accounting/reference) for the full schema of invoices, bills, payments, and reports.
- [Webhooks reference](/apis/webhook/reference) to invalidate the forecast cache the moment an invoice, bill, or payment changes.
