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 | GET /partner, GET /apps | 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.
Discovering Teams Before Selection
An interactive bearer user can call list teams or
look up teams without x-team-id. These discovery routes filter
the regional result to teams the user can access. They do not make other reads
or mutations team-optional.
Use the collection's search parameter for a general filtered list:
GET /teams?search=GPAZ%20LLCUse the dedicated lookup when the client needs only name-based discovery:
GET /teams/lookup?name=GPAZ%20LLCBoth calls still require authentication. For a portfolio spanning regions,
select one regional slice with x-data-region as described below.
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.
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.
Creating a Team in a Region
Send POST /teams to the generic API gateway and set the new team's
dataRegion in the request body. The gateway forwards the request once to the
matching regional cell. The x-team-id header still identifies the creating
team for authorization; it does not select where the new team's data lives.
If dataRegion is omitted, creation uses the receiving cell. A request sent
directly to a different strict regional cell returns 421 with the selected
region and its API base URL.
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.
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) - Look up teams (
GET /teams/lookup) - 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.
