Semaphor

Groups API

Create and manage user groups, members, and group-based permissions

Manage user groups and their memberships programmatically. Groups let you share dashboards and visuals with multiple users at once.

For conceptual background on group types and permission model, see Groups.

Authentication

All endpoints require a project-scoped JWT token:

Authorization: Bearer YOUR_PROJECT_TOKEN

See Authentication for token generation.

Base URL

/api/management/v1

List Groups

GET /groups

Returns groups accessible to the authenticated user, with pagination.

Query Parameters

NameTypeRequiredDescription
typestringNoFilter by type: ORG_GROUP, TENANT_GROUP, or all. Default: all types visible to user.
searchstringNoSearch by group name
tenantIdstringNoFilter to groups for a specific tenant
includeMembersbooleanNoInclude member details in response. Default: false
pageintegerNoPage number. Default: 1
limitintegerNoResults per page, 1-100. Default: 20
sortstringNoSort field: name, created, or members
orderstringNoSort direction: asc or desc

Request

curl "https://semaphor.cloud/api/management/v1/groups?type=ORG_GROUP&search=analytics&limit=10" \
  -H "Authorization: Bearer eyJhbG..."

Response

200 OK
{
  "groups": [
    {
      "id": "grp_a1b2c3d4",
      "name": "Analytics Team",
      "description": "Data analytics department",
      "type": "ORG_GROUP",
      "organizationId": "org_x9y8z7",
      "tenantId": null,
      "createdBy": "usr_abc123",
      "createdAt": "2026-01-15T10:30:00Z",
      "updatedAt": "2026-02-20T14:00:00Z",
      "memberCount": 12,
      "permissions": {
        "canEdit": true,
        "canDelete": true,
        "canManageMembers": true
      }
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 10,
  "hasMore": false
}

Create Group

POST /groups

Create a new organization or tenant group.

Required role: ADMIN for org groups, POWER_USER for tenant groups.

Request Body

NameTypeRequiredDescription
namestringYesGroup name. 1-100 characters. Must be unique within scope.
descriptionstringNoGroup description. Max 500 characters.
typestringYesORG_GROUP or TENANT_GROUP
tenantIdstringConditionalRequired when type is TENANT_GROUP

Request

curl -X POST https://semaphor.cloud/api/management/v1/groups \
  -H "Authorization: Bearer eyJhbG..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Analytics Team",
    "description": "Data analytics department members",
    "type": "ORG_GROUP"
  }'

Response

201 Created
{
  "id": "grp_a1b2c3d4",
  "name": "Analytics Team",
  "description": "Data analytics department members",
  "type": "ORG_GROUP",
  "organizationId": "org_x9y8z7",
  "tenantId": null,
  "createdBy": "usr_abc123",
  "createdAt": "2026-03-06T10:30:00Z",
  "updatedAt": "2026-03-06T10:30:00Z"
}

Errors

StatusCauseFix
400Invalid name or missing required fieldsCheck name length (1-100) and type value
403Insufficient roleRequires ADMIN (org groups) or POWER_USER (tenant groups)
409Group name already exists in this scopeChoose a unique name

Get Group

GET /groups/:id

Retrieve a specific group with metadata about shared resources.

Query Parameters

NameTypeRequiredDescription
includeMembersbooleanNoInclude member details. Default: false

Request

curl "https://semaphor.cloud/api/management/v1/groups/grp_a1b2c3d4?includeMembers=true" \
  -H "Authorization: Bearer eyJhbG..."

Response

200 OK
{
  "id": "grp_a1b2c3d4",
  "name": "Analytics Team",
  "description": "Data analytics department members",
  "type": "ORG_GROUP",
  "organizationId": "org_x9y8z7",
  "tenantId": null,
  "createdBy": "usr_abc123",
  "createdAt": "2026-01-15T10:30:00Z",
  "updatedAt": "2026-02-20T14:00:00Z",
  "members": [
    {
      "id": "mem_001",
      "groupId": "grp_a1b2c3d4",
      "orgUserId": "usr_abc123",
      "tenantUserId": null,
      "addedBy": "usr_abc123",
      "createdAt": "2026-01-15T10:30:00Z"
    }
  ],
  "sharedResources": {
    "count": 5
  }
}

Errors

StatusCauseFix
403No permission to view this groupEnsure you belong to the group's org/tenant
404Group not foundVerify the group ID

Update Group

PATCH /groups/:id

Update a group's name or description. Only include the fields you want to change.

Request Body

NameTypeRequiredDescription
namestringNoNew group name. 1-100 characters. Must be unique within scope.
descriptionstring|nullNoNew description. Set to null to clear. Max 500 characters.

Request

curl -X PATCH https://semaphor.cloud/api/management/v1/groups/grp_a1b2c3d4 \
  -H "Authorization: Bearer eyJhbG..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Analytics & BI Team",
    "description": "Updated team description"
  }'

Response

200 OK
{
  "id": "grp_a1b2c3d4",
  "name": "Analytics & BI Team",
  "description": "Updated team description",
  "type": "ORG_GROUP",
  "organizationId": "org_x9y8z7",
  "tenantId": null,
  "createdBy": "usr_abc123",
  "createdAt": "2026-01-15T10:30:00Z",
  "updatedAt": "2026-03-06T11:00:00Z"
}

Errors

StatusCauseFix
400Invalid nameCheck name length (1-100)
403No edit permissionMust be group creator, ADMIN, or POWER_USER in scope
404Group not foundVerify the group ID
409Name already takenChoose a unique name within the scope

Delete Group

DELETE /groups/:id

Delete a group. Removes all member associations and revokes any access granted through this group.

Required: Creator, ADMIN, or POWER_USER in scope.

Request

curl -X DELETE https://semaphor.cloud/api/management/v1/groups/grp_a1b2c3d4 \
  -H "Authorization: Bearer eyJhbG..."

Response

200 OK
{
  "message": "Group grp_a1b2c3d4 deleted successfully"
}

Errors

StatusCauseFix
403No delete permissionMust be group creator, ADMIN, or POWER_USER in scope
404Group not foundVerify the group ID
409Group has shared resourcesRevoke resource shares before deleting, or handle cascading removal

List Members

GET /groups/:id/members

Returns all members of a group with user details (name, email).

Request

curl "https://semaphor.cloud/api/management/v1/groups/grp_a1b2c3d4/members" \
  -H "Authorization: Bearer eyJhbG..."

Response

200 OK
{
  "groupId": "grp_a1b2c3d4",
  "members": [
    {
      "id": "mem_001",
      "groupId": "grp_a1b2c3d4",
      "user": {
        "id": "usr_abc123",
        "name": "Alice Chen",
        "email": "alice@acme.com"
      },
      "userType": "org",
      "userId": "usr_abc123",
      "addedBy": "usr_xyz789",
      "createdAt": "2026-01-15T10:30:00Z"
    },
    {
      "id": "mem_002",
      "groupId": "grp_a1b2c3d4",
      "user": {
        "id": "usr_def456",
        "name": "Bob Martinez",
        "email": "bob@acme.com"
      },
      "userType": "org",
      "userId": "usr_def456",
      "addedBy": "usr_xyz789",
      "createdAt": "2026-02-01T09:00:00Z"
    }
  ],
  "totalCount": 2
}

Add Members

POST /groups/:id/members

Add one or more users to a group. Users must match the group type (org users for org groups, tenant users for tenant groups).

Request Body

NameTypeRequiredDescription
userIdsstring[]YesUser IDs to add. 1-100 per request.
userTypestringYesorg or tenant — must match the group type

Request

curl -X POST https://semaphor.cloud/api/management/v1/groups/grp_a1b2c3d4/members \
  -H "Authorization: Bearer eyJhbG..." \
  -H "Content-Type: application/json" \
  -d '{
    "userIds": ["usr_abc123", "usr_def456"],
    "userType": "org"
  }'

Response

201 Created
{
  "message": "Successfully added 2 members to the group",
  "addedMembers": [
    {
      "id": "mem_003",
      "groupId": "grp_a1b2c3d4",
      "orgUserId": "usr_abc123",
      "addedBy": "usr_xyz789",
      "createdAt": "2026-03-06T11:30:00Z"
    },
    {
      "id": "mem_004",
      "groupId": "grp_a1b2c3d4",
      "orgUserId": "usr_def456",
      "addedBy": "usr_xyz789",
      "createdAt": "2026-03-06T11:30:00Z"
    }
  ]
}

Errors

StatusCauseFix
400Invalid user IDs, wrong userType, or exceeds 100 usersVerify user IDs exist and match group type
403No permission to manage membersMust be group creator, ADMIN, or POWER_USER in scope
404Group not foundVerify the group ID

Remove Members (Batch)

DELETE /groups/:id/members

Remove multiple members from a group in one request.

Request Body

NameTypeRequiredDescription
userIdsstring[]YesUser IDs to remove. 1-100 per request.

Request

curl -X DELETE https://semaphor.cloud/api/management/v1/groups/grp_a1b2c3d4/members \
  -H "Authorization: Bearer eyJhbG..." \
  -H "Content-Type: application/json" \
  -d '{
    "userIds": ["usr_abc123", "usr_def456"]
  }'

Response

200 OK
{
  "message": "Successfully removed members from the group"
}

Remove Single Member

DELETE /groups/:id/members/:memberId

Remove a specific member by their membership ID (not user ID).

Path Parameters

NameTypeDescription
idstringGroup ID
memberIdstringMembership record ID (from the members list response)

Request

curl -X DELETE https://semaphor.cloud/api/management/v1/groups/grp_a1b2c3d4/members/mem_001 \
  -H "Authorization: Bearer eyJhbG..."

Response

200 OK
{
  "message": "Successfully removed member from the group",
  "removedMemberId": "mem_001"
}

Errors

StatusCauseFix
403No permission to manage membersMust be group creator, ADMIN, or POWER_USER in scope
404Member not found in this groupVerify both group ID and member ID

List Group Permissions

GET /groups/:id/permissions

Returns all resources (dashboards and visuals) shared with this group, with pagination.

Query Parameters

NameTypeRequiredDescription
resourceTypestringNoFilter by DASHBOARD or VISUAL
pageintegerNoPage number. Default: 1
limitintegerNoResults per page, 1-100. Default: 20

Request

curl "https://semaphor.cloud/api/management/v1/groups/grp_a1b2c3d4/permissions?resourceType=DASHBOARD" \
  -H "Authorization: Bearer eyJhbG..."

Response

200 OK
{
  "groupId": "grp_a1b2c3d4",
  "permissions": [
    {
      "id": "ace_001",
      "resourceType": "DASHBOARD",
      "resourceId": "d_abc123",
      "resource": {
        "id": "d_abc123",
        "title": "Q1 Revenue Dashboard",
        "description": "Quarterly revenue metrics",
        "createdAt": "2026-01-10T08:00:00Z",
        "updatedAt": "2026-03-01T12:00:00Z"
      },
      "role": "EDITOR",
      "scope": "ORG_GROUP",
      "grantedBy": {
        "id": "usr_xyz789",
        "name": "Admin User",
        "email": "admin@acme.com"
      },
      "grantedByType": "org",
      "grantedAt": "2026-02-15T10:00:00Z"
    }
  ],
  "summary": {
    "totalResources": 5,
    "byType": {
      "dashboards": 3,
      "visuals": 2
    }
  },
  "pagination": {
    "page": 1,
    "limit": 20,
    "totalCount": 5,
    "totalPages": 1,
    "hasMore": false
  }
}

Sharing with Groups

To share a dashboard or visual with a group, use the Sharing API with scope: "group":

curl -X POST https://semaphor.cloud/api/management/v1/dashboards/d_abc123/share \
  -H "Authorization: Bearer eyJhbG..." \
  -H "Content-Type: application/json" \
  -d '{
    "shares": [
      {
        "scope": "group",
        "groupId": "grp_a1b2c3d4",
        "role": "EDITOR"
      }
    ]
  }'

Code Examples

Create a Group and Add Members

const API_BASE = 'https://semaphor.cloud/api/management/v1';
 
async function createTeamWithMembers(
  token: string,
  name: string,
  memberIds: string[]
) {
  // 1. Create the group
  const groupRes = await fetch(`${API_BASE}/groups`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      name,
      type: 'ORG_GROUP',
      description: `Auto-created group for ${name}`,
    }),
  });
 
  if (!groupRes.ok) {
    const err = await groupRes.json();
    throw new Error(err.error);
  }
 
  const group = await groupRes.json();
 
  // 2. Add members
  const membersRes = await fetch(`${API_BASE}/groups/${group.id}/members`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      userIds: memberIds,
      userType: 'org',
    }),
  });
 
  if (!membersRes.ok) {
    const err = await membersRes.json();
    throw new Error(err.error);
  }
 
  return group;
}

Sync Group Members from External Source

async function syncGroupMembers(
  token: string,
  groupId: string,
  desiredMemberIds: string[],
  userType: 'org' | 'tenant'
) {
  const API_BASE = 'https://semaphor.cloud/api/management/v1';
 
  // Get current members
  const res = await fetch(`${API_BASE}/groups/${groupId}/members`, {
    headers: { Authorization: `Bearer ${token}` },
  });
  const { members } = await res.json();
  const currentIds = members.map((m: any) => m.userId);
 
  // Add missing members
  const toAdd = desiredMemberIds.filter((id) => !currentIds.includes(id));
  if (toAdd.length > 0) {
    await fetch(`${API_BASE}/groups/${groupId}/members`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ userIds: toAdd, userType }),
    });
  }
 
  // Remove extra members
  const toRemove = currentIds.filter(
    (id: string) => !desiredMemberIds.includes(id)
  );
  if (toRemove.length > 0) {
    await fetch(`${API_BASE}/groups/${groupId}/members`, {
      method: 'DELETE',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ userIds: toRemove }),
    });
  }
}

Error Format

All error responses return:

{
  "error": "Human-readable error message",
  "details": "Optional additional context"
}

For the full error reference, see Errors.