Team Setup
Discover, create, and initialize a team for an integration
Use a partner API key for the partner-scoped routes on this page. Partner API
keys are issued by VRPlatform during partner onboarding — see
Authentication and Credentials. Collection routes
like list teams are partner-scoped and take no
x-team-id header. Singleton team routes use x-team-id.
Discover Teams
curl 'https://api.vrplatform.app/teams' \
-H 'x-api-key: <partner-api-key>'Store the returned team ID. Use it for team-scoped product requests and embedded sessions.
Each listed team includes isBillable, a non-nullable effective
billingPartnerId, and matching billingPartner metadata. The effective
billing partner is the explicit billing partner when present, otherwise the
managing partner, otherwise VRPlatform. The billingPartnerId query filter
uses that same effective relation.
status is the derived lifecycle state. It is active only when both the
team and its effective billing partner have active calculated subscription
state. There is no separate administrative status in team responses. The
status query parameter uses the same calculation, so a returned
row always matches the requested status.
Team rows returned by GET /partner/billing use the same status and
capability contract.
Before presenting an activation action, inspect capabilities.activate. It
contains enabled and typed disabledReasons. Disable activation when it is
not enabled and show the corresponding reason. Deactivation remains available.
PUT /team rechecks the same condition and returns 400 BAD_REQUEST with the
requested and current status plus the disabled reasons in structured context
if state changed after the read.
Add includeAggregates=true for partner-dashboard summaries:
curl 'https://api.vrplatform.app/teams?includeAggregates=true' \
-H 'x-api-key: <partner-api-key>'That opt-in adds active-listing, non-owner member, active-connection,
current connection-issue, and direct child-team counts. Omitting the parameter
omits aggregates entirely and keeps the default request lightweight. Partner
listing totals include the partner's direct listings and listings belonging to
clients billed through that partner.
The singleton get team response always includes
isBillable and aggregates; it does not require includeAggregates.
Team resolution uses the control plane for identity, placement,
billingPartnerId, and billingPlan. Hyperline customer identity,
subscription, payment-method, cancellation, and accounting state remain
regional. If you need billingCustomerId, call GET /team at the
apiBaseUrl returned by GET /teams/resolve; the resolve response does not
include this regional field.
When PUT /team completes onboarding, it does not initialize or change
subscription status, billing plan, or cancellation date. Missing billing state
remains visible until the Hyperline workflow reconciles it.
Test and demo teams use Hyperline Development for billing, while teams billed by VRPlatform or Ximplifi use Hyperline Production. When a team moves between these billing setups, the source environment is cleaned up before the target environment creates its subscription. Moving from Hyperline billing to an external billing partner cancels the subscription in its source environment while retaining the customer for a later return. Changes between VRPlatform and Ximplifi stay in Hyperline Production and retain the customer.
Team administration views can filter with billingPartnerId,
isOnboarding, billingSubscriptionStatus, billingPlan, product, and
comma-separated featureIds. Subscription filters support missing,
viaPartner, and pendingCancellation; plan filters support other and
none; product accepts vrtrust or vrintegrations. Filters are applied
before pagination, so pagination.total describes the complete filtered
regional result. Request each portfolio region separately with x-data-region.
Read Monthly Partner Billing
Use list partner billing to load the property-manager teams billed by the authenticated partner for one month:
curl 'https://api.vrplatform.app/partner/billing?month=2026-06' \
-H 'x-api-key: <partner-api-key>' \
-H 'x-data-region: us'For a completed month, each team uses its latest durable billing listing fact strictly before the first instant of the following month. A prior fact carries forward when no listing changed during the selected month, so completed-month billing does not depend on analytics delivery or retention. The current month always uses live counts. Live non-GL counts use calculated listing status, including manual and PMS status, while live GL counts use current ownership-period deactivation.
Completed-month reads return SERVICE_UNAVAILABLE until the regional fact
dataset has passed import, baseline, reconciliation, and retained-month parity
checks. The same response applies to a month before the recorded imported
range. An incomplete regional rollout therefore cannot become a zero-row
billing response. Reconciliation records only current state at the time it
runs; it never backdates live tenant, partner, listing, or ownership state into
a completed month.
Deleted teams remain as historical rows. They keep the original team ID and
monthly listing count, use status: deleted, and return unavailable live-team
fields as null. Consumers must disable actions that require a live team.
The endpoint supports team/company search, lifecycle, accounting partner,
product, and billing-plan filters plus deterministic sorting and standard
pagination. Sort by name, created, partnerName, product,
activeListings, or billingPlan; prefix the value with - for descending
order. The default is sort=-created. Filtering happens before totals and
page selection. One request reads one regional partition; request each
portfolio region separately and retain region identity when combining results
or exporting CSV.
Export Active Listings
Use list partner listings to export active property-manager listings for the current partner or VRP-admin dashboard:
curl 'https://api.vrplatform.app/partner/listings?partnerId=<partner-id>' \
-H 'x-api-key: <partner-api-key>' \
-H 'x-data-region: us'The endpoint accepts the dashboard's team IDs, team/company search, lifecycle,
onboarding, accounting-partner, effective billing-partner, subscription,
billing-plan, product, and enabled-feature filters. Filters run before
pagination.total and page selection. Only listings that pass the same live
active-listing calculation as partner billing are returned: non-GL listings
use calculated listing status, while GL listings use ownership-period
deactivation.
Each row contains the listing ID and calculated display name, owning team ID
and name, structured address fields, and the full stored address. Rows are
ordered by team name and calculated listing name with stable ID tie-breakers,
so clients can combine every page directly into the existing Listings CSV.
Request each advertised portfolio region separately. When explicit team IDs
are supplied, any ID outside the authorized selected region returns 403
instead of producing an incomplete export.
Choose an Operating Country
The signup and team-creation UIs can choose placement before a team exists by calling the public country list:
curl 'https://api.vrplatform.app/countries'The response is sorted by English country name. Each item contains its
two-letter country code, full name, lowercase ISO 4217 currencies, and
the dataRegion to use for team provisioning:
{
"data": [
{
"code": "CA",
"name": "Canada",
"currencies": ["cad"],
"dataRegion": "us"
}
]
}Use get country when only one
country is needed.
The path accepts a two-letter code case-insensitively, such as
GET /countries/ca, and returns the country object directly. An unknown code
returns 404.
Country placement follows this fixed policy:
| Country continent | dataRegion |
|---|---|
| North America or South America | us |
| Europe, Africa, or Antarctica | eu |
| Asia or Oceania | ap |
Pass the returned dataRegion when provisioning through the generic gateway.
When a country has more than one currency, the API returns every applicable
code; the client must keep or ask for the intended team default instead of
silently discarding the additional choices.
Create A Team
curl 'https://api.vrplatform.app/teams' \
-X POST \
-H 'x-api-key: <partner-api-key>' \
-H 'content-type: application/json' \
--data '{
"name": "Example Team",
"email": "team@example.com",
"generalLedger": false
}'The generated operation defines all optional billing, address, portal, and
member fields. generalLedger: false creates the team without accounting
setup. When generalLedger is omitted, true, or a configuration object, the
API runs team initialization as part of the create request: it provisions the
chart of accounts, tax rates, statement layouts, and recurring fees. By
default, initialization preserves the existing regional VRPlatform-team
template behavior. Set generalLedger.useCentralDefaults to true to use the
published Control Plane template and reservation defaults instead:
{
"name": "Example Team",
"email": "team@example.com",
"generalLedger": { "useCentralDefaults": true }
}The central path resolves the current published template for the managing
partner, or the VRPlatform global template when that partner has no template.
An existing but unpublished partner template fails setup instead of inheriting
global data. Every region reads the same immutable version, while the new team
still receives normal regional rows with newly generated IDs. Account-linked
fees, rates, and layouts are rebound through portable logical keys. Posting
accounts owned by recurring fees are recreated with the fee instead of being
copied as reusable ledger accounts. Preview does not create a partner default
set; invalid source components return 422 UNPROCESSABLE_CONTENT with paths
that must be corrected before publication. A published snapshot includes only
the source team's current default statement layout. Historical non-default
layouts do not block publication or become defaults for new teams.
Create team does not support dry run.
A team created with type: "partner" receives its subscription during creation
when it is billed directly by VRPlatform. A propertyManager receives its
subscription when it is billed directly by VRPlatform or managed by Ximplifi.
Both rules apply when isOnboarding is true. The successful create response
reflects the expected active subscription status and plan while billing setup
completes. Externally billed teams remain delegated without a VRPlatform
subscription.
An explicit copyFromTeamId must belong to the target data region. A source
in another region returns 400 BAD_REQUEST with the source and target regions
in structured error context, and no team is created. useCentralDefaults
cannot be combined with copyFromTeamId or
copyLineMappingsFromPartnerId; invalid combinations return 400 before the
team is created.
When defaultCurrency is provided, it must be present in the target region's
supported currency catalog. An unsupported value returns 400 BAD_REQUEST
with context.defaultCurrency, and no team is created.
The data region does not select a different central ledger template. Reservation line mappings are not embedded in the snapshot. The central bootstrap applies centrally managed defaults from the same partner or global scope only for missing line types; any mapping or exclusion already saved by the team remains unchanged.
When provisioning with a partner API key, uniqueRef is required: send your
own stable identifier for the customer. Exactly repeating a completed create
fails with 409; the error's context.existingTeamId identifies the existing
team. A changed payload for the same key fails without a resumable id. Load the
existing team from Partner teams with that ID
instead of re-posting. Retrying a create request that failed before completing
is safe; a request that succeeded must not be repeated. A team is not returned
by partner discovery until its global identity, regional data, and requested
initialization have completed provisioning.
When calling the forwarding gateway, General Ledger team creation requires an
explicit dataRegion of us, eu, ap, or hostaway. Omitting the region
or selecting crunchy returns 400 BAD_REQUEST before the team is created.
Because General Ledger is the default, the same requirement applies when
generalLedger is omitted.
A propertyManager assigned to the Hostaway partner must use the hostaway
data region. Any other region returns 400 BAD_REQUEST before the team is
created. Operating country may still determine currency and tax defaults, but
it must not determine storage placement for a Hostaway client.
When calling a strict regional API worker directly, dataRegion may be omitted
and defaults to that worker's cell. An explicit region must match the cell;
otherwise the worker returns 421 MISDIRECTED_REQUEST with its placement.
Non-General Ledger teams must set generalLedger to false and use crunchy.
An omitted dataRegion defaults to crunchy; an explicitly selected regional
cell returns 400 BAD_REQUEST before the team is created. A direct request to
a regional worker with an omitted region returns 421 MISDIRECTED_REQUEST
with the Crunchy placement.
Generated team slugs are checked against both the global tenant registry and the target region before the team is created. If the normalized name is already used in either store, the API selects a unique generated slug and writes it to both registries. Explicit slugs must be available in both stores.
Duplicate A Team
Use POST /team/duplicate to create an asynchronous test-team copy of a
partner-managed property-manager team. Select the source with x-team-id and
send a partner API key that has the partner:provisioning:v1 bundle. A
restricted key also needs partner:audit:v1 to poll completion.
curl 'https://api.vrplatform.app/team/duplicate' \
-X POST \
-H 'x-api-key: <partner-api-key>' \
-H 'x-team-id: <source-team-id>' \
-H 'content-type: application/json' \
--data '{
"name": "Blue Crush Demo",
"uniqueRef": "blue-crush-demo-001",
"anonymize": true
}'The response identifies the deterministic target and the audit action:
{
"actionId": "c8e5e122-e568-4b10-959e-ca88aa6778ce",
"anonymized": true,
"name": "Blue Crush Demo",
"sourceTeamId": "277698df-a242-4f8d-b2f7-af69b79d85be",
"status": "queued",
"targetTeamId": "b8c49c19-3528-5c10-8b67-f3b72ad525df",
"uniqueRef": "blue-crush-demo-001"
}anonymize defaults to false. When enabled, the worker deterministically
replaces visible guest, owner, listing, contact, address, bank-display, and
free-text data while preserving amounts, dates, and relationships.
The target is always a test team assigned to the same partner. The supported business and accounting graph includes listings, ownership periods, reservations, payments, transactions, bank records, journals, statement layouts, recurring fees, and owner statements. Users, tokens, audit history, files, webhooks, automations, tasks, live credentials, and sync runtime state are not copied. Connections are copied only as disabled shells with empty credentials and persistent state.
Accounting references are rebound to the target graph, including account IDs embedded in recurring-fee formulas. A duplicated fee therefore calculates from the cloned team's postings rather than retaining a hidden dependency on the source team's chart of accounts.
Treat uniqueRef as the idempotency key. An exact retry returns the same
targetTeamId and safely resumes the audit work. Changing the name or
anonymize value for that key returns 409; use a new uniqueRef for a
different clone. A key already used by another team for the partner also
returns 409.
Poll GET /audit/actions/{actionId} with the source x-team-id. The initial
response means the work was queued; use the action's effect counts to decide
when the duplicate is complete or failed. Polling requires audit:read, which
is included in partner:audit:v1.
Completion includes the target's control-plane identity and connection routes. If the background runtime cannot access separate regional and control-plane databases, the effect fails instead of returning a regional-only duplicate.
Initialize Later
Use initialize team only when the selected team
still needs GL initialization or a specific copy/include plan. Send the
selected team in x-team-id. The operation supports dry run.
Initialization can copy selected accounts, tax rates, mappings, fees, or
statement layouts. Omit useCentralDefaults (or send false) to preserve the
existing regional bootstrap. Send { "useCentralDefaults": true } to use the
currently published partner template and reservation defaults, or the
VRPlatform global set when that partner has no template. With
copyFromTeamId, it retains the explicit regional team-to-team copy behavior.
The central opt-in cannot be combined with either explicit copy source.
Publishing a newer central version does not mutate existing teams; it affects
later central initialization only. When a team already has statements, central
initialization retains every layout referenced by those statements as
historical and non-default. It removes unused layouts and makes the copied
central layout the current default.
Treat initialization as an accounting setup operation, not a repeated
idempotent bootstrap call.
Manage the Onboarding Guide
Use GET /team/guides/onboarding to read the selected team's VRPilot onboarding
checklist. Every response contains the fixed step set with status and the
server-owned updatedAt timestamp.
Use PUT /team/guides/onboarding with exactly one guide or step key. A step can
be set to completed, skipped, or null. These are manual checklist choices:
the API does not require the step's normal product action to exist before it
accepts completed. Sending null records an explicit incomplete choice, so a
live completion signal does not immediately complete that step again.
PUT /team/guides/onboarding
Content-Type: application/json
{
"openingBalances": "completed"
}Automatic completion still applies to untouched steps when the related setup is detected. Reopening a required step also reopens a completed guide. Optional steps can be reopened without changing the guide-level completion status.
Guide-level status: completed remains valid only after every required step is
either completed or skipped. This is the overall checklist gate; it does not
prevent manual status changes on individual steps.
Verify
Load the selected team and inspect ID, status, type, GL state, books-closing date, partner relationship, and available connection capabilities. Then continue to Connections.
Update Team Branding
Use update team with the selected team in x-team-id to
set logo or colorPrimary. Both fields support explicit clearing: send
null to remove the stored logo or primary color. Omitting either field leaves
its current value unchanged.
List Managed-Team Users
Use list partner users to build a user directory across managed child teams. The collection excludes anyone who has a direct membership on the partner team itself, even when that person also has a managed child-team membership. Search, user views, team filters, totals, and pagination all operate after this exclusion.
When the portfolio spans regions, request each region advertised by GET /me
with x-data-region; one response contains only the selected regional
partition.
List Partner-Team Members
Use list partner members to build the
partner team's own member directory. Each root row includes the member's
role, status, and lastInvitedAt from the partner-team membership. Its
memberships array contains only managed child-team memberships.
Invite A Team Member
Use invite team member with the
selected team in x-team-id. Set sendEmail: false to create a copyable link
instead of sending the email immediately. The endpoint does not support dry
run because it creates an invite token and dispatches notification work.
An email already used by an owner account cannot also be invited as a team
member. That request returns 400 BAD_REQUEST; use the structured error code
for behavior and display its message to explain the conflict.
If the email provider has suppressed the address after a bounce, complaint, or
manual suppression, the request also returns 400 BAD_REQUEST. Offer
sendEmail: false to create a copyable invite link, or ask for another email
address. The response uses a static message and does not expose provider or
recipient diagnostics.
Re-inviting an existing member with the same role preserves their identity and
explicit permission bundles. If that membership changes while the invitation
is being prepared, the request returns 409 CONFLICT. Reload the member list
before trying again.
Delete A Team
DELETE /team?confirm=true queues deletion of the team selected by
x-team-id. A partner API key can delete a team only while that team is
directly managed by the key's partner. Restricted keys require the
partner:team-deletion:v1 bundle. Partner deletion cannot use force=true, so
an active subscription or active listings, connections, and automations block
the request. Resolve those blockers before retrying.
Authenticated active VRPlatform members retain the administrative deletion
path. For that path, force=true bypasses active-resource checks and
accounting-period locks during whole-team removal, but it does not bypass the
active-subscription guard. Service tokens and ordinary tenant users receive
403.
Deletion runs asynchronously after validation. A successful response confirms that the application-owned job was queued, not that every cleanup step has completed:
{
"operationId": "00000000-0000-0000-0000-000000000000",
"status": "queued"
}A retry from the same requester returns the same active operation. A request
from another actor while deletion is active returns 409 CONFLICT instead of
transferring operation ownership.
Retain operationId and poll GET /operations/{id} for the normalized
queued, running, completed, or failed lifecycle. After partner
deletion, send the partner team in x-team-id when polling because the target
team no longer exists. Immediate task dispatch can fail without losing the
durable job; a recovery schedule starts queued work and resumes stale
checkpoints. Provider run IDs, deletion steps, attempts, and failure details
remain available only to internal operators.
At most five team-deletion tasks execute concurrently. Each deletion locks its
target team before child cleanup so background writes cannot refill the team
during removal. Regional cleanup advances through committed groups, and
bounded table and journal batches commit independently. A retry resumes from
the last completed group instead of replaying the complete regional purge.
Account rows are locked while their dependent mappings are removed. The final
connection pass commits before the team row is removed. Tenant-owned
connections also cascade with the team row, so a remaining connection cannot
veto the confirmed deletion. Reservation mapping rows likewise cascade when
their account has already passed deletion safety checks.
If a legacy owner statement line from another team references a reservation
owned by the deleted team, deletion preserves the statement line and clears
only that reservation reference before removing reservations.
The job completes only after its audit mutation and follow-up work are stored;
those records can outlive the deleted team so recovery remains possible.
API Reference
- List teams —
GET /teams - List monthly partner billing —
GET /partner/billing - List active partner listings —
GET /partner/listings - Create team —
POST /teams - Duplicate selected team —
POST /team/duplicate - Get selected team —
GET /team - Update selected team —
PUT /team - List managed-team users —
GET /partner/users - List partner-team members —
GET /partner/members - Invite team member —
POST /team/members/invite - Initialize team —
POST /team/init - Delete selected team —
DELETE /team
