Skip to main content
assign_to= on await_human() tells the server “who should do this.” The router resolves it to a specific user record, stamps that user as the assignee, and notifications go out per-channel.

The four shapes

All four resolve to a directory user (or stay unassigned if no match — see below).

The user directory

Users live in the users table, manageable from the dashboard’s Settings → Users page. Each user has:
  • email (or null for Slack-only users)
  • slack_team_id + slack_user_id (optional)
  • role — free-form string, e.g. kyc-reviewer, support-tier-1
  • access_level — free-form string, e.g. junior, senior
  • pool — free-form string, e.g. ops, compliance
  • is_operator — bool, dashboard admin
  • active — bool, default true
The fields are deliberately free-form. We don’t enforce a schema; you pick the names that match your org.

Fairness model

Within a resolved set (pool, role, etc.), the router picks the least-recently-assigned user. The user record carries last_assigned_at; on every routing decision it gets bumped on the picked user. This spreads load — fresh tasks rotate through a pool rather than piling up on whoever’s listed first. It’s Option C from the routing pillar: not perfectly even (a slow reviewer ends up with fewer recent tasks), but trivially understandable and good-enough for v0.1. Operators who need a different fairness model (round-robin, weighted, on-call schedules) override the router. See Custom routers.

Resolution rules

Slack-only users

Users without an email but with a Slack identity work fine for routing:
The audit log records assigned_to_user_id (stable across email changes) so attribution stays consistent.

Marketplace (reserved for Phase 3)

Currently raises MarketplaceNotAvailableError. Reserved for the post-Phase-2 workforce marketplace where tasks can be sourced from external reviewers. See the roadmap.

Custom routers

The router lives in server/services/task_router.py. The function:
Override it for round-robin, weighted, or schedule-driven routing. Drop a replacement module and import from task_router. This is one of the four buckets — extension points by design. New routing strategies are a Phase-2 community contribution surface.

Example: KYC review queue

Each call stamps a different reviewer; load spreads naturally via least-recently-assigned within each pool.