PPactDocs
Team & Org

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's manager_chain: who they report to, all the way to the top.
  • downward — the caller's subtree: 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_id edges. Admins have no upward chain — 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:manage permission (can_reassign in 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.