The actor.* family (7 verbs). Every verb enters through the one front door: it is capability-checked, permission-checked, scope-checked and audited before it runs (see Security & permissions). Each entry below lists its capability, risk class (which fixes the minimum autonomy level), the minimum caller permission, its availability tier, whether it is idempotent, and any outbound egress.
Setactor.avatar.set
| Property | Value |
|---|---|
| Capability | actor.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Member |
| Availability | Core - always on (MCP tier 1) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
(30-user-profiles). Set the ACTING actor’s own avatar - a small
data:image/ URL (<= 256KB) or an https:// URL. Self only (the acting actor); surfaced back by
session.get as avatarUrl. The verb is the one governed write path (INV-1).
Call POST /v/actor.avatar.set with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool actor.avatar.set.
Deactivateactor.deactivate
| Property | Value |
|---|---|
| Capability | actor.write |
| Risk class | Sensitive - autonomy L2 |
| Min. permission | Admin |
| Availability | On demand (MCP tier 2) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
Offboard a HUMAN: flip status ACTIVE→REVOKED. { actorId }.
Reversible via actor.reactivate. Refuses the sole CoS + the caller themselves. A REVOKED actor’s
tokens no longer resolve (auth.rs §6 - status != 'ACTIVE' is a fail-closed deny at resolve).
Call POST /v/actor.deactivate with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool actor.deactivate.
Getactor.get
| Property | Value |
|---|---|
| Capability | actor.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Admin |
| Availability | On demand (MCP tier 2) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - drives an inline chat artefact and a GUI panel |
Read one actor row in the caller’s workspace. { actorId }. RLS-scoped
- an explicit
workspace_id = $predicate so a cross-workspace id is unreachable (never leaks).
Call POST /v/actor.get with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool actor.get.
Inviteactor.invite
| Property | Value |
|---|---|
| Capability | actor.write |
| Risk class | Sensitive - autonomy L2 |
| Min. permission | Admin |
| Availability | On demand (MCP tier 2) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
Mint a NEW HUMAN roster user at a starting firm tier.
{ displayName, tier, email? } (tier ∈ MEMBER|ADMIN|OWNER). The post-IdP human-onboarding path; the new
actor is ACTIVE but carries no sessions yet. Per [2026] VJS-CC-OPBOX 29 D2 (following CC-OPBOX 9 D8): when
an email is given it is persisted at mint AND a one-time INVITE token is minted - the human redeems it
through the existing unauthenticated reset/confirm door (/auth/reset/confirm) to set THEIR OWN first
password (no admin-sets-another verb in the normal path). The plaintext inviteToken is returned ONCE
(only its hash is stored). With no email it is the legacy seat-only invite (the D4 no-email/seed fallback
sets the first password). Refuses a non-firm tier (EXTERNAL is a portal kind, not a settable firm tier).
Call POST /v/actor.invite with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool actor.invite.
Listactor.list
| Property | Value |
|---|---|
| Capability | actor.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Admin |
| Availability | On demand (MCP tier 2) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - drives an inline chat artefact and a GUI panel |
List actors in the caller’s workspace. { kind?, status?, limit? }.
RLS-scoped (ws B never sees ws A’s actors). Paginated (NFR-PERF-3). Optional kind/status filters.
Call POST /v/actor.list with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool actor.list.
Reactivateactor.reactivate
| Property | Value |
|---|---|
| Capability | actor.write |
| Risk class | Sensitive - autonomy L2 |
| Min. permission | Admin |
| Availability | On demand (MCP tier 2) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
Reinstate a previously-offboarded HUMAN: REVOKED→ACTIVE.
{ actorId }. The reversible twin of actor.deactivate (the same HUMAN-only guard; the self / sole-
CoS guards are inert on a re-enable but kept for symmetry - a re-activate is never harmful).
Call POST /v/actor.reactivate with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool actor.reactivate.
Set Roleactor.setRole
| Property | Value |
|---|---|
| Capability | actor.write |
| Risk class | Sensitive - autonomy L2 |
| Min. permission | Owner |
| Availability | On demand (MCP tier 2) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
Move a HUMAN’s firm tier (MEMBER<->ADMIN<->OWNER). { actorId, tier }. The ONE role-change write path (INV-1) - the former ADMIN-gated actor.tier.set was deleted
(F4) because it had no last-OWNER guard. BLOCKS the last-OWNER-demote (the workspace must keep at least
one OWNER) AND is OWNER-gated (an ADMIN can no longer self-promote to OWNER). HUMAN-only load + the
firm_tier write (an AGENT’s standing is the org lattice, not firm_tier).
Call POST /v/actor.setRole with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool actor.setRole.