Skip to content

Members and invitations

Members are users with a role in an organization. The four roles form a ladder:

  • viewer — read-only access to flags, configs, segments, evaluation, and audit.
  • editor — adds: create / update / delete flags, configs, segments, environments, and projects.
  • admin — adds: org settings, API key minting, member invitations.
  • owner — adds: change member roles, delete the org.

Bearer tokens go through a different gate (scopes) and are not subject to the role ladder for resource access — but org-management endpoints (member updates, invitations, API key minting, org delete) are session-only.


List members

http
GET /api/v1/orgs/{slug}/members

Response: 200 → Member[]

json
[
  {
    "userId": "9f7a32b5-…",
    "email": "pat@example.com",
    "displayName": "Pat Doe",
    "role": "owner",
    "createdAt": "2026-04-12T10:00:00Z"
  }
]

Soft-deleted users are filtered out.

Update a member's role

http
PATCH /api/v1/orgs/{slug}/members/{userId}
Cookie: actuator_session=…
Content-Type: application/json

{ "role": "editor" }

Session-authenticated, owner role only. Same-role PATCH is a no-op (returns the current row, no audit entry).

Last-owner guard: demoting the last active owner returns 409 last_owner_cannot_demote_or_remove. Promote someone else to owner first.

Response: 200 → MemberErrors: 400 invalid_request, 403 insufficient_role, 404 not_found, 409 last_owner_cannot_demote_or_remove.

Remove a member

http
DELETE /api/v1/orgs/{slug}/members/{userId}
Cookie: actuator_session=…

Session-authenticated, owner role only. The last-owner guard applies.

Response: 204 No ContentErrors: 403 insufficient_role, 404 not_found, 409 last_owner_cannot_demote_or_remove.


Invitations

Invitations let an admin or owner add a user to the org. The flow:

  1. Admin mints an invitation (POST /api/v1/orgs/{slug}/invitations). The server returns the raw acceptUrl once — share it via the auto-sent email or by hand.
  2. The invitee follows the link, lands on /app/invite/{token}.
  3. The dashboard calls GET /api/v1/invitations/{token} (unauth) to render org name and role.
  4. The invitee submits, calling POST /api/v1/invitations/{token}/accept (unauth) — this creates the user (if new), attaches the membership, mints a session, and sets the actuator_session cookie.

List pending invitations

http
GET /api/v1/orgs/{slug}/invitations
Cookie: actuator_session=…

Session-authenticated, admin or owner. Used and expired invitations are filtered out.

Response: 200 → Invitation[]

json
[
  {
    "id": "9f7a32b5-…",
    "email": "newhire@example.com",
    "role": "editor",
    "expiresAt": "2026-05-13T10:00:00Z",
    "createdAt": "2026-05-06T10:00:00Z"
  }
]

Mint an invitation

http
POST /api/v1/orgs/{slug}/invitations
Cookie: actuator_session=…
Content-Type: application/json

{
  "email": "newhire@example.com",
  "role": "editor"
}

Session-authenticated, admin or owner. Returns the invitation row plus the one-shot raw token and accept URL — store or share these immediately, the server stores only the SHA-256 hash.

An email is sent automatically via the configured mailer. Admins on self-hosted deploys without SMTP can share the acceptUrl directly.

Response: 201 → InvitationMintResponse

json
{
  "id": "9f7a32b5-…",
  "email": "newhire@example.com",
  "role": "editor",
  "expiresAt": "…",
  "token": "inv_…",
  "acceptUrl": "https://useactuator.ai/app/invite/inv_…"
}

Errors: 400 invalid_request, 403 insufficient_role, 409 already_member, 409 invitation_pending.

Revoke an invitation

http
DELETE /api/v1/orgs/{slug}/invitations/{id}
Cookie: actuator_session=…

Session-authenticated, admin or owner. Revokes a pending invitation. Already-redeemed invitations are preserved as audit-trail evidence and return 404.

Response: 204 No ContentErrors: 403 insufficient_role, 404 not_found.


Public invitation endpoints

These are unauthenticated — the token in the URL is the credential. See Authentication → Invitation accept.