Opbox

Organisations & Workspaces API

Manage the two-level hierarchy: Organisations (company/firm) contain one or more Workspaces (project/team/division). All resources (forms, tables, matters, etc.) are scoped to a workspace. Users have roles at both the organisation level and workspace level.

Data Model

  • Organisation - the top-level entity representing a company or firm. Has a unique slug, settings, and member list.
  • Workspace - a child of an Organisation. All data (forms, tables, matters, documents, workflows) is scoped to a workspace. Users switch between workspaces within their organisation.
  • Role Hierarchy - Organisation OWNER/ADMIN have implicit access to all workspaces. Organisation MEMBER/VIEWER only access workspaces they are explicitly added to.
Org RoleWorkspace AccessCan Create Workspaces
OWNERImplicit OWNER in all workspacesYes
ADMINImplicit ADMIN in all workspacesYes
MEMBEROnly explicitly-added workspacesNo
VIEWEROnly explicitly-added workspaces (read-only)No

Organisation Endpoints

MethodEndpointDescription
GET/api/organizationsList all organisations you belong to
POST/api/organizationsCreate a new organisation
GET/api/organizations/:orgIdGet organisation details
PATCH/api/organizations/:orgIdUpdate organisation (name, slug, settings)
DELETE/api/organizations/:orgIdDelete organisation (OWNER only, cascades)
GET/api/organizations/:orgId/workspacesList workspaces in an organisation
POST/api/organizations/:orgId/workspacesCreate workspace within organisation
GET/api/organizations/:orgId/membersList organisation members
POST/api/organizations/:orgId/membersAdd member to organisation (existing user or create new user)
PATCH/api/organizations/:orgId/members/:memberIdUpdate member role (OWNER only)
DELETE/api/organizations/:orgId/members/:memberIdRemove member from organisation (cascades to workspace memberships)
POST/api/organizations/:orgId/members/:memberId/reset-2faReset a member's two-factor authentication
POST/api/organizations/:orgId/members/:memberId/unlock-loginClear login rate limits for a locked-out member

Workspace Member Endpoints

Manage members within individual workspaces. Users must be organisation members before they can be added to a workspace.

MethodEndpointDescription
GET/api/organizations/:orgId/workspaces/:workspaceId/membersList members of a workspace
POST/api/organizations/:orgId/workspaces/:workspaceId/membersAdd an organisation member to a workspace
DELETE/api/organizations/:orgId/workspaces/:workspaceId/members/:memberIdRemove a member from a workspace

Workspace Endpoints

MethodEndpointDescription
GET/api/workspacesList all workspaces you belong to
POST/api/workspacesCreate a new workspace (optionally within an org)
GET/api/workspaces/:idGet workspace details with member list and resource counts
PATCH/api/workspaces/:idUpdate workspace (name, slug, settings, logo)
DELETE/api/workspaces/:idDelete workspace (OWNER only, requires name confirmation)
POST/api/workspace/switchSwitch active workspace (saves both workspace and org context)
GET/api/user/workspacesList user's workspaces with parent org info and active marker

List Organisations

Returns all organisations the authenticated user belongs to, with workspace and member counts.

GET /api/organizations

Response

{
  "organizations": [
    {
      "id": "cm_org_abc123",
      "name": "Clara Labs",
      "slug": "clara-labs",
      "settings": null,
      "createdAt": "2026-01-15T09:00:00.000Z",
      "updatedAt": "2026-02-20T14:30:00.000Z",
      "role": "OWNER",
      "_count": {
        "workspaces": 3,
        "members": 8
      }
    }
  ]
}

Create Organisation

Creates a new organisation. The authenticated user becomes the OWNER. Slug is auto-generated from the name if not provided.

POST /api/organizations
Content-Type: application/json

Body

{
  "name": "Acme Inc.",
  "slug": "acme-inc"
}
FieldTypeRequiredDescription
namestringYesOrganisation name (1-255 characters)
slugstringNoURL-friendly identifier. Auto-generated from name if omitted.

List Workspaces in Organisation

Returns workspaces within an organisation. OWNER/ADMIN see all workspaces; MEMBER/VIEWER only see workspaces they have been explicitly added to.

GET /api/organizations/:orgId/workspaces

Response

{
  "workspaces": [
    {
      "id": "cm_ws_xyz789",
      "name": "ADGM Operations",
      "slug": "adgm-ops",
      "orgId": "cm_org_abc123",
      "createdAt": "2026-01-20T10:00:00.000Z",
      "role": "OWNER",
      "_count": {
        "members": 5,
        "forms": 12,
        "tables": 8,
        "workflows": 3
      }
    }
  ]
}

List Organisation Members

Returns all members of an organisation. Requires OWNER or ADMIN role.

GET /api/organizations/:orgId/members

Response

{
  "members": [
    {
      "id": "cm_member_123",
      "userId": "cm_user_456",
      "organizationId": "cm_org_abc123",
      "role": "ADMIN",
      "createdAt": "2026-01-15T09:00:00.000Z",
      "user": {
        "id": "cm_user_456",
        "name": "Jane Smith",
        "email": "jane@example.com",
        "image": null
      }
    }
  ]
}

Add Organisation Member

Add a member to the organisation. Supports two flows: add an existing user by ID, or create a new user by providing name, email, and password. Requires OWNER or ADMIN role. Admins can only assign MEMBER or VIEWER roles.

POST /api/organizations/:orgId/members
Content-Type: application/json

Option 1: Add Existing User

{
  "userId": "cm_user_789",
  "role": "MEMBER"
}
FieldTypeRequiredDescription
userIdstringYesID of an existing user to add
rolestringNoOWNER, ADMIN, MEMBER (default), or VIEWER

Option 2: Create New User

{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "securePassword123",
  "role": "MEMBER"
}
FieldTypeRequiredDescription
namestringYesFull name of the new user
emailstringYesEmail address (if user exists, adds them instead of creating)
passwordstringYesMinimum 12 characters, validated for strength
rolestringNoOWNER, ADMIN, MEMBER (default), or VIEWER

If a user with the given email already exists, they are added to the organisation without creating a duplicate account. Returns 409 if the user is already an organisation member.

Update Member Role

Change a member's organisation role. Requires OWNER role. Cannot change your own role or demote the last owner.

PATCH /api/organizations/:orgId/members/:memberId
Content-Type: application/json
{
  "role": "ADMIN"
}

Remove Organisation Member

Remove a member from the organisation. Also removes them from all workspaces within the organisation. Requires OWNER or ADMIN role. Cannot remove yourself or the last owner.

DELETE /api/organizations/:orgId/members/:memberId

Reset Member 2FA

Disable two-factor authentication for a member, removing their authenticator app connection and backup codes. The member will be required to set up 2FA again on their next login. Requires OWNER or ADMIN role. Cannot reset your own 2FA through this endpoint. Admins cannot reset 2FA for owners or other admins.

POST /api/organizations/:orgId/members/:memberId/reset-2fa

Response

{ "success": true }
StatusCondition
2002FA successfully reset
4002FA is not enabled for this user, or attempting to reset own 2FA
403Insufficient role (admin trying to reset owner/admin)
404Member not found in organisation

This action is audit-logged. The member's TOTP secret, backup codes, and verification timestamps are all cleared.

Unlock Member Login

Clear login rate limits for a member who has been locked out due to too many failed login attempts. Also clears 2FA verification rate limits. Requires OWNER or ADMIN role. Admins cannot unlock owners or other admins.

POST /api/organizations/:orgId/members/:memberId/unlock-login

Response

{ "success": true }
StatusCondition
200Rate limits cleared successfully
403Insufficient role (admin trying to unlock owner/admin)
404Member not found in organisation

Rate limits are stored in-memory (or Upstash Redis if configured). Login limits are 10 attempts per 15 minutes per identity and 25 per IP. This endpoint clears the per-identity counter. The member can attempt to log in again immediately after unlocking.

Workspace Members

Manage which organisation members have access to a specific workspace. Users must be organisation members first. Organisation OWNER/ADMIN have implicit access to all workspaces.

List Workspace Members

GET /api/organizations/:orgId/workspaces/:workspaceId/members
{
  "members": [
    {
      "id": "cm_ws_member_abc",
      "userId": "cm_user_456",
      "workspaceId": "cm_ws_xyz789",
      "role": "ADMIN",
      "createdAt": "2026-02-01T12:00:00.000Z",
      "user": {
        "id": "cm_user_456",
        "name": "Jane Smith",
        "email": "jane@example.com",
        "image": null
      }
    }
  ]
}

Add Member to Workspace

Add an existing organisation member to a workspace. The target user must already be a member of the parent organisation.

POST /api/organizations/:orgId/workspaces/:workspaceId/members
Content-Type: application/json
{
  "userId": "cm_user_456",
  "role": "MEMBER"
}
FieldTypeRequiredDescription
userIdstringYesID of an organisation member to add to the workspace
rolestringNoOWNER, ADMIN, MEMBER (default), or VIEWER

Remove Member from Workspace

DELETE /api/organizations/:orgId/workspaces/:workspaceId/members/:memberId

Context Switching

The active workspace determines which data is returned by all other API endpoints (forms, tables, matters, etc.). Switch workspace to change context.

POST /api/workspace/switch
Content-Type: application/json
{
  "workspaceId": "cm_ws_xyz789"
}

Switching workspace also updates the active organisation to the workspace's parent org. Organisation OWNER/ADMIN can switch to any workspace in their org without needing an explicit workspace membership.

Oversight Permissions & Members

When a workspace is overseen by another workspace, you can grant resource-level access to the overseer's members. The overseer-members endpoint returns members of all workspaces that oversee the current workspace, for use in permission pickers across tables, documents, boards, and submissions.

Get Overseer Workspace Members

GET /api/oversight/overseer-members
{
  "overseerWorkspaces": [
    {
      "workspaceId": "cm_ws_overseer",
      "workspaceName": "Regulator Workspace",
      "members": [
        { "userId": "cm...", "name": "Inspector", "email": "inspect@example.com", "role": "ADMIN" },
        { "userId": "cm...", "name": "Auditor", "email": "auditor@example.com", "role": "MEMBER" }
      ]
    }
  ]
}

Only returns members from workspaces with an active oversight relationship. Used by the unified permission picker across Tables, Knowledge Base, Matters, and Submissions.

Unified Grant Types

Resource permissions (tables, documents, boards, submissions) support three grant types via a unified AccessGrant model:

targetTypetargetIdDescription
userUser IDGrant to a specific user (local or oversight workspace)
workspace_roleRole name (OWNER, ADMIN, MEMBER, VIEWER)Grant to all users with the specified workspace role
oversight_workspaceWorkspace IDGrant to all members of an overseer workspace (validated against active relationships)

Source Workspace Labels

When a workspace is configured as an overseer of subordinate workspaces, aggregated views automatically include source workspace labels. The isOverseer flag is returned by the Tasks and Matters APIs when the user has subordinate workspaces.

ViewLabelDetails
Task listOrigin columnBuilding2 icon + workspace name. Shown when isOverseer is true.
Matter list (table)Source columnBuilding2 icon + workspace name. Pass includeSubordinates=true to aggregate.
Matter kanban boardCard badgeSmall Building2 icon + workspace name on each kanban card.
Audit logWorkspace badgeAlready implemented. Building2 icon badge on each audit entry.

Delete Organisation

Permanently deletes an organisation and all its workspaces. Requires OWNER role and name confirmation.

DELETE /api/organizations/:orgId
Content-Type: application/json
{
  "confirmName": "Clara Labs"
}

Warning: Deleting an organisation cascades to all workspaces within it and all their data (forms, tables, matters, documents, workflows).