Changelog
Current public contract changes and migration links
Read Versioning and deprecation for breaking-change notice periods and migration rules.
Unreleased
-
POST /connections/pms-cutover/applykeeps the old PMS reservation lines on the retired reservation when the new PMS reservation has its own lines, so the stay no longer posts twice. Deposits dated beforebooksClosedAtnow follow the re-link to the new PMS reservation without reopening the books. Apply rejects withreason: "reservationLocked"when such a deposit would change listing. See PMS migration. -
A historic statement import rejected for an overlapping statement returns the overlapping statement's
overlappingStatusin the error details. See Historic statements. -
Connecting Ramp sets every available Ramp flow without a status on the connection to
active, not only the chart of accounts push. A status set earlier, includinginactive, is kept on reconnect. See Bank connections. -
A CSV preview of deposits or expenses for a team without a statement start date completes with every row
invalidand reasongeneralLedgerStartDateMissing, instead of failing and retrying. See CSV imports. -
A team without a
uniqueReftakes theuniqueRefof its Hostaway connection when it connects Hostaway, soGET /partner/billingnames teams that signed up in the app. An existing value is never replaced, and a value another team of the partner holds is skipped. Existing Hostaway teams were backfilled. See Team setup. -
GET /partner/connectionsreturnsuniqueRef,createdAtandupdatedAton each connection, asGET /connectionsdoes.uniqueRefis the connected account at the provider, so a partner can match every team to its provider account withappId. See Connections. -
PUT /teamacceptsuniqueRef, so a partner can set its identifier on a team created in the app, change it, or clear it withnull. It needsteams:provisionand must be unique among the partner's teams, otherwise409.uniqueRefchanges appear in team audit events. See Team setup. -
The
unmappedReservationLineTypesteam issue returnscontext.suggestions: per unmapped linetype, a suggestedaccountand areason(teamMapping,similarTeamMapping,platformDefault,taxReviewornoMatch). Tax line types never get an account. See Reservations. -
Deprecated:
copyLineMappingsFromPartnerIdinPOST /team/initand in thegeneralLedgerobject ofPOST /teams. Removal on 2027-02-01. It copies line mappings from the partner's existing client teams, including changes made on those teams. Omit it to apply the partner's published defaults, and sendpmsAppIdto apply them before the team connects the PMS. See Team setup. -
POST /accounts/{id}/banking-initreturnsreason: beforeStatementStartDate, withstatementStartAtandstartingBalanceDateincontext, whenstartingBalanceDateis before the team's statement start date. -
POST /team/initand thegeneralLedgerobject ofPOST /teamsacceptpmsAppId. WithlineMappingsininclude, it applies that PMS's published line mappings without a connection to the PMS. Any app other than a PMS returns400. See Team setup. -
A sync that fails on a rejected token refresh ends with
Connection requires re-authentication. Reconnect it, then run the sync again.instead of the provider's status text, such asBad Request. See Connections. -
GET /sources/{id}/downloadreturns the CSV rows behind each transaction and bank record created by/csv-imports, including the Stripe and Booking.com payout presets. Before, it returned 404 for them. Imports from before this release still return 404. See CSV imports. -
POST /partner/vri-to-vrt/cutoveris removed, andPOST /partner/vri-to-vrtno longer acceptsmoveConnectionCredentials. A migration never moves credentials: the customer reconnects the PMS on the VRT team and the VRI connection is set inactive. The assisted preview no longer copies reservations. See VRI migration. -
The VRI to VRT bootstrap no longer copies inactive listings that have no PMS reference, statements or reservations. Listing owners without a split share equally. The historical import keeps the start of the current ownership period when an earlier period has different owners, and its account mapping preview suggests
Guest Revenue:accounts by their short title. The VRPilot connection steps (PMS, payment processors, Airbnb, Ramp, workflows) ignore disabled connection shells. See VRI migration. -
Ramp Connections create a feed for every Ramp-held bank account, including accounts that are not mapped in Ramp's accounting settings and accounts that cannot fund Bill Pay.
capabilities.achPayments.disabledReasonsaddsproviderPaymentIneligiblefor a Ramp account that cannot fund Bill Pay, such as one set to "No debits allowed". See Bank Connections. -
Statement PDF and CSV exports show the saved totals of In Review and Published statements, the same as
GET /statements/{id}. Before, they recomputed the summary from current journal entries, so an imported historical statement could show a different starting balance. -
New
GETandPUT /accounts/{id}/payment-filesset up Nacha files and check printing on a bank account, andPOST /accounts/{id}/payment-files/nacha-test-filereturns a Nacha test file. Accounts includecapabilities.nachaFileandcapabilities.checkPrinting. See Accounting Administration. -
The team's
extractableConnectionsincludesexpense: active Ramp and Guesty connections with their active expense flows inflowIds. See Connections. -
GET /teams/issuesreports nine new issue codes for regular work queues:listingsNeedSetup,unmappedReservationLineTypes,bankRecordsWithSuggestedMatch,unmatchedBankDeposits,unmatchedBankExpenses,unreconciledBankTransactions,unassignedGuestDeposits,departedReservationBalanceMismatchandcancelledReservationBalances. Each count equals the rows of the matching list. The bank issues list the count per bank account incontext.accounts, most open items first. See Portfolio health. -
GET /meissueslists every issue code computed for the team, withcount: 0when nothing is open. A code missing fromissueswas not computed for the team yet. -
GET /connectionswithoutappIdnow lists Plaid connections, as it already lists Ramp. Other system connections such as CSV Import and manual bank feeds stay hidden unlessappIdnames them or is*. -
The
paymentssummaries inGET /transactions,GET /statements,GET /statements/{id},GET /statements/by-periodandGET /contacts/{contactId}/payoutsincludescheduledAt. Ascheduledpayment without it is unscheduled in Ramp. See Transactions. -
GET /partner/vri-to-vrt/statements/account-mappings,.../listing-mappingsandGET /partner/vri-to-vrt/reservation-line-mappingsread the legacy source from its own region when the request is served from the target's region (x-data-region) and the partner is homed elsewhere. Before, they answered404. -
PUT /contacts/{id},PUT /contacts/{id}/bank-accountandPOST /contacts/{id}/payment-methods/achreturn403for owner users with viewer access. Owner users need admin access to the contact. On teams without UI permission roles,POST /contacts/{id}/payment-methods/achandPOST /contacts/bank-details-requestsrequire a Team Admin. See Portfolio. -
Bank account change emails go to every team, not only teams with Payment Setup, and are not sent when the saved bank details did not change. When the Ramp hand-off fails,
POST /bank-details-links/submitsaves nothing and the link stays open. See Portfolio. -
POST /transactions/{id}/returnsalso requirestransactions:post-journals, likePOST /payments/{id}/reverse; tokens without it get403. It records one owner receipt per returned payment. Before, a second returned payment on the same payout got no receipt. See Transactions. -
POST /plaid/connectinupdatemode opens Plaid's account selection. The user can share an account the Item does not include yet, andPOST /plaid/connect/completereturns it as arequiredfeed. Before, update mode only re-authenticated, and the only way to add an account was areplacesession. See Choose the session mode. -
POST /connections/connectreturns422with codeUNPROCESSABLE_CONTENTand the integration's message when the integration rejects the connection for a reason the user can fix. Before, it returned502with "Connection provider request failed". -
GET /partner/vri-to-vrt/statements/account-mappingsreturnsunmatchedAccounts, the account names on statement lines that match no legacy account.POST /partner/vri-to-vrt/statementsacceptslegacyAccountAliasesandexcludedLegacyAccountKeysand returns400withcontext.legacyAccountKeyswhile an unmatched account has no decision. Before, the import matched such a line to a parent or last-segment account and skipped it silently when no layout showed that account. See VRI migration. -
POST /partner/vri-to-vrt/statementsreturns400withcontext.legacyAccountKeysorcontext.sourceListingIdswhen a required account or listing mapping is missing. Before, it queued the import and the operation failed withOperation failedand no detail. See VRI migration. -
Mark as paid on a payout with an open Ramp payment fails with
providerPaymentOpen, andactions.recordOfflinereports that reason; cancel the Ramp payment first. Editing or archiving a payout with Ramp history no longer records an offline payment.markUnpaidis available on a payout paid offline after Ramp history. See Transactions. -
GET /transactionsand the transaction CSV export acceptappIdfor the app of the transaction's connection andhasRecurringTemplatefor transactions generated from any recurring template. Transactions returnbankRule: { id, name }for the BankRule that created them. See Transactions. -
Granting Team Admin or ACH Payer on a team with UI Permission Roles enabled for all users emails the team's direct Team Admins, except the member and the user who granted it. The email names the member, the roles, who granted them, and the partner they acted through. See Access and permissions.
-
Owner payouts paid through Ramp post nothing until a payment settles; the settled payment posts the whole payout journal on its settlement date. A failed or canceled Ramp payment leaves nothing in the books. Statement
payoutslist payouts that still owe money, and preview warns about them withstatementPayoutInFlightandstatementPayoutOpenwithout blocking the payment. See Transactions and Statements. -
POST /transactions/{id}/returnsrecords a returned owner payout as an owner receipt linked throughreturnedFromTransactionId. Transaction reads addreturnTransactionId.POST /payments/{id}/reverserefuses payout payments withpayoutReturnRequired. -
Offline payments and offline reversals dated in closed books are refused instead of moving to the first open date. Inside a published statement period, offline expense payments post on their date.
payment.status: "unpaid"removes the payment's journal entries and records a reversal of kindofflineUndo. Unmatching a bank record keeps the transaction paid. -
Matching a bank record to a transaction already paid offline on the same date and account links the existing payment. Before, the match reversed that payment and recorded a new one, which added reversing journal entries. See Transactions.
-
Only an open Ramp payment locks a transaction. Failed, canceled and succeeded Ramp payments no longer block
PUT /transactions/{id}(edits, archive, unarchive, Mark as unpaid). A refused change returns the reasonpaymentOpen, which replacespaymentAttempted. Changing the vendor or Bill ID of an expense with a Ramp bill returnsproviderBill, and deleting a transaction with any Ramp payment returnsproviderHistory. See Transactions. -
GET /transactions,GET /transactions/{id}/paymentsreturnactions.archive,unarchive,delete,markUnpaid,recordReturnandeditFinancialsnext topayandrecordOffline, each withavailableand areason. New reasons:providerHistory,providerSettled,periodLocked,statementLocked,transactionActive,notPaid,returned. -
A credit card can no longer hold the
payout_bankAccountassignment (notBankAccount), no longer receives it as the first bank account of a team, andPOST /statements/payrejects it as an owner payout account (bankAccountInvalid). Reclassifying an account as a credit card removes the assignment. -
Payments add
providerReference, the provider's customer-facing payment ID (Ramp'sPAY#),nulluntil the provider assigns one. Transaction payment summaries addproviderStatus. See Transactions. -
Ramp payment statuses follow the provider more closely.
schedulednow covers every state from approval pending to payment scheduled, andprocessingstarts when Ramp initiates the transfer. Before, Ramp states up to scheduled mapped toprocessingand draft states toscheduled. A draft bill leaves the paymentpendingorsubmitting. -
providerFailureCodeof a rejected Ramp submission carries a static code such ascategoryAccountMissinginstead ofnull. See Transactions. -
Payments that stay
submittingfor 30 minutes get theunknownOutcomeissue. -
The Ramp flows "VRPlatform Payouts and Transfers to Ramp Bills" and "VRPlatform Expenses to Ramp Bills" are removed. "VRPlatform Payments to Ramp" replaces them. Bills of payouts and transfers use the Owner Payouts account as category.
-
Contacts return
bankDetailsStatus(notSetUp,rampOnly,requested,expired,ready) and the latestbankDetailsRequestwith its delivery, link expiry and open time. They replacebankDetailsRequestedAt.GET /contactsfilters by everybankDetailsStatusvalue, andPOST /contacts/bank-details-requestsskips contacts asked within the last hour withrecentlyRequested. See Portfolio. -
On teams where Ramp ACH setup is available,
PUT /contacts/{id}/bank-accountandPOST /bank-details-links/submitsend the bank account to Ramp whatever the contact'sdefaultPaymentMethod.bankAccount.updatedByApiKeyIdnames the API key that last changed a bank account when no user did. See Portfolio. -
On a client team with UI Permission Roles,
POST /team/members/invitereturns400withreason: "partnerManagedMember"for every member of the managing partner. Before, only members who already reached the team were rejected. Members of another partner can still be invited.PUT /partner/members/{userId}/membershipswithmanagedTeamAccess: "all"now deletes the member's direct memberships on managed teams with UI Permission Roles. See Access and permissions. -
Plaid failure classifications add the outcome kind
recaptchaFailedfor the error typeRECAPTCHA_ERROR.POST /plaid/connect/exitreturns it with actionretryinstead ofunknownwith actioncontactSupport. See Plaid Link exit diagnostics. -
A partner member with
managedTeamAccess: "assigned"reaches only assigned managed teams on every client team. Before, client teams without UI Permission Roles admitted every member of the managing partner. Other managed teams now return401and no longer appear inGET /me/teams. A direct membership on the client team still grants access. Without UI Permission Roles,managedTeamAccessfollows the role on a partner team:POST /team/members/invitegives a new admin"all", andPUT /team/members/{userId}sets"all"for admins and"assigned"for users. Demoting an admin there requires at least one managed team. -
PUT /contacts/{id}/bank-accountandPOST /contacts/bank-details-requestsrequireach-payments:executefrom team members in addition toach-payment-methods:write. A Team Admin without the ACH Payer role gets403. Owners setting their own bank account are not affected. See Portfolio. -
Connecting or reconnecting Ramp now sets the
vendorOwnersetting of thepushRampVendorsflow to the Ramp user who authorized the connection, when no owner is set and that user is active. The setting stays editable, and a connection with no match keeps it empty, so ACH setup still returnsRAMP_VENDOR_OWNER_REQUIREDthere. See Bank connections. -
POST /accounts/{id}/banking-initand the PlaidstartingBalanceacceptisCreditBalancefor a credit card whose issuer owes the business. Without it, the amount is still recorded as owed. The flag is rejected on other account types. See Bank connections. -
PUT /transactions/{id}andPOST /transactions/batchnow match a line sent withoutidto the existing line with the sameuniqueRef. The line keeps itsidand any field the request omits.nullclears a field. Resending an unchanged paid expense keeps its lines and no longer returnspaymentHistory. See Transactions. -
POST /transactions/batchrolls back an item that is returned as an issue. Before, a refused line change could still leave the transaction total from that item stored, so the total no longer matched the lines and marking the transaction paid failed. See Transactions. -
POST /contacts/bank-details-requestsemails owners and vendors a link to enter their own bank details. Contacts returnbankDetailsRequestedAt, andGET /contactsacceptsbankDetailsStatus(notSetUp,requested,ready). Vendors complete a one-time form through the publicPOST /bank-details-links/exchangeandPOST /bank-details-links/submit, which return the newBANK_DETAILS_LINK_INVALIDandBANK_DETAILS_LINK_EXPIREDcodes. See Portfolio. -
Transaction responses (
GET /transactions,GET /transactions/{id}and the other routes that return a transaction) includecreatedBywithtype,idandnameof the creating actor.typeisuser,api_key,sync,automation,system,bank_ruleorcsv_import. It isnullwhen no bank rule application, CSV connection or audit creation event identifies the source, for example for most transactions created before audit recording started in their region (26 Feb 2026 at the earliest). See Transactions. -
GET /transactions,GET /transactions/csvandPOST /transactions/csvacceptsortwithdate,amount,createdAtand their-descending forms. The default stays-date, and other values return400. The CSV export uses the same order as the list. See Transactions. -
The MCP connector tools
vrt_api_readandvrt_api_mutationaccept an optionaldataRegionthat is sent asx-data-region, so a partner readsGET /operations/{id}for an import into a team in another region at once instead of after the control-plane copy, up to about 5 minutes later. The connector no longer treatsGET /teams/{teamId}as a team-less read; that route does not exist. Read a team withGET /teamand itsteamId. See MCP API access. -
GET /partner/vri-to-vrt/statements/account-mappingsandGET /partner/vri-to-vrt/statements/listing-mappingsacceptlimitandoffsetand returnhasMoreplus the full list totals. Withoutlimitthey return everything as before.POST /partner/vri-to-vrt/statementsis now placed bytargetTeamIdin the body, so the gateway forwards it to the target's region instead of answering421. See VRI migration. -
Every team now emails owner statements to owner contact addresses with a no-login link.
GET /statements/email-recipientsno longer returnsdelivery. It returnsviewers, the owners' active portal viewers, andskipReason: "notificationsDisabled"for owners whose statement emails are off.GET /contactsandGET /contacts/{id}returnstatementEmailEnabled, andPUT /contacts/{id}accepts it. See Statements. -
POST /transactions/batchitem issues now return the specific staticmessageof the failure, such asCannot modify expense - attached to published statement, instead ofTransaction batch item failed.code,reason, andcontextare unchanged. Integration sync history shows the same message. See Errors. -
GET /transactionsandGET /transactions/{id}returntransferReconciliationwithsourceanddestinationbooleans for transfers, andnullfor other types. Each is the same active bank-record match thereconciledfilter uses, so clients no longer derive the legs frompayment.bankRecordIds. See Bank Reconciliation. -
POST /transactions,PUT /transactions/{id}and transaction batch items that use a currency other than the team's default currency now carryreason: "currencyMismatch"(400 BAD_REQUEST; in a batch, the item issue). Recurring transaction templates and historical general ledger imports share the check. See Transactions. -
GET /partner/billing,/partner/listings,/partner/connections,/partner/owner-statements,/partner/users,/partner/users/{id},/partner/audit-events,GET /teams,/teams/lookupand/teams/issuesnow return400for a Partner API key sent tohttps://api.vrplatform.appwithoutx-data-region,x-team-idor a regional host. Before, they returned only the data of the gateway's own region, which was empty for partners with teams elsewhere. See Team context. -
PUT /csv-imports/uploads/{uploadRef}no longer returnscsvDuplicateHeadersfor headers that differ only by punctuation, such asCommissionandCommission %in an unmodified Booking.com payout export. Headers that repeat exactly are still rejected. See CSV imports. -
Ramp operating, treasury, and investment feeds now receive bank records for deposits and withdrawals on the Ramp account, read from Ramp's treasury transactions. Before, these feeds stayed empty. See Bank connections.
-
GET /statements,GET/POST /statements/csvandGET/POST /statements/detail/csvreturncontext.rejectedContactIdsand the caller's owncontext.ownerAccessContactIdson the403for an owner user requesting anownerIdsvalue outside their access. See Statements. -
GET /contactsandGET /contacts/{id}addactiveOwnerships[].listingName, so clients no longer need aGET /listingslookup to label an owner's listings. -
Reservation tax (
taxCentTotal,financials.totals.tax, manager dashboard taxes) counts only accounts in the Taxes Payable – Lodging & Occupancy and Taxes Payable – Other categories. Accounts with the expense sales tax assignment in another category no longer count.taxCentTotalnow also counts reservation lines on Taxes Payable – Other accounts. Stored values update when a reservation is next recalculated. See Reservations. -
GET /reservationsacceptsincludeRent=trueand then returnsrentCentTotal, the owner rent from the Rents Revenue column of the listing's statement layout, ornullwhen that layout has no such column. See Reservations. -
GET /metrics/occupancy-rate-over-timeno longer counts available nights for a group parent that only groups its children. A parent counts only when it is synced from a PMS or has booked stays of its own. See Reports. -
New
PUT /contacts/{id}/bank-accountstores an owner's or vendor's bank account in VRPlatform. Contact reads addbankAccount, which shows the routing number and only the last 4 digits of the account number. Contacts adddefaultPaymentMethodandaddressEditedAt. PMS sync no longer overwrites an address edited in VRPlatform. Each change emails the contact and the team's Team Admins. Contacts paid by ACH via Ramp get the stored account sent to Ramp for verification. During the rollout, the app screens and emails are enabled per team. See Portfolio. -
DELETE /accounts/{id}now returns400 USER_ERRORwith reasonaccountInUsewhen a tax rate references the account, or when a record that references the account is created while the delete runs. Before, these cases failed with500 INTERNAL_SERVER_ERROR.onLocked=archivearchives the account. See Accounting administration. -
GET /transactionsnow requires both transfer legs to be matched when noaccountIdis sent. Partially matched transfers appear under Unmatched. UsereconciliationScope=transactionto keep this behavior withaccountId; the selected-account default and matching candidates keep their per-account behavior. See Bank Reconciliation. -
GET /partner/billingrows now includeisTest, using the same rule as team responses. See Team Setup. -
GET /team/membersno longer lists members who reach the team through its managing partner, also when UI Permission Roles is off. See Access and permissions. -
With UI Permission Roles, a member who reaches a managed team through its partner gets only the partner membership's bundles there, also when they hold a direct membership, and
GET /mereturnsaccessSource: "partnerDerived"for them. Audit events for their actions on that team name the partner as the represented organization.PUT /team/members/{userId}/permission-bundles,PUT /team/members/{userId},DELETE /team/members/{userId}andPOST /team/members/invitereturn400withreason: "partnerManagedMember"for such a member. Assigning a team to a partner member who is already a direct member now replaces that membership, andPOST /teamsadds the new team to the selected-team reach of partner members inaddMembers. See Access and permissions. -
PUT /transactions/{id}now returnsreason: "paymentHistory"on its403when the request drops a line with payment allocations. Send the line'sidto keep it. See Transactions. -
GET /metrics/average-daily-rateandGET /metrics/average-daily-rate-over-timecount the occupied nights ofGET /metrics/occupancy-rate-over-time: stays on the "Owner Block" booking channel, owner block days on the PMS calendar and days outside an active ownership period are no longer nights, and overlapping stays count a listing-day once.GET /metrics/average-daily-rate-over-timebuckets cover the whole day, week or month, as in occupancy.GET /metrics/occupancy-rate-over-timecounts a group child's days under its own active ownership periods as well as its parent's. See Reports. -
Ownership periods whose omitted splits are distributed now store
100000as the total, not100. This coversPUT /listings/ownership-periods/{id}withoutsplit, listing creation and the CSV owner-group import. Explicit splits keep their scale, and a period with explicit 100-point splits fills omitted ones on 100 points.GET /contactsandGET /contacts/{id}returnactiveOwnerships[].spliton the 100000 scale for periods that still total100. Stored periods are unchanged. See Portfolio. -
POST /teamsandPOST /team/initfor a General Ledger team that copies from a team other than VRPlatform now take additional-fee templates and their accounts from the published template, not from the VRPlatform team in the region. Creating such a team withrecurringFeesininclude(or noinclude) now fails before the team exists when no template is published. See Team Setup. -
POST /team/initnow returns400for a team without General Ledger. Connecting a PMS on such a team no longer queues reservation line-mapping initialization. -
GET /email-templatesandPUT /email-templates/{type}accept the new typeownerStatementLink: the statement email that owner contacts open by link without a login. Owner contacts no longer receive theownerStatementPublishedtemplate, which now goes only to owner portal users. Until a team saves anownerStatementLinktemplate, owner contacts get the default no-login wording. See Statements. -
POSTandDELETE /calendar-blocksnow return403to an owner user with viewer access to the owner of a requested date. Before, owner viewers could block and unblock those dates. Admin owners and team users are unchanged. See Portfolio. -
GET /filesacceptscontactIdsto list only the files linked to those contacts. For owner users it narrows their owner-access scope and never widens it. See Portfolio. -
POST /teamsandPUT /teamnow storeemailandsupportEmailin lowercase, matching user and contact emails. Existing team and contact emails with capital letters were lowercased. See Team Setup. -
GET /metrics/owner-performanceandGET /metrics/owner-rentread imported historical statements. Their stays count as guest stays, and their Rents lines count as rent in the month of their date. Stays on the "Owner Block" channel and owner blocks on the PMS calendar now count as owner nights, next to live stays without rent. Both metrics also leave out archived stays, stays on an inactive source and days of set-inactive ownership periods. See Reports. -
Teams with an active owner portal now show the portal
nameas the brand in owner statement and owner statement summary PDFs, owner invitation and statement emails, the owner navigation and the owner statement pages. Before, PDFs showed the company name and emails, navigation and pages showed the team name. Teams without an active portal are unchanged. Legal documents keep the company name. See Custom Owner Portal. -
The
vri-statement-importoperation queued byPOST /partner/vri-to-vrt/statementsnow endsfailedwith "No source statement precedes the cutover" when the source team has no statement before the effective cutover, including a source with no statements at all. Before, it endedcompletedand sethistoricalStatementsImportedAtwhile writing nothing. See VRI migration. -
PUT /listings/ownership-periods/{id}now accepts a split correction on a locked period with a single owner when the request keeps the same owner, for example 50% to 100%. Before, it failed with400"Period is locked, cannot change owners". Posted statement amounts do not change. Changing or adding owners on a locked period stays rejected. See Portfolio. -
The VRI to VRT migration now builds recurring fee drafts from collection-level rates, collection zero formulas and booking-channel zero formulas, not only listing settings. A channel zero formula or zero rate becomes a
!<channel uniqueRef>entry in the draft'sbookingChannelsFilter. A channel rate other than the default, non-zero formulas and unresolved setting targets are listed in the draft's migration review record. Existing drafts are unchanged. A fee with a!channel entry now also refreshes reservations that have no booking platform or channel (they were skipped). See VRI migration. -
useCentralDefaultsis removed fromPOST /teams(generalLedger) andPOST /team/init. Requests that still send it are accepted and the field is ignored. New General Ledger teams with nocopyFromTeamId, or with the VRPlatform team ascopyFromTeamId, now bootstrap every included part from the published Control Plane template.POST /teamsfails before creating the team when no template is published, whateverincludecontains.POST /team/initon a General Ledger team without a copy source (or with the VRPlatform team) uses the latest published template for every included part. The VRI to VRT team migration now uses the template and fails before creating the team when none is published. A PMS connection on any General Ledger team applies the latest published defaults for that PMS, matching accounts to the team's own active accounts; a line type stays unmapped when any of its rows has no matching account. Copies from other teams and partner line-mapping copies are unchanged. See Team setup. -
A request for a team that is still provisioning now fails with
409and the message "Team is still being provisioned". Before, it failed with500"Tenant has invalid status". Retry after provisioning completes. See Team setup. -
A reservation whose journal refresh is discarded by an unmapped line now stores its payment totals. A reservation with no entries reads
payment.expected0,payment.received0 andpayment.statuspaidinGET /reservationsand thepaidStatusfilter. Before, the stored status stayed empty: the list showedunpaidand thepaidStatusfilter did not match the row. Stored totals that differ from the entries are recalculated for general ledger teams; no journal entry changes. See Reservations. -
POST /recurring-feeswithlistingSubscriptions,PUT /recurring-fees/{id}andDELETE /recurring-fees/{id}withonLocked=archiveno longer plan the reservation refreshes inside the request when a fee touches more than 500 reservations. The fee change commits first and the refreshes are queued in the background. Before, saving a fee with thousands of reservations could fail with503and an empty body. ThePUTresponse keeps thereservation-journal-refreshoperation. Reservations showrecalculatingfor the change only after the background planning has queued them. See Recurring fees. -
GET /reservations/{id}financial lines of typereservation_feeandreservation_fee_taxcarryleg,debitorcredit. The credit line holds the fee amount, the debit line is its counter leg. Other line types omit it. See Recurring fees. -
POST /recurring-fees/listing-periodsno longer rejects a new period that cuts a neighbor period an owner statement includes, with "Period end cannot be before locked range end". It followsPUT /recurring-fees/listing-periods/{id}and returns400 USER_ERRORonly when the cut would leave a fee on an owner statement without a matching period. The error details carryuncoveredFeeCountandreservationIds. Fees on owner statements never change. See Match by Stay Date or Booked Date. -
The stored payment totals of a reservation with co-host accounts-receivable payments at or below the manager share now count them as received, as
payment.receivedonGET /reservationsandGET /reservations/{id}already did.paidStatusfilters onGET /reservationsfollow the same amount. Before, the filter readunpaidwhile the row readunderpaid. A co-host payment at or below the manager share plus other payments above it readsunderpaidagainst the full charge. Stored totals of unlocked reservations are recalculated. -
POST /listings/{id}/children/mergeno longer refuses a child withownershipMismatchonly because it has a deactivation period (setListingInactive=true). The period stays on the child; only the active period must match the group parent. A child statement that ended earlier in the month, because its ownership ended, now folds into the month's group statement. Large merges (several thousand journal entries) no longer fail with500and an empty body. See Portfolio. -
POST /contactsandPUT /contacts/{id}now return409 CONFLICTwith reasonduplicateUniqueRefandcontext.contactIdof the existing contact whenuniqueRefis already used in the team. Before, both failed with500 INTERNAL_SERVER_ERROR. See Portfolio. -
Metrics that take
startDateandendDateordateRange, such asGET /metrics/revenueandGET /metrics/cash-flow, now return400 BAD_REQUESTwhen the range is reversed. Before, they failed with500 INTERNAL_SERVER_ERROR.GET /metrics/reservation-volumeandGET /metrics/sync-by-statusstill return an empty result for a reverseddateRange. See Reports. -
DELETE /connections/{id}now returns400 BAD_REQUESTwith reasoninUseand the lock reasonslisting recordsorbank record linkswhen records of other connections reference a connection listing, or bank records of the connection still have reconciliation links. Before, these cases failed with500 INTERNAL_SERVER_ERROR.onLocked=archivearchives the connection. See Connections. -
PUT /reservations/{id}no longer setsuniqueRefandpmsReferenceCodeto the request'sconfirmationCode. Before, an update that sentconfirmationCodewithout them overwrote both, which detached an integration reservation from its source. Both now change only when the request sends them. Creation still defaults them fromconfirmationCode. See Reservations. -
Manager dashboard metrics (
GET /metrics/revenue,checkin-counts,reservation-count,average-daily-rate,average-daily-rate-over-time,take-rate,take-rate-trend,booked-revenue,future-booked-revenue,occupancy-rate-over-time) no longer count reservations whose source is inactive. This matchesGET /reservations, which already hides them. Figures can drop. See Reports. -
Manager dashboard revenue (
GET /metrics/revenue,take-rate,take-rate-trend,booked-revenue,average-daily-rate,average-daily-rate-over-time) now comes from active reservation journal entries and groups by posting date instead of check-in date. Revenue excluding taxes subtracts taxes on Taxes Payable – Lodging & Occupancy and Taxes Payable – Other accounts, including tax a fee moves there but not tax a fee charges on itself. ADR spreads each booked stay's revenue evenly over its nights. Reservation lines without journal entries no longer count.checkin-counts,reservation-count,future-booked-revenueand the occupied nights ofoccupancy-rate-over-timecount a stay only when it has an active reservation journal entry. Archived stays and stays outside an active ownership period drop out; stays in an earlier active ownership period count.GET /metrics/take-rate-trendno longer returnscoverageortotalRevenueExcludingTaxes, andstatusisavailableornoData; theinsufficientCoveragestatus is removed. See Reports. -
The reservation financials
totals.tax(GET /reservations/{id}) now counts reservation and fee journal entries on Taxes Payable – Lodging & Occupancy and Taxes Payable – Other accounts. Tax that a fee such as "Taxes Withheld from Owners" moves to the liability now shows, somanager + tax + ownerequalsreservation. Tax a fee charges on itself, such as VAT on the management commission, still does not count. The storedtaxCentTotalis unchanged. See Reservations. -
GET /metrics/occupancy-rate-over-timecounts each booked night in the bucket it falls in instead of the check-in bucket. Owner block nights ("Owner Block" booking channel reservations and PMS calendar owner blocks) are no longer occupied and are subtracted fromavailableNights. Only nights on available listing-days are occupied, and archived reservations no longer count. Weekly buckets now return occupied nights. See Reports. -
payment.expected,payment.receivedandpayment.statusonGET /reservationsandGET /reservations/{id}now read a reservation asunderpaidagainst the full guest charge when its co-host accounts-receivable payments are above the manager share but below the charge. Before, only the manager share was expected and the reservation readoverpaidwhile the ledger still showed the guest balance open. This replaces the manager-only result for amounts above the manager share. At or below it the manager-only rule is unchanged. Stored totals of unlocked reservations are recalculated. -
Any route that the database cancels because of a lock timeout (
55P03) or deadlock (40P01) now returns409 CONFLICTwithretryable: trueand the messageAnother update is in progress. Try again.Before, it returned500 Internal Server Error. See Error contract. -
POST /statements/paynow runs selections of five or more statements in a background job and returns202when the job does not finish withinwaitSeconds. Before, selections up to 100 ran during the request, and a run cut off mid-batch stayedrunningfor about two hours before it restarted. Selections of up to four statements are unchanged and restart within about fifteen minutes if cut off.POST /statements/pay/previewwith anIdempotency-Keyfollows the same cutoff. See Statements. -
payment.expected,payment.receivedandpayment.statusonGET /reservationsandGET /reservations/{id}now treat all co-host deposits of a reservation together. When their accounts-receivable legs and other active A/R payments meet or exceed the guest charge, expected stays the full charge, for example two deposits that each cover half of it. Before, each deposit was checked alone and the reservation showed only the manager share as expected with the deposits counted as received, so it readoverpaid. Stored totals of unlocked reservations are recalculated. Merchant fee lines on the deposits do not change the result. -
GET /metrics/cash-flowbuckets by paid date instead of transaction date and skips unpaid transactions until they are paid.outflowis the negated sum of expenses, so expenses with a positive amount (credits) reduce it instead of adding to it. -
GET /partner/teamsno longer returns404 Regional tenant not foundwhenx-team-idnames the Partner's own team and that team is homed in another region. The route reads the control plane and is served by any host. See Team Context. -
GET /statements/layoutsacceptstag(rentsRevenue, or!rentsRevenuefor layouts without the tag) andisUsed(layouts the team's active listings use).pagination.totalnow counts only the layouts matching all filters, includingisDefaultandids. See Statements. -
settingsonGET /teamandPUT /teamaddsshowOwnerDashboardNetIncome,showOwnerDashboardExpenses,showOwnerDashboardStats,showOwnerDashboardMonthlyPerformance,showOwnerDashboardFutureRevenue,showOwnerDashboardOccupancy,showOwnerDashboardReservations, andshowOwnerDashboardDraftStatements. The card settings default totrue,showOwnerDashboardDraftStatementstofalse. For owner sessions,GET /metrics/owner-financialsnow includes drafts according toshowOwnerDashboardDraftStatementsinstead ofownerPortalShowDraftStatements. NewGET /metrics/owner-dashboard-detailsreturns the rows of the dashboard's Reservations and Expenses cards for one month under the same setting, without returning whole statements. See Reports. -
Directly billed property-manager and partner teams require customer and subscription bindings plus active billing before
capabilities.activate.enabledbecomes true. Ordinary updates provision eligible missing bindings even when the payer stays the same, without requesting paid activation. See Team setup. -
GET /team/membersreturnspartnerAccesson each member. It istruewhen the member also reaches the team through its managing partner, so partner-derived bundles apply on top of the direct role. See Access and permissions. -
New
GET /metrics/owner-rentreturns monthly ownerrentfrom the column tagged Rents Revenue in each listing's statement layout. It fails with400andreason: "rentsRevenueTagMissing"when a selected listing's layout has no such column.spreadRentshows how much of a month's rent moved there from another month by spreading single-date entries across the stay's nights. WithasOf, a stay cancelled after that day counts at its current amounts, including the inactive entries of a channel stay's cancelled lines.GET /metrics/owner-performanceno longer returnsrent,adr, orrevpar. Divide owner rent by itsnightsoravailableNightsinstead. Adjustment lines no longer count toward a stay's Rents lines, which decide owner stays and weigh estimated owner revenue.nights,ownerNights,checkIns,occupancyRate, andownerRevenuecan change for stays with Rents adjustments. See Reports. -
POST /team/members/inviteandPUT /team/members/{userId}/permission-bundlesnow rejectui:ach-payer:v1unless the same array containsui:team-admin:v1orui:standard-member:v1. The response is400withreason: "achPayerBaseRoleRequired". See Access and permissions. -
The
403with reasonpaymentAccountRequiresMarkPaidnow says what to change instead of naming the missing scope:This payment can be recorded only without a bank account or bank records. Remove them, or ask an admin to record the payment.The status, code, and reason stay the same. See Transactions. -
PUT /team/members/{userId}now rejects demoting a partner admin touserwhen the member would keep no managed team. The response is400withreason: "managedTeamRequired". Assign at least one managed team, orallreach, first. See Access and permissions. -
New active teams created outside onboarding now enter the normal billing activation workflow after successful subscription creation. Suspended or cancelled teams remain excluded. Existing pending subscriptions require separate activation. See Team setup.
-
payment.expected,payment.receivedandpayment.statusonGET /reservationsandGET /reservations/{id}now follow the journal after a team changesstatementStartAtor a reservation is set togeneralLedgerStatus=inactive. Both changes made entries inactive without recalculating the stored totals. A deposit inactivated by the start date kept counting as received, a reservation whose charges the start date inactivated kept its old expected amount in the list, and an inactive reservation kept its old status, for exampleunpaidat 0 expected and 0 received. An inactive reservation with no active deposit now readspaid. The recalculation also covers a deposit that reaches the reservation only through a transaction line. See Reservations. -
The Mews connection now imports a stay that starts and ends on the same local day, for example a late arrival from 01:00 to 11:00 or an evening stay from 20:00 to 21:30. It is stored as one night starting on the check-in date, the night Mews bills. After the time zone change below, VRP rejected these stays as starting and ending on the same day, and their revenue did not import. A late arrival stored as the night before check-out moves one day later on its next sync. See Connections.
-
GET /partner/vri-to-vrt/statements/account-mappings,GET /partner/vri-to-vrt/statements/listing-mappingsandPOST /partner/vri-to-vrt/statementsnow work when the legacy VRI source is incrunchyand the VRT target is in another region such asus. They previously failed with404 Tenant not found, or the import wrote nothing. The import must run in the target's region: send the request to the target's regional API or setx-data-region; a request to another region returns421with the target's region and API base URL. A target that is not linked to the source returns the error reasonmigrationLinkMissing; link it withPUT /teamandmigratedFromTenantId. See VRI migration. -
The Mews connection now stores reservation check-in and check-out as dates in the Mews property's time zone instead of the UTC date. A late-evening local check-in that falls after midnight UTC, such as 21:00 US Central (02:00Z), was stored one day late, which shortened the stay by one night and moved revenue. A late one-night stay was rejected as starting and ending on the same day. See Connections.
-
Owner statement layout columns accept
tags: ["rentsRevenue"]on Net RevenueaccountsandsubTotalcolumns visible to owners. Each tag can be on one column per layout.POST /statements/layoutsandPUT /statements/layouts/{id}reject a tag on any other column, or on a second column, with400. A tag-only update keeps the layoutversion. WhenshowOwnerDashboardis on, the manager view ofGET /statements/{id}andGET /statements/by-periodadds the warningrentsRevenueTagMissingfor a statement whose current layout has norentsRevenuecolumn. Owner views never include tags. See Statements. -
New team setting
settings.showGuestName, defaulttrue. When a team turns it off,GET /reservationsandGET /reservations/{id}returnguestNameandbookerNameasnullfor owner users, owner reservation search ignores guest names, and owner reservation CSV exports omit theGuest Namecolumn. Manager users are not affected. See Reservations. -
Generating export
GETroutes that wait longer than 55 seconds now addcontext.recommendedMethod: "POST"andcontext.reason: "awaitTimedOut"to their504 GATEWAY_TIMEOUTresponse. The status and code stay the same. Send the same selector to the matchingPOSTroute, which is not bound to the request deadline. See Batch imports. -
Team onboarding is a required boolean with a default of false. Existing null values are migrated to false. Create/update requests reject explicit null, and true/false filters cover all teams. Eligible teams outside onboarding request activation after provisioning. See Team setup.
-
Changing an ownership period's dates, adding a period next to it, or deleting a period now moves the stored dates of that listing's draft owner statements with it. A draft created before the change kept its old start date, so on a mid-month ownership change the incoming owner's draft blocked the outgoing owner's statement with
Statement already exists for listing and start date. A draft whose month lies wholly before its ownership period is deleted, and an ownership change that would move a draft onto the start date of another statement fails with that same error. A draft that ends up after its ownership period keeps its full month window instead of a shortened one. See Statements. -
POST /partner/vri-to-vrtno longer fails for legacy teams whose owners have the company typecompany. Those owners migrate as contacts without a company type and show as individuals in VRT until you set their tax classification. The operation previously failed after three retries withOperation failed. A legacy owner company type that VRT does not support now fails withUSER_ERRORanddetails.ownerIdanddetails.companyType. See VRI migration. -
DELETE /teamreturns400 BAD_REQUESTwithdetails.childTenantIdswhen the team is still the partner or billing partner of another team, even withforce=true. Deleting such a partner previously left those teams pointing to a missing partner and madeGET /teamsfail for pages that included them. See Team setup. -
POST /statements/layoutsandPUT /statements/layouts/{id}now reject a net revenue or custom section namedPayoutsorUnallocated Accounts, ignoring case and surrounding spaces. The request fails with400andSection name is reserved for a system section. Statement rows identify their section by name, so a custom section with a system name could not be told apart from the system section. No existing layout uses these names. See Statements. -
POST /statements/layoutsandPUT /statements/layouts/{id}now reject a layout in which two net revenue or custom sections share a name, ignoring case and surrounding spaces. The request fails with400andDuplicate section names. Statement rows identify their section by name, so two sections with one name could not be told apart. No existing layout has duplicate names. The layout editor now names each added section uniquely. See Statements. -
GET /reports/balance-sheetwithview=ledgernow adds up journal entries with different parties on the same account. An account with owner, manager, or no-party entries showed only one of those amounts, so Accounts Payable could appear as zero on the Trust column and the column did not balance. Equity inview=ledgernow includes no-party entries in the owners row. Inview=ledgerandview=party, retained earnings now include no-party entries in the owners total. Other views are unchanged. See Reports. -
The reservation drawer now lists a recurring fee that posts to owners on both sides under Fees and Commissions. These fees were posted correctly but hidden because the section showed manager lines only. See Recurring fees.
-
For general-ledger teams, expense, deposit, and owner CSV previews now resolve a listing that
GET /listingsreturns as active. Listings with a PMS status ofinactiveand no current deactivation ownership period were reported aslistingNotFound. See CSV imports. -
GET /reservationsno longer returns a stale expected amount andunpaidstatus for a reservation in a currency other than the team default currency. Such a reservation posts nothing to the ledger, so list and detail both returnexpected0. Stored totals of existing reservations are corrected when their journal is refreshed. See Payment Status. -
PUT /recurring-fees/listing-periods/{id}savesstartAt,endAt, andmatchBychanges on a period that an owner statement includes. It returns400 USER_ERRORonly when the change would leave a fee on an owner statement without a matching period, and the error details carryuncoveredFeeCountandreservationIds.DELETE /recurring-fees/listing-periods/{id}returns the same error when deleting the period would leave such a fee without a matching period. Fees on owner statements never change. See Match by Stay Date or Booked Date. -
Exports that return a signed download link, such as CSV downloads and multi-statement PDF downloads, no longer fail with
502 Export renderer failedwhile a team is frozen for a region migration. Other writes still return423. See Team Migration Freeze. -
With a deferred-revenue account, a pro-rata owner adjustment is no longer split into daily receivable debits. The reservation receivable debit at check-in carries its full amount and Deferred Revenues holds the unearned nights, as for pro-rata reservation lines. Journal entries and the guest balance of existing reservations change when their journal is refreshed. See Reservations.
-
GET /metrics/owner-performancereportsrentafter owner fees, as on the owner statement. Adjustment lines on Rents accounts now count, and the owner's fee postings on Rents accounts reduce or add to rent.adrandrevparfollow. See Owner Dashboard Metrics. -
GET /internal/message-overwritesnow requires a user Bearer token and no longer needs a team. Team API keys and the admin secret receive403. The TypeScript client sends the request to the VRPlatform US API. -
GET /metrics/owner-performancereturnsownerNights: nights of booked stays without rent. These owner stays no longer count innightsorcheckIns.adrdivides rent bynights, so it leaves owner nights out.occupancyRatedivides by available nights minus owner nights. RevPAR is unchanged. See Owner dashboard metrics. -
Plaid failure classifications add the outcome kind
userSetupRequiredforUSER_SETUP_REQUIRED.POST /plaid/connect/exitreturns it with actionupdateinstead ofitemErrorwith actionreplace. See Plaid Link exit diagnostics. -
Reservation detail returns historical statement provenance, the nullable historical statement amount and actual base-line availability. Historical-only details explain unavailable booking lines and retain genuine zero amounts. See Reservations.
-
PUT /teamwithcancelation.effectiveAt: nullnow requests subscription recovery even when the cancellation date is already null. Access is granted after billing confirmation. Payment failures preserve the existing subscription inerroredstate; no replacement subscription is created. See Team setup. -
Provider connect task payloads and outputs now carry database references instead of credentials or callback parameters. Credential refresh task outputs retain status and omit stored credentials. The public connection request and response schemas are unchanged.
-
Older deployments reject changes to payment-owned transactions during staged ACH rollout, preserving their cash journals. Existing transactions without payment records remain writable.
-
Historical VRI statement imports accept an optional target-owned
statementLayoutIdfor the first import. The queued operation uses the current layout configuration. See VRI migration. -
Team responses and billing rows no longer expose
isBillable. Use the effectivebillingPartnerIdto identify the payer andbillingSubscriptionIdwith the subscription status to inspect billing state. Every active client team has its own subscription, including partner clients. See Team setup. -
Team and listing billing plan filters match an exact plan name. The retired
othercategory has been removed. Usenoneto select teams without an assigned plan. See Team setup. -
Partner-client creation and status updates apply the same billing relationship rules for all partners. Subscription ownership remains separate from the payer.
-
New teams no longer receive a billing rollout feature assignment. General Ledger and Operations Accounting assignments remain. See Team setup.
-
Older or contradictory provider success events cannot settle payments. Payment-only updates emit transaction webhooks, legacy conversion reads locked state, and team deletion removes payment operation history.
-
Payment updates preserve legacy paid transactions during staged rollout and reject incompatible older writers. Provider currency mismatches cannot post cash; imported allocations are audited. Team deletion retains payment evidence protection without requiring schema-owner privileges.
-
Payment journal refresh retries preserve settlements recorded concurrently. Paid status excludes settlements in another currency, and bank unmatching reverses payments recorded together with a paid transaction. Ramp partial payments map only allocated lines; return funding evidence uses the executed payment amount. Existing recovery operation IDs remain readable. See Transactions.
-
Payment reversals honor the selected posting date and accept identical retries after posting. Ramp replacement payments resume after closure and refund, and cancel if the predecessor settles.
-
PMS connection initialization preserves regional mapping setup for non-GL teams and charts copied from regional sources. Copying accounts clears a prior central chart selection without changing other team settings. The central-defaults rollout remains opt-in. See Team setup.
-
GET /bank-recordsacceptsappIdto filter by the source app independently ofmatchedByBankRule. BankRecord reconciliation statuses arepaidorunpaid;underpaid,overpaid, andnullare removed from the BankRecord contract. See Bank reconciliation. -
Bank-rule dry-runs validate proposed Transactions before reporting
wouldApply. Matching previews returnisAutoApplicable=falsewhen the team's GL start date is missing. Asynchronous runs arefailedwhen every attempted record fails; their result endpoint still returns the per-record outcomes. See Bank reconciliation. -
Editing archived recurring fees recalculates only reservations already referencing the fee. Deactivation still removes obsolete postings from unlocked referenced reservations. See Recurring fees.
-
Ramp ACH setup, expense payments, and statement payouts now return
Ramp is not connectedwhen the team has no active Ramp connection.Ramp payment flow is not activenow means only that the flow was set to inactive. The status code, reason, and issue code are unchanged. See Bank connections. -
GET /contactsandGET /contacts/{id}returnpaymentMethods.ach.setup = { available, reason }, so clients can hide ACH setup until the team's Ramp setup accepts bank details. See Bank connections. -
Owner statement
issues[].messagetexts now name the condition and the next step.balanceMismatch_start,balanceMismatch_end,netRevenueMismatch,expensesMismatch, andpayoutsMismatchaddcontext.statementAmount,context.journalAmount, andcontext.deltain cents.balanceMismatch_endalso addscontext.figure(balanceEndornetIncome). Journal refresh failure messages injournalStatus.failures[].messageand operationfailure.messagenow say what to check. Codes, severities, and reasons are unchanged. See Statements. -
GET /meacceptspreferActiveTeam=truefor remembered portal preferences. An accessible inactive requested team is replaced with the active default team when available. Explicit selections omit this option. See Team Context. -
The TypeScript client sends control-plane-only requests, including team discovery and embedded session bootstrap exchange, to the VRPlatform US API. These requests no longer initialize an unused regional database. See Team Context.
-
New
GET /partner/vri-to-vrt/reservation-line-mappingsreturns sanitized VRI reservation setup for an authorized linked source and target, including cross-region pairs. The custom Claude MCP connector adds review guidance for comparing these facts with native VRT mappings. See VRI migration. -
Listing fee drawers, listing-detail fee tabs and listing tables now show archived recurring-fee definitions as Archived, even when their subscription dates include today. See Recurring fees.
-
Internal user upsert lock timeouts now include
retryable: truein their existing409 CONFLICTresponse. Control-plane writes may already have committed before a regional failure. See Requests, Locks & Issues. -
Authentication cache invalidation now retries local deletion failures. Persistent failures return a retryable
503 SERVICE_UNAVAILABLEinstead of a generic server error. A preceding mutation may already have committed. See Requests, Locks & Issues. -
Split-owned statement views, owner previews, and individual-owner PDFs show the active owner's share below the payable summary amount. Full ownership hides the split row. Statement financials and payout behavior are unchanged. See Owner statements.
-
The
Owner Splitrow on statement summaries and PDFs reads ownership splits that total100as percentages. A sole owner at100shows no row; a 60/40 period shows 40% instead of 0.04%. -
GET /metrics/owner-performanceaccepts an optionalasOfdate. It then counts only stays booked by the end of that day (UTC) and not cancelled by then, likelastYearAtDateRevenueofGET /metrics/booked-revenue. WithoutasOf, nothing changes. See Owner Dashboard Metrics. -
Team
settingshas a newshowOwnerDashboardflag onGET /team,PUT /team, andGET /me. It defaults tofalse. When true, the owner portal opens on the owner dashboard for teams that have the dashboard feature. See Owner Dashboard Metrics. -
New
GET /metrics/average-daily-rate-over-timereturnsrevenue,nights, andaverageDailyRate(cents,nullwithout booked nights) per check-in bucket of booked stays. It accepts the sameintervaland date range as the other interval metrics. See Manager Dashboard Metrics. -
GET /metrics/occupancy-rate-over-timenow countsavailableNightsfrom ownership periods: only the days a listing is owned and active count. Before, every listing counted for every day of the bucket, so months before a listing joined showed too-low occupancy. A listing without ownership periods no longer adds available nights. See Manager Dashboard Metrics. -
VRI migration now creates flat fee drafts with a zero formula and separate inactive posting accounts for review. Previously, flat fees copied the management commission formula and accounts. See VRI migration.
-
New scope
apps:read. Every dashboard bundle now includes it.GET /appsandGET /apps/{id}accept eitherapps:readorintegrations:read. Before, they requiredintegrations:read, so roles without integration access, such as Operations, got 403 when a page showed an app icon. ThehasConnectionfilter still requiresintegrations:read. See Access and permissions. -
POST /connections/{id}/csv-importnow accepts afileUrionly if it is a signed upload URL issued by VRP. Any other URL returns 400 "fileUri must be a signed upload URL" and no import is queued. Before, VRP fetched any URL. Send the file asfileDatainstead. -
GET /statements/historical/{operationId},GET /general-ledger/historical/{operationId}, andGET /bank-rules/{id}/runs/{operationId}now return the saved error of a failed operation (message,reason,context,issues) with status 409. Before, a failed operation returned the same 409 "not completed" as a running one, so a client polling it never saw the failure. A running operation still returns 409 "not completed". -
GET /teams/issuesnow includes Plaid connections inbrokenConnections. Before, Plaid connections were left out, so a Plaid connection withhealth.statusactionRequiredorwarningraised no portfolio issue. See Portfolio Health. -
New Mews reservations that share a channel booking number and a room import with the confirmation code
<channel number>-<Mews number>. Before, VRP rejected the second one as a duplicate, so it was never imported. Reservations already imported keep their code. See Connections. -
A Mews date-range fetch now also returns reservations created in the range. Before, it returned only reservations last updated in the range, so a range picked by booking date missed older bookings. See Connections.
-
Listing periods of a recurring fee accept
matchBy(stayDateorbookedDate, defaultstayDate) onPOST /recurring-fees/listing-periods,PUT /recurring-fees/listing-periods/{id}, and inlinelistingSubscriptionsofPOST /recurring-fees. Responses andactiveRecurringFeePeriodsreturn it. AbookedDateperiod matches reservations by booked date (created date when never booked); posting dates do not change. Periods with different values do not conflict, and the booked-date period wins when both match.PUTreturns400when amatchBy,startAt, orendAtchange would leave a fee on an owner statement without a matching period. The unique start date per fee and listing now includesmatchBy. Audit events forrecurringFeeListingSubscriptionincludematchBy. See Match by Stay Date or Booked Date. -
GET /teams/resolvecaches the control-plane fields (name,slug, partner, billing partner,members,enabledFeatures,isOpex) for 30 seconds. A change to a member, a feature, the partner, or the billing partner can take up to 30 seconds to appear; an update to the team itself appears immediately. Regional fields andoperationsare read live. Access checks are not cached by this change: a removed member is denied on the next request. See Team context. -
Deposit CSV previews report
issues[].code: "coHostWithoutAppliedAmount"for a row with a non-zero Co-Host amount and a zero Applied Amount. The record staysvalidand imports. The row records no payment for the reservation. See CSV imports. -
New
GET /reports/owner-balancesreturns the owner balance at the end of any date, per ownership and currency, including activity not yet on a published statement. It changes no payout, statement, or paid status. See Owner Balance Rows. -
A Plaid connection whose Item has no valid accounts (
NO_ACCOUNTS,NO_AUTH_ACCOUNTS) now reportshealth.statusactionRequiredwith areplacementRequiredissue and thereplaceaction. Before,GET /connectionsreportedhealthywhile every scheduled sync failed. See Stored Bank Feed Health. -
Date filters and date fields accept years 1000 to 9998 only.
dateRange,paidAtRange, the reservationdatefilter, audit timelinefromandto, and date boundary fields return400with issue messageInvalid date range,Invalid date filter,Invalid time boundary, orInvalid datefor other years. Before,GET /bank-recordswithdateRange=9999returned500after Postgres rejected year 10000. Date fields read the year from its four-digit part, so month-first dates such as09/06/2026stay accepted. -
GET /mewithout a requested team resolves the oldest active membership in an active team. A membership in an inactive team is the default only when the user has no membership in an active team. See Team context. -
PUT /teamandPOST /teamsresolve themigratedFromTenantIdsource through the control plane, so a non-GL source incrunchycan be linked to a GL target in any other region. The non-GL source, GL target, and same-partner rules are unchanged. A missing source returns404with messageSource tenant not found. The link is recorded asmigratedFromTenantIdon theteamaudit event. See VRI migration. -
POST /connections/{id}/syncwithtype: reservationnow refetches the named Mews reservations.uniqueReftakes Mews reservation ids or Commander reservation URLs, separated by commas. Before, Mews had no single-reservation sync and the request fetched nothing. A sync fails when Mews does not return a requested reservation. See Connections. -
POST /connections/connectreturns400with messageConnection provider returned an errorandcontext.providerError(the provider'serrorcode, orunrecognized) when a callback carries anerrorparameter and itsstateis not accepted. Other unverifiable callbacks keepInvalid connection state. See Connections. -
GET /teams/issuesaddsmissingAccountAssignments. It lists the account assignment types that no account on the team holds, incontext.assignments. Query one team withteamIds={teamId}&codes=missingAccountAssignments. See Missing Account Assignments. -
Operations (
ui:operations:v1) now includesteam-issues:read, so its members can readGET /teams/issuesfor their team. See Access and permissions. -
New scope
transactions:mark-paid:reimbursement, granted to Operations. It records an expense as paid only without a funding account:POST /transactions/{id}/paywithprovider: null,POST /transactions, andPUT /transactions/{id}accept it when the stored and resultingaccountIdare null and no bank records are involved. Any other payment returns403with reasonpaymentAccountRequiresMarkPaid. APUTthat changes the funding account of a paid expense now requires a payment scope. Recurring template create and update withpaymentStatus: "paid"now require a payment scope, so keys holding onlytransactions:expenses:writecan no longer create paid templates. See Transactions. -
DELETE /listings/{id}?onLocked=archivenow archives a listing that belongs to a group instead of returning400. The listing stays in the group and gets its own deactivation period, so itsstatusisinactive. Owner-side postings of that listing dated from the archive date move to the deactivation period and drop out of the group's owner statements, including bookings the PMS sends later. Entries attached to a statement stay where they are. Archive is no longer refused for booked reservations or recurring fees after today, andcontext.reason: groupedListingScheduledActivityno longer exists.DELETE /listings/ownership-periods/{id}on that period reactivates the listing and returns its entries to the group's ownership. Ungroup refusals caused by published statements or statement-attached journals addcontext.nextAction: archiveListing. See Listings & Ownership. -
PMS cutover listing mappings add the
suggestedstatus: the PMS listing name or shared reservations point at one source listing. Apply rejects unconfirmed suggestions withreason: "listingMappingRequired". -
PMS cutover
impactedReservations.unmatchedReservationsaddssuggestedReservation, the only new-PMS reservation on the same listing and dates. Preview and apply acceptreservationMappingsto resolve each unmatched reservation. See PMS Migration. -
POST /plaid/connect/completereturns422 PLAID_CONNECT_REQUIRES_NEW_LINKinstead of503 PLAID_PROVIDER_UNAVAILABLEwhen Plaid reportsNO_ACCOUNTSorNO_AUTH_ACCOUNTSfor the Item. See Bank Accounts and Connections. -
POST /flows/{id}/connections/{connectionId}/settings/{settingKey}withvalue: nullon a setting that is not set returns200 {}instead of400. See Connections. -
POST /account-connections/batchreturns400instead of500whensourceIdoraccountIdis not a UUID.PUT /account-connections/{id}returns400instead of500for a non-UUIDaccountIdand now rejects a non-UUIDsourceId, which it previously ignored. See Integration writes. -
GET /menow includes Hostaway and other populated regions inpartnerContext.managedTeamRegionsfor global admins with the VRPlatform team selected. Counts cover all active and inactive property-manager teams. See Team Context. -
New
POST /contacts/{id}/previewstarts a read-only owner portal preview as one owner contact sees it, including owners who have not been invited. It requirescontacts:readand returns a single-useurlon the team's active custom domain, or on the calling page's origin. NewPOST /owner-portal-previews/exchangetrades the code in thaturlfor a one-hour bearer token that keeps the caller's permissions, andPOST /owner-portal-previews/endrevokes it. Under the token,GET /mereturns the owner portal context and every non-GETrequest exceptPOSTexports returns403. See Listings & Ownership. -
The locked accounting history banner on owner statements no longer persists for reservations dated before the accounting start. A queued journal refresh that is blocked only from writing inactive pre-start entries into closed books or a locked statement period now completes. A refresh that would change an active entry stays locked and still shows the banner. See Reservations.
-
Guest Balances and reservation
payment.receivednow count a deposit that posts a payment plus an offsetting merchant-fee leg (a payment grossed up for credit card fees). They previously dropped it as a net-zero rebalance. See Owner dashboard metrics. -
GET /reports/owner-statement-summaries-per-layoutand itsdetails/{contactId}route no longer fail with500when an owner contact has a blank stored email; the email is omitted. Contact create and update, including batch imports, store a blank email asnull. See Portfolio. -
DELETE /listings/ownership-periods/{id}withonLocked=archiveon a locked last period that has a previous period now requiresonLast=none. The defaultextendPreviousreturns400withcontext.suggestedOnLast: "none"instead of creating overlapping periods. See Portfolio. -
PMS cutover no longer moves stays cancelled before the cutover date. They post on their cancellation date and stay with the old PMS, so they no longer block a cutover after books close. Apply now rejects old-PMS reservations with an amount and no match on the new PMS with
reason: "reservationUnmatched"; preview lists them inimpactedReservations.unmatchedReservations. See PMS Migration. -
New
GET /connections/pms-cutover/apply/{operationId}returns the PMS cutover result. A failed preview or cutover now returns itsreasonandcontext, such asreservationLockedwith up to 100 locked reservation IDs. Ambiguous listing matches returnreason: "listingMappingAmbiguous". PreviewimpactedReservations.lockedReservationslists up to 100 locked reservations withbooksClosedorstatementAttachment. Operation results that are not ready now returnOperation result is not ready. See PMS Migration. -
Reservation batch items rejected because the PMS connection has no accounting start date now return
reason: "pmsAccountingStartMissing". -
New
GET /metrics/take-rate-trendandGET /metrics/booked-revenuereturn take rate and revenue excluding taxes by check-in month for manager dashboards. Take rate counts only stays with a PM/owner split and returnsnullwith astatuswhen the split does not cover at least 90% of a month's revenue. Booked revenue compares with last year's final and same-date totals.GET /metrics/take-rateandGET /metrics/future-booked-revenueare deprecated. See Reports. -
YYYY-MMmonth parameters, such asmonthon statements and metrics, now reject months outside01to12with a 400 instead of failing later. -
Billing-partner changes now activate a newly created first subscription for teams that have completed onboarding. Pricing rollout and cancellation checks still apply. See Team setup.
-
Team status now stays active during onboarding with a pending subscription, provided the team and its billing partner are not suspended. Team reads, resolution, team-list status filters, billing rows, and active-listing counts use this rule. Subscription activation remains tied to onboarding completion. See Team setup.
-
Business rule errors now return a top-level
reason, such astransactionReconciledorbooksClosed, next to the unchangedcode,message, andcontext. Batch item failures carry the same field. Errors withoutreasonare access or integration failures. See Reasons. -
GET /statements/by-periodacceptsownerIdand optional comma-separatedlistingIdsfor one month's owner-view details. It omits empty unsaved drafts and applies owner draft visibility. Draft and In Review details are visible to owners when draft previews are enabled. See Statements. -
Owner financials accepts
status=published|allfor previews; owners cannot bypass hidden drafts. Owner metrics now default to an exclusive range end. Owner performance counts only owned days for available nights, occupancy, and RevPAR. See Reports. -
New
GET /metrics/owner-performanceandGET /metrics/owner-financialsreturn an owner's monthly booking performance (nights, rent, owner revenue, check-ins, ADR, occupancy, RevPAR) and statement totals with payment status, filtered byownerId,listingIds, andcurrency. Owner sessions can read them for their own contacts and listings. Both requiremetrics:read. See Reports. -
Owner statement details now show attachment changes at once. Uploading, removing, or changing the owner visibility of an expense attachment previously left
attachmentsstale in cached owner views, including drafts. Previously cached details also refresh on the first read after deployment. -
A duplicate bank last 4 digits error now returns one
issues[]entry per affected field, withcode: duplicateLast4, a staticmessage, and the fieldpath.POST /accountsandPUT /accounts/{id}point atbanking.last4,PUT /account-connections/{id}atlastDigits,POST /account-connections/batchatdata[index].lastDigits, andPOST /plaid/connect/configureatbankAccounts[index]. Error envelope issues can now carry an optionalcode. The top-levelcode,message, andcontextare unchanged. See Error Contract. -
GET /reports/journal-entriesacceptsincludeHistoricalLedger. Whentrue, imported historical-ledger entries are returned alongside the entries matched byledgerandstatus. Owner statement and owner statement summary drilldowns set it, so their journal-entry detail includes imported historical activity. See Owner dashboard metrics. -
Owner statement summaries count payouts on imported historical statements. A historical payout without a contact is split by ownership share. Pre-go-live postings that an imported statement's balance already carries, such as the listing opening balance, are no longer counted again as activity.
-
First-party tracking preserves authenticated owner and partner attribution, including partner users working in managed PM teams. Service events retain users with managed partner access. See Integration Writes.
-
New
POST /listings/{id}/children/mergemerges standalone listings and their published owner statement history into the group parent{id}. Run it withdryRun=true, then apply with the returnedplanDigest. It requires the newlistings:merge-childrenscope, granted by the Team Admin role and the newpartner:listing-merges:v1bundle. NewGET /listings/{id}/childrenlists a group parent's child listings (listings:read). See Listings & Ownership. -
PUT /listings/{id}/parentrefusals caused by published statements, statement-attached journals, or locked accounting history now returncontext.nextAction: mergeListingChildreninstead ofsupportAction: listingGroupHistoryMigration, and namePOST /listings/{id}/children/merge. Ungroup refusals are unchanged. -
GET /statementsandGET /statements/totalsaggregates includepaymentReceived: the sum ofpayment.receivedfor every matching statement in that currency, before pagination. See Statements. -
Team
isTestnow includes the direct classification of the accounting and billing partners, including partners in another region. Inherited classifications do not recurse. See Team Setup. -
PUT /reservations/{id}statement-period lock errors now includecontext.ownerStatementId, identifying the Review or Published statement that established the lock boundary. The error message and lock decision are unchanged. See Reservations. -
GET /partner/billingandGET /partner/listingsnow returnuniqueRefwith the partner-provided stable identifier for the team. -
A statement
journalStatusin afailedstate carries an optionalfailuresarray naming the reservations behind it:reservationId,confirmationCode,invalidationId,month(YYYY-MM), its ownreason, and a fixed displaymessage. It appears on every failed reason, includingjournalLocked, where it names the unresolved build failure behind the lock. Completed accounting-lock skips are never listed. It is scoped to the statement's ownership period, month and currency. Returned byGET /statements,GET /statements/by-periodandGET /statements/{id}. See Statements. -
Reservations return the
unmappedFinancialLineserror issue withcontext.lineIdswhen a non-excluded, non-zero line has no account mapping. A single unmapped line discards the whole reservation's journal build, and the reservation'sjournalStatusnow reportsjournalConfigurationInvalid/fixJournalConfigurationfor it instead of asking for a retry. A bulk journal refresh operation reports the same cause instead of the generic "Journal refresh failed". See Reservations. -
Reservation financial lines return
inactiveReason=priorToStartDatewhen a line is inactive only because it falls before the accounting start, not thesystemcatch-all; a line also excluded for a higher-precedence reason keeps that reason. Totals are unchanged. The field is on the reservation detail response, so every route returning it carries it:GET /reservations/{id},POST /reservations,PUT /reservations/{id},POST /reservations/{reservationId}/adjustments,PUT /reservations/{reservationId}/adjustments/{id},DELETE /reservations/{reservationId}/adjustments/{id}. See Reservations. -
GET /plaid/healthnow checks live provider health even when the last sync failed. Item errors retain their recovery action and include available institution warnings incontext.institutionHealth. A failed institution lookup is recorded separately incontext.institutionHealthError. See Bank Connections. -
GET /teamsacceptsbillingCustomerIdfor an exact regional lookup of current customer bindings within the caller's access scope. See Team Setup. -
Listing create, update, and batch import now include
stateCodein generatedaddress.full, with fallback to the deprecatedstatefield. Listing reads also include the saved state for existing addresses that match the old generated format without a state. Custom address strings and stored data remain unchanged by reads. -
Internal billing updates accept subscription assignment and billing state in one request. Customer/environment validation and conflicting-assignment rejection still apply. See Team Setup.
-
GET /contactssearch now matches a case-insensitive partial full name acrossfirstNameandnamein display order. -
Interactive signed-in callers on all 17
/partnerroutes now receive403 FORBIDDENwith context (selectedTeamId,selectedTeamType,requiredTeamType,partnerTeamId) when the selected team is not the partner team. Partner API keys keep404 Not a partner API key. See Partner Context. -
Transaction updates that change a date and its lines together check the previous lines at the old date and the resulting lines at the new date. Manager-side lines leaving a statement period no longer fail with a statement-period lock; owner-side lines leaving a published period still do. See Locking and Historical Integrity.
-
First-party tracking now checks test/demo teams and internal users in the queue worker, before clearing missing or team-mismatched user references. Deletion events and replays use the same queue. Sec services use the VRPlatform team header and can submit events for deleted teams through the body. See First-party Tracking.
-
GET /teamand billing team rows add nullablebillingSubscriptionId. The field identifies a team's regional billing subscription. See Team Setup. -
GET /teams/issuesaddsjournalLocked,journalConfigurationInvalid, andjournalUnbalancedfor outstanding failed reservation refreshes. Detail context includes affected reservation IDs and a supported action; fetch each reservation's journal preview for current blocking details. See Reservation Journal Refresh Issues. -
GET /reservations/{id}/journal-previewreports proposed journal mutation counts and typed accounting blockers without changing stored data. The preview contract replacesblockerwith sharedlockandissuesfields, includes safe configuration and imbalance details, and pairs each reason with its supported status and action. Regenerate the client before integrating this response; clients built against the initial preview contract must stop readingblocker. Refresh still checks current locks before writing. See Reservations. -
Flow responses accept command event listeners such as
payment.command. -
Transaction creation accepts unpaid payment input with no date or bank records without
transactions:mark-paid. Update and batch permissions are unchanged. -
GET /reservationsacceptsjournalChangesFromFeeIdto find a fee's pending journal changes. Applied changes and completed accounting-lock skips are excluded. -
Ownership-period linking includes unassigned journals from current child listings. Existing date boundaries and accounting locks still apply.
-
Reservation adjustments reject missing, deleted, and other-team recurring fees with
404 NOT_FOUNDbefore saving the adjustment. -
GET /teams/issuesincludes shared bank-feed health inbrokenConnectionsdetail context. Supported connections use the same assessment as account badges, including unknown evidence and account-scoped consent actions. The issue can now have warning severity. See Bank Connection Health. -
Payments are a first-class resource.
POST /transactions/{id}/paymentsrecords an offline payment or submits a Ramp ACH payment for an expense or payout, andGET /transactions/{id}/payments,GET /payments/{id},GET /payments?ids=,POST /payments/{id}/cancel, andPOST /payments/{id}/reverseread and act on them. Transactions exposebalance; statement payouts andGET /contacts/{contactId}/payoutsexposebalanceandpayments. Each payment is one cash movement withsettledAt,postingDate,allocations, and an optional whole-paymentreversal. An offline payment above the available balance while a Ramp payment is open needscancelOpenPayments: trueand otherwise fails withproviderPaymentOpen. An offline expense payment withoutaccountIdis a reimbursement and has no account.GET /transactionsitems includepaymentsandactions; provider payments includeproviderStatus,providerMethod,scheduledAt,initiatedAt, andproviderFailureCode; cancel returns itsoperation. Deleting an expense or payout also deletes its offline payments; provider payment history still blocks deletion.POST /statements/payresults includepaymentId, andproviderOperationis the first Ramp payment's operation. Removed:POST /transactions/{id}/pay, the/transactions/{id}/provider-paymentsroutes,POST /transactions/{id}/payment-returns, and theproviderPayment,providerPaymentActions, andreturnSummaryfields. See payments. -
Connections from supported services expose stored
healthin Account, AccountConnection, and Connection lists and details. The summary includes typed recovery actions and observation times; account-specific consent issues retain their feed scope. Missing, stale, or unreadable evidence returns unknown. See Stored Bank Feed Health. -
Failed reservation journals now offer accounting-lock review when current locks prevent a full refresh, including older failures without a typed cause. This changes recovery guidance without clearing unapplied changes.
-
Reservation refresh includes deferred-revenue entries for owner adjustments recognized on a different date from the shared reservation receivable.
-
Non-current reservation, recurring-fee, and statement
journalStatusvalues now include a typedreasonand supportedaction. Statement guard errors return the same values asjournalReasonandjournalAction. -
Recurring fees no longer report completed accounting-lock skips as failed calculations. The protected reservation history remains unchanged.
-
Earlier owner statements can be created, reopened, and republished while all later statements for the ownership period remain Draft. A later Review or Published statement returns
409 CONFLICTwith the blocking statement ID, start date, status, andnextAction: returnStatementsToDraft. -
The support-managed owner portal pilot adds registered custom domains and optional platform promotion in owner emails and PDFs. Support manages registrations at runtime and the portal edge delivers branding to each page; no public lookup endpoint is exposed. See Custom Owner Portal for setup requirements.
-
Reservation detail now returns
financials.totals.openingGuestReceivable. This amount identifies guest receivables carried into accounting on the day before the team's start date while remaining included infinancials.totals.guestandpayment.expected. -
GET /reservationsacceptsjournalReviewOperationIdto list distinct reservations still awaiting review after accounting locks blocked an operation's journal refreshes. Applied changes and active retries are excluded. The existing pagination, reservation detail fields, and owner access apply. See fee recovery. -
Expense CSV previews identify each missing mapping field. Ambiguous mapped dates return separate
mappingRequiredissues forissueDateandpaymentDate, including their original CSV headers in preview-record details. -
CSV preview confirmation returns an expected
409 CONFLICTwithcontext.reason: duplicateOverrideNotApplicablewhen a duplicate override is sent without an exact prior import. Confirm the preview without the override. -
Operations now includes
tax-rates:read, allowing expense forms and the read-only Tax Rates settings page to load. Tax-rate changes and deletion remain excluded. -
Statement payment preview and recording accept up to 1,000 statements. Omitted or null payout dates resolve when the submission is accepted and remain fixed during queued execution and retries. Completed responses use
200; pending responses use202withLocation. Await throughGET /operations/{id}?waitSeconds=20, then retrieve the same final response throughGET /operations/{id}/result. Payment results now separateproviderOperationfrom recording and include nullablefailure. Use the UUIDIdempotency-Keyheader for retries, including manual payments; it is required for payments above 100 statements. CSV contracts are unchanged. See bulk statement payments. -
MCP OAuth calls to
/partnerroutes now require the partner team throughteamIdor the connection'steam_idand forward it asx-team-id. Partner API-key calls remain bound to the credential tenant and omit the header. Source and target customer team IDs stay in each route's documented query or body fields. -
Operations can create and edit vendors through the new
contacts:vendors:writepermission.POST /contacts,PUT /contacts/{id}, andPOST /contacts/batchaccept either that permission for vendor-only changes or the existingcontacts:writepermission. Vendor-only access does not permit owner changes, invitations, viewer management, deletion, CSV import, or ACH setup. -
PUT /reservations/{id}no longer advertises unsupportedadjustments_updateoradjustments_deletefields. Use the dedicated adjustment resource routes to update or delete an adjustment. -
Team responses expose a required, read-only
isTestboolean based on system identities and direct Demo and Test Console partner references. See team setup. -
Audit events identify PMS imports with
source.type=integrationandsource.connectionIdwhen the action records a sync actor. Captured diffs omit unchanged fields. Identical reservation imports no longer create changes when adults and children match. Fully captured settled updates with no public changes do not create new events; existing event identities remain stable. -
PM statement JSON, CSV, and PDF routes use the dedicated
pm-statements:readandpm-statements:exportscopes. Operations (ui:operations:v1) no longer reads or exports PM statements. Team Admin, Standard Member, Read Only, and the partner and embedded statement bundles keep access. -
Owner-statement CSV and PDF exports reject explicit ownership-period selections containing a period without an owner.
400 BAD_REQUESTincludescontext.reason: ownershipPeriodsWithoutOwnersand the affectedownershipPeriodIds; no export operation is queued. -
SISS OAuth uses a short, single-use state reference valid for 15 minutes. Complete the callback as the initiating user and team. Restart the flow after cancellation, expiry, or failed completion; legacy SISS states are rejected. Apps requiring server-reference state must start at
/connections/connect;/connections/oauthrejects them and unknown app IDs before issuing state. See OAuth connections. -
Team locks now distinguish migration, deletion, and unknown freeze reasons. Confirmed deleted teams return
410 TEAM_DELETED; missing teams without deletion evidence retain404 NOT_FOUND. Consumers must handle these codes before rollout. See Errors & Retries. -
GET /recurring-fees/listing-periodsrejects malformedrecurringFeeIdfilters with400 BAD_REQUESTbefore querying listing periods. -
Transaction updates reject duplicate empty-string
uniqueRefvalues before writing. Usenullor omit the field for transactions without a reference. -
Recurring-template deletion waits for an instance creation that already locked the template, then keeps the expense and clears its template link.
-
Reservation financial rows include locks from every journal leg sharing the same payment-line ID. Fee adjustments attached to owner statements therefore appear locked on both their owner and manager rows.
-
CSV imports infer date formats from valid calendar dates across the full file and reject conflicting evidence and impossible dates. Field errors identify their mapping field and original source columns. Bank previews return projected ending balances. Bank mappings accept an optional stable transaction-reference column; same-date-and-amount matches remain valid with possible-duplicate warnings. Preview and import record endpoints accept an
issueCodefilter. See CSV imports. -
Owner-statement batch PDF ZIPs require
POST /statements/pdf/batch. Valid GET selections return422 EXPORT_REQUIRES_POSTbefore rendering. Submit the same selector with POST and download the completed operation's result file. See Statement exports. -
Draft statement list and detail reads reflect current journal totals after financial changes, without mismatch warnings against previously saved draft amounts. Non-draft statements retain their saved totals and validation.
-
Statement freshness excludes completed journal refreshes skipped by accounting locks. These skips no longer produce a statement warning or block review, publication, or payout; other outstanding refreshes still do.
-
Independently balanced reservation adjustments can post on an open date after the reservation is archived. Existing journal history and accounting locks remain unchanged.
-
Audit event app references accept string app IDs such as
airbnb. Other entity reference IDs remain UUIDs. -
Range statement exports skip unattached journal months before ownership begins, so older ledger entries do not fail the whole export.
-
Partner team deletion returns a field issue for the prohibited
force=trueflag. -
Connection deletion, archival, and settings changes return
400 BAD_REQUESTbefore removing a PMS accounting source still required by active connections without accounting dates. Use the PMS cutover flow or deactivate those connections first. -
Team deletion confirmation errors include a field issue for
confirm. -
Reservation, fee, and statement
journalStatusincludes optionalreason: journalLockedwhen all outstanding changes were skipped because of accounting locks. A later calculation timestamp does not clear unapplied work. -
Connection reads and nested bank-feed connections expose an optional
consentsnapshot with status, expiry date, and last check timestamp when the integration declares bank-data consent support. This replacessissConsent.GET /meandGET /teamexpose the persisted teamaddressCountry. See Bank Accounts and Connections. -
Team aggregates include
totalListings, counting active and inactive listings. It is available inGET /teamandGET /teams?includeAggregates=true. ExistingactiveListingscounts retain their meaning. See Team setup. -
Fees and statements with no known journal changes now report
currentinstead of a falsestalestatus. Actual pending work and locked-history failures remain visible. -
Exhausted database socket connection attempts return
503 SERVICE_UNAVAILABLEinstead of a generic500. Writes still require outcome reconciliation before retrying. -
Transaction reads explain why Cancel is unavailable for a failed or canceled ACH request. These terminal requests no longer show a waiting-for-reconciliation reason. Available payment and archive actions are unchanged.
-
MCP
/directorysupports reviewed accounting reads and confirmed record creation, updates, and deletion, including payment bookkeeping and owner statement preparation. It rejects provider payment execution, credential changes, unreviewed routes, and input switches. The custom/endpoint retains full API access. See MCP setup. -
Path, query, and body validation failures use the documented public error envelope. Pagination rejects fractional and unsafe numeric values before SQL.
-
Journal refreshes detect changed sources and postings before applying their changes. Book closing coordinates with writes already in progress.
-
Bank-balance recalculation corrects balances before the first checkpoint and processes large update sets in bounded, atomic batches. Existing stored balances change when recalculated; this release does not perform a backfill.
-
The TypeScript client now attempts writes once, including when
attemptsis greater than one. Automatic retries remain available forGET,HEAD, andOPTIONS; there is no write-retry opt-in. Reconcile writes with an uncertain outcome before sending another request. Custom response assertions still run on writes unless explicitly disabled withassertOnLastAttempt: false. -
Both
throwIfErrorforms preserve public error codes and recovery fields. Malformed or missing error envelopes throwApiClientResponseErrorwith the HTTP status. Error context is typed asunknownand requires narrowing. See Errors & Retries. -
Bank CSV previews add
result.details.balanceProjection.from, the earliest date among rows the file checks accept and do not exclude. Together withasOfit is the statement period the upload covers, read before rows are validated against the bank account. See CSV imports.
2026-10-05
- Published Versioning and deprecation. The API keeps one live version. Deprecated operations return
Deprecation,Sunset, and a noticeLink, including error responses. - Deprecated:
GET /teams/lookup. UseGET /teams?search=,GET /me/teams, orGET /partner/teamsfor your authentication type. - Deprecated: generating
GETCSV, PDF, and ZIP operations that have aPOSTcounterpart. Submit the same selection toPOST, pollGET /operations/{id}, then download the completed file. Synchronous downloads remain supported. - Deprecated:
deletedin theDELETE /bank-rules/{id}response. Readstatus: "deleted"instead. Field-only deprecations are marked in OpenAPI and do not add operation headers. - The historical
includeMatchingTransactionsbank-record query was already removed before this policy. UseincludeMatchingTransactionand readmatchingTransactionSets[]; this publication does not restore the old query. - Existing deprecated legacy metric operations and the provider-payment unlink operation also carry lifecycle headers. Use the current operations in the Reports guide; choose the metric for the value your client needs. Provider payment history remains permanent; use its documented cancel or archive action when available.
Initial notice date: 2026-10-05.
Initial Sunset: 2027-01-03, after the approved notice window of 90 days.
Removal remains subject to the policy’s contract-partner notice requirements. Publishing these dates does not bypass direct notice.
2026-09-05
- Offline
POST /transactions/{id}/paysupports expenses whose terminal Ramp bills were permanently archived, including after expense unarchive. It retains payment history and accounting locks. Terminal Unpaid resets also accept the UI's explicit null payment date and bank account. - Deposit updates containing only
lines_updatepreserve unrelated source lines and journal postings. Invoice matching no longer fails because an untouched legacy fee line would otherwise change party during a full deposit refresh. Accounting locks still protect the selected lines and their resulting postings.
2026-09-04
PUT /syncs/{id}acceptsqueued,started,completed, orfailedasstatus. Cancellation requiresPOST /syncs/{id}/cancel.- Manual
POST /transactions/{id}/paynow reads its selected GL bank account fromfundingAccountId, matching the existing client payload. The temporaryaccountIdfield is removed. - Failed Ramp expense dispatches automatically return the expense to Unpaid and refresh its journal payment entries. The failed attempt remains in provider history and Pay becomes available for retry.
- Ramp expense Cancel now holds the existing bill, deschedules its payment, and returns the expense to Unpaid. A later Pay releases and reschedules the same bill. Archive remains the separate permanent bill action.
- Owner CSV imports now create or update standalone owners when a row has no
listing.
Company Typecan determine individual or company kind, and a sourceTypecolumn cannot change the contact type from owner. - CSV resource metadata now publishes the versioned detection aliases, value maps, mapping and row requirements, fixed values, and import effects used by generic processors. Expense and deposit detection infers date format and leaves ambiguous slash dates for explicit mapping.
2026-09-03
- Public audit events now include event-time actor organization,
attemptedEntityChanges, finite lock evidence, supported recovery actions, and theembeddedsource. Blocked reservation events show only reviewed attempted fields; committed changes remain separate. - Audit collections add inclusive
occurredFromandoccurredTofilters. Contact create, update, archive, restore, delete, batch, and import activity now uses the reviewed contact field catalog. Owner-statement attachment evidence stays in the statement event that caused it. - Selected-team audit reads now use
audit:read. Team Admin and the additive Audit Log bundle grant it; Standard Member does not. Signed-in partner members can use the regional partner collection within their stored all, assigned, and direct team reach. - Added subscribable
audit.event.changedinvalidations for each new public event revision. The payload identifies the event, revision, and current detail endpoint and follows existing self and managed-team subscription scope. - Embedded sessions can receive
embed:banking:v1for the three-step Plaid connect workflow. PMS reservation and contact imports use their Connection as sync actor, identical reservation imports no longer emit activity, and request IDs are no longer derived from session or correlation headers. GET /api-tokens/permission-bundlesnow exposes every assignable API-token bundle with its stable name, display title and description, and expanded permissions. Token-management clients no longer need a hardcoded catalog.- Audit action, effect, mutation, and timeline reads now retain the stored actor name and email after the user is deleted. The actor ID was already retained.
- Partner API keys can now queue deletion of a directly managed team through
DELETE /team?confirm=true. Restricted keys requirepartner:team-deletion:v1; partner deletion cannot useforce=true, and the returned operation remains readable from the partner team after deletion. - Failed
POST /teamsrequests no longer leave discoverable global-only teams. Slugs are checked in the global registry and target region, incomplete teams remain hidden, and an interrupted create can be retried safely. - Failed CSV imports now return
csvParsingFailedfor file and header errors orcsvImportRecordFailedfor invalid data rows instead of the genericoperationFailedresult. POST /teamsnow rejects a Hostaway-managed property-manager team unlessdataRegionishostaway. Operating country can still determine currency and tax defaults, but it cannot place a Hostaway client in another region.- Expense Pay requests now post accounts payable against the selected Ramp
funding account before dispatch. Manual Mark as paid accepts the selected GL
bank
accountIdand uses it for the payment journal. - Canceled Ramp expense payments keep their existing bill, return the expense to Unpaid after confirmation, and allow another payment attempt on that bill. Payout retry behavior is unchanged.
- ACH setup rejects routing numbers with an invalid ABA checksum. Failed setup reads preserve Ramp's bank-detail rejection instead of replacing it with an unrelated Vendor-owner instruction.
- Missing or stale Ramp funding selections return the actionable validation
message
Select an available Ramp funding accountand are not reported as unexpected server failures. - Journal-relevant recurring fee updates now return one
reservation-journal-refreshoperation. Reservation, recurring fee, and owner statement reads exposejournalStatusplus the same operation while recalculation is in progress. Operation detail accepts typedact_andop_identifiers, and existing UUID operation identifiers remain valid.GET /operationsacceptsinitiatedBy=me, and operation responses identify a user or system initiator. Authenticated owners can poll embedded journal refresh operations that affect reservations in their owner access. Statement finalization, payout preview, and payout reject non-current journal scopes withJOURNAL_RECALCULATION_PENDINGafter the tenant guard is enabled. - VRI historical statement import now consolidates same-date source statements when re-listed properties map to one target listing. All derived balances and eligible lines are retained in the single target statement.
- VRI historical listing previews now separate statements owned by a listing
from statements that only reference one of its reservations. Use
importedStatementCountfor owned statements andreferencedStatementCountfor all mapping references.
2026-09-02
- Historical statement imports now classify
lineitems that reference the team'spayout_distributionaccount as signed transfers. Statement details, summaries, and stored financials therefore agree on payouts and expenses. - VRI-to-VRT preview preparation and activation now emit one durable API-owned lifecycle event per pair. The authoritative migration observer owns the verified event through the same shared tracking contract and transport.
GET /booking-channelsnow accepts eitherintegrations:readorrecurring-fees:read. Operations and recurring-fee API keys can load fee-referenced channel data without receiving broader integration access.- Breaking before permission launch: Operations now receives
calendar:readwithoutcalendar:writeorcalendar:delete. Operations users can view calendar blocks, while create, edit, and delete controls and API mutations are denied. - Read Only team members can now resolve the selected team before opening connection details, Team General, Owner Portal settings, Owner Statements settings, and Opening Balances. The role gains no write permission.
- Owner CSV resource metadata now orders identity and contact fields before reference and listing fields. First name, last name, company name, and company type are marked required; owner kind and listing are optional.
2026-09-01
- App connect and reconnect requests that exhaust the 25-second provider wait
now return
504 GATEWAY_TIMEOUTwith the connection-specific static message. The provider run may continue after the request stops waiting. The export-renderer message remains limited to export tasks. - Reservation adjustment creates without an explicit posting date now move a
locked natural date to the first open period by default. Pass
onLocked=errorto retain rejection. Reservation detail now marks inactive adjustment entries as statement-period locked when deletion would rewrite protected history. - Replacement payouts now receive their own
PAY-YYYYMM-XXXXXreference when the create request omitsuniqueRef. Ramp dispatch rejects payouts without a stored reference before provider work is queued, and transaction reads report Pay as unavailable. - Authenticated owners can now read an ACH verification operation when its primary contact target is in their server-derived owner access. Other contacts and operation types return an opaque not-found response.
- Contact payout
providerPaymentnow includes the masked destination account suffix captured for that attempt and its requested arrival date. Historical attempts remain unchanged after ACH account updates; legacy attempts without a reliable snapshot returndestinationAccountLast4: null.
2026-08-31
- Added
POST /transactions/{id}/payment-returnsfor full and partial external owner-payout returns. It creates a linked compensation, supports dry run and exact BankRecord reconciliation, and leaves the original payout active. - Transaction reads now expose return links, effective time, cumulative return status, remaining amount, compensation IDs, and direct replacement ID.
- Added atomic
POST /bank-records/actions/batchfor dry-run or apply of unmatch, exclude, and reactivate across at most 500 distinct records. - Exact-file CSV confirmation now blocks completed duplicate imports with
csvExactFileDuplicateunless the caller sends the explicit intentional-reimport override. - Ramp provider-payment Archive now leaves Returned payout accounting active. Failed and Canceled payments keep the existing accounting archive behavior.
- The banking guides now explain how published ledger templates can provide ordinary Bank Fees and Interest Income accounts. A team can use a BankRule as its repeatable interest mapping; there is no global beneficiary default.
- The banking and transaction guides now document the existing channel-held security-deposit flow, open-period release and forfeiture adjustments, and the existing base-reservation refund and manual-adjustment flow. They also reserve exclusion for non-cash rows and describe the existing transfer behavior: one Transaction with one bank-record match on each bank-account side.
- Breaking before release: bank-record reads no longer accept
includeMatchingTransactionsor returnmatchingTransactions[]. Request exact proposals withincludeMatchingTransaction=true, set the optionalmatchingTransactionDaysOffset, and read every proposal frommatchingTransactionSets[]. - Projection-backed statement and owner-summary reads now return a freshly computed result when only the derived-cache write exhausts a database connection. Stale projections remain unusable, the persistence failure stays visible in monitoring, and other database failures retain their existing response behavior.
2026-08-30
POST /connections/{id}/connectnow derives the app from the existing team-owned connection whenappIdand signed callback state are omitted. UnscopedPOST /connections/connectrequests still require one of them.- High-volume team deletion now commits bounded table and journal batches and checkpoints each regional cleanup group. Retries resume from the last committed group, while the internal job status exposes that progress. The deletion task and stale-job lease now support a 12-hour safety window.
2026-08-29
- Account deletion now recognizes BankRule selectors and posting lines plus
recurring-fee formula inputs as dependencies. BankRule and inactive-fee
references can use
onLocked=archive; every account used by an active fee remains protected from both deletion and archive. - Reservation journal refreshes now propagate database deadlocks and lock timeouts to their transaction owner so a failed transaction is not treated as a successful partial refresh.
- Ramp ACH setup now returns
RAMP_VENDOR_OWNER_REQUIREDwith the connection setting destination when configuration is missing. Existing ownerless Ramp vendors return manual remediation in their failed ACH state. Failed payment requests that never created a Ramp bill archive accounting locally, while an existing payout is booked to the selected Ramp funding account before its first provider attempt. - Breaking before release: public HTTP and batch errors now use one finite
uppercase code registry. Regional misroutes return an exact
MISDIRECTED_REQUESTcontract, migration freezes exposeteamId, and unknown failures use static messages. - Breaking before release: connection prompts remain HTTP 200 with the finite
lower-camel-case codes
extensionRequired,formRequired,linkRequired, andoauthRequired. Provider failures now return502 BAD_GATEWAY; the 25-second awaited limit returns504 GATEWAY_TIMEOUT. The former HTTP 200errorresponse is removed. - Operation, CSV-import, and team issue schemas now publish finite code sets. Batch item repository aliases are mapped to their public HTTP equivalents.
- Breaking before release: CSV preview and import failures now use typed
camel-case recovery codes. The generic
csvImportFailedandoperation_failedvalues are replaced by phase-specific codes andoperationFailed. Row issue codes are now a closed CSV-owned enum; resource validation failures retain their original issue in structured details. The API validates every reviewed plan chunk before resource writes and exposes import outcomes only after their manifest is finalized. - Shared full-date range filters now normalize the documented legacy comma
separator before route execution. Account reconciliation summaries therefore
accept both
start...endandstart,endwithout passing a combined value to database date serialization. - Re-inviting a team member now returns
409 CONFLICTif the membership changes during invitation preparation. Same-role re-invites continue to preserve the existing identity and explicit permission bundles.
2026-08-28
- Breaking before release: the forwarding gateway now requires
POST /teamsGeneral Ledger requests to provide an explicitdataRegionofus,eu,ap, orhostaway. A strict regional worker may omit the region to use its own cell and returns421 MISDIRECTED_REQUESTfor an explicit mismatch. Crunchy GL placement returns400 BAD_REQUESTbefore provisioning. Because General Ledger is the default, these rules also apply whengeneralLedgeris omitted. Non-General Ledger teams remain restricted to Crunchy, and an explicitly selected non-Crunchy region now returns400 BAD_REQUESTbefore provisioning. Their omitted region defaults to Crunchy; direct regional workers return421 MISDIRECTED_REQUESTwith that placement. GET /statements/pdfnow renders one owner statement PDF directly and returns its signed URL without creating an operation. The queuedPOST /statements/pdfremains available.- All 32 generated artifact paths now expose the same delivery split. GET
awaits admitted work and returns
{ url, expiresAt }; POST queues a durable export and returns the standard operation acknowledgement. Oversized GET requests returnEXPORT_REQUIRES_POST. - CSV, range, batch, PDF, and ZIP renderers run in Trigger and upload directly to regional storage. The public request Worker materializes only one explicitly selected statement PDF, and durable exports no longer invoke a public GET or copy through a transient artifact.
GET /statements/by-period/csvnow waits for its single-statement Trigger renderer to upload directly into regional transit storage, then returns only the signed URL metadata.- Trust Reconciliation PDF GETs now wait for rendering outside the regional Worker and return signed transit URLs. Matching POST routes create durable export operations, avoiding large report data and PDF buffers in Worker memory.
- Temporary export links now expose an absolute
expiresAtmatching their seven-day lifetime. R2 CSV and download metadata is aligned across temporary and durable artifacts. - Queued export configuration failures now retry within a fixed limit and
become terminal failed operations instead of remaining
running. - Completed export operation resources now include a stable authenticated file download URL. Reservation and transaction journal refreshes both run inline for one explicit ID and queue for multiple IDs or an omitted selection.
- Webhook test and replay responses now nest the standard operation acknowledgement instead of merging its fields into the delivery response.
2026-08-27
- Trust Reconciliation JSON, CSV, and PDF requests now validate
listingIdas a comma-separated list of listing UUIDs. Malformed values return400 BAD_REQUESTbefore report work starts. - CSV upload, inspection, preview, confirmation, and import failures now expose
stable recovery reasons in OpenAPI. Validation details are returned in
issues; asynchronous source failures retain their specific failure code. Expired results use410 GONE, and oversized requests use413 PAYLOAD_TOO_LARGE. Preview and import status now use the shared operation lifecycle and failure object, and acknowledgements no longer expose the internal CSV Connection. - Internal operators can prepare one disconnected VRT preview with
POST /internal/teams/{teamId}/vri-vrt-preview, selecting the source users who receive access. Activation usesPOST /internal/teams/{teamId}/vri-vrt-preview/activate?confirm=trueand preserves preview configuration. Credentials do not move: the customer reconnects the PMS on the VRT team. Repeating activation repairs missing lifecycle effects. GET /menow identifies prepared VRI-to-VRT preview relationships for the selected source or target team, including the snapshot timestamp and preview lifecycle. After activation, the selected source user receives the active target link while the target no longer presents itself as a preview.- When test or demo teams move between Hyperline Development and billing through VRPlatform or Ximplifi in Hyperline Production, the previous environment is now cleaned up before the target subscription is created. Moving to an external billing partner cancels the subscription but retains the customer for a later return; VRPlatform-to-Ximplifi changes also retain the Production customer.
- Forced team deletion now removes statement-period-locked journal data and processes at most five team deletions concurrently. It also locks the target team and accounts before child cleanup, commits that cleanup, and performs a final connection pass in the locked transaction that removes the team. Tenant-owned connections now also cascade with team deletion, so a remaining connection cannot block the confirmed removal. Reservation mapping rows likewise cascade with an account that has already passed deletion safety checks. The active-subscription safeguard remains enforced.
- Effective permissions and permission bundles returned by
GET /me,GET /team/members, andGET /partner/membersare now enum-backed in the OpenAPI contract. Generated API client consumers receive literal unions and the exportedApiScopeandUiPermissionBundleNametypes instead of untyped strings.
2026-08-26
- Completing team onboarding no longer initializes or changes subscription status, billing plan, or cancellation date. Missing billing state remains visible until the Hyperline workflow reconciles it.
- Breaking before release: the Stripe payout reconciliation CSV preset now
requires only the payout bank account. Reported categories and fees resolve
through
stripe_<name>transaction line mappings; configured application IDs, fee rates, fixed fees, and category account IDs were removed. GET /reservationsandGET /reservations/csvnow acceptdateField=cancelledAt. The existing single-date and inclusivestart...endsyntax filters on the reservation cancellation timestamp and composes with other reservation filters.- Listing-parent dry runs and mutations now return the structured
lockedAccountingDataandlistingGroupHistoryMigrationcontext when a zero-value owner revenue or expense row predates the parent statement's open date. The blocked operation still leaves listing, ownership, statement, and journal state unchanged. - Explicit
POST /reservations/journal-entriesID selections now finish inline and atomically.totalcounts completed refreshes; a missing, locked, or failed reservation returns structured per-ID details and rolls back the selected set. Omittingidsretains queued portfolio refresh behavior. - Transaction create and batch-item requests now reject a missing description before execution for every non-transfer type. Transfers may still use the server-generated bank-context description.
POST /statementsnow returns409 CONFLICTwith structured identity details when another statement already occupies the requested listing and effective start date. Reload current statements instead of retrying the create as a transient server failure.- Breaking before permission launch: the dashboard role catalog now contains Team Admin, Standard Member, Operations, and Read Only. Accountant, Owner Statements, and Reports Viewer were removed; stored shadow assignments are migrated to their replacements.
- Replaced broad transaction writes with independent expense, deposit,
transfer, and payout write scopes. Operations receives only
transactions:expenses:write; broad roles receive all four. Type changes require both scopes, mixed batches are checked before mutation, and payment state still needs the separate payment permission. - Operations can now read calendar blocks, recurring fees, and statement layouts and can create or edit expenses. It still has no calendar mutation, report, bank, reconciliation, non-expense write, transaction delete, or payout access.
- Read Only now includes matching exports for listings, contacts, reservations, transactions, accounts, statements, reports, and bank records.
- Standard Member no longer includes owner or vendor ACH setup. Team Admin includes ACH setup, while ACH execution remains an additive ACH Payer grant.
2026-08-25
- Ramp payout Cancel and Archive now return the standard asynchronous
operationIdcontract. PollGET /operations/{id}for theprovider-payment-recoverylifecycle, then refresh the transaction and provider-payment history for the canonical outcome. The status read requiresoperations:read; it no longer requires access to integration sync details. - Ramp payout recovery cancellation normalizes to the public terminal
failedstate. Provider-specific cancellation details remain internal. - Banking documentation now starts with a short workflow index. Use Reconcile Bank Records for transaction and BankRule suggestions, explanations, ambiguity, automatic execution, and multi-booking payouts. Use Bank Accounts and Connections for Plaid, feed mapping, imports, ACH readiness, and recovery.
2026-08-24
- Audit Events now cover major team and access settings, accounting rules and mappings, recurring configuration, integration mappings, API tokens, webhooks, and templates. Protected values appear only as redacted field names. Coverage is forward-only and does not backfill earlier changes.
- Breaking: Ramp payouts can be submitted only once. Provider payment history cannot be unlinked, and Failed, Returned, or Canceled payouts must be archived before creating a replacement payout. Vendor-expense retry behavior is unchanged.
- Transaction reads now include
providerPaymentActionswith server-owned availability and disabled reasons. Added provider-first Ramp payout Cancel and Archive operations plus explicitrecreatedFromTransactionIdlinks. - Ramp payout fields and hard delete stay locked after every ACH attempt.
Same-bill retries made directly in Ramp remain in attempt history, and
Returned now requires Transfer API
RETURNED_BY_RDFIevidence. - Bank-record transaction suggestions now include typed match reasons,
matchingDate, ambiguity, and related listing and reservation context. Bank-rule suggestions include direction, account-scope, and per-condition outcomes so clients can explain why each suggestion exists. - Bank-record reads now accept
includeMatchingTransactionand a separatematchingTransactionDaysOffsetthat defaults to seven days. The orderedmatchingTransactionSets[]items contain one to five eligible transactions. Bank-rule suggestions remain separate inmatchingRules[]; multi-member discovery uses an internal seven-day maximum, considers at most 20 eligible nearby candidates, and returns at most 10 sets. The overloadedincludeMatchingTransactionsquery remains supported but is deprecated. - Rules remain ordered deterministically, but same-mode rules with the same
priority are marked ambiguous. If multiple automatic rules share the highest
matching priority during bank-record ingest, the record remains unmatched
and the batch returns a
CONFLICTissue withcontext.bankRuleIds. - Reconciliation suggestions still require an exact total. This change does not add amount tolerances, adjustments, or multi-bank-record matching.
- The CSV workflow guide is now named Import Data from CSV.
- Breaking before release: normalized the CSV import contract. Preview
requests now use
selection, operation responses are bare objects, lifecycle status usesstartedandcanceled, leaf paths use{id}, and import status and records use/csv-imports/{id}. - CSV upload references now retain their resource. Clients send
resourceonly toPOST /csv-imports/uploads; upload, inspection, and preview requests do not resend it. - CSV record pages now accept up to 250 records and use the shared opaque cursor contract. Preview confirmation no longer accepts a JSON body.
GET /teams?excludeTestTeams=truenow excludes teams explicitly marked for testing in addition to the demo tenant and its accounting or billing teams.- Partial
PUT /recurring-fees/{id}requests now preserve an existing percentage rate type whenrateTypeis omitted. New fee definitions still default an omitted rate type to Flat Rate. - Bank-rule create and preview requests now return validation errors when an expense omits its vendor or a deposit supplies expense-only fields.
- Ownership-period create and update requests now reject a repeated owner contact during request validation.
POST /team/members/invitenow returns the documented400 BAD_REQUESTconflict when the selected team already contains an owner membership for the email. The request creates no invite token and sends no email.
2026-08-22
- Management Commission tax now posts separately for included or excluded tax behavior when the tax rate has a payable account. If its optional debit tax override is omitted, the fee's debit account is used for that side.
PUT /listings/{id}/opening-balancecan clear one existing listing opening balance to zero across a statement-period boundary. Non-zero changes, books closing, and journal entries directly attached to an owner statement remain locked.- Added public
GET /countriesandGET /countries/{countryCode}endpoints for pre-provisioning country selection. Responses include the English name, two-letter country code, applicable currencies, and assignedus,eu, orapdata region.
2026-08-21
- Added
GET /partner/teams, a control-plane team catalog that lists the authenticated Partner's managed teams across all data regions. vrt_team_lookupnow searches globally throughGET /me/teamsfor OAuth orGET /partner/teamsfor Partner API tokens. Callers no longer select a regional partition before finding a team.- Deprecated
GET /teams/lookup. UseGET /teams?search=for rich regional inventory,/me/teamsfor OAuth discovery, or/partner/teamsfor Partner API token discovery.
2026-08-20
- Breaking: the Connection-scoped CSV import API now supports fixed templates
only. Its reviewed upload, preview, confirmation, and asynchronous status
routes were removed; reviewed imports use the top-level
/csv-importsworkflow.
2026-08-19
- Ramp owner ACH setup now treats contact address and phone as optional. New vendors use the connected Ramp business location when needed and no longer inherit another vendor's owner or location. Structured Ramp vendor validation failures are returned as the setup failure reason when available.
- Contact ACH payment methods now include nullable
failureReasondetail for failed setup attempts. The value comes from safe sync/change status and never exposes raw provider bank-response bodies. POST /transactions/{id}/provider-payments/{providerPaymentId}/cancelrequests Ramp cancellation for a materialized payment that has not initiated. Ramp remains authoritative, while the VRT payout accounting and payment link remain intact.- Email team-member invitations now reuse their prepared identity and link after authorization commits instead of repeating identity and membership preparation. Delivery failures and copy-link behavior are unchanged.
- Ramp
providerPaymentresponses now include provider status detail, payment method and reference, the submitted arrival target, and Ramp-returned scheduled-initiation, initiated, and completed timestamps. Ramp payment events are refreshed from the current bill and cannot regress a processing attempt back to requested. - New and existing teams now deny partner-derived
ach-payments:executeby default. Existing denial policies are preserved, direct memberships remain unaffected, and an authorized direct manager can explicitly remove the ACH denial for a team. - Guest Balances now retains the positive accounts-receivable reversal from a zero-dollar co-host clearing transaction. The reversal can offset the reservation's earlier payment, so fully cleared reservations no longer appear as payment-only credits.
- Authenticated team-scoped writes return HTTP
423with codeTEAM_MIGRATION_FROZENwhile a controlled regional migration is copying and verifying that team's data. Reads remain available; clients should refresh team discovery after the freeze to obtain the new regional API base URL. - Schema-less DELETE endpoints accept an empty body even when a client sends
Content-Type: application/json. DELETE endpoints that declare a body keep validating their JSON schema. - Targeted reservation fee, adjustment, and A/R journal refreshes now return
the same structured
journalEntrieslock as full refreshes when applying only unlocked changes would split an otherwise balanced journal group. - PMS line-mapping initialization now proves multi-team line-type usage with bounded indexed lookups instead of scanning every matching reservation line.
2026-08-18
POST /statements/pay/previewnow returns awarningsarray. An explicit positive amount above the statement's remaining available balance adds the non-blockingamountExceedsAvailableBalancewarning with structuredavailableBalanceandexcessAmount; overpayment remains allowed.POST /teamsacceptsmigratedFromTenantIdto atomically link a newly created GL team to the non-GL VRI team it replaces. The source must be managed by the same partner; invalid links are rejected before team creation.- Historical statement lines can now link an existing reservation with
matchReservationConfirmationCode. Matching is scoped to the statement listing; unknown, ambiguous, or conflicting references are rejected before import. - Guest Balances
datefilters now accept a single date or shared date-range syntax. A range uses its inclusive end as the report's as-of date; malformed ranges return HTTP 400 before report queries run.
2026-08-17
- Long-running routes now return
{ operationId, type, status: "queued", resource }.GET /operationsadds requester-scoped history, filters, retained status counts, and cursor pagination. - Supported resource reads include retained operation counts plus actionable queued, running, and latest unresolved failed work. Team lists retain this state across refresh without per-row operation requests.
- Calendar updates, connection sync and extract, CSV preview/import, payment dispatch, migration, journal batches, recurring runs, and other long work now use the same four-state operation lifecycle.
- Generated CSV, PDF, and ZIP routes now have
POSTinitiation and a durable file result. Existing generatingGETroutes are deprecated compatibility endpoints. Download routes remain synchronous. - CSV preview and import status now read persisted VRPlatform results instead of Trigger.dev. Public operation responses never expose runtime IDs, effects, attempts, child progress, or provider details.
- Team deletion now retains its audit action through destructive cleanup, so authorized deletion runs can persist their final mutation and follow-up work after the selected team row is gone.
- Bank-record list and CSV-export filters now validate every UUID in
ids,accountId, andaccountConnectionId. Malformed values return HTTP 400 instead of a database error. - Calendar block and unblock failures no longer expose or classify
PMS-specific messages in the API. Integrations map recognized denials to the
stable
calendarRequestRejectedreason; unknown failures remain server errors and are reported by the integration runtime.
2026-08-16
-
Asynchronous operation status is now read from the top-level
GET /operations/{id}endpoint. The short-lived/partner/operations/{id}path was removed before wider adoption. -
Operation responses now include a stable public
type. Reads remain scoped to the tenant that initiated the operation, not a target entity named in the request, and internal payloads, errors, effects, and runtime ids remain private. -
Team deletion acknowledgments now return
operationIdandstatus=queued. The same operation resource reports the provider-neutralteam-deletetype; Trigger run IDs and destructive-job diagnostics remain internal. -
Concurrent removal, demotion, or permission replacement can no longer remove every direct access manager from a team after UI Permission Roles is enabled.
-
Re-inviting an existing member with the same legacy role now preserves all assigned dashboard roles. Requesting a different role replaces the complete assignment with that role's compatibility defaults before retryable email delivery begins. A delivery error does not roll back that committed change.
-
UI Permission Roles and Operations Accounting feature changes now invalidate every affected authorization cache, including feature deletion.
-
Authorization mutations now clear affected cached access before returning, and delayed membership projection events cannot restore an older role or dashboard-role assignment after a newer revision was applied.
-
Mutations that use the asynchronous operation contract return
{ operationId, type, status: "queued", resource }. PollGET /operations/{id}for the provider-neutraltype, queued/running/completed/failed status, and lifecycle timestamps. Reads are scoped to the tenant that initiated the operation; payloads, errors, effects, and runtime ids remain private. See Asynchronous operations. -
Concurrent removal, demotion, or permission replacement can no longer remove every direct access manager from a team after UI Permission Roles is enabled.
-
Re-inviting an existing member with the same legacy role now preserves all assigned dashboard roles. Requesting a different role replaces the complete assignment with that role's compatibility defaults before retryable email delivery begins. A delivery error does not roll back that committed change.
-
UI Permission Roles and Operations Accounting feature changes now invalidate every affected authorization cache, including feature deletion.
-
Authorization mutations now clear affected cached access before returning, and delayed membership projection events cannot restore an older role or dashboard-role assignment after a newer revision was applied.
2026-08-15
- VRI-to-VRT historical statements now select ownership periods only from the statement's mapped target listing. Reservations referenced by a statement can remain on another mapped listing without redirecting statement ownership.
- Historical account previews now collapse lines onto their preserved structured account paths. Optional description rules are restricted to broad paths, and reservation reuse checks PostgreSQL's real deduplication identity before inserting rows.
2026-08-14
-
Current early-access Partner bundles now grant their complete domain workflows, including deletes, exports, journal posting, statement lifecycle, account and tax-rate maintenance, bank cleanup, team configuration, and partner-member management. Existing managed keys derive the expanded scopes from their bundles without replacement. Payment recording, ACH execution, credential administration, and team partner-deny policy remain separate.
-
partner:general-ledger:v1now grantsbooks:closeforPUT /team/books-closing. Managed Partner-token authorization derives effective scopes from its bundles, so existing keys with this bundle gain the capability without replacement. -
VRI-to-VRT historical import reruns now preserve imported reservations that are referenced by live journal entries and update their corrected listing mapping in place. Existing payout and deposit links remain attached.
-
UI Permission Roles can now be enabled by team and canaried by user. Until enabled, interactive users keep legacy authorization while migrations and every membership writer maintain the new role-bundle data in parallel.
-
Existing and newly created legacy
usermemberships receive Accountant. Team Admin, Partner Admin, and ACH Payer remain separate additive roles; Standard Member is available only as an explicit new assignment. -
Bundle and partner-policy mutations return
403for legacy-mode actors. Last-access-manager protection is enabled with bundle authorization and a flag rollback restores the earlier member-management behavior. -
Team partner-deny policy affects partner API keys and embedded sessions only after full-team enablement. Partial user canaries do not change integration access.
-
Ownership-period journal attribution retries now finish without changing journals when their target period was deleted after the original mutation. Current ownership and all journal locks remain authoritative.
-
VRI-to-VRT historical listing previews now include listings used by reservations referenced on importable statements. Imports resolve those targets before writing and return a user error for any missing target instead of a database foreign-key failure.
-
Recurring-fee reservation refreshes now update list-level manager, owner, tax, and payment totals in the same transaction as the fee journal. List and detail totals remain aligned when a fee formula includes deductions such as channel commissions.
2026-08-13
- Reservation detail, list, filters, and owner-statement payment projections now keep the full guest charge when co-host A/R activity meets or exceeds it. Excess payment remains an overpaid credit instead of being compared with only manager revenue; partial co-host payouts retain manager-only collectibility.
POST /transactions/batchnow reuses an existing transaction when itssourceIdmatches, even if the submitteduniqueRefchanged. Duplicate or concurrentsourceIdsubmissions return an item-level issue instead of a raw database constraint error.- Posting-dated recurring-fee adjustments now preserve a dependent fee's complete historical group when any row is attached to an owner statement. The new adjustment still posts its balanced open-period correction; fully editable dependent fees continue to recalculate transitively.
POST /account-connections/batchnow returns item-level issues for conflicting create or update groups without leaving the request transaction unusable. The failed group is fully rolled back and later batch processing can continue; no partial group is persisted.POSTandDELETE /calendar-blocksnow return a 400 user rejection when the provider says that a property is muted. Superseded on August 17 by the provider-neutral integration-owned classification above.- Guest Balances now preserves the A/R relief leg of a zero-dollar co-host reclassification when the reservation charge is on accounts receivable. Paid channel-collect reservations no longer appear with their full charge and zero payments; zero-charge phantom co-host credits remain excluded.
2026-08-12
- Default General Ledger initialization now selects a published template by managing partner rather than data region. Teams inherit the VRPlatform global template only when their partner has no template. Existing but unpublished partner templates fail setup instead of silently inheriting global settings.
- Centrally managed reservation-line defaults use the same partner or global scope as the selected General Ledger template. Existing team-owned mappings and exclusions remain unchanged.
- An explicit mutation of an independently balanced reservation adjustment can now refresh its targeted journal group while the parent reservation remains GL-inactive. Reservation status changes continue to inactivate the complete reservation journal.
- Owner statement layout
otherSectionsnow keep reservation adjustments with different source line IDs as separate rows. Each row retains its own description, account category, and amount; net-revenue reservation aggregation is unchanged. - Guest Balances now excludes reservation-scoped zero-dollar co-host reclassifications when the reservation charge is outside accounts receivable. An A/R leg paired with an equal Co-Host Payouts offset no longer appears as a guest payment or creates a phantom credit balance.
- Posting-dated reservation adjustments now preserve statement-attached historical recurring-fee rows. The adjustment and its line-linked fee effect post on the selected open date without normalizing older fee rounding; recurring-fee adjustments still refresh dependent fee definitions.
PUT /listings/{id}/parentcan now group a child with no published statement history when its current in-review statement matches a published parent statement by dates and currency. Published child history and non-zero pre-close activity remain blocked.- Plaid configuration and
POST /accounts/{id}/banking-initcan now create an explicit opening balance before the team selects its statement start date. Regular transaction writes still require the accounting start date. - Bank-record CSV previews now accept null unused mapping columns from completed task output. Signed mappings can leave debit and credit null, debit/credit mappings can leave their signed source null, and clients can submit the reviewed mapping unchanged when confirming the import.
GET /reports/journal-entriesand its CSV export now return an empty result when a non-emptyentityTypesselection maps to no supported journal relation. UI empty-state display values can no longer produce invalid SQL.- Ramp connection now automatically activates the unconfigured
pushRampAccountslistener and starts a full Category synchronization whenautoExtractis enabled or omitted. Reconnect preserves an explicitly inactive listener. Payment preview can reportrampCategoryAccountNotReadyonly while that asynchronous push is pending or after it fails. - Concurrent
POST /statementsrequests for the same ownership period are now serialized before financial detail is calculated. A retry reuses the committed statement and journal attachments instead of applying stale empty financials. UsePUT /statements/{id}when changing status. - Reservation journal refresh now returns a structured
journalEntrieslock when changing only unlocked rows would split a balanced posting group. The refresh writes nothing; genuine journal calculation imbalances remain internal errors. - Scheduled recurring expenses no longer retry owner-side occurrences that fall inside an inactive listing ownership period. No transaction is created for that date; interactive creates and template runs remain rejected.
- Background connection, line-mapping, recurring-fee, recognition, and tax-rate changes no longer schedule journal refreshes for legacy reservations missing their canonical listing link. Explicit reservation refresh still reports the invalid data so it can be repaired rather than posted to an inferred listing.
2026-08-11
-
Added additive dashboard roles backed by versioned
ui:*permission bundles. Team Admin handles access, settings, API keys, integrations, and ACH setup but does not execute ACH; ACH Payer is a separate role. -
GET /team/permission-bundlesandGET /team/permissionsexpose stable string names for role selectors.PUT /team/members/{userId}/permission-bundlesreplaces a member's roles, andGET /mereturns assigned bundles plus effective permissions. -
Added
GETandPUT /team/partner-permission-policy. A selected team can deny primitive permissions to partner-derived dashboard sessions, API keys, and embedded sessions without restricting direct team members. -
Existing admins receive Team Admin; partner-team admins also receive Partner Admin; existing users receive Accountant. Operations covers expense editing and owner-statement publishing without bank management, reconciliation, or general reports. Accountant retains broad day-to-day accounting access with read-only settings. V1 roles and partner policy are team-wide, not bank-account-specific.
-
Recurring-fee adjustment mutations now recalculate chained formula fees in the same targeted journal refresh. For example, changing a cleaning-fee adjustment updates Management Commission when its formula reads the cleaning posting account, while unrelated recurring fees remain unchanged.
-
Recurring-fee listing-period writes now preserve inheritance when their rate equals the definition default. Changing a definition default carries those periods to the new amount and refreshes unlocked reservation journals, while distinct listing-specific overrides remain fixed.
-
DELETE /statements/{id}now rejects statements referenced by payout transaction lines, including requests withonLocked=unlockAndDelete. UsePUT /statements/{id}for lifecycle transitions so the statement ID and payout association remain stable. -
Grouped owner statement list and detail now use only the parent listing's authoritative opening balance. Legacy child balances no longer inflate the first statement after grouping.
-
PUT /listings/{id}/parentnow rejects a child with a non-zero opening balance, including during dry-run. Clear the child balance before grouping, then set the opening balance on the group parent. -
Reservation journal refresh now orders formula-dependent recurring fees only across overlapping listing periods for the same listing. Adjacent historical and future fee definitions no longer create false cycles that can calculate a management commission before its channel-fee deduction. Deployments do not rewrite existing journals; refresh affected, unlocked reservations to apply the corrected calculation.
2026-08-10
PUT /statements/{id}now treats the current status as an idempotent read, and transitions betweeninReviewandpublishedpreserve their existing journal attachments. A current projection avoids accounting writes; a stale projection refreshes without detaching entries. A repeated published request can still request email delivery.- Owner statement payout preview and execution now validate the requested date
against the listing's current statement-period lock. Preview returns
statementPeriodLockedwith structuredopenFrom, and execution creates no payout transaction while locked. SelectopenFromor a later date and preview again. - Reservation detail, list, paid-status filters, and owner-statement rows now
recognize an owner-direct co-host reclass that completes the remaining guest
balance after an earlier deposit.
payment.expectedandpayment.receivedremain the full guest charge when their active A/R total settles it; genuine co-host payouts retain manager-only collectibility. - Reservations spanning the accounting start now apply inactive pre-start owner adjustments to opening A/R and its matching opening-balance assignment. Reversing imported charges before the start date and replacing them after it no longer stacks both amounts into the guest total.
POST /teamsnow allocates generated slugs from the global tenant registry and validates explicit slugs there before regional creation. A name already used in another region receives the next available suffix instead of failing provisioning after email verification.- Plaid Link sessions for Hostaway-region teams now select the
hostawayDashboard customization with its required United States country scope and theHostawayclient name. This applies to create, update, and replace flows; all other regions use theVRPlatformclient name and default United States and Canada customization. POST /connections/{id}/csv-importnow validates importers against canonical integration capabilities. A stale regional app projection no longer rejects a supported Booking.com payout CSV, and invalid importer requests are rejected before import state or transit files are created.- Breaking: payment authorization now separates accounting records from
external money movement.
transactions:payis replaced bytransactions:mark-paid, andstatements:payis replaced bystatements:record-payout. provider: "ramp"onPOST /transactions/{id}/payandPOST /statements/payadditionally requiresach-payments:execute. Unlinking a Ramp provider attempt requires that scope plustransactions:mark-paid.provider: nullnever requires the ACH scope.POST /contacts/{id}/payment-methods/achnow requires the isolatedach-payment-methods:writescope instead ofcontacts:write.- Added
partner:payments:v1for read and record-only payment workflows and additivepartner:ach-payments:v1for ACH setup and execution. Restricted keys must combine both bundles to trigger Ramp payments. GET /apps,GET /apps/{id},GET /connections, andGET /teamnow use canonical integration capability metadata. Stale regional metadata no longer hides supported Hostaway reservation or Stripe payout Fetch actions; storedsync: "none"capabilities remain excluded.- Ramp statement payout preview and execution now require current
chart-of-accounts synchronization evidence for the
payout_distributionCategory. Preview returnsrampCategoryAccountNotReady, and execution creates no payout until a newer full Ramp account push or exact account-event push completes. POST /transactions/{id}/payapplies the same check to every outbound Ramp bill Category and returns422before creating a provider-payment request. Existing Ramp connections without identifiable sync evidence must run the Ramp chart-of-accounts flow once before their next Ramp payment.
2026-08-06
- Reservation list and detail responses now share one materialized issue
snapshot and expose
issuesComputedAtplusissuesStale. A stale response keeps the prior issue set while a bounded background evaluation runs; clients should re-read until freshness advances and must not recalculate issues. - Corrected Airbnb extractor metadata so Payout is the visible ranged
capability, Reservation remains hidden but available to supported
connections, and disabled Listing, Metric, and Review capabilities remain
hidden.
GET /appsandGET /apps/{id}return only validatedfullandcursorcapabilities; storednonevalues are not client actions. - Owner statement reads now invalidate when the team's
payout_distributionaccount assignment changes. Selected manual or Ramp bank accounts remain funding accounts only and do not need that assignment; their owner distribution entries continue to use the separately assigned payout account.
2026-08-04
GET /reports/trial-balanceand/reports/trial-balance/csvnow validate every comma-separatedclassificationvalue at the request boundary. The case-insensitive supported values areasset,liability,revenue, andexpense; unsupported values return400 BAD_REQUESTwith a query issue.POST /reservations/{reservationId}/adjustmentsdocumentation now states that active bookings require eithertypeorrecurringFeeId. A selectedaccountIdalone does not classify the line; canceled bookings remain exempt from this existing rule.
2026-08-01
PUT /transactions/{transactionId}/lines/{id}now permits a reconciled deposit line to move between reservation and non-reservation items when the bank match is its only lock. The bank-record link is preserved. Source and destination saved statement periods, books closing, and an attached owner statement still reject the move; locks on unrelated lines do not.GET /teams/lookupcan now authenticate an interactive bearer user before a team is selected, withoutx-team-id. The response remains limited to teams the user can access in the selected regional partition. UseGET /teams?search=for general team-list search.
2026-07-31
- Breaking: team responses now expose the derived lifecycle state as
status; the separateeffectiveStatusfield was removed.GET /teamsandGET /teams/lookupapply the same derived semantics to thestatusfilter, and/partner/billinguses the same contract for its team rows.capabilities.activatereports whether activation can be requested, andPUT /teamreturns400 BAD_REQUESTwith structured disabled reasons when the team cannot be activated. Deactivation remains available. - Flow contact mapping creates and updates now reject unavailable contacts
with
400 BAD_REQUESTandcontext.contactIdbefore persistence. Refresh stale contact selections before retrying. POST /teamsnow rejects adefaultCurrencyabsent from the target region's supported currency catalog with400 BAD_REQUESTandcontext.defaultCurrency, before creating any team data.- The audit events API is marked work in progress: its contract is not yet frozen and can change in ways that are not backward compatible. Read audit events on demand rather than mirroring them into your own store, and contact us if you need a local copy. See Audit Events.
- Added the generated Audit Catalog: every public audit
subject with its field paths, value types, and entity-reference targets,
plus the action-code format. The
Audit Events guide now leads with reading
history directly and documents the
429rate-limit response. - Breaking: statement payment is now per owner.
POST /statements/payrequires one selection (contactId,provider,bankAccountId) per ownership member;provideris the sole ACH trigger andprovider: nullnever dispatches, even on a Ramp-backed bank account. Forprovider: "ramp"the selected bank account's Ramp account connection is the funding account; the former statement-levelprovider,bankAccountId,fundingAccountId, andselectionsfields were removed. Results now associate every payout transaction and dispatchsyncIdwith its owner contact and add arecordedstatus for book-only payments. - Breaking:
POST /statements/{id}/paywas removed. Pay a single statement throughPOST /statements/paywith one statement entry; per-statementamountcovers additional partial payments and owner receipts. - Breaking:
POST /statements/pay/previewnow accepts the per-ownerstatementsshape and returns authoritative owner allocations after rounding, default bank accounts, ACH readiness, and typed blockers. Omitpayoutsto receive server-computed defaults. GET /contactssearch now matches secondary emails on linked owner users. ContactaccessandGET /contacts/{id}/viewersresponses now includesecondaryEmails.- Audit-event collection cursors now preserve PostgreSQL's full timestamp
precision, preventing duplicate ascending pages and skipped descending rows
when a projection batch shares one timestamp. Cursors issued before this
repair return
400; restart the walk without the old cursor. - Audit-event requests with
updatedFrombefore the retained-history watermark now return the same410andearliestAvailableAtrecovery context as an expired cursor.
2026-07-30
- Breaking pre-consumer change: public audit-event collections now expose
typed entity values and
accountingImpact.netEffect. The formeraffected,changedDimensions,postingChanges, andinclude=postingChangescontracts were removed. GET /audit/events/{id}now returns revision-bound, paginatedentryChangeswith observed journal-entry identity, typed snapshots, and safe cause attribution. Continue withentryCursor; restart from page one after409 AUDIT_EVENT_REVISED.- Audit-event collections add
hasChanges,changedEntityType, andchangedPathfilters.accountIdnow matches observed before and after entry sides even when the account/currency net effect is zero. - Active team administrators can read their team's public audit events. Ordinary members, owners, and embedded sessions remain denied. Recurring fees, bank records, and account connections are now reviewed root entities.
GET /teams/resolveno longer returnsbillingCustomerId. Integrations that need the regional Hyperline customer ID must callGET /teamat the returnedapiBaseUrl.
2026-07-29
POST /accounts/{id}/banking-initnow commits account state, the opening transaction, and the feed checkpoint atomically and returns the complete opening transaction from that operation. Concurrent account cleanup can no longer turn an otherwise successful initialization into an invalid response.POST /teamsrequests sent to the generic API gateway now route an explicit bodydataRegionto the matching regional cell. The creator remains authorized throughx-team-id; omitted placement continues to use the receiving cell.- Concurrent duplicate
DELETE /transactions/{id}requests now return the standardNOT_FOUNDresponse after the winning request removes the row, instead of exposing an internal databaseno resulterror. GET /teamandGET /teams/resolvenow return effective billing-partner metadata when the selected team and partner are homed in different regions. Team application data remains regional; partner identity and lifecycle status come from the control plane.- Error
messagevalues are now static per condition across the production API. UUIDs, refs, emails, indexes, raw values, provider diagnostics, causes, and lock data are returned in structuredcontextinstead. Integrations should continue branching oncodeand readingcontext, not parsing display messages. GET /teams/issuesnow includes a stablefeatureon every issue for partner-native queue grouping. Issue state remains source-controlled: fixed conditions disappear and later recurrence reuses the issue ID with a higher version. Snooze and application navigation remain partner UI concerns.- Audit events (
GET /audit/events,GET /audit/events/{id},GET /partner/audit-events) are now the only audit surface available to external callers, and thepartner:audit:v1bundle grants exactly that family. VRPlatform's internal forensic reads —/audit/actions,/audit/actions/{id},/audit/effects,/audit/journal-deltas,/audit/mutations, and/audit/timeline— are no longer reachable with partner API keys or ordinary first-party users. They return raw change records, effect retry mechanics, and journal-row identity that the public event contract deliberately aggregates and redacts. - Audit change records now record that a sensitive field changed without storing its value. Credential, secret, token, and password fields are reduced to the changed key names.
GET /appsandGET /apps/{id}now returncategory: nullfor legacy apps that do not have a category. This aligns the response with the existing nullable app data and prevents one uncategorized app from failing the full app collection.- Reconnect-created provider syncs now change to
failedwhen their asynchronous dispatch fails instead of remaining indefinitely queued without a Trigger run. Effect retries preserve the sync and its idempotency key. - VRI-to-VRT migration is now a supported Partner API workflow under
/partner/vri-to-vrt. Bootstrap requirespartner:provisioning:v1; account and listing previews plus historical statement import requirepartner:general-ledger:v1. Requests usesourceTeamIdandtargetTeamId, and both teams must be managed by the calling partner. The former/internal/migrate-vri-to-vrtroutes have been removed. - Added partner-only
POST /team/duplicatefor asynchronous test-team copies. The source comes fromx-team-id;uniqueRefprovides deterministic exact retries; and optional anonymization preserves accounting amounts, dates, and relationships while replacing visible PII. Cloned connections are disabled and contain no credentials or sync state. Poll the returned audit action for completion; restricted keys needpartner:audit:v1for that read.
2026-07-27
- Partner API keys can use every documented public partner workflow for a selected managed team. New restricted-key bundles cover audit, calendar, files, general ledger, integrations, recurring fees, statement layouts, and team members. Destructive operations remain outside standard bundles. Credential management, self-service, internal, demo, and legacy routes stay unavailable to partner API keys.
2026-07-25
- Team responses now return
billingPartnermetadata for the same effective partner represented bybillingPartnerId: the explicit billing partner, otherwise the accounting partner, otherwise VRPlatform. GET /teamsnow acceptsexcludeTestTeams=trueto exclude the demo tenant and teams assigned to the demo accounting or billing partner while retaining VRPlatform and its direct customers.- Team responses began exposing the now-retired
effectiveStatusalongside the administrativestatus. See the 2026-07-31 breaking replacement.
2026-07-24
GET /teamandGET /teams/resolvenow return the effective non-nullablebillingPartnerId, falling back to the accounting partner and then VRPlatform when no explicit billing partner is stored. Theirpartnermetadata uses the same VRPlatform fallback when no accounting partner is stored.- Embedded
membersinGET /teamandGET /teams/resolvenow includelastNamealongside the existingfirstName, matching the member name fields returned byGET /team/members. - Added bearer-only
PUT /meso an authenticated user can synchronously update their own first and/or last name. Supplied values are trimmed, blank names and target identifiers are rejected, and the response uses the same user shape asGET /me. API keys and current read-only embedded bundles cannot call the mutation. - Plaid sandbox completion no longer treats shared test account masks as GL account duplicates when returning configuration suggestions, issues, and action capabilities. Production sessions continue to reserve last four digits across active and inactive GL accounts.
2026-07-24
- Added public audit events for synchronizing who or what changed a business
entity, which public fields changed, and what the action did to accounting.
GET /audit/eventslists upsertable events for the selected team andGET /audit/events/{id}returns one event with signed posting-bucket before and after totals. RegionalGET /partner/audit-eventsreads all managed teams in one data region. First-party access is administrator-only; partner API keys need theaudit:readscope from the newpartner:audit:v1bundle; owner users and embedded sessions are denied. - Audit events cover
account,listing,listingOwnershipPeriod,ownerStatement,reservation, andtransactionroots with event-time actor, team, and account snapshots and allowlisted field changes. For synchronization, requestorder=asc, persistnextCursor, and upsert byidwhenrevisionincreases; accounting revisions reappear on the same event ID. Ascending reads trail real time by a short visibility lag so concurrently committed revisions are never skipped. Online history is guaranteed for at least 180 days; a cursor pointing before the earliest retained event returns410withearliestAvailableAt.
2026-07-23
DELETE /accounts/{id}/connections/{accountConnectionId}now returns the detached connection ID withstatus: "deleted".- Global VRPlatform admins can now list, create, reveal, and revoke API tokens for any selected team without joining that team. Team admins retain their existing inherited access; API-key and ordinary-user callers remain denied.
- Expanded the pre-release
embed:reports:v1bundle with read access toGET /accounts,GET /listings, andGET /contactsfor embedded report filters. Their detail, CSV, and mutation routes remain unavailable to embedded sessions, and every request remains bound to the session team. - Added regional
GET /partner/listingsfor the partner dashboard Listings CSV. It returns active property-manager listings with owning-team identity, calculated display names, structured/full addresses, the existing dashboard team filters, deterministic team/listing ordering, and standard pagination. Explicit team IDs outside the authorized regional slice return403. - Added partner-only
GET /partner/connectionsfor regional connection inventory. It returns managed VRI and VRT teams, including empty teams, with active connection app metadata, error state, and current sync state. Team, company, lifecycle, app, and app-scoped error-state filters run before team totals and pagination. - Standardized sorting on
GET /bank-records,/bank-records/csv,/files,/internal/feature-flags,/partner/billing,/partner/connections,/partner/owner-statements,/partner/users,/syncs, and/teamson one signedsortparameter. An unprefixed field sorts ascending and-fieldsorts descending. Generated schemas explicitly list both variants and each endpoint's default. - This is a breaking change:
sortBy,sortDirection, and direction-suffix values are no longer accepted. MigratesortBy=date&sortDirection=desc,sortBy=-date, orsort=created_desctosort=-date. Text ordering is case-insensitive, nullable fields place nulls last, and deterministic ID tie-breakers keep pagination stable.
2026-07-21
- Added VRP-admin message-overwrite administration under
/internal/message-overwrites. The collection supports pagination, total counts, deterministic ordering, and case-insensitive partial search across patterns and replacement messages; create, replace, and delete operations reject blank fields and return not found for missing resources. - Owner statement summary list, detail, and exports now allocate manual listing payouts tagged to a non-owner payee across the ownership-period percentages. Payouts tagged directly to an owner still belong fully to that owner.
2026-07-19
POST /plaid/connectnow accepts optionalenvironment=sandbox|production. New Items default to production, and the returnedconnectIdowns the environment for completion and configuration.- PRO-16794: added team-name search, pagination, and status-count ordering to
GET /partner/owner-statements. Partner dashboards can traverse regional VRT teams withpageandlimit, oroffsetandlimit, usepagination.totalfor the complete matching count, and order bydraft,inReview, orpublishedcounts. Search is a case-insensitive partial match. - Added
GET /partner/owner-statementsfor regional partner dashboards. It returns VRT child teams with persisted owner-statement counts fordraft,inReview, andpublished; VRI and manager statements are excluded. GET /menow includes the resolved team'sdataRegionandapiBaseUrl. When the resolved team is a partner, it also includespartnerContext.managedTeamRegionswith regional API URLs and active/inactive managed-team counts.- Regional partner portfolio reads now accept
x-data-region. The header selects one storage partition without changing the authenticated partner or team context. Initial support covers team lists/lookups, team issues, and partner-user reads. - Portfolio pages and cursors remain regional. Clients that combine a multi-region portfolio call each advertised region independently.
2026-07-18
- Renamed the external banking identity resource from BankAccount to
AccountConnection. Use
/account-connections,POST /accounts/{id}/connections, andDELETE /accounts/{id}/connections/{accountConnectionId}. - BankRecord contracts now use
accountConnectionId,accountConnectionRef, andaccountConnectionfor the optional external identity.accountIdremains the canonical posting Account. - CSV bank-record imports remain valid with only
accountId; they do not need an AccountConnection. CSV exports now label the external identity asAccount Connection. - Statement payout
bankAccountIdcontinues to mean a canonical bank Account and is validated accordingly.
2026-07-17
- Added compact, source-versioned
connection.changed,sync.status.changed,reservation.changed,listing.changed,statement.status.changed, andtransaction.changedwebhook contracts. Current authenticated API reads remain the source of truth. - Webhook creation now requires an explicit verification test after the receiver stores its secret. URL/event-set changes and reactivation require a test of the current subscription revision.
- Secret rotation now emits signatures from the new and previous secrets for a 24-hour overlap. Added webhook administration quotas, 90-day terminal delivery retention, and one-minute delivery recovery.
- Outbound delivery now uses private resolve-and-pin egress. Every attempt rejects mixed or non-public DNS answers, dials one validated numeric address, preserves the original TLS hostname, and never follows redirects. Public enablement remains gated on pre-release failure drills.
- Embedded-session bearer tokens now authenticate on
GET /meand preserve their issued team binding whenx-team-idis omitted. GET /menow returnsuser.email: nullfor embedded identities, which use the partner-providedsubinstead of an email address. Email remains required for ordinary authenticated users.- Team-member reads no longer fail for users created with an empty secondary email list; stored values are constrained to JSON arrays.
- Added
onLocked=nextOpento reservation adjustment creation. It resolves books-closed and statement-period conflicts to the earliest valid concrete posting date;onLocked=errorremains the default. - Reservation adjustment dry runs now return the resolved
generalLedgerPostingAtwithout saving the adjustment. - Clarified that reservation line exclusion is set through a mapping with
accountId: null;status=excludedand lineisExcludedare derived.
2026-07-16
- Retired partner edge hosts; the public API is served only at
https://api.vrplatform.app. - Removed machine-to-machine credential references from public documentation;
public authentication is
x-api-keyandAuthorization: Beareras listed in the Authentication summary. - Documented the webhook signature headers and HMAC-SHA256 signed-string format in Webhooks.
- Documented the rate-limit policy in the Error contract:
no enforced limits currently, and the API never emits
429.
2026-07
- Added materialized, versioned
GET /teams/issuessnapshots. - Added team-owned outbound webhook subscriptions, delivery history, retries, secret rotation, testing, and replay.
- Added
team.issues.changedas the first subscribable public event. - Published generated issue and webhook catalogs from typed runtime registries.
Exact endpoint schemas are generated from current OpenAPI. Historical design context remains in repository RFCs and version logs rather than in current behavior pages.
Documentation
- 2026-07: Consolidated UI business rules, concepts, partner setup, imports, and operations into task-oriented journeys while preserving old documentation URLs with permanent redirects.
