Identity mapping: principal, billing account, and user
Vluna keeps two separate mappings inside a realm:
principal_id -> billing_account_id(billing_account_id, user_id) -> billing_user_id
The principal owns billing. The user owns runtime usage, grants, wallet balance, limits, events, and self-service reports under that billing account. Team seat capacity is owned by the billing account.
In most integrations, principal_id should be the most stable billing-owner
identifier you already have. user_id should be the stable application user
identifier for the end user making the runtime request.
Rules (contract)
- The mapping is immutable after first creation.
- The first time Vluna sees a new
principal_id, it creates and binds abilling_account_id. - The first time Vluna sees a new
user_idunder that account, it creates and binds abilling_user_id. - Treat
billing_account_idas sensitive. Do not trust it from untrusted clients. - Choose
principal_idcarefully. It should represent the entity that owns billing in your system, not just the current auth or collaboration context. - Choose
user_idseparately. It should represent the runtime user whose usage, grants, limits, and self-service reports are being measured.
Choosing principal_id
Start with the simplest stable identifier you already have:
- If billing is shared by an org, team, or workspace, use that stable
org/team/workspace id as
principal_id. - If billing is truly per-user,
principal_idanduser_idmay be the same application identifier, but keep the concepts distinct in code.
You only need a separate internal billing-owner id when your ownership model is more complex, for example when users can later move from personal billing to shared org billing, or can join someone else's org while keeping separate billing ownership.
If you are unsure, ask: "Who is the long-lived billing owner for these charges?"
Use that identifier as principal_id. Then ask: "Which end user performed this
runtime work?" Use that identifier as user_id.
Recommended pattern: prefetch mapping via token issue
Even if the mapping can be created lazily, prefetch it in your backend:
- User signs in or your backend receives a customer-scoped request.
- Your backend calls
POST /mgt/v1/token/issuewithprincipal_idanduser_id. - Vluna returns:
billing_account_idbilling_user_id- a short-lived bearer token
- Your backend stores the returned identifiers in your database if you need support or analytics joins.
Why this helps:
- You validate connectivity early.
- You avoid "first-request creates mapping" surprises in critical paths.
- You get a stable foreign key into Vluna data for support and analytics.
What to store in your database
At minimum:
principal_id(string)billing_account_id(uuid string)user_id(string)billing_user_id(uuid string)realm_id(string)- timestamps:
created_at,updated_at
Next
- Tenancy model: Tenancy: realm and billing account
- Verify integration: Verify installation and integration