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
The member editor has three tabs: Details, Caps & Limits, and Usage, which charts the member’s spend and tokens by surface and is available once the member is saved. Details holds the name, description, status, and identifiers.
- Clearing Enabled blocks the caller immediately and keeps its record: the team gate denies a disabled member outright instead of falling through to anonymous.
- Delete Member removes it from listings and from attribution. The record is kept, and its usage series are removed from the usage charts.
| Field | Default | Description |
|---|---|---|
| Member ID | Server-assigned UUID | The reference a team uses to link the member. Shown once the member is saved. |
| Name | Not set | Display name; falls back to the member’s first identifier value when absent. |
| Description | Not set | Free-text description. |
| Enabled (only enabled members are matched at request time) | On | A disabled member is denied by the team gate; disabling the built-in anonymous member is how an admin enforces “recognised callers only”. Built-in members can be disabled but not deleted. |
| Created | Set automatically | Creation timestamp. |
The members list marks a member that was auto-provisioned from a verified caller identity rather than created by an administrator.
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 recognised 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 unrecognised 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.
| Field | Default | Description |
|---|---|---|
| Identifiers | None | How an inbound request is matched to this member. Add as many as you need. |
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 identity an API key method produces: the Secret ID for API Key (secret store), or the Key ID for API Key (provider). |
| 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, set on the Caps & Limits tab (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 Budget (USD) or Token cap: without one, a single caller can run away with spend before the team-wide cap even notices.
- Requests / minute and Tokens / minute enforce 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 |
|---|---|---|
| Budget (USD) | Unlimited | Member-wide monthly USD ceiling across every team and surface. Must not be negative. A cap of 0 means no cap. When enforced, requests are blocked with HTTP 429 team_quota_exceeded once spend has reached the cap. The request whose spend crosses the cap is not itself blocked. |
| Token cap | Unlimited | Member-wide monthly token ceiling across every team and surface. A cap of 0 means no cap. When enforced, requests are blocked with HTTP 429 team_quota_exceeded once tokens have reached the cap. The request whose usage crosses the cap is not itself blocked. |
| Resets monthly from | Not set (calendar month, UTC) | Date and time the member’s budget period starts from. When set, Budget (USD) and Token cap reset on the monthly anniversary of this date and time; a date on the 31st falls on the last day of shorter months. Use calendar month clears it, so the caps reset on the first of each month (UTC). |
| Requests / minute | Unlimited | Per-minute request cap for this member (the “virtual key” RPM), enforced by the team gate over a trailing 60-second window. Blocked requests get HTTP 429 member_rate_limit_exceeded. |
| Tokens / minute | Unlimited | Per-minute token cap for this member (the “virtual key” TPM), counted over the same trailing 60-second window. Blocked requests get HTTP 429 member_rate_limit_exceeded. |
| 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 HTTP 403 model_not_allowed. A request that omits the model (surface-controlled model) is never blocked. |
For a saved member, the tab also shows a usage bar under Budget (USD) and Token cap for the current budget period, and the period itself as “Current period: start to end”.
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.
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 recognise 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 / 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 setting on the Teams page’s Enrolment tab, which needs Administrator access by default. A surface only opts in to it.
| Field | Default | Description |
|---|---|---|
| Enable auto-provisioning | Off | Master switch. While off, no caller is auto-provisioned, whatever a surface’s opt-in says. |
| 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 & safety caps | Not set | Defaults stamped on each new member: Allowed models, Monthly budget (USD), Monthly token cap, Requests / minute, and Tokens / minute. The two safety caps below sit in the same section. |
| Team assignment 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 / minute | Unlimited | Max member creations per minute across the appliance (a guard against a hostile token issuer filling the disk). |
Each identifier rule has a source (JWT claim, JWT subject (sub), mTLS subject CN, mTLS email SAN, or API key name) and the identifier kind to store it as. Auto kind uses the source’s natural kind, for example Email for an mTLS email SAN.
Each team-assignment rule:
| Field | Description |
|---|---|
| When | Always applies, or a condition on the caller’s JWT claims: Claim equals, Claim contains, or Claim present. |
| Assign to | An existing team, picked from Team, or A team per claim value, built from a Team id template such as tenant-{{claim.tid}}. |
| Create the team if it doesn’t exist | Creates the derived team the first time a caller needs it, using the team name template, monthly budget (USD), and monthly token cap entered below it. When off, a rule pointing at a team that doesn’t exist is skipped. |
The Dry-run preview panel shows what provisioning would do for a sample identity, using the current, unsaved configuration, without creating anything.
Built-in members
The appliance always seeds two system members, so an unmatched or test caller still resolves to a member:
| 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”. |
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.