# Cap spend and rate per member

> Set a member's monthly budget, per-minute rate caps, and model allow-list, so each caller behaves like its own virtual key.

This guide configures a member’s own monthly ceilings, per-minute rate caps, and model allow-list, so a single caller, behaving like its own virtual key, cannot exhaust a shared surface budget or use a model it shouldn’t. For the conceptual model, see [Teams and attribution →](/products/affinidi-trust-fabric/agent-stream/concepts/teams-and-attribution.md).

A surface-wide usage limit caps total spend, but cannot stop one caller from consuming the entire budget before anyone else gets a share, and cannot restrict which models a specific caller may use.

Use this guide when:

- Different callers on the same surface need separately enforced budgets, not one shared ceiling.

- A specific caller should be restricted to a subset of models.

- You need to bound a short burst from one caller without throttling every other caller on the surface.

You do not need per-member caps if every caller sharing a surface can be trusted to the same shared budget from [Cap spend per pipeline stage](/products/affinidi-trust-fabric/agent-stream/how-to-guides/cost-and-usage/cap-spend-per-pipeline-stage.md).

## Prerequisites

- A member already created and attributed. See [Attribute usage to members and teams](/products/affinidi-trust-fabric/agent-stream/how-to-guides/teams/attribute-usage-to-members-and-teams.md) if you do not have one yet.

## Steps

Set the member’s monthly ceilings

On the Teams page, switch to the Members tab and open the member to configure. Set Budget (USD) and/or Token cap: a personal ceiling across every team and surface this member touches.

Set its per-minute rate caps

Set Requests / minute and Tokens / minute. Both are enforced over a trailing 60-second window, not a fixed wall-clock minute boundary.

Restrict which models it may call

Add model names under Allowed models, the member’s model allow-list, to reject any request naming a different model with 403. Leave this empty for an unrestricted member. A request that omits model entirely (a surface-controlled model) is never blocked here.

Turn on Enforce, if not already

Back on the surface’s team-attribution block, confirm Enforce team / member limits (block at 429) is on. Without it, every cap configured above is tracked for reporting but never blocks a request.

Optional: allow individual overage

Turn on Allow individuals to exceed their personal cap (team cap still applies) on the surface’s team-attribution block if a member’s personal cap should be advisory rather than a hard stop; only the member’s team-wide caps still block in that case.

Save

Save the member and, if you changed it, the surface. Changes apply immediately.

## Confirm

Replace <YOUR_APPLIANCE_HOST> and <YOUR_SURFACE_ROUTE> with your surface’s values, and use a credential matching your member’s identifier.

### Test 1: a request naming a disallowed model returns 403

```bash
curl -k -X POST "https:///v1/chat/completions" \
  -H "Authorization: Bearer " \
  -H "Content-Type: application/json" \
  -d '{
    "model": "a-model-not-on-the-allow-list",
    "messages": [
      { "role": "user", "content": "Say hello." }
    ]
  }'
```

The -k flag disables TLS certificate verification. Use this for local testing only. Remove it in production.

Expected: 403 Forbidden, since the requested model is not in this member’s Allowed models list.

### Test 2: exceeding the per-minute cap returns 429

Send requests naming an allowed model faster than the configured Requests / minute within a trailing 60-second window. Expected: once the cap is exceeded, further requests return 429 until the window rolls forward.

## Troubleshooting

| Symptom | Likely cause | Fix |
| Caps are configured but never trigger. | Enforce team / member limits (block at 429) is off on the surface’s team-attribution block. | Turn it on; without it, every cap here is recorded but never blocks. |
| The model allow-list rejects even the intended model. | Allowed models doesn’t include the model name exactly as the request sends it. | Confirm the model string matches character-for-character, including any provider-specific suffix. |
| A member exceeds its own monthly budget without being blocked. | Allow individuals to exceed their personal cap is on; only the member’s team-wide caps still enforce in that case. | Turn it off if the member’s own cap should be a hard stop too. |
| The per-minute cap trips sooner than expected. | The window is a trailing 60 seconds from each request, not a fixed per-minute clock boundary; a short burst anywhere within that span counts. | This is expected behaviour; space requests out, or raise the cap if the burst pattern is legitimate. |

## Next steps

- [Alert on budget breaches and cost spikes](/products/affinidi-trust-fabric/agent-stream/how-to-guides/cost-and-usage/alert-on-budget-breaches-and-cost-spikes.md): Add a surface-wide notification alongside this member-level hard cap.

- [Control access to a surface with an OPA policy](/products/affinidi-trust-fabric/agent-stream/how-to-guides/policies/control-access-to-a-surface-with-an-opa-policy.md): Gate access by claim in addition to a member’s own caps.

## Related

- [Members reference](/products/affinidi-trust-fabric/agent-stream/reference/teams/members.md): Every member field, identifier kind, and the auto-provisioning defaults that can stamp these caps automatically.

- [Teams and attribution](/products/affinidi-trust-fabric/agent-stream/concepts/teams-and-attribution.md): The conceptual model this guide is built on.

- [Teams reference](/products/affinidi-trust-fabric/agent-stream/reference/teams/teams.md): The team gate’s exact enforcement order, and how team-wide caps interact with a member’s own.

- [Cost and usage limits reference](/products/affinidi-trust-fabric/agent-stream/reference/surfaces/cost-and-usage-limits.md): The surface-wide usage-limit mechanism this member-level cap is distinct from.
