VRPlatformVRPlatform
Core Concepts

Reports

Choose server-calculated financial and operational report views

Last Updated: 2026-10-03

Version: 1.4

Reports are server-calculated views over journals and business resources. Use returned rows, totals, currency, dates, and filters rather than recomputing financial statements in a client.

Choose A Report

QuestionReport
What revenue and expense activity occurred in a period?Profit and loss
What is the financial position at a date?Balance sheet
Do account debits and credits agree?Trial balance
What guest or channel receivables remain?Guest balances
How do trust liabilities compare with trust assets?Trust reconciliation
What sales-tax liability is recorded?Sales-tax liability
What manager-side activity belongs to each month?Manager statements
What did each owner earn and receive over a date range?Owner summaries
How much does each ownership owe its owners on a date?Owner balances
Which postings make up a reported balance?Journal entries

The generated operation defines exact date, listing, ownership, account, currency, view, and export parameters. JSON, CSV, and PDF endpoints can have different response contracts; do not assume a format query exists unless the operation declares it.

Calculation Rules

Manager Statement Totals

Manager statements report active trust-ledger postings of the manager party in the team's default currency. Postings whose unique reference ends in -a/p are excluded.

  • The year list returns the 12 months of year. Months before the team's statementStartAt are omitted, but their activity still reaches later balances.
  • balanceStart includes all earlier revenue, expense, adjustment, and transfer activity. Assets and liabilities and the Other section never enter it.
  • netIncome (shown as Net earnings) is netRevenue + expenses.
  • balanceEnd is balanceStart + netIncome + adjustments + transfers.
  • assetsAndLiabilities is cumulative through the statement end.
  • availableBalance is balanceEnd + assetsAndLiabilities.
  • total is netIncome + adjustments.
  • The detail endAt parameter and every returned endAt are exclusive. dateRange follows isDateRangeEndInclusive.

Owner Statement Summary Totals

Owner statement summaries return one row per owner contact and currency, sorted by contact name.

  • The range end is exclusive only when startAt and endAt are both the first of a month. Otherwise the end day is included. An end before the start returns 400 BAD_REQUEST.
  • When the team has a statementStartAt, the range starts no earlier than the earlier of statementStartAt and the first Published statement. A range ending on or before that date returns no rows.
  • The manager view sums all owner-party postings on accounts mapped in the listing's layout, attached or not and whatever the statement status. Postings attached to an imported Published statement from before statementStartAt are replaced by that statement's stored net revenue and expenses.
  • The owner view counts postings attached to Published statements. When ownerPortalShowDraftStatements is true, it also counts postings not yet attached to a statement. Postings attached to In Review statements never count.
  • Amounts are split across the ownership period's owners by ownership percentage. Payouts follow the attribution rule in Statements.
  • When an ownership period has an In Review or Published statement before the range, that statement's ending balance replaces earlier activity as the starting balance.

Owner Balance Rows

GET /reports/owner-balances returns one row per ownership and currency. It needs statements:read and is not available to owner sessions. date is required and inclusive. listingIds and ownerIds are optional comma-separated UUID lists.

  • balance is the owner balance at the end of date. Positive means the owner is owed money. It sums the owner revenue, expense, and payout postings that statements count, from the later of the ownership start and the team's statementStartAt, whatever their statement status. Draft activity, activity attached to Published statements, and payouts all count.
  • A first ownership that covers statementStartAt adds the listing's opening balance once date reaches statementStartAt. A managed ownership that starts where a co_host ownership ended takes over that ownership's last saved ending balance, and the co_host row no longer shows it. Any other ownership starts at zero.
  • reserve is the ownership's reserve. availableBalance is balance - reserve.
  • Every ownership active on date is listed, with a zero balance when it has no activity. An ownership that ended before date is listed only while its balance is not zero.
  • A listing group reports on the parent listing's ownership. Child listing activity rolls up to it.
  • The balance is read from journals, not from saved statements. It equals a statement's balanceEnd on the statement's last day when that statement's saved totals match the journals. The endpoint changes no payout, statement, or paid status.

Profit and Loss Views

  • view is ledger, party (default), listing, month, quarter, or year.
  • Without party, the report includes owner and manager activity.
  • Without endDate, the end is today. Without startDate, the start is the first of the end month for party, listing, and ledger, and January 1 of the end year for time views.
  • Reversed dates are swapped. startDate and endDate are both inclusive; dateRange follows isDateRangeEndInclusive.
  • Amounts are negated journal totals of active postings in the team's default currency, so revenue is positive and expenses are negative.

Guest Balance Rows

Only reservations and transactions with a non-zero receivable balance appear.

  • occupancyStatus selects departed (booked, check-out on or before the as-of date), inHouse (booked, checked in on or before the date and checking out after it), upcoming (booked, check-in after the date), cancelled, unassigned (receivable postings without a reservation), or all.
  • channels filters reservations by comma-separated booking channels.
  • charges sums reservation charge postings. payments sums deposit postings with the sign flipped.
  • Rows group by booking status (booked, cancelled, unassigned), then by listing.
  • Unassigned rows identify their transaction through transactionReferenceId, transactionDate, and transactionDescription.

Trust Reconciliation Sections

Trust Reconciliation uses active trust-ledger postings through the as-of date. Its asset sections are Bank Accounts and Other Assets. Its liability sections are Taxes Payable, Other Liabilities, Guest Receivables & Deposits (by occupancy status), Owner Payables (by listing), and Manager Payable.

Owner Dashboard Metrics

Metrics that take a date range return 400 BAD_REQUEST when startDate is after endDate or dateRange is reversed. GET /metrics/reservation-volume and GET /metrics/sync-by-status return an empty result for a reversed dateRange.

Three monthly metrics feed an owner dashboard for one owner contact. All take ownerId, an optional comma-separated listingIds (default: every listing of the owner), an optional currency (default: the team currency), and a dateRange with an exclusive end by default. Each month is returned once, dated on its first day.

GET /metrics/owner-performance returns booking activity:

  • Each booked stay is split per night. Only nights inside the owner's ownership periods count, and each night carries an equal share of the stay's owner revenue. Days of a period that sets the listing inactive are not owned.
  • Stays count as in the manager metrics: archived stays, stays on an inactive source and stays whose check-in day has no active ownership period are left out. A stay counts on an active connection. A stay without a connection counts only when it is on an imported historical statement.
  • Owner revenue comes from the reservation's journal. A stay without a journal, such as one before the books start, is estimated as its rent × the listing's owner share of rent over its journaled stays in the requested range. Such a month has estimated: true. Rent here is the sum of reservation lines, adjustments excluded, whose account is in the Rents revenue category. A line uses its own account, else the booking-channel mapping of its line type, else the base mapping.
  • A stay on an imported historical statement counts as a guest stay. It adds no owner revenue and is never estimated, because its statement already reports that revenue. This covers stays the import created and live stays it matched.
  • Owner stays are stays on the "Owner Block" booking channel and other booked stays without rent. Reservations carry no owner-stay flag. Owner stay nights and owner blocks on the PMS calendar count once per day in ownerNights. An owner stay adds no check-in, and its owner revenue still counts, for example a cleaning fee charged to the owner. A guest night on an owner block day does not count in nights. GET /metrics/occupancy-rate-over-time treats a stay without rent outside the Owner Block channel as occupied, so the two occupancies can differ.
  • availableNights sums the owned days of each selected physical listing in the month. A purchase includes its start day; a sale excludes its end day. Overlapping periods count each listing-day once. Occupancy is booked ÷ (available − owner) nights. It is null when owner stays fill every available night. Across several listings, nights are summed before dividing.
  • Before the first booked night in the range, hasBookingHistory is false and occupancy is null.
  • Stays in another currency are left out. A stay without a currency counts in the team currency.
  • In a listing group, children's stays count under the parent's ownership periods. Selecting the parent includes its children; selecting a child returns only that child's stays. Each included physical listing contributes capacity, even without bookings, and estimates use only its own journaled stays. Selecting siblings does not pool their owner shares. Statements of a selected child are the parent's statements.
  • With asOf, only stays booked by the end of that day (UTC) and not cancelled by then count, including stays cancelled later. A cancelled stay without a cancellation date does not count. Amounts use each stay's current lines, as in GET /metrics/booked-revenue. Cancellation deactivates a stay's journal, so a stay cancelled later gets an estimated owner revenue from its current rent lines and is flagged estimated.

GET /metrics/owner-rent returns rent. It takes the same query, including asOf:

  • Rent comes from the column tagged Rents Revenue in each listing's statement layout: the first layout linked to the listing, default layout first, else the team's default layout. Only listings the owner held during the range count.
  • The current layout applies to every month, including months with a published statement. A published statement keeps the template it was published with, so after a layout change its rent can differ from the dashboard.
  • A tagged accounts column contributes its accounts. A tagged subtotal contributes every accounts column before it in its section.
  • Rent is the sum of the owner's active journal entries and imported historical statement lines on those accounts for stays in the owner's ownership periods, credits counted positive. Stays and periods follow the rules of GET /metrics/owner-performance. A grouped child's entry counts under the parent's period that covers its date, as on the statement. Owner fees and adjustments on those accounts lower it, as on the statement.
  • An entry recognized per night counts in its night's month, so a stay across two months splits as on the statements. An imported historical statement line counts in the month of its date, as on its statement. A live entry recognized on one date, such as check-in, check-out, booking, a posting date, or a cancellation, is spread evenly across the stay's nights inside its ownership period. The statement counts such an entry in the month of its date. spreadRent is the month's rent minus the rent it would have with those entries on their own date. When it is not 0, the month differs from the statement.
  • A stay without an active journal adds no rent, except with asOf as described below. Nothing is estimated.
  • With asOf, a channel stay cancelled after that day also counts the inactive entries of its cancelled lines, which keep their amounts. Inactive entries on a nonposting account never count. A cancelled manual reservation keeps its entries active, so it counts the same way.
  • The owner dashboard shows ADR as rent ÷ nights and RevPAR as rent ÷ availableNights from GET /metrics/owner-performance, owner nights included in RevPAR. Before the first booked night, both are empty.
  • The owner dashboard's Rent revenue chart and Future revenue use rent for every month, including months with a statement. Future revenue compares with last year's rent, final or with asOf set to the same day last year. Cards that show a month with spreadRent other than 0 note that a month can differ from the statement.
  • When a selected listing's layout has no Rents Revenue column, the request fails with 400, reason: "rentsRevenueTagMissing", and the layouts in context.layoutIds. There is no fallback to another revenue figure. The owner dashboard then hides ADR, RevPAR, the Rent revenue chart, and the Future revenue card; the manager's preview names the layouts to tag.

GET /metrics/owner-financials returns the owner's statements summed per month in the selected currency, plus currencies for a currency switch:

  • A month with statements has source: statement and a payment status. With several statements it is paid when all are paid or overpaid, partiallyPaid when some are, and otherwise the least advanced status.
  • A month without a statement has source: estimate and the booked owner revenue from the performance metric as netRevenue, or source: none before the first booked night.
  • ownershipSplit is the owner's percentage when exactly one listing is requested and every statement of the month has the same split.

An owner session can only request its own contact and listings; other values return 404. It sees draft and In Review months only when settings.showOwnerDashboardDraftStatements is on. This dashboard setting is separate from ownerPortalShowDraftStatements, which controls the statement summary. Hidden statements do not contribute totals, counts, payment status, payout dates, ownership splits, or the currency list. A month containing only hidden statements uses the same estimate or none result as a month without a statement. status=published selects published statements; status=all includes drafts only when permitted. Manager previews may use either value. Owner sessions can read settings.showOwnerDashboardDraftStatements from GET /team.

GET /metrics/owner-dashboard-details returns the rows of the Reservations and Expenses cards for one month (YYYY-MM), with the same ownerId, listingIds, and currency. It returns only what those cards list, never whole statements: reservations holds the stays and other net revenue rows with the owner-visible money columns of the layout, their totals, and the headline net revenue subtotal; expenses holds the expense rows of the layout's other sections with their owner-accessible attachments. reservations.itemized is false when the month has no visible statement. Draft and In Review statements count only when settings.showOwnerDashboardDraftStatements is on, for owners and manager previews alike. Full draft statements stay behind ownerPortalShowDraftStatements.

settings.showOwnerDashboard on GET /team and PUT /team decides whether the owner portal opens on the dashboard. It defaults to false; owners then land on their statements. The setting changes only the owner portal home page. Both metrics return the same data either way.

Each dashboard card has its own setting on GET /team and PUT /team: settings.showOwnerDashboardNetIncome, showOwnerDashboardExpenses, showOwnerDashboardStats, showOwnerDashboardMonthlyPerformance, showOwnerDashboardFutureRevenue, showOwnerDashboardOccupancy, and showOwnerDashboardReservations. They all default to true; showOwnerDashboardDraftStatements defaults to false. The Reservations card follows showOwnerDashboardReservations only; showReservations controls the reservation list and calendar. The card settings decide which cards the owner portal and the manager preview show; the metrics return the same data either way. With showOwnerDashboardDraftStatements off, the dashboard opens on the latest month with a published statement and its month arrows skip months without one. An owner without a published statement sees an empty state and the Future revenue card, which shows booked rent whatever showOwnerDashboardDraftStatements is set to.

Manager Dashboard Metrics

Manager dashboard revenue comes from active reservation journal entries and groups by posting date, not by check-in month. Revenue recognized per night spreads over the months the nights fall in. Amounts are in cents.

  • Gross guest revenue is the reservation's line and adjustment entries on revenue, expense or tax accounts, including taxes. A/R and deferred-revenue postings do not count.
  • Taxes are the reservation's and its fees' entries on accounts in the Taxes Payable – Lodging & Occupancy or Taxes Payable – Other categories. Tax that a fee moves to a tax liability counts. Tax a fee charges on itself, such as VAT on the management commission, does not. Other fee entries, such as commission and cleaning fee moves, do not count either.
  • Revenue excluding taxes is gross guest revenue minus taxes.
  • PM revenue is the manager's entries on revenue and expense accounts.

Revenue, take rate and booked revenue include the active entries of cancelled stays, such as kept cancellation fees. ADR counts booked stays only.

A reservation line without journal entries counts nowhere. Archived stays and stays outside an active ownership period, including stays before a listing's first period or after it is set inactive, have only inactive entries and drop out. Stays in an earlier active ownership period count. Check-ins, reservation count, nights and occupancy count a stay only when it has an active reservation journal entry. $0 stays and cancelled reservations keep an active $0 entry, so they still count. Stays on an inactive source never count; the reservation list hides them the same way. Reservation count groups stays by booking date and never counts reservations on the "Owner Block" booking channel.

GET /metrics/take-rate-trend returns the requested month and the 11 months before it:

  • takeRate is pmRevenue ÷ revenueExcludingTaxes as a percentage, both posted in the month.
  • status is available when the month has revenue excluding taxes and noData otherwise. takeRate is null for noData. A zero take rate is a real zero.

GET /metrics/booked-revenue returns revenue excluding taxes for the month of the reference date and the next three, each with the same month last year:

  • revenue and lastYearFinalRevenue count all entries posted in the month.
  • lastYearAtDateRevenue counts entries of last year's stays booked by the end of the same day last year (UTC; Feb 29 uses Feb 28) and not cancelled by then, including stays cancelled later. A cancelled stay without a cancellation date does not count.
  • All amounts use today's entries. Changes made to a stay after last year's date are not reversed.

GET /metrics/average-daily-rate-over-time returns the average daily rate per bucket, the same way GET /metrics/average-daily-rate computes it for one window:

  • nights are the occupied nights of GET /metrics/occupancy-rate-over-time, so owner blocks, days outside an active ownership period and the check-out day are not nights, and two stays on the same listing-day are one night. A bucket covers the whole day, week or month, as in occupancy.
  • revenue spreads each booked stay's gross guest revenue, from all its active entries, evenly over its nights, and sums the nights in the bucket. Revenue and nights share the stay's dates, whatever date the team posts on.
  • averageDailyRate is revenue ÷ nights, rounded to cents, and null without nights.
  • To compare with last year, request a range that starts one year earlier and match each bucket with the same bucket a year before.

GET /metrics/occupancy-rate-over-time returns occupancy per bucket. A bucket always covers the whole day, week or month, even when startDate or endDate cuts it.

  • availableNights counts each listing-day covered by an active ownership period, minus owner block days. A group child counts on the days its own or its parent's active periods cover, and drops out on the days a deactivation period of either covers. A group parent counts only when it is synced from a PMS or has booked stays of its own; its children's stays don't count for it. A parent that only groups its children adds no available nights. A purchase includes its start day; a sale excludes its end day. A listing without ownership periods adds no available nights. Owner block days are days of "Owner Block" reservations and owner blocks on the PMS calendar; a day with both counts once.
  • occupiedNights counts the available listing-days that a booked stay covers, each in the bucket it falls in. A stay from Oct 31 to Nov 29 adds 1 night to October and 28 to November; the check-out day is not a night. Nights outside an active ownership period or on an owner block day are not occupied, and two stays on the same listing-day count once. Stays on the "Owner Block" booking channel are not occupied nights.
  • Stays without an active reservation journal entry are not occupied nights. Archived owner blocks and owner blocks on an inactive source do not block days.
  • The owner portal's GET /metrics/owner-performance uses a different owner-stay rule: any booked stay without rent.
  • occupancyRate is occupiedNights ÷ availableNights as a percentage, and 0 without available nights.

Generated Artifact Scope

Every generated report artifact supports awaited GET and durable POST. Follow the generated export contract for request shapes, fallback handling, operation polling, and result links. The limits below determine whether GET can await a selection. They do not change the POST contract.

A GET for a one-month manager-statement PDF or a one contact-month owner-summary PDF or ZIP renders in the API request and returns the same file and download link. Every other report artifact renders in Trigger, and the API receives only artifact metadata. Limits measure the deduplicated data query, not how many files the selected data produces. Every PDF date range spans at most 60 calendar months, and each selector accepts at most 100 unique values.

Manager Statements

Manager-statement JSON routes require pm-statements:read. CSV and PDF routes also require pm-statements:export. General report permissions do not grant manager-statement access, and neither do the owner-statement permissions.

Manager-statement PDF scope is the complete contiguous month span from the earliest through the latest selected month. Sparse selections still load the months between those boundaries.

  • A span through 60 queried months is supported.
  • GET awaits spans through 12 queried months; larger supported spans require POST.
  • A span above 60 months fails the export operation.
  • Duplicate startAts are removed before counting, and no more than 100 unique startAts may be supplied.

Selecting January and December has a 12-month query scope even when the archive contains only those two selected statement PDFs.

Owner Statement Summaries

An explicit owner-summary batch uses contact-month scope:

unique contactIds × queried calendar months
  • POST supports a scope through 1,200 contact-months.
  • GET awaits a scope through 100 contact-months; larger supported scopes require POST.
  • A scope above 1,200 contact-months fails the export operation.
  • contactIds and listingIds each accept at most 100 unique supplied values.

Omitting contactIds means all matching contacts. The export task resolves and deduplicates the contacts and validates the final scope before loading per-contact detail.

A one-contact owner-summary detail PDF uses the same operation flow and remains subject to the 60-month range and 100-listing selector limits.

The export task publishes a file only after rendering and upload succeed. A failure marks the operation as failed without publishing a partial result.

Trust Reconciliation by Listing JSON requests accept source, nestedParent, and parentRollup display modes. The CSV and PDF exports support parent and group totals only: omit listingDisplay or set it to parentRollup. The main Trust Reconciliation JSON, CSV, and PDF endpoints accept listingId as a comma-separated list of listing UUIDs and reject malformed IDs before report work starts.

Trust Reconciliation also exports as a PDF for readers outside the product, such as a qualifying broker. The PDF is rendered as of the same date the JSON and CSV exports use, restates the on-screen section totals, and expands each section into its rows. The main report groups its sections under Assets and Liabilities; the by-listing report keeps its own group order. Both PDF routes support GET for awaited temporary download metadata and POST for a durable export operation. Rendering runs outside the API Worker for both methods. Trust artifact GETs admit at most 100 selected or tenant listings; use POST for larger supported portfolios.

Journal-entry CSV GETs use the filtered total before rendering and admit at most 5,000 rows. Resource CSVs have the same 5,000-row result cap. Use POST when the filtered journal export is larger or durable status is required.

Guest Balances is an as-of accounts-receivable report. Guest payments are gross amounts applied to accounts receivable; payment-processing fees are separate expenses and do not reduce payment received. Reservation-scoped deposit rebalances whose deposit postings net to zero are internal accounting reclassifications, so they do not appear as guest payments or change Guest Balances. A deposit that nets to zero only because a payment-direction accounts-receivable leg is offset by a merchant-fee leg (a payment grossed up for credit card fees) is a real guest payment and is counted. A Co-Host Payouts offset contributes to net-zero detection for a payment-direction accounts-receivable leg when the reservation has no active charge on an accounts-receivable account. A positive accounts-receivable reversal remains in the report so it can clear an earlier payment. When the charge is on accounts receivable, the matching negative deposit leg remains payment relief so the paid charge and payment net to zero. An aggregate payout can still contain valid activity for other reservations. The date filter accepts a single date or a date range; a range uses its inclusive end as the report's as-of date. Malformed ranges are rejected before report queries run.

Manager-statement section filters apply consistently to journal-entry JSON and CSV exports. Preserve the section ID when moving from statement detail to an export. Omit an unselected comma-separated journal filter. For compatibility, the journal endpoints treat -, inclusive all, and exclusion none UI sentinels as no selection. Relationship ID filters accept unmapped to select journal entries whose corresponding relationship is null, including transaction and attached owner-statement filters. The trial-balance accountIds filter accepts comma-separated account UUIDs or unmapped and rejects malformed IDs. On its own, unmapped returns no named account rows; the Unassigned figures are always included. The sentinel is ignored when real account UUIDs are also selected, because the Unassigned figures are included either way. An entity selection that does not map to a supported journal relation returns an empty report instead of a database error.

The Sales-tax liability report derives taxable income from the selected reservation-line mappings. Its taxableLineIds filter accepts a comma-separated list of reservation line-mapping UUIDs and rejects malformed IDs. Synthetic A/R adjustment mirrors do not contribute to taxable income, including when a selected excluded line type has no mapped account. Taxable Income drilldowns contain the exact journal entries used by the report. Collectable Taxes includes active Trust postings to the lodging-and-occupancy tax liability category when they are either direct reservation postings or reservation-attributed transaction postings backed by a revenue posting in the same transaction. Tax-authority remittances and other cash- or payable-backed transaction activity are excluded. An exact transaction duplicate of a direct reservation tax posting is counted once, and Collectable Taxes drilldowns contain the exact journal entries used by the report.

For historical reporting, distinguish live and historical ledgers. Historical imports preserve pre-go-live detail without changing live post-go-live balances.

Owner statement summaries include imported historical statements. For a Published imported statement, net revenue, expenses and payouts are the totals stored on the statement, so the summary matches the statement even when an imported account is not in the statement layout. Transfer lines attached to the statement replace its stored payout only when they add up to it, which is how a payout reaches a specific owner. Otherwise the stored payout counts. A historical payout without a contact is split by ownership share. Pre-go-live postings that an imported statement's balance already carries are not counted again. Summary drilldowns set includeHistoricalLedger, so journal-entry detail shows the imported entries next to live trust activity.

Common Workflows

For month-end review, use the trial balance to check account balance, trust reconciliation to inspect trust position, profit and loss for period activity, and owner or manager statements for their respective audiences. These reports answer different questions and should not be reconciled by comparing unrelated totals.

For a discrepancy, preserve the selected date, view, currency, listing, ownership, and account filters when opening journal-entry detail. Rebuilding a report with different filters can create a false mismatch.

See Statements, Accounting model, and Locking.

On this page