Reports

Income statement report

Shows income and expenses over a date range, ending with net income. One row is one chart of account, nested under its parent account. Unlike the cash flow report it has a single money column and does not track the cash balance.

GET
https://api.portal.magicdoor.com/company-app/reports/income-statement

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
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.

includeSubsidiesboolean

Whether housing-authority subsidy payments count towards income.

default: true
accountingBasisenum<string>

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

default: cashcashaccrual

Response

200 · The income statement figures.

groupHeadersobject[]

The money columns. Always exactly one, with an id of balance and a name of Balance.

incomeobject[]

Operating income accounts.

expensesobject[]

Operating expense accounts.

totalIncomeobject

Total operating income, keyed by column id.

totalExpenseobject

Total operating expenses, keyed by column id.

otherIncomeobject[]

Non-operating income accounts.

otherExpensesobject[]

Non-operating expense accounts.

totalOtherIncomeobject

Total non-operating income, keyed by column id.

totalOtherExpenseobject

Total non-operating expenses, keyed by column id.

netOperatingIncomeobject

Operating income minus operating expenses.

netOtherIncomeobject

Non-operating income minus non-operating expenses.

netIncomeobject

Net operating income plus net other income.

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.

Every amount in this report is an object keyed by column id, the same shape the cash flow report uses. But this report has no groupBy, so there is always exactly one column and its id is always balance.

That means totalIncome.balance is the total income for the whole period, and a line's amount is line.groups.balance.

One line, one column
{
  "groupHeaders": [
    { "id": "balance", "name": "Balance" }
  ],
  "income": [
    {
      "name": "Rent income",
      "children": [],
      "groups": { "balance": 55500.00 }
    }
  ],
  "totalIncome": { "balance": 55500.00 }
}
Only accounts allowed on an income statement appear

Postings against an account that is not marked as allowed for the income statement are skipped, as are postings whose accounting date falls outside the range.