Org chart
The reporting hierarchy Pact builds from each member's manager, how visibility differs by role, and how to reassign a report's manager.
Org chart
Pact keeps a live reporting hierarchy for your workspace, built entirely from
one field: each member's manager_id. There's no separate org-chart data store
to keep in sync — the tree is the tenant_users table, walked by manager
edges. The chart drives who can see whose records elsewhere in the product, so
it's a first-class part of the RBAC surface, not a cosmetic diagram.
Live
The org chart is served by GET /v1/org-chart and edited via
PATCH /v1/users/{user_id}/manager (api/routes/org_chart.py). Chain and
subtree traversal live in core/visibility/hierarchy.py; both endpoints are
tenant-scoped and behind verify_api_key.
What the chart returns
GET /v1/org-chart responds with four things, computed relative to the
authenticated caller:
viewer— the caller's own node (id, name, email, role,manager_id, status).upward— the caller'smanager_chain: who they report to, all the way to the top.downward— the caller'ssubtree: their direct and transitive reports.can_reassign— whether the caller may move people (see below).
Visibility is role-dependent
The tree you get back depends on your role:
- Bypass roles (owner, admin — the roles that see the whole tenant) receive
every active user in the workspace as one flat set of nodes; the frontend
renders the tree from the
manager_idedges. Admins have noupwardchain — they sit outside the rep hierarchy. - Everyone else sees only their own slice: their upward chain so they know who they report to, plus their downward subtree — their reports. They cannot enumerate peers in unrelated branches.
This mirrors the record-visibility model used elsewhere: a manager sees their reports' work; a rep sees their own.
Reassigning a manager
PATCH /v1/users/{user_id}/manager moves a member under a new manager (or to
the root by clearing it). Two guardrails apply:
- Permission. The mutation is gated on the
team:managepermission (can_reassignin the read response tells the UI whether to even show the control). Bypass roles are always allowed. - Cycle safety. A member cannot be placed under one of their own reports — the hierarchy stays a tree, never a loop.
One field, many consumers
Because reporting lines are just manager_id, changing a manager instantly
re-shapes every downstream feature that reads the hierarchy — record
visibility, manager roll-ups, and subtree-scoped queries — with no extra
sync step.