Skip to main content

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 a billing_account_id.
  • The first time Vluna sees a new user_id under that account, it creates and binds a billing_user_id.
  • Treat billing_account_id as sensitive. Do not trust it from untrusted clients.
  • Choose principal_id carefully. It should represent the entity that owns billing in your system, not just the current auth or collaboration context.
  • Choose user_id separately. 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_id and user_id may 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.

Even if the mapping can be created lazily, prefetch it in your backend:

  1. User signs in or your backend receives a customer-scoped request.
  2. Your backend calls POST /mgt/v1/token/issue with principal_id and user_id.
  3. Vluna returns:
    • billing_account_id
    • billing_user_id
    • a short-lived bearer token
  4. 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