Actions: block, notify, reroute
What a rule does when it fires — and the two switches that decide whether it really acts.
Every rule ends in an action. There are three:
| Action | What it does | You set |
|---|---|---|
| Block Request (BLOCK) | Drops the request through the SDK before the call reaches your provider. | Nothing extra. |
| Alert Admin (NOTIFY) | Lets the call through, and raises an alert. | Which channels, and a throttle. |
| Reroute Model (REROUTE) | Swaps the call's model for a cheaper one before dispatch. | A target provider and model. |
When the action fires
An action fires in one of two ways, depending on whether the rule has a limit:
- With a limit — the rule counts spend for each pool and acts only once a pool crosses the limit. This is what people mean by a budget.
- With no limit (unconditional) — the rule acts on every call its filter matches, with no
counting at all. "Block every call from
plan = free" needs no threshold.
In the custom builder this is the Unconditional checkbox. Reroute defaults to unconditional; Block and Notify default to budget-gated. Uncheck it on a reroute (or tick it on a block) to swap the behaviour.
Block
The request is stopped in the SDK and your code receives a TokenPoliceBlockedError — the one
exception TokenPolice ever raises into your app, and only under enforce. Everything else stays
fail-open. See Handling blocks for the catch
pattern.
Blocking happens before the provider request, so a blocked call costs you nothing. Earlier calls in the same agent run already happened and were billed.
Notify
The call still happens; TokenPolice just tells you it crossed the line. Pick one or more verified channels to send to, and set a throttle so a noisy rule doesn't flood you — a cooldown (default 300 seconds) and a maximum alerts per hour (default 20). Those throttles apply to external channels only; the in-app inbox always receives the alert.
Notify needs somewhere to send. See Notifications to add and verify a channel first.
Notify rules have no Dry-run/Enforce dial. They never affect a request, so there is nothing to gate — the rules table shows a dash in the Current Mode column, and a Notify rule cannot be promoted to Enforce.
Reroute
Instead of blocking, swap the model for a cheaper one. You give a target provider and model (for
example openai / gpt-4o-mini), and matching calls run on that model instead. If the rule has
a limit, the swap starts once the pool crosses it; with no limit, every matching call is swapped.
The target model has to be one TokenPolice already has pricing for, or the rule won't save.
Reroute supports same-provider swaps only — pick a smaller or cheaper model from the same provider. A cross-provider target is rejected at run time and shows as Reroute rejected in the Audit log.
Reroute does not switch the model on some frameworks — including LangChain, LangGraph, LlamaIndex, Pydantic AI, and the Vercel AI SDK. Those calls still run on the original model. Confirm a swap actually happens in your app before you enforce this in production. Look for the Reroute rejected rows in the Audit log — they are in the default All tab — and for the rejected-reroute count on each run in Traces. Not on the Routing tab: that feed lists reroutes that did happen, so a swap the SDK refused never appears there.
Watch before you act
Every Block and Reroute rule starts in Dry-run, and while it's there — or while your app's SDK is in dry-run — the rule only records what it would have done. Two switches have to agree before anything is stopped:
- The rule is set to Enforce, and
- your app initialized the SDK with
firewall: "enforce".
See Dry-run vs enforce.
If a rule is Enforce but your app most recently reported its SDK firewall mode as dry_run
or off, the Firewall page shows a banner — "Rules set to Enforce, app in Dry-run" (it reads
"app in Off" when the SDK reported off). The rule evaluates but never acts until your app
initializes with firewall: "enforce".
The Audit log is where you see which switch was open: a would-be action is labelled Would deny or Would reroute, with a chip naming SDK in dry-run, Rule in dry-run, or SDK & rule in dry-run.

