TokenPolice
Docs
Troubleshooting

Fix anonymous users in the dashboard

Traffic showing as anonymous/free? Set identity on the session.

Everything's arriving in the dashboard — but every call shows up as anonymous on the free plan, so per-user and per-plan breakdowns are empty and plan-scoped rules never match the right people. That means identity isn't reaching the wrapper. Those anonymous / free values are just the defaults the SDK uses when nothing was attached.

The usual cause: parameter names don't match

Identity binding is spelling-exact. If the names are off, the values are silently ignored.

Python @tp.workflow — the wrapped function's parameters must be named literally user_id, paid_plan, session_id. Not userId, not uid, not plan.

@tp.workflow(name="handle_turn")
def handle_turn(user_id: str, paid_plan: str, session_id: str, message: str):
    return client.chat.completions.create(...)

Node tp.workflow — the wrapped function's first argument must be an object carrying userId / paidPlan / sessionId (snake_case keys are accepted too).

const handleTurn = tp.workflow(
  { name: "handle_turn" },
  ({ userId, paidPlan, sessionId, message }) => client.chat.completions.create({ ... }),
);

Or set it at the call site — wrap the call in a session(...) and pass the values directly, regardless of your function's parameter names:

with tp.session(
    user_id=user.id,
    paid_plan=str(user.plan or "free").strip().lower(),   # rules match the plan string exactly — normalize once, here
    session_id=convo_id,
):
    client.chat.completions.create(...)

Normalize the tier value the same way everywhere you pass it. paid_plan is stored verbatim and matched byte for byte, so "Free" and " free " are different plans from "free" — the rule you meant to hit just never fires. See Unexpected blocks.

See Identity for the three fields and their defaults, and Cap free-tier users for a worked @tp.workflow example.

If sessions aren't grouping — every turn shows as its own one-message conversation — the session_id isn't stable across turns. A fresh id per call is a broken integration: re-use the same id (your app's own thread/conversation id) for every turn of one conversation. The rules are in the stable-session_id contract.

These are correctness warnings, not blocks — TokenPolice keeps recording either way. Fixing identity just makes the dashboard's per-user, per-plan, and per-conversation views meaningful.

Next