VRPlatformVRPlatform
Integrate & Migrate Data

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 continentdataRegion
North America or South Americaus
Europe, Africa, or Antarcticaeu
Asia or Oceaniaap

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

On this page