Team Context
Know when a request selects one team and when it is partner scoped
Most accounting resources belong to one team. Select that team with:
x-team-id: <team-id>Route Shapes
| Shape | Example | Selection |
|---|---|---|
| Regional portfolio | Teams, audit events | x-data-region |
| Partner catalog | Partner, managed teams | No managed-team selector |
| Team singleton | GET /team | x-team-id for multi-team credentials |
| Team resource | GET /transactions | x-team-id for multi-team credentials |
| User bootstrap | GET /me | Optional; response identifies resolved team |
| User teams | GET /me/teams | No team selector; reads the control plane |
| Embedded call | Allowed classified read with session bearer | Session's team; header optional |
Do not send two competing team selectors. An embedded session is already bound
to the team it was issued for: x-team-id is unnecessary there, and sending
an x-team-id that names a different team is rejected with 403. The team ID
in the response is the selected context; store it with any team-scoped client
cache. Embedded sessions cannot use allowTeamFallback; losing access to the
signed team ends that session instead of selecting another membership.
Team discovery and portfolio inventory responses that clients use to route
subsequent team requests include dataRegion. This covers GET /me/teams,
GET /partner, GET /partner/teams, partner billing, connections, owner
statements, and listings, plus internal team billing. Region-selected issue,
membership, feature-assignment, and audit event responses do not duplicate the
selected region on team summaries.
Discovering Teams Before Selection
An interactive bearer user can call list teams without x-team-id.
The route filters the regional result to teams the user can access. It does not
make other reads or mutations team-optional.
Use the collection's search parameter for a general filtered list:
GET /teams?search=GPAZ%20LLCSet excludeTestTeams=true for a production-facing regional directory. This
excludes teams marked for testing and teams assigned to the Demo and Test
Console as their accounting or billing partner. VRPlatform and its direct
customers remain eligible.
The call still requires authentication. For a portfolio spanning regions,
select one regional slice with x-data-region as described below.
A Partner API key can instead call GET /partner/teams without x-team-id or
x-data-region. It returns only that Partner's managed teams from the global
control-plane catalog, so search, filters, pagination, and totals span all data
regions. Use each result's id and apiBaseUrl for subsequent team-scoped
requests.
Switching Teams
Load GET /me/teams for the team switcher. It reads the control plane and can
therefore return teams from every region in one paginated response. VRPlatform
admins receive all teams regardless of membership. Other users receive their
direct teams and child teams of partners they belong to.
Use the comma-separated ids query parameter to hydrate selected or recent
teams. IDs only narrow the caller's existing access, and they compose with
search, status, type, sorting, and pagination. pagination.total reflects the
fully filtered accessible set.
Each row includes the team's regional API base URL, isGeneralLedger, and the
nullable effective billingPartnerId. The effective billing partner prefers
an explicit billing partner and otherwise uses the accounting partner.
GET /team and GET /teams/resolve return effective partner identity even
when the selected team and its partner are stored in different regions. The
selected team's application data still comes only from its own regional API.
Regional team and listing eligibility treats an absent local partner row as a
cross-region relationship, not as an inactive partner. A partner row that is
present in the selected region must still be active.
User membership provisioning follows the selected team's dataRegion, even
when the provisioning request is authenticated through a partner or platform
team in another region. The control plane returns the membership immediately,
and its regional projection is sent only to the cell that owns the selected
team. Continue team-scoped work through that team's advertised API base URL.
When the user selects a team:
- cancel or ignore in-flight requests for the old team;
- replace the request header;
- clear team-scoped entities, issues, cursors, and snapshots;
- load the new team bootstrap state; and
- establish new webhook or polling state for that team.
Resource IDs do not grant cross-team access. The API verifies both the credential's access scope and the selected team's ownership of referenced resources.
Portal bootstrap can send allowTeamFallback=true to GET /me. When the
requested team is inaccessible or deleted, including a recently deleted team
still held in a short-lived client or server cache, the response resolves the
user's oldest active membership instead. Treat the returned team.id as the
selected context before loading team-scoped resources.
Creating a Team in a Region
See Team Setup for the canonical creation and
placement rules. The x-team-id header identifies the creating team for
authorization; it does not select where the new team's data lives.
Team Migration Freeze
An internal region migration temporarily freezes the selected team before its
application data is copied. While the freeze is active, authenticated
team-scoped POST, PUT, PATCH, and DELETE requests return 423 with
code TEAM_MIGRATION_FROZEN. The response context contains the team ID and
migrationStatus: "frozen".
{
"teamId": "11111111-2222-4333-8444-555555555555",
"migrationStatus": "frozen"
}Reads remain available from the source region during the freeze. Clients must not redirect writes themselves or retry them against another regional host. After the verified placement flip, team discovery and bootstrap responses advertise the new regional API base URL; clear team-scoped caches and resume writes there.
Frontend Context
GET /me returns selected-team setup signals under frontendContext.
hasOwnerBlockingConnection is true when the team has an active connection
to a property-management-system app whose declared actions include
blockCalendar. The capability does not depend on the team's stored owner
portal setting or the connection's accounting window.
hasAchConnection is true when an AccountConnection links an active Ramp
Connection to an active Account for the selected team. Inactive Connections,
Connections for other apps, and inactive linked Accounts do not count. This is
a setup-presence signal; use Account and AccountConnection ACH capabilities
when payment readiness is required.
The response evaluates team feature assignments and user approvals from the global control plane. Notification preferences and owner access remain scoped to the selected team's regional database.
Switching Partner Portfolio Regions
Interactive partner applications discover regions from GET /me when it
resolves a partner team. partnerContext.managedTeamRegions lists every region
containing active or inactive managed teams. Each entry has the region's API
base URL and active/inactive counts. Backend partner integrations use the
regional API endpoints supplied in their integration configuration. Do not
hardcode a customer- or partner-specific hostname.
A partner API key is global: use the same credential for every regional API
endpoint. The partner itself does not need an application row in each region.
The selected managed team must still belong to the serving region; a strict
regional host returns 421 when x-team-id points elsewhere.
Use GET /partner/teams when the integration needs one cross-region managed
team catalog. Use the regional portfolio reads below when the response includes
regional application data; those reads still select one partition per request.
For a supported portfolio read, send:
x-data-region: usx-team-id and x-data-region have different meanings: the first is the
authorization/current-team context, while the second selects one regional
data partition. The API reads one region per request. Call the advertised
regional endpoints independently when the UI needs more than one region, and
keep page/cursor state per region. Do not send x-data-region as a substitute
for x-team-id on team-scoped routes.
Supported Partner Regional Reads
The following partner-facing reads currently declare x-data-region:
- List teams (
GET /teams) - List team issues (
GET /teams/issues) - List partner users (
GET /partner/users) - Get a partner user (
GET /partner/users/{id}) - List owner-statement status (
GET /partner/owner-statements) - List partner audit events (
GET /partner/audit-events)
The generated operation page is authoritative. If an operation does not
declare the header, x-data-region does not make it a regional portfolio read.
Fetch More Than One Region
Use the configured endpoint for each region and send its matching region identifier. Fetch regions independently; a slow or unavailable partition should not corrupt another partition's pagination state.
const resultsByRegion = await Promise.allSettled(
regionalApiEndpoints.map(async ({ dataRegion, apiBaseUrl }) => {
const url = new URL('/partner/audit-events', apiBaseUrl);
url.searchParams.set('order', 'asc');
url.searchParams.set('limit', '100');
const response = await fetch(url, {
headers: {
'x-api-key': partnerApiKey,
'x-data-region': dataRegion,
},
});
if (!response.ok) {
throw new Error(`${dataRegion} request failed: ${response.status}`);
}
return { dataRegion, page: await response.json() };
})
);Store page numbers or cursors by region, endpoint, and filter set. Tag every record with its region before combining results in a UI or warehouse. There is no cross-region cursor or guaranteed global ordering, so sort merged display data only after the regional responses arrive.
