TokenPolice
Docs
Firewall & Budgets

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:

ActionWhat it doesYou 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:

  1. The rule is set to Enforce, and
  2. 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.

Next