Members
See Teams and attribution → for the conceptual model: why a member behaves like a virtual key. This page is the field reference for the Member record and how it is resolved from an inbound request.
What a member is
A member is a client of the appliance (a caller that consumes surface capacity), deliberately detached from the administrator users who sign in to operate the appliance: a client never needs sign-in access. The server assigns each member a stable UUID; a team references members by that id, and per-member spend/token ceilings live here so a member’s cap spans every team and surface it touches.
Identity and status
Every member has a name, description, and status alongside its server-assigned ID.
- Disabling a member blocks the caller immediately without touching its record or history: the team gate denies a disabled member outright instead of quietly falling through to anonymous.
- Delete a member to remove it from listings entirely. It’s tombstoned rather than erased, so historical usage stays attributable.
| Field | Default | Description |
|---|---|---|
| ID | Server-assigned UUID | Doubles as the storage filename and the reference a team uses to link the member. |
| Name | Not set | Display name; falls back to the member’s first identifier value when absent. |
| Description | Not set | Free-text description. |
| Status | Enabled | One of Enabled, Disabled, or Deleted. A disabled member is denied by the team gate (a disabled anonymous member is how an admin enforces “recognised callers only”). A deleted member is tombstoned: hidden from listings and excluded from attribution, but retained on disk so historical usage stays attributable. |
| System | Off | Built-in system record flag: on for members seeded from the default-config directory (e.g. the anonymous member). System records can be disabled but never deleted. Set by the server, not the administrator. |
| Auto Provisioned | Off | Marks a member that was auto-provisioned on first sight from a verified caller identity (see Auto-provisioning) rather than created by an administrator. Set by the server, not the administrator. |
| Created At / Updated At | Set automatically | Record timestamps. |
Identifiers
Identifiers are how an inbound request gets matched to this member. A member may present several credentials (an Entra object ID, an email, an mTLS SAN), and any one of them resolves the caller. Registering more of a caller’s real-world credentials means that caller is recognized as the same member no matter which app, device, or channel they use.
- Add one identifier per credential the caller might present: a web app’s JWT claim, a mobile app’s mTLS certificate, a service’s API key. Registering only one leaves the caller unrecognized whenever it shows up through a different channel.
- Use Email or JWT subject for anything backed by a JWT, API key name for machine callers already authenticated by key, and Custom value when the caller’s identity lives in an arbitrary header or claim your identity source doesn’t natively support.
- A legacy single identifier still works fine on read (it’s folded into a one-item list), so there’s no need to migrate existing members just to add a second one.
| Field | Default | Description |
|---|---|---|
| Identifiers | None | How an inbound request is matched to this member. Add as many as you need; a legacy single identifier is still accepted on read (folded into a one-item list). |
Identifier kinds, as shown in the Match by dropdown:
| Kind | Carries | Description |
|---|---|---|
| an email address | From a header, JWT email claim, or mTLS SAN. Validated as a plausible email (a single @, non-empty local part, dotted domain), not full RFC 5322. | |
| JWT subject | a subject value | A JWT subject claim or any opaque subject identifier. |
| API key name | a key name | The validated API key’s name. |
| Custom value | an arbitrary value | An arbitrary header or claim value, matched verbatim (case-insensitively). |
A member must have at least one identifier; each identifier’s value must be non-empty (and, for email, plausible). Values are matched case-insensitively, trimmed and lowercased for storage and comparison.
Spend and rate caps
Per-member ceilings (a monthly budget and token cap, a per-minute request/token rate limit, and an optional model allow-list) travel with the caller across every team and surface it touches. That stops a single integration, key, or identity from running up unexpected spend, regardless of which team it’s attributed to or surface it calls.
- Trusted, internal callers covered by an adequate team-wide budget can leave every cap unlimited.
- External, trial, or otherwise less-trusted callers are the ones that need a Monthly Budget or Monthly Token Cap: without one, a single caller can run away with spend before the team-wide cap even notices.
- Requests/Tokens Per Minute enforces a per-minute rate limit that blunts a burst or a misbehaving client in the moment, acting independently of the monthly ceilings.
- Allowed Models matters for a caller that should never reach certain models, your most expensive one, say. Otherwise leave it unrestricted and let the surface decide which model runs.
| Field | Default | Description |
|---|---|---|
| Monthly Budget (USD) | Unlimited | Member-wide monthly USD ceiling across every team and surface. Must not be negative. |
| Monthly Token Cap | Unlimited | Member-wide monthly token ceiling across every team and surface. |
| Requests Per Minute | Unlimited | Per-minute request cap for this member (the “virtual key” RPM), enforced by the team gate over a trailing 60-second window. |
| Tokens Per Minute | Unlimited | Per-minute token cap for this member (the “virtual key” TPM), counted over the same trailing window. |
| Allowed Models | Unrestricted | Model allow-list (the “virtual key” scope). When set, a request naming a model that isn’t on the list is rejected with a 403 error. A request that omits the model (surface-controlled model) is never blocked. |
Resolving a member from a request
A surface that participates in team attribution extracts a caller identifier from the inbound request using its configured caller-identity sources (see Teams →), then looks it up against the member registry:
- The team gate’s lookup matches against any non-deleted member (including a disabled one), so the gate can recognise and explicitly block a disabled member rather than silently falling through to anonymous.
- The general lookup matches only enabled members.
A lookup first checks a per-identifier index, then falls back to a linear scan re-verifying the match. A stale index entry never yields a wrong member.
Auto-provisioning
When a surface opts in (its team-attribution auto-provision switch) and a caller presents a verified identity (a JWT, mTLS certificate, or API key, but never a caller-controlled header or request body) that matches no existing member, the appliance can mint a member on first sight instead of requiring an administrator to pre-register one. This draws on one appliance-level provisioning configuration, not per-surface rules. It matters most when callers are already provisioned somewhere else (an identity provider, a partner’s API-key issuer) and you just want Agent Stream to recognize each one the first time it shows up.
- Turning this on makes sense once callers arrive from a trusted, already-verified source; the alternative is manually creating a member for each one.
- Keep it off for a small, deliberately curated caller list where an administrator creates every member explicitly.
- After enabling it, set Max Auto Provisioned Members and Creations Per Minute as safety ceilings so a compromised or unusually noisy token issuer can’t fill the member store on its own.
Provisioning only trusts verified sources: there’s no header or body-JSON-path option for it. A surface that identifies callers from a spoofable header can still attribute usage to a member, but it can never create one.
This provisioning configuration is an appliance-level capability rather than a per-surface setting: it lives on the Teams page’s Enrolment tab, not on an individual surface.
| Field | Default | Description |
|---|---|---|
| Enabled | Off | Master switch. While off, no caller is ever auto-provisioned regardless of a surface’s opt-in. |
| Identifier Rules | None | Ordered rules deriving the member’s identifiers from the verified caller identity. At least one is required when enabled. A provisioned member carries every identifier that resolves (deduped), so it matches under any of them next time. |
| Name Claim | Not set | JWT claim to use as the member’s display name (e.g. name). |
| Member Defaults | Not set | Defaults stamped on each new member: monthly budget, monthly token cap, per-minute request/token caps, allowed models. |
| Team Rules | None | Team-assignment rules, evaluated top-down; a member may match several. |
| Max Auto Provisioned Members | Unlimited | Hard ceiling on how many auto-provisioned members may exist. |
| Creations Per Minute | Unlimited | Max member creations per minute across the appliance (a guard against a hostile token issuer filling the disk). |
Each identifier rule: a source (a named JWT claim, the JWT subject, the mTLS certificate’s Subject CN, the mTLS certificate’s email SAN, or the API key name) and the identifier kind to store it as (defaults to the source’s natural kind, for example an mTLS email SAN defaults to Email).
Each team-assignment rule:
| Field | Description |
|---|---|
| when | An optional predicate over the caller’s JWT claims: claim equals, claim contains, or claim present. Absent means the rule always applies. |
| team_id / team_id_template | A fixed team id, or a {{claim.X}} / {{identifier}} template, slug-sanitised. |
| ensure_team | Create the resolved team if it doesn’t exist; otherwise a rule referencing a non-existent team is skipped. |
| team_name_template | Name template used when a team is auto-created. |
| team_defaults | Monthly budget / monthly token cap for a team created this way. |
An admin can preview what provisioning would do for a given identity, or for an unsaved config, without writing anything.
Built-in members
The appliance always seeds two system members, so the attribution gate never has to special-case an unmatched or synthetic caller:
| Id | Purpose |
|---|---|
anonymous | The default identity for a caller that can’t be matched to a real member. Disabling it is how an admin requires “recognised callers only”, with no special-case code path. |
sandbox | Sandbox (Test-panel) executions have no resolved caller, so their usage is attributed here instead of to anonymous, keeping test traffic visible yet distinguishable from real anonymous/live callers. |
Related
- Teams: The Team record, the surface-level team-attribution block, and the team gate.
- Teams and attribution concept: Why member and team attribution behaves like a full virtual key.
- OPA policies: The source-authentication identity a policy evaluates, distinct from, but often the same credential as, a member identifier.
- Cost and usage limits: The separate per-stage/per-surface usage-limit mechanism, distinct from these per-member and per-team monthly caps.
Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.