TokenPolice
Docs
Firewall & Budgets

Filter vs apply-to

Every rule answers two questions: which calls does it watch, and who gets their own budget — plus how the window resets.

When you build a custom rule (or open a template to see how it works), the two settings that matter most are the IF filter and the APPLY TO pool. Get these right and the rest is just numbers.

IF — which calls the rule watches

The filter picks the subset of traffic the rule cares about. Rules can match on any of these fields:

FieldWhat it matches
user_idThe end user your app identifies on the call.
paid_planThe plan string your app stamps on the call.
modelThe model that ran.
providerThe provider that ran it.
operationThe kind of call — chat, embedding, image, and so on.
session_idYour app's conversation or session id.
trace_idOne agent run.
metadata.<key>Any custom tag your app attaches — metadata.team, metadata.feature, metadata.department.

The engine accepts all of them. What you can author today is narrower, and it is worth being precise about where the line falls:

Custom rule builderTemplatesAPI
Match on user_id, paid_plan, operation, metadata.<key>✅ (paid_plan, user_id, a metadata key)
Match on session_id✅ (the session templates)
Match on model, provider, trace_id
Operators is, contains, exists✅ (is, exists)
Operators is not, in
Composite AND / OR filters

"API" means a direct call to the rules API with a management key — there is no dashboard or template route to those rows yet. If you need one of them, build the rule through the API and it will show up in the rules table like any other.

is is an exact match — case- and whitespace-sensitive. The dashboard says it plainly: "Values are compared case-sensitively, spaces included — free never matches Free." The same is true of APPLY TO: pooling by paid_plan gives free and Free two separate budgets. Send one canonical spelling from your app (lowercase and trimmed is the safe convention) and use exactly that string in the rule.

More than one condition

A rule's filter can be a single condition or a composite one: an AND or OR group of up to 20 conditions. Groups don't nest — one combinator, one flat list. The rules table renders composite filters as IF … AND … / IF … OR …, and flags two degenerate shapes for you: an empty AND matches everything, an empty OR matches nothing.

Composite filters are API-only today. The enforcement engine evaluates them, and the rules table displays them, but neither the Custom rule builder nor any template will author one — the builder sends a single condition. Create composites through the rules API.

APPLY TO — who gets their own budget

This decides how the limit is pooled:

Apply toMeaning
Everyone matching this rule (shared pool)One budget shared across all matching traffic.
Each unique user_id (individual pool)Every user gets their own copy of the limit.
Each unique <field> (individual pool)Every distinct value of the field gets its own copy.

You can group by any of the fields above, not just user_id — grouping by metadata.team gives every team its own budget from one rule.

The filter and the pool are independent. The filter says which calls count; the pool says how many separate budgets the limit becomes.

A worked example

Give every user on the free plan their own $5 / month cap.

  • IF paid_plan is free — the rule only watches free-plan calls.
  • APPLY TO each unique user_id — so the $5 is per user, not $5 shared across all of them.

Flip APPLY TO back to everyone matching and the same rule becomes "$5 / month shared across the entire free tier" — a very different limit from the same filter.

When the limit resets

The third setting that changes a rule's meaning is its reset period. There are three kinds, and they behave differently.

Reset periodSet it withWhen a paused pool comes back
Calendar — hourly, daily, weekly, monthlyThe Resets every dropdown in the builder and most templatesAt the start of the next period, aligned to the UTC calendar: a day starts at UTC midnight, a week on Monday, a month on the 1st.
Sliding — 5 to 1440 minutesThe session and per-tag templates ("resets after N minutes of no spend")Only after that many minutes with no matching spend for that pool.
LifetimeChoosing Lifetime, or setting a limit with no periodNever. The cap is permanent until you change the rule.

A sliding window is an inactivity window, not a rolling "last N minutes" window. While a pool keeps spending with no quiet gap that long, its total keeps growing and its budget never resets. That fits something that genuinely goes idle — a chat session, one customer's burst of work. For a pool that is always busy, or a cap that must reset on a schedule, use a calendar period instead.

The rules table shows which one a rule uses: per DAILY, resets 30m after last spend, or LIFETIME.

Next