Reports

Cash flow report

Shows money in and money out over a date range, split into operating income and expenses, other income and expenses, and other items, ending with the cash the properties started and finished the period with. One row is one chart of account, nested under its parent account.

GET
https://api.portal.magicdoor.com/company-app/reports/cash-flow

Authorization

Authorizationstringrequired
Bearer token — Send your API token in the Authorization header as Bearer <token>. Requests without a valid token are rejected with 401 Unauthorized.

Query parameters

dateRange.startstring · daterequired

First day of the period. Read in the company's timezone.

example: 2026-01-01
dateRange.endstring · daterequired

Last day of the period, included in the range.

example: 2026-03-31
groupByenum<string>

What each money column stands for: one column for everything, one per property, one per portfolio, or one per calendar month, quarter or year in the range.

default: totalexample: quartertotalpropertyportfoliomonthquarteryear
portfolioIdsstring[]

Narrow the report to these portfolios. Repeat the parameter once per id. An id that does not exist returns 404.

propertyIdsstring[]

Narrow the report to these properties. Repeat the parameter once per id. An id that does not exist returns 404.

ownerIdsstring[]

Narrow the report to properties these owners have a stake in. Repeat the parameter once per id. An id that does not exist returns 404.

companyBillsboolean

Filter on who pays. Leave it out to include everything, set it true for bills the management company pays, false for bills the owner pays.

accountingBasisenum<string>

Whether to count money when it moves (cash) or when it is earned or owed (accrual).

default: cashcashaccrual

Response

200 · The cash flow figures.

groupHeadersobject[]

The money columns, in order.

incomeobject[]

Operating income accounts.

incomeTotalobject

Total operating income per column.

expensesobject[]

Operating expense accounts.

expensesTotalobject

Total operating expenses per column.

otherIncomeobject[]

Non-operating income accounts.

otherIncomeTotalobject

Total non-operating income per column.

otherExpensesobject[]

Non-operating expense accounts.

otherExpensesTotalobject

Total non-operating expenses per column.

netOperatingIncomeobject

Operating income minus operating expenses, per column.

netOtherIncomeobject

Non-operating income minus non-operating expenses, per column.

netIncomeobject

Net operating income plus net other income, per column.

otherItemsobject[]

Cash movements that are neither income nor expense, such as owner contributions and distributions.

netOtherItemsobject

Total of those other items, per column.

cashFlowobject

Net income plus net other items, per column. This is the change in cash.

beginningCashobject

Cash held at the start of each column's period.

endingCashobject

Cash held at the end of each column's period.

401 · The access token is missing, expired, or not valid for this company.

404 · A portfolio, property or owner id in the query does not exist in the caller's company.

How scoping works

portfolioIds, propertyIds and ownerIds are all resolved down to one set of properties, and they narrow each other rather than adding up. Sending a portfolio and a property that is not in it returns nothing. Sending none of the three covers every property in the company.

This report is a table with a variable number of money columns. groupHeaders names them, in order: each has an id and a name. Every amount in the report is then an object keyed by those ids, not an array.

So to render the table, read groupHeaders for the column order, then for each line read line.groups[header.id].

What the id actually looks like depends on groupBy:

groupByColumn idColumn name
totaltotalTotal
propertyThe property's Snowflake id, or unknownThe property's display name, or Unknown property
portfolioThe portfolio's Snowflake id, or unknownThe portfolio's name, or Unknown Portfolio
monthyyyy-MM, for example 2026-03March 2026
quarterYear and quarter number, for example 2026-1Q1 2026
yearThe year, for example 20262026
The quarter id is not the quarter name

With groupBy=quarter the id is 2026-1, while the heading reads Q1 2026. Key your lookups off id, never off name.

Two quarters, one line
{
  "groupHeaders": [
    { "id": "2026-1", "name": "Q1 2026" },
    { "id": "2026-2", "name": "Q2 2026" }
  ],
  "income": [
    {
      "name": "Rent income",
      "children": [],
      "groups": { "2026-1": 55500.00, "2026-2": 56200.00 }
    }
  ]
}
The date range is required

Unlike most reports here, dateRange has no default on this endpoint. Leaving it out is a validation error.