Authentication and Credentials
Select the credential and team-context contract for each surface
Keep credentials on a trusted backend. Never place a team API key, partner API key, or webhook secret in browser code.
Getting Credentials
Team and partner API keys use the same API-token management surface. A
signed-in admin creates them for the selected team through the dashboard or
POST /api-tokens. Contact VRPlatform to register a team as a partner.
A signed-in team admin can list, create, reveal, or revoke API keys. Global VRPlatform admins may perform the same operations for any selected team without joining that team. API-key-authenticated requests cannot manage credentials, including keys linked to an admin user. VRPlatform generates the token secret and returns it in the create response; callers never supply secret material. The dashboard list exposes only metadata and a four-character hint; opening one key reveals its full value. Revocation takes effect on the next authenticated request.
| Credential | Header | Use |
|---|---|---|
| Team API key | x-api-key: <team-api-key> | Direct backend calls for one or more teams |
| Partner API key | x-api-key: <partner-api-key> | Partner backend and managed-team calls |
| Embedded session | Authorization: Bearer <token> | Short-lived iframe product calls |
| Interactive session | Authorization: Bearer <token> | Signed-in or OAuth user calls |
API Keys
Use x-api-key on backend product API requests. A key that can access more
than one team must select the target with x-team-id. A single-team key can
omit the header when the route can resolve its only accessible team.
Treat the full value shown by the dashboard as a secret. Audit records include the token record ID and non-secret metadata, never the token value.
API tokens may include up to 32 allowedIpCidrs entries. Individual IPv4 and
IPv6 addresses are normalized to /32 and /128; networks are normalized to
their CIDR boundary. When the list is non-empty, every x-api-key request must
originate from a matching Cloudflare CF-Connecting-IP. Omit the field or send
an empty array for no IP restriction. X-Forwarded-For is never trusted for
this decision.
Partner-scoped collections such as GET /teams operate across managed teams
and need no managed-team selection. When the portfolio spans regions, use
x-data-region on supported collection reads to select one regional slice;
the partner API key remains the authorization context. Team-scoped routes (for
example GET /team, GET /transactions) require a selected team and ignore
x-data-region.
New keys owned by a partner team may declare reviewed, versioned capability
bundles. When bundles are present, the server expands them into exact primitive
scopes and exposes both on token metadata. Omitting bundles creates an
unrestricted partner token (scope: "*") with every partner API scope. The
partner authorization boundary still rejects bearer-only, internal, and
explicitly denied routes.
For restricted tokens, a route must be explicitly available to partner credentials and the key must contain every required scope. Write access does not imply delete, publish, pay, execute, import, or journal-posting access.
Available partner bundles:
partner:accounting-data:v1: listings, contacts, ownerships, reservations, transactions, accounts, tax rates, and line mappings.partner:audit:v1: team audit actions, effects, mutations, journal deltas, and timeline reads.partner:banking:v1: bank accounts, account connections, Plaid configuration, bank records, and bank rules.partner:calendar:v1: calendar-block reads and writes.partner:files:v1: team-file reads, downloads, uploads, and metadata writes.partner:general-ledger:v1: opening balances, opening trial balance, and historical GL or statement imports.partner:integrations:v1: apps, connections, flows, mappings, settings, syncs, booking channels, imports, and source downloads.partner:onboarding:v1: team onboarding guide and issue operations.partner:provisioning:v1: partner/team discovery, provisioning, initialization, and embedded-session issuance.partner:recurring-fees:v1: recurring-fee and listing-period reads, previews, and writes.partner:reports:v1: report reads and exports plus metric reads.partner:statement-layouts:v1: statement-layout reads, creates, updates, and listing assignments.partner:statements:v1: statement reads, layout reads, and exports.partner:team-members:v1: selected-team member reads, invites, role updates, and removals.partner:webhooks:v1: webhook subscription, delivery, test, rotation, and replay operations.
Destructive calendar, file, recurring-fee, and statement-layout operations remain outside their standard bundles. An unrestricted partner key can use their separate delete scopes; restricted keys cannot acquire those scopes through another bundle. Credential management, self-service, internal, demo, and legacy routes remain unavailable to partner API keys.
GET /me is a bearer-user bootstrap route for interactive and embedded
sessions. An interactive user can update their own trimmed first and last name
through PUT /me or send touchLastSeen: true to record the
server's current time as their latest activity. The server does not accept a
caller-provided timestamp. The session supplies the user identity, so the
request cannot select another user or team. The mutation persists before
returning the same user shape exposed by GET /me; lastSeen is not added to
that response.
Scoped bearer sessions need self:write for PUT /me. The current embedded
bundles are read-only and do not grant that scope. Partner API keys resolve
their partner or managed-team context through the partner and team endpoints;
they cannot call either /me operation.
Embedded Sessions
A partner backend sends its API key to
POST /partner/embed-sessions. It receives a 60-second, single-use embed URL,
not the bearer token. The iframe exchanges the fragment code directly and
keeps its 15-minute reports session in memory. The token is bound
to one user, team, audience, embed origin, parent origin, lifecycle channel,
and read-only bundle set; a conflicting team header or an unclassified route
is rejected.
The partner and selected managed team may be stored in different data regions.
With autoProvision: true, VRPlatform records the embedded identity centrally
and routes each persistent team membership to the region that owns that team;
the partner backend does not need to select or coordinate those regions.
Failed Authentication
Branch on the HTTP status and the structured API error code (see the
Error contract). Do not parse the
human-readable message. When 401 or 403 responses repeat, send the user
back to credential, access-scope, and team-context checks; do not fall back to
another credential automatically.
