VRPlatformVRPlatform

Historic Statements

Import pre-platform owner statement history from any source through the API

Historic Statements

When a team joins VRPlatform, its owners usually have months or years of statement history in the previous system — spreadsheets, PDFs, or a legacy platform. Importing that history preserves owner-facing balances and lets carry-forward amounts line up from the first live statement.

POST /statements/historic accepts already-resolved statement data from any client. The API does not parse files — your client code extracts the data (from CSV, PDF, an old database, anything) and resolves listings and accounts via the existing endpoints; the API turns each payload into a real owner statement with journal history.

This is the same mechanism the VRI migration uses for its historical statement import.

Concepts

The historical ledger

Imported statement lines become journal entries with ledger = historical and status = inactive. They render on owner statement details and feed owner-facing balances, but they never mix into the live general ledger — trial balances and GL reports are unaffected.

Rendering by account

A historic statement has no stored row layout. The statement detail maps its journal entries onto the team's statement layout by account, the same way live statements render. So the import payload references real chart accounts (accountId), and your client decides which account each source line belongs to — typically with a mapping table the user confirms once, like the VRI account mappings.

Reservation rows

A line can carry an optional reservationId. Reservation-linked lines render as reservation rows on the statement detail — guest name, check-in/check-out, and confirmation code come from the referenced reservation — instead of anonymous one-off lines.

The reservation must already exist in the team: either synced from a PMS connection or created through the reservation endpoints before posting the statement. This endpoint never creates reservations; an unknown reservationId is rejected. Lines without a reservationId are perfectly fine — they render as plain statement lines.

Idempotency

Both the statement and each line carry a client-chosen uniqueRef. Re-posting a statement uniqueRef replaces the previous import — the statement row is updated and its journal entries are deleted and recreated — so imports can be re-run safely while the user iterates on mappings.

Prerequisites

  1. The listing exists and has an ownership period covering the statement range (with members, when payout lines are imported).
  2. The chart of accounts contains the accounts your lines reference, and a payout_distribution account exists when payout lines are imported.
  3. A statement layout exists (the listing's assigned layout or the default is frozen onto the statement).

Import a statement

POST /statements/historic
{
  "uniqueRef": "csv-import:2025-03:ocean-view",
  "listingId": "listing-uuid",
  "startAt": "2025-03-01",
  "endAt": "2025-04-01",
  "currency": "usd",
  "status": "published",
  "centBalanceStart": 0,
  "lines": [
    {
      "uniqueRef": "csv-import:2025-03:ocean-view:rent",
      "type": "line",
      "accountId": "rents-account-uuid",
      "centTotal": 80000,
      "description": "March rent",
      "date": "2025-03-15"
    },
    {
      "uniqueRef": "csv-import:2025-03:ocean-view:cleaning",
      "type": "line",
      "accountId": "housekeeping-account-uuid",
      "centTotal": -20000,
      "description": "Cleaning"
    },
    {
      "uniqueRef": "csv-import:2025-03:ocean-view:payout",
      "type": "payout",
      "centTotal": 50000
    }
  ]
}

Amounts are owner-facing: revenue positive, expenses negative, payouts positive (paid to the owner). The statement financials are derived from the lines — here net income 600.00, payouts -500.00, balance end 100.00 — and centBalanceEnd can be supplied explicitly when the source data carries its own balances.

Payout lines without a contactId are split across the ownership period members by their ownership share, one journal entry per member.

The response reports what was created:

{
  "id": "statement-uuid",
  "uniqueRef": "csv-import:2025-03:ocean-view",
  "status": "published",
  "replaced": false,
  "journalEntryCount": 3,
  "financials": {
    "centBalanceStart": 0,
    "centBalanceEnd": 10000,
    "centTotal": 60000,
    "centExpenses": -20000,
    "centNetRevenue": 80000,
    "centTransfer": -50000
  }
}

Guardrails

  • The statement range must be covered by one listing ownership period; statements overlapping an existing statement on the same period are rejected.
  • A uniqueRef belonging to a statement that was not created by this import is rejected — live statements can never be overwritten.
  • Deleting an imported statement (DELETE /statements/{id} with onLocked=unlockAndDelete) also removes its historical journal entries.

Carry-forward balances

Statements chain balances through centBalanceStart / centBalanceEnd. Import months oldest-first and carry each month's balanceEnd into the next month's centBalanceStart (or pass explicit balances from the source data), ending with the balance the first live statement should start from.

On this page