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.
Related: conversations look single-turn
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.

