Economy funnel
The operator report over the real economy funnel — question opened, free question consumed, decision run, call locked, call graded, XP, quest, badge, settled purchase and second paid run — plus the activation metric. Operator-only (x-admin-key); no user tracking.
The economy funnel is the operator's view of how far a visitor travels: from a decision question being opened, through a free question being consumed and a decision being served, to a player call being locked, the outcome resolving, the call being graded, XP, quests, badges — and finally a settled purchase and a second paid run.
It is server-side only. Every count is an aggregate over state Tollbridge already keeps (the decision journal, the billing ledger, the call record, the Arena journals). There is no visitor tracking, no analytics vendor and no per-user read event: an anonymous read is deliberately unmeasurable and is reported as such rather than guessed.
GET /v1/admin/economy-funnel (operator-only)
The newest funnel snapshot and its trend. It is an operator surface: it requires
the x-admin-key header (the same control-plane key the other operator routes
use) and is not part of the public discovery documents. When no key is
configured the route does not exist at all.
curl "https://api.tollbridge.app/v1/admin/economy-funnel?limit=30" \
-H "x-admin-key: <ADMIN_KEY>"data:
{
"funnel": {
"at": "2026-10-15T09:00:00.000Z",
"window_calls": 1000,
"xp_by_kind": { "ask": 120, "correct": 40, "quest": 9 },
"steps": [
{ "step": "anonymous_read", "count": null, "measurable": false, "note": "not measurable — no server-side read tracking exists" },
{ "step": "question_open", "count": 120, "measurable": true, "derivation": "count(*) from decisions" },
{ "step": "free_credit_claim","count": 40, "measurable": true, "derivation": "credit_ledger where kind = 'free_question'" },
{ "step": "decision_run", "count": 110, "measurable": true, "derivation": "decisions served, non-refused" },
{ "step": "player_call_lock", "count": 60, "measurable": true, "derivation": "count(*) from player_calls" },
{ "step": "decision_resolve", "count": 80, "measurable": true, "derivation": "decision_outcomes realized + unresolvable" },
{ "step": "player_call_grade","count": 55, "measurable": true, "derivation": "player_calls where graded_at is not null" },
{ "step": "xp_award", "count": 300, "measurable": true, "derivation": "count(*) from xp_journal" },
{ "step": "quest_complete", "count": 12, "measurable": true, "derivation": "count(*) from quest_completions" },
{ "step": "badge_award", "count": 7, "measurable": true, "derivation": "count(*) from badges" },
{ "step": "rank_up", "count": null, "measurable": false, "note": "derivable on demand — a per-payer running-XP crossing, never run in the tick" },
{ "step": "credit_purchase_begin", "count": 4, "measurable": true, "note": "server-side there is no separate client begin event; equals the settled purchase below" },
{ "step": "credit_purchase", "count": 4, "measurable": true, "derivation": "credit_lots (pack_purchase | plan_recharge) — settled only" },
{ "step": "second_paid_run", "count": 3, "measurable": true, "derivation": "billing accounts with >= 2 debit ledger rows" }
]
},
"activation": {
"activated_payers": 12,
"graded_calls": 55,
"first_lock_to_first_grade": { "median_ms": 3600000, "n": 12, "window_calls": 1000 }
},
"trend": [ /* one compact point per snapshot, newest first */ ],
"window": { "limit": 30, "snapshots": 30, "newest_at": "2026-10-15T09:00:00.000Z", "oldest_at": "2026-10-14T11:00:00.000Z" }
}A step whose count is null is unmeasured, never zero: the anonymous read
has no server-side source, and a rank-up is a per-payer running-total crossing
that is derived on demand rather than recomputed on every tick.
Activation
A payer activates on their first graded player call — the moment a call
they locked before the outcome is scored against the house, not the moment they
lock it. activation.activated_payers is the count of distinct payers with at
least one graded call; activation.first_lock_to_first_grade.median_ms is the
median gap between a payer's first lock and their first grade over the bounded
window of recent calls. An empty window reports a null median, never a zero.
TypeScript
// The Tollbridge operator report is a private read — call it with the admin key
// from your own backend, never from a browser.
const base = 'https://api.tollbridge.app';
const res = await fetch(`${base}/v1/admin/economy-funnel?limit=30`, {
headers: { 'x-admin-key': process.env.TOLLBRIDGE_ADMIN_KEY! },
});
const { data } = (await res.json()) as {
data: { funnel: { steps: Array<{ step: string; count: number | null }> } | null; trend: unknown[] };
};
for (const step of data.funnel?.steps ?? []) {
console.log(step.step.padEnd(20), step.count === null ? 'unmeasured' : step.count);
}Python
import os
import httpx
res = httpx.get(
"https://api.tollbridge.app/v1/admin/economy-funnel",
params={"limit": 30},
headers={"x-admin-key": os.environ["TOLLBRIDGE_ADMIN_KEY"]},
)
data = res.json()["data"]
funnel = data["funnel"] or {"steps": []}
for step in funnel["steps"]:
print(step["step"], "unmeasured" if step["count"] is None else step["count"])MCP
The economy funnel is an operator report, so it is deliberately not part of the public MCP tool catalog — agents read the economy through the existing catalog tools instead (decisions, calls, the Arena), never the private operator surface.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}{
"tools": [
{ "name": "tollbridge_decision" },
{ "name": "tollbridge_calls" },
{ "name": "tollbridge_arena_profile" },
{ "name": "tollbridge_arena_leaderboard" }
]
}