Multi-tenant SaaS budgets
Give every customer (tenant) its own budget using a custom metadata field.
If you sell to organizations, you usually budget per tenant, not per end user. Any metadata
field you attach — like tenant_id — can drive its own separate budget.
The rule
Attach a tenant_id to your calls (below), then, on the Firewall page, click
New rule from template and pick Give each team or feature its own budget:
Which tag? — tenant_id. Every value of that tag gets its own separate budget: when one
tenant spends past the limit, only that tenant is paused; the others are untouched.
Limit for each ($) and Resets after (minutes of no spend) — the limit each tenant gets, and how long a tenant has to go quiet before its counter starts again. That's an inactivity window: a tenant that keeps spending never resets. For a calendar period (hourly, daily, weekly, monthly) build a Custom rule instead.
Want one shared budget for a single named tenant instead of a budget per tenant? That's Shared limit for a group — same tag, one specific value, one pool. The difference is the APPLY TO: see Filter vs apply-to.
Prefer Block to cut a tenant off, or Reroute to keep them running on a cheaper model.
The snippet
Attach tenant_id as metadata. With @tp.workflow, a metadata argument on your entrypoint is
picked up automatically:
import token_police as tp
@tp.workflow(name="tenant_app")
def handle(user_id: str, metadata: dict, query: str):
return client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": query}],
)
handle(user_id="u_9", metadata={"tenant_id": "acme_co"}, query="...")import { workflow } from "token-police";
const handle = workflow({ name: "tenant_app" }, (input: {
userId: string; metadata: Record<string, string>; query: string;
}) => client.chat.completions.create({ /* ... */ }));
handle({ userId: "u_9", metadata: { tenant_id: "acme_co" }, query: "..." });You can also pass a fixed metadata={"tenant_id": ...} to @tp.workflow(...) / workflow({...}),
or set it per call with session(...). Tag values are matched exactly, so keep the spelling
stable — see Identity.

