Member API
Table of Contents
upsquad/member/v1/member.proto
AddTeamMembershipRequest
AddTeamMembershipRequest adds a member to a team.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id is the UUID of the member. | |
| team_id | string | team_id is the UUID of the team. | |
| role | string | role is the membership role; default "member". |
AddTeamMembershipResponse
AddTeamMembershipResponse confirms the team membership addition.
| Field | Type | Label | Description |
|---|---|---|---|
| team_membership | TeamMembership | team_membership is the created or existing team membership record. | |
| already_exists | bool | already_exists is true if the membership already existed (idempotent no-op). |
BulkImportMembersRequest
BulkImportMembersRequest imports up to 500 members in a single transaction.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| format | string | format is the input format: "csv" or "json". | |
| payload | bytes | payload is the raw CSV or JSON data (max 500 rows). |
BulkImportMembersResponse
BulkImportMembersResponse returns the import results.
| Field | Type | Label | Description |
|---|---|---|---|
| total_rows | int32 | total_rows is the number of rows parsed from the payload. | |
| success_count | int32 | success_count is the number of successfully imported rows. | |
| error_count | int32 | error_count is the number of rows that failed. | |
| results | ImportRowResult | repeated | results is the per-row import result detail. |
CheckEmailUniqueRequest
CheckEmailUniqueRequest asks whether an email is unused in the current org. Org scope comes from the gateway-enforced session context; emails from other orgs are NOT leaked.
| Field | Type | Label | Description |
|---|---|---|---|
| string | email is the address to check. Compared case-insensitively after trimming surrounding whitespace. Required. |
CheckEmailUniqueResponse
CheckEmailUniqueResponse returns the uniqueness verdict.
| Field | Type | Label | Description |
|---|---|---|---|
| unique | bool | unique is true iff no active member of the caller's org currently claims the email. Soft-deleted members do not block uniqueness. | |
| collision | EmailCollision | collision is populated iff unique == false. It describes the existing member that owns the email so the UI can render an actionable error ("Already used by Alice — Engineering Lead"). |
ClearanceTrailEntry
ClearanceTrailEntry is a single step in the clearance derivation walk.
| Field | Type | Label | Description |
|---|---|---|---|
| from_member_id | string | from_member_id is the UUID of the member at this step. The first entry's from_member_id equals the requested member_id; subsequent entries climb the reports_to chain. | |
| from_member_name | string | from_member_name is the denormalized display_name of the member at this step, included so the UI does not need a second round-trip. | |
| level | int32 | level is the clearance level of the member at this step (1..5). | |
| rule | string | rule labels the derivation rule that explains this step's level. Stable values: "explicit" — set independently of the supervisor; this is the canonical label for humans and for agents at the org root. "parent-minus-one" — agent inherited supervisor.clearance - 1 (the GAP-40 InferAgentClearance default). "capped-at-manager" — agent at supervisor.clearance, the cap imposed by the agent.clearance <= supervisor invariant in CreateMember / MoveMember. "floor" — clamped to 1 because supervisor.clearance was 1 and parent-minus-one would underflow. |
CreateMemberRequest
CreateMemberRequest adds a new member to the org.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_type | MemberType | member_type distinguishes human operators from AI agents. | |
| display_name | string | display_name is the human-readable name. | |
| string | email is required for humans, optional for agents. | ||
| clerk_user_id | string | clerk_user_id is required for humans. | |
| clearance | int32 | clearance is the member's clearance level (1-5). | |
| reports_to | string | reports_to is the UUID of the supervisor; empty = root node. | |
| team_ids | string | repeated | team_ids is an optional list of initial team assignments. |
| title | string | title is the member's free-text role label ("Sales head", "Staff Engineer"); optional, empty string = no title. Persisted on members.title (migration 120, #1597). |
CreateMemberResponse
CreateMemberResponse returns the created member.
| Field | Type | Label | Description |
|---|---|---|---|
| member | Member | member is the newly created member record. |
DeleteMemberRequest
DeleteMemberRequest soft-deletes a member.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id is the UUID of the member to soft-delete. | |
| successor_id | string | successor_id is the UUID of the member that direct reports of the deleted member should be reparented to. REQUIRED when the deleted member has at least one active direct report; the server returns INVALID_ARGUMENT pointing at this field otherwise. The successor must be a different active member of the same org and must NOT be a descendant of member_id (would create a cycle). Empty string is permitted only when the member has no direct reports — in that case the legacy "reparent to deleted member's supervisor" path is preserved (a no-op for leaf members). GAP-V2-5 (#1062). Powers the redesigned Remove modal's blocking successor-pick flow. |
DeleteMemberResponse
DeleteMemberResponse confirms the soft deletion.
| Field | Type | Label | Description |
|---|---|---|---|
| deleted_at | google.protobuf.Timestamp | deleted_at is the timestamp when the member was marked as deleted. | |
| reparented_count | int32 | reparented_count is the number of direct reports moved to the chosen successor (or to the deleted member's supervisor when no successor was required — i.e. the leaf-member case). |
EligibleManagerCandidate
EligibleManagerCandidate is the projection of a Member used by the manager dropdown — display_name + role label + clearance level.
| Field | Type | Label | Description |
|---|---|---|---|
| member_id | string | member_id is the UUID of the candidate. | |
| display_name | string | display_name is the human-readable name shown in the dropdown. | |
| role | string | role is the denormalised label combining member_type with the most useful role hint for the picker UI. For now this is the member_type string ("human" / "agent"); future iterations may inline a richer label without breaking the wire shape. | |
| level | int32 | level is the clearance level (1..5) of the candidate. Surfacing this alongside the name lets the dropdown render the clearance pill without a second round-trip. |
EmailCollision
EmailCollision is the projection of the colliding member surfaced to the Add Person modal.
| Field | Type | Label | Description |
|---|---|---|---|
| member_id | string | member_id is the UUID of the existing member. | |
| display_name | string | display_name is the existing member's name, for UI presentation. | |
| role | string | role is the denormalised role label — see EligibleManagerCandidate.role for the encoding. |
GetAncestorsRequest
GetAncestorsRequest retrieves the chain from a member up to root.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id is the UUID of the starting member. |
GetAncestorsResponse
GetAncestorsResponse returns the ancestor chain.
| Field | Type | Label | Description |
|---|---|---|---|
| ancestors | Member | repeated | ancestors is ordered from immediate supervisor to root. |
GetClearanceTrailRequest
GetClearanceTrailRequest asks for the derivation chain of a single member's effective clearance.
| Field | Type | Label | Description |
|---|---|---|---|
| member_id | string | member_id is the UUID of the member whose clearance derivation is requested. Tenant scope is taken from the gateway-enforced session context — the request body intentionally has no org_id field. |
GetClearanceTrailResponse
GetClearanceTrailResponse returns the derivation chain.
| Field | Type | Label | Description |
|---|---|---|---|
| entries | ClearanceTrailEntry | repeated | entries is ordered from the target member at index 0 up the reports_to chain to the root. Each entry explains how that member's clearance level was set. |
GetDirectReportsRequest
GetDirectReportsRequest retrieves immediate children of a member.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id is the UUID of the member whose reports to list. | |
| page_size | int32 | page_size is the maximum number of results per page. | |
| page_token | int32 | page_token is the 0-based offset for pagination. |
GetDirectReportsResponse
GetDirectReportsResponse returns the direct reports.
| Field | Type | Label | Description |
|---|---|---|---|
| direct_reports | Member | repeated | direct_reports is the list of immediate subordinates. |
| total_count | int32 | total_count is the total number of direct reports. |
GetEligibleManagersRequest
GetEligibleManagersRequest asks for the candidate manager set for a given member. The org is resolved from the gateway-enforced session context — the request body intentionally omits org_id, mirroring GetClearanceTrail.
| Field | Type | Label | Description |
|---|---|---|---|
| member_id | string | member_id is the UUID of the member whose manager dropdown is being populated. Required. |
GetEligibleManagersResponse
GetEligibleManagersResponse returns the cycle-filtered candidate set.
| Field | Type | Label | Description |
|---|---|---|---|
| candidates | EligibleManagerCandidate | repeated | candidates is the ordered list of members that may legally be set as member_id's supervisor. The set excludes member_id itself and all of its descendants (transitively); soft-deleted members are excluded. Order is alphabetical by display_name for stable UI rendering. |
GetMemberRequest
GetMemberRequest retrieves a single member by ID.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id is the UUID of the member to retrieve. |
GetMemberResponse
GetMemberResponse returns the member with team memberships.
| Field | Type | Label | Description |
|---|---|---|---|
| member | Member | member is the retrieved member record with team_memberships populated. |
GetMemberTreeRequest
GetMemberTreeRequest retrieves the full reporting tree for the org.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| root_member_id | string | root_member_id optionally starts the tree from a specific member; empty = full org tree from roots. | |
| max_depth | int32 | max_depth limits the depth of the tree; 0 = unlimited. | |
| include_deleted | bool | optional | include_deleted controls whether soft-deleted members appear as nodes. Default: FALSE — set to TRUE for audit tooling that needs to see tombstones. See ListMembersRequest.include_deleted for the wave history behind the default value. |
GetMemberTreeResponse
GetMemberTreeResponse returns the flat tree representation.
| Field | Type | Label | Description |
|---|---|---|---|
| nodes | MemberTreeNode | repeated | nodes is the flat list of tree nodes ordered by depth-first traversal. |
| total_members | int32 | total_members is the total number of active members in the org. |
ImportRowResult
ImportRowResult describes the outcome of a single import row.
| Field | Type | Label | Description |
|---|---|---|---|
| row_number | int32 | row_number is the 1-based index of the row in the input. | |
| success | bool | success is true if the row was imported successfully. | |
| error | string | error is the error message (empty on success). | |
| member_id | string | member_id is the UUID of the created member (populated on success). |
InferAgentClearanceRequest
InferAgentClearanceRequest asks for the inferred clearance level an agent would receive if assigned to the given supervisor. Tenant scope is taken from the gateway-enforced session context — the request body intentionally has no org_id field.
| Field | Type | Label | Description |
|---|---|---|---|
| reports_to_member_id | string | reports_to_member_id is the UUID of the candidate supervising member. Required. |
InferAgentClearanceResponse
InferAgentClearanceResponse returns the inferred clearance and the rule label that explains how it was derived.
| Field | Type | Label | Description |
|---|---|---|---|
| clearance | int32 | clearance is the inferred level (1..5). | |
| reason | string | reason is the stable rule label that explains the derivation: "parent-minus-one" — the canonical default (supervisor.clearance - 1). "floor" — clamped to 1 because supervisor.clearance was 1 and parent-minus-one would underflow. Future labels are additive — consumers MUST treat unknown values as "trust the number and surface the label as-is". | |
| supervisor_clearance | int32 | supervisor_clearance is the supervising member's own clearance, surfaced so the UI can render "inherited from X (L4) → L3" without a second round-trip. Always populated. |
ListMembersRequest
ListMembersRequest returns a paginated list with optional filters.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_type_filter | MemberType | member_type_filter restricts results to a specific member type; UNSPECIFIED = all types. | |
| status_filter | string | status_filter restricts results to a specific status; empty = all non-deleted. | |
| team_id_filter | string | team_id_filter restricts results to members in a specific team; empty = all teams. | |
| page_size | int32 | page_size is the maximum number of results per page; default 50, max 100. | |
| page_token | int32 | page_token is the 0-based offset for pagination. | |
| include_deleted | bool | optional | include_deleted controls whether soft-deleted members (deleted_at IS NOT NULL) are returned. Uses proto3 field presence so the server can distinguish "unset" from "explicitly false". |
Default: FALSE — soft-deleted members are hidden. Set to TRUE for audit tooling, reconciler clients, or any consumer that needs to see tombstones. Explicit status_filter values (e.g. "active") still filter independently.
History: Wave 1a (#698) introduced this field with a transitional default of TRUE; Wave 1b (#699) flipped the default to FALSE after every in-tree caller was audited to set the flag explicitly. External API consumers that relied on the Wave-1a default must now set include_deleted = true explicitly. |
ListMembersResponse
ListMembersResponse returns a paginated list of members.
| Field | Type | Label | Description |
|---|---|---|---|
| members | Member | repeated | members is the list of member records. |
| total_count | int32 | total_count is the total number of matching members. |
ListRolesRequest
ListRolesRequest asks for the role catalog visible to the caller's org.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. The authoritative org is taken from the gateway-enforced session scope; this field is accepted for symmetry with the other list RPCs and must match the session org when set. |
ListRolesResponse
ListRolesResponse returns the merged catalog: global defaults (role_catalog.org_id IS NULL) plus the caller's org-scoped custom roles, ordered by sort_order then label, soft-deleted rows excluded.
| Field | Type | Label | Description |
|---|---|---|---|
| roles | Role | repeated | roles is the ordered catalog the dropdown renders. |
ListTeamMembershipsRequest
ListTeamMembershipsRequest lists memberships with optional filters.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id filters to teams for this member; empty = no filter. | |
| team_id | string | team_id filters to members in this team; empty = no filter. | |
| page_size | int32 | page_size is the maximum number of results per page. | |
| page_token | int32 | page_token is the 0-based offset for pagination. | |
| include_deleted | bool | optional | include_deleted controls whether memberships whose underlying member is soft-deleted (members.deleted_at IS NOT NULL) are returned. The membership rows themselves have no deleted_at — this flag JOINs the members table to filter. Default: FALSE — set to TRUE for audit tooling that needs to see tombstones. See ListMembersRequest.include_deleted for the wave history. |
ListTeamMembershipsResponse
ListTeamMembershipsResponse returns the team memberships.
| Field | Type | Label | Description |
|---|---|---|---|
| team_memberships | TeamMembership | repeated | team_memberships is the list of matching records. |
| total_count | int32 | total_count is the total number of matching records. |
Member
Member is the unified representation of a human or agent org member.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is the UUID primary key of the member. | |
| org_id | string | org_id is the tenant organisation UUID. | |
| reports_to | string | reports_to is the UUID of the supervisor; empty string = root node. | |
| member_type | MemberType | member_type distinguishes human operators from AI agents. | |
| display_name | string | display_name is the human-readable name of the member. | |
| string | email is the member's email address; empty for agents. | ||
| clerk_user_id | string | clerk_user_id is the Clerk authentication user ID; empty for agents. | |
| clearance | int32 | clearance is the member's clearance level (1-5). | |
| status | string | status is one of: active, invited, suspended, removed. | |
| created_at | google.protobuf.Timestamp | created_at is the creation timestamp. | |
| deleted_at | google.protobuf.Timestamp | deleted_at is set when the member is soft-deleted; null for active members. | |
| team_memberships | TeamMembership | repeated | team_memberships for this member (populated by GetMember only). |
| updated_at | google.protobuf.Timestamp | updated_at tracks the last mutation to any Member column (display_name, clearance, status, reports_to, deleted_at, etc.). Maintained by the trg_members_set_updated_at BEFORE UPDATE trigger (migration 071). Added for Wave 1a (#698) to let the client reconciler skip field-level compare when timestamps match. | |
| title | string | title is the member's free-text role label ("Sales head", "Staff Engineer"). Persisted on the members.title column (migration 120, #1597). Empty string = no title set. Distinct from member_type (human/agent) and from clearance — it is purely a human-readable descriptor. |
MemberSearchResult
MemberSearchResult is a single search hit. tree_path lets the UI render the matched member's ancestor chain for context without a second round-trip per hit.
| Field | Type | Label | Description |
|---|---|---|---|
| member | Member | member is the matched member record (no team_memberships populated — call GetMember if those are needed). | |
| tree_path | string | repeated | tree_path is the ordered list of member_ids from a root (index 0) to the matched member's immediate supervisor. The matched member's own id is NOT included. Empty for root members. |
| tree_path_names | string | repeated | tree_path_names mirrors tree_path one-for-one with display_names so the UI can render breadcrumbs ("Acme > Engineering > Platform") without a follow-up batch GetMember call. |
MemberTreeNode
MemberTreeNode is a flat representation of a tree node with depth. The client constructs the nested tree from the flat list using reports_to + depth.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is the UUID of the member. | |
| reports_to | string | reports_to is the UUID of the supervisor; empty = root node. | |
| member_type | MemberType | member_type distinguishes human operators from AI agents. | |
| display_name | string | display_name is the human-readable name of the member. | |
| clearance | int32 | clearance is the member's clearance level (1-5). | |
| status | string | status is one of: active, invited, suspended, removed. | |
| depth | int32 | depth is the tree depth (0 = root). | |
| direct_report_count | int32 | direct_report_count is the precomputed number of direct reports for UI display. | |
| team_memberships | TeamMembershipRef | repeated | team_memberships lists every team the member belongs to. Populated by GetMemberTree via a single batched org_unit_memberships ⋈ org_units query over the whole tree (no N+1). Empty for members with no team affiliation. |
| title | string | title is the member's free-text role label ("Sales head", "Staff Engineer"); empty string = no title set. Surfaced on tree nodes so the Org chart can render the role without a per-node GetMember round-trip. Persisted on members.title (migration 120, #1597). | |
| string | email is the member's email address; empty string = no email set (members.email is nullable — agents need not have one). Surfaced on tree nodes so the Edit modal can pre-fill email without a per-node GetMember round-trip. |
MoveMemberRequest
MoveMemberRequest changes a member's reports_to (reparent).
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id is the UUID of the member to move. | |
| new_reports_to | string | new_reports_to is the UUID of the target supervisor; empty = root. | |
| leave_reports_behind | bool | leave_reports_behind controls whether the moved member's existing direct reports (and their descendants, transitively) follow the move. - false (default, legacy behaviour): the entire subtree moves with the member; direct reports keep pointing at the moved member, so the whole branch slides intact under new_reports_to. This matches the pre-V2-extension MoveMember semantics so legacy callers that omit the flag keep the pre-existing behaviour. - true: only the moved member relocates; its existing direct reports are reparented to the moved member's previous supervisor so they stay where they were on the org chart. New behaviour, opt-in. GAP-V2-6 (#1062). Field name + default chosen so omitting the flag never silently strips a manager of their reports. |
MoveMemberResponse
MoveMemberResponse returns the updated member.
| Field | Type | Label | Description |
|---|---|---|---|
| member | Member | member is the updated member record. |
PromoteMemberRequest
PromoteMemberRequest atomically promotes member_id into the slot previously held by replaces_member_id. Tenant scope is taken from the gateway-enforced session context — the request body intentionally has no org_id field, mirroring GetClearanceTrail / GetEligibleManagers.
| Field | Type | Label | Description |
|---|---|---|---|
| member_id | string | member_id is the UUID of the member being promoted into the slot. REQUIRED. Must be an active member of the caller's org and must not equal replaces_member_id. | |
| replaces_member_id | string | replaces_member_id is the UUID of the member whose slot is being vacated. REQUIRED. Their active direct reports are reparented under member_id and they are marked status='pending_reassignment' so a follow-up ReassignAgents call can re-home any agents that reported to them. The replaced member is NOT deleted. |
PromoteMemberResponse
PromoteMemberResponse returns both members in their post-promote state.
| Field | Type | Label | Description |
|---|---|---|---|
| promoted | Member | promoted is the post-call state of member_id. The reports_to chain is unchanged by PromoteMember itself — only the inbound reports of the replaced member are moved. | |
| replaced | Member | replaced is the post-call state of replaces_member_id. Its status will be 'pending_reassignment'; its reports_to chain is preserved so the org chart still shows them under their previous supervisor until a follow-up RPC removes / re-roles them. | |
| reparented_count | int32 | reparented_count is the number of active direct reports moved from replaces_member_id under member_id. |
RemoveTeamMembershipRequest
RemoveTeamMembershipRequest removes a member from a team.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id is the UUID of the member. | |
| team_id | string | team_id is the UUID of the team. |
RemoveTeamMembershipResponse
RemoveTeamMembershipResponse confirms the team membership removal.
| Field | Type | Label | Description |
|---|---|---|---|
| removed | bool | removed is true if a membership was actually removed. |
Role
Role is a single entry in the member-role catalog. The label is what the dropdown shows and what gets stored into members.title when chosen; id and sort_order let an admin curation UI (follow-up) edit and re-order entries without text-matching on the label.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is the UUID primary key of the catalog row. | |
| label | string | label is the human-readable role name ("Staff Engineer", "Head of Sales"). This is the value stored into members.title when the user picks it. | |
| sort_order | int32 | sort_order is the ascending dropdown ordering hint (lower first). Ties are broken alphabetically by label. |
SearchMembersRequest
SearchMembersRequest is the paginated full-text search input.
| Field | Type | Label | Description |
|---|---|---|---|
| query | string | query is the free-text term the caller typed. Trimmed and lower-cased server-side. Empty / whitespace-only queries are rejected with INVALID_ARGUMENT. The match is a substring (case-insensitive) over display_name, email, and the member_type label ("human" / "agent"). | |
| page_size | int32 | page_size is the maximum number of matches per page; default 25, max 100. | |
| page_token | int32 | page_token is the 0-based offset for pagination. |
SearchMembersResponse
SearchMembersResponse returns paginated matches plus the next-page cursor.
| Field | Type | Label | Description |
|---|---|---|---|
| matches | MemberSearchResult | repeated | matches is the page of search hits. |
| next_page_token | string | next_page_token is the 0-based offset to pass back for the next page, or empty when no more pages remain. | |
| total_count | int32 | total_count is the total number of matching members (pre-page). |
TeamMembership
TeamMembership represents a member's assignment to a team.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is the UUID primary key of the team membership record. | |
| member_id | string | member_id is the UUID of the member. | |
| team_id | string | team_id is the UUID of the team. | |
| team_name | string | team_name is the denormalized team display name. | |
| role | string | role is one of: member, lead. | |
| created_at | google.protobuf.Timestamp | created_at is the creation timestamp. |
TeamMembershipRef
TeamMembershipRef is a compact reference to one team a member belongs to, embedded in MemberTreeNode. A member may belong to many teams (org_unit_memberships allows multiple), so this is a repeated field — a single team_id would mis-represent the affiliation.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the UUID of the org_unit (team) the member belongs to. During the dual-write window this equals the legacy team_id. | |
| unit_name | string | unit_name is the denormalized team display name (org_units.name). | |
| role | string | role is the member's role within the team: one of member, lead, admin. |
UpdateMemberRequest
UpdateMemberRequest modifies mutable member fields.
| Field | Type | Label | Description |
|---|---|---|---|
| org_id | string | org_id is the tenant organisation UUID. | |
| member_id | string | member_id is the UUID of the member to update. | |
| display_name | string | display_name is the new name; empty string = no change. | |
| clearance | int32 | clearance is the new clearance level; 0 = no change. | |
| status | string | status is the new status; empty = no change. | |
| reports_to | string | reports_to is the new supervisor; empty = no change; "NULL" = set root. | |
| title | string | title is the new free-text role label; empty string = no change (mirrors the display_name / status no-change convention). Persisted on members.title (migration 120, #1597). |
UpdateMemberResponse
UpdateMemberResponse returns the updated member.
| Field | Type | Label | Description |
|---|---|---|---|
| member | Member | member is the updated member record. |
MemberType
MemberType distinguishes human operators from AI agents within the unified member tree.
| Name | Number | Description |
|---|---|---|
| MEMBER_TYPE_UNSPECIFIED | 0 | MEMBER_TYPE_UNSPECIFIED is the default zero value. |
| MEMBER_TYPE_HUMAN | 1 | MEMBER_TYPE_HUMAN represents a human operator. |
| MEMBER_TYPE_AGENT | 2 | MEMBER_TYPE_AGENT represents an AI agent. |
MemberService
MemberService manages the unified member tree (humans + agents), team memberships, and reporting chain operations.
--- Member CRUD ---
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| CreateMember | CreateMemberRequest | CreateMemberResponse | CreateMember adds a new member to the org. If member_type=AGENT and reports_to is set, enforces clearance <= supervisor.clearance. |
| GetMember | GetMemberRequest | GetMemberResponse | GetMember retrieves a single member by ID with team memberships. |
| UpdateMember | UpdateMemberRequest | UpdateMemberResponse | UpdateMember modifies mutable fields. Clearance changes trigger supervisor constraint re-validation for agents. |
| DeleteMember | DeleteMemberRequest | DeleteMemberResponse | DeleteMember soft-deletes a member (sets deleted_at). Direct reports are reparented to the deleted member's supervisor. |
| ListMembers | ListMembersRequest | ListMembersResponse | ListMembers returns a paginated list with optional filters. |
| GetMemberTree | GetMemberTreeRequest | GetMemberTreeResponse | GetMemberTree returns the full reporting tree for the org as a flat list with depth, suitable for client-side tree construction. |
| MoveMember | MoveMemberRequest | MoveMemberResponse | MoveMember changes reports_to (reparent). Includes cycle detection and clearance re-validation for agent members. |
| GetAncestors | GetAncestorsRequest | GetAncestorsResponse | GetAncestors returns the chain from a member up to root. |
| GetDirectReports | GetDirectReportsRequest | GetDirectReportsResponse | GetDirectReports returns immediate children of a member. |
| GetClearanceTrail | GetClearanceTrailRequest | GetClearanceTrailResponse | GetClearanceTrail walks the reports_to chain from the target member to root and returns one entry per visited member showing how that member's clearance level was derived (explicit / parent-minus-one / capped-at-manager / floor). Powers the redesigned Member detail page's "How is this clearance set?" panel. GAP-23 (#1045). |
| GetEligibleManagers | GetEligibleManagersRequest | GetEligibleManagersResponse | GetEligibleManagers returns the list of members that may legally be set as the supervisor of the given member without creating a cycle. The requested member itself and all of its descendants are excluded. Powers the cycle-filtered manager dropdown on the redesigned Edit / Move modals. GAP-V2-17 (#1069). |
| CheckEmailUnique | CheckEmailUniqueRequest | CheckEmailUniqueResponse | CheckEmailUnique resolves an email to a uniqueness verdict for the current org, returning a collision descriptor when a member already claims the address. Powers the live empty / malformed / collision / clean state machine on the redesigned Add Person modal. Email matching is case-insensitive and trimmed. GAP-V2-18 (#1070). |
| InferAgentClearance | InferAgentClearanceRequest | InferAgentClearanceResponse | InferAgentClearance returns the clearance an agent would receive if assigned to the supplied supervisor — the redesigned Deploy wizard's "Quad will inherit clearance from X, capped one level below" hint that updates as the user changes the Reports-to dropdown. Rule: supervisor's clearance, capped one level below (floor 1). RLS-scoped via gateway-enforced session context. GAP-40 (#1087). |
| PromoteMember | PromoteMemberRequest | PromoteMemberResponse | PromoteMember atomically promotes one member into another's slot: the replaced member's active direct reports are reparented under the promoted member, and the replaced member is marked status='pending_reassignment' so a follow-up ReassignAgents call can re-home any agents that previously reported to them. PromoteMember is a pure-metadata operation — no agents are migrated by this RPC. Powers the redesigned Org Restructure tab's "Promote" action. GAP-V2-7 (#1063). |
| SearchMembers | SearchMembersRequest | SearchMembersResponse | SearchMembers performs a paginated tree-pathed full-text search across the org's member directory. Each match carries the ancestor chain of member_ids leading from a root down to the matched member so the Org Explore search box can highlight context for every hit. Match scope: display_name, email, member_type label. Soft-deleted members are excluded. RLS-scoped — never crosses org boundaries. GAP-V2-1 (#1059). |
| AddTeamMembership | AddTeamMembershipRequest | AddTeamMembershipResponse | AddTeamMembership adds a member to a team. Idempotent. |
| RemoveTeamMembership | RemoveTeamMembershipRequest | RemoveTeamMembershipResponse | RemoveTeamMembership removes a member from a team. |
| ListTeamMemberships | ListTeamMembershipsRequest | ListTeamMembershipsResponse | ListTeamMemberships lists memberships. Supports filtering by member_id (teams for a member) or team_id (members in a team). |
| BulkImportMembers | BulkImportMembersRequest | BulkImportMembersResponse | BulkImportMembers imports up to 500 members in a single transaction. |
| ListRoles | ListRolesRequest | ListRolesResponse | ListRoles returns the managed catalog of role labels that backs the member "Role" dropdown on the Add / Edit / Restructure flows. The result merges global defaults (role_catalog.org_id IS NULL) with the caller's org-scoped custom roles, ordered by sort_order then label, with soft-deleted rows excluded. The chosen label is stored into members.title (free-text, migration 120) — there is no enum constraint on members.title; this catalog only constrains the UI input. #1603. |
Scalar Value Types
| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
|---|---|---|---|---|---|---|---|---|
| double | double | double | float | float64 | double | float | Float | |
| float | float | float | float | float32 | float | float | Float | |
| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| bool | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | |
| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |