Core Resources
View as Markdown

User Group

A named collection of people you can grant Droplet access to in one shot. The roster is the membership; visibility controls who sees the row.

A User Group is a named bag of people. Grant a Droplet to @engineering and you grant it to all 24 current members; add a 25th member later and they gain access without anyone re-saving. Distinct from Spaces — Spaces group content; Groups group people. Two system role groups (@drop-people-admins, @drop-library-admins) ship today; they carry systemRole: true, live pinned at the top of the Groups directory, and replace the older per-user admin flags.

The User Group object

Every User Group carries these attributes.

Attributes

slug
string
URL-friendly identifier. Used in /directory/groups/{slug} and as the @{slug} token when sharing Droplets. Globally unique, max 40 chars, lowercase alphanumeric + hyphens. Renames record a 30-day soft redirect so existing links keep resolving.
name
string
Display name. Max 80 chars.
description
string· nullable
Markdown description. Rendered server-side with a constrained sanitizer (strips images and external URLs in href).
visibility
enum
PUBLICMEMBER_ONLYADMIN_ONLY
Who can see this Group. PUBLIC — listed in the directory, roster visible to everyone. MEMBER_ONLY — listed in the directory, roster hidden from non-members (count still shown). ADMIN_ONLY — invisible to non-admins; only People Admins can navigate to it. System role groups are pinned to ADMIN_ONLY.
systemRole
boolean
When true, this Group is a curated system role (@drop-people-admins or @drop-library-admins). System role groups cannot be deleted, cannot have their slug renamed, cannot leave ADMIN_ONLY, and the last-remaining member can only be removed by a site admin (User.isAdmin).
source
enum
MANUALSLACK_SYNCED
MANUAL — created in Drop by a People Admin. SLACK_SYNCED — reserved for V2 (mirrored from a Slack usergroup); no synced groups exist today.
tags
string[]
Lowercase tag strings, ≤24 chars each, ≤10 per group. Used by the directory tag filter. UI for editing tags was removed in the 032 follow-up; the field is preserved for future taxonomy work.
linkedSpaceId
string· nullable
Optional pointer to a paired Space (e.g. @engineering Group links to the engineering Space). Pure cross-reference — no membership sync.
slackHandle
string· nullable
Reserved for V2 Slack-sync. Unique-when-set. Today: always null for MANUAL groups.
More attributes+ Show 5
id
string
CUID primary key. Use slug in API paths.
slackId
string· nullable
V2-reserved Slack usergroup id (S0123456). Unique-when-set. Today: always null.
createdById
string
The User who created the Group. Carried for audit only — does not confer special permissions (People Admin role does).
createdAt
timestamp
ISO 8601 timestamp of creation.
updatedAt
timestamp
ISO 8601 timestamp of last metadata update.
THE USER GROUP OBJECT
{
  "id": "clxgrp2...",
  "slug": "engineering",
  "name": "Engineering",
  "description": "The team building Drop and adjacent tools.",
  "tags": [
    "team"
  ],
  "source": "MANUAL",
  "visibility": "PUBLIC",
  "systemRole": false,
  "linkedSpaceId": null,
  "slackHandle": null,
  "slackId": null,
  "createdById": "usr_alex",
  "createdAt": "2026-05-20T10:00:00.000Z",
  "updatedAt": "2026-05-28T09:00:00.000Z"
}

Methods

Endpoints to operate on a User Group. The example switches between curl, drop CLI, and MCP via the dropdown — your choice persists across the page.

GET

List User Groups

/api/user-groups

Return User Groups visible to the caller. Non-admins never see ADMIN_ONLY rows (the visibility filter strips them server-side). Each row carries systemRole so clients can render the pinned "System Roles" section. Supports ?q= (substring across name/slug/description + exact tag) and ?tag= (exact tag, case-folded).

Parameters

q
string· query
Substring search across name, slug, description; also matches an exact tag.
tag
string· query
Exact tag filter (lowercased server-side).
GET/api/user-groups
drop groups list --q engineering
Response200
[
  {
    "id": "clxgrp2...",
    "slug": "engineering",
    "name": "Engineering",
    "descriptionPreview": "The team building Drop and adjacent tools.",
    "memberCount": 24,
    "tags": [
      "team"
    ],
    "source": "MANUAL",
    "visibility": "PUBLIC",
    "systemRole": false
  }
]
POST

Create a User Group

/api/user-groups

People Admin only. Create a new Group. Slug must be unique across all Groups. If the slug collides with an existing Space slug, the response includes a non-blocking collisionWarning so the UI can offer to link the two as a cross-reference.

Parameters

name
string· body· required
Group display name (1–80 chars).
slug
string· body· required
URL slug — lowercase alphanumerics + hyphens, 3–40 chars.
description
string· body
Markdown description (max 10k chars).
tags
string[]· body
Up to 10 lowercase tag strings.
visibility
enum· body
PUBLIC | MEMBER_ONLY | ADMIN_ONLY. Default: PUBLIC.
POST/api/user-groups
drop groups create --name Engineering --slug engineering --description "The team building Drop and adjacent tools." --visibility PUBLIC
Response201
{
  "group": {
    "id": "clxgrp2...",
    "slug": "engineering",
    "name": "Engineering",
    "descriptionPreview": "The team building Drop and adjacent tools.",
    "memberCount": 0,
    "tags": [],
    "source": "MANUAL",
    "visibility": "PUBLIC",
    "systemRole": false,
    "description": "The team building Drop and adjacent tools.",
    "descriptionHtml": "<p>The team building Drop and adjacent tools.</p>",
    "linkedSpace": null,
    "slackHandle": null,
    "slackId": null,
    "createdAt": "2026-06-03T10:00:00.000Z",
    "updatedAt": "2026-06-03T10:00:00.000Z",
    "createdBy": {
      "id": "usr_alex",
      "email": "alex@himarley.com",
      "name": "Alex Chen"
    }
  },
  "collisionWarning": null
}
GET

Retrieve a User Group

/api/user-groups/{slug}

Return full Group detail — metadata, member roster, Droplet grants, linked Space, and a systemRole flag. ADMIN_ONLY groups return 404 to non-admins; MEMBER_ONLY groups return an empty roster to non-members. Renamed slugs return 301 with Location pointing at the current slug (30-day window).

Parameters

slug
string· path· required
The Group slug.
GET/api/user-groups/{slug}
drop groups get engineering
Response200
{
  "group": {
    "id": "clxgrp2...",
    "slug": "engineering",
    "name": "Engineering",
    "descriptionPreview": "The team building Drop and adjacent tools.",
    "memberCount": 24,
    "tags": [
      "team"
    ],
    "source": "MANUAL",
    "visibility": "PUBLIC",
    "systemRole": false,
    "description": "The team building Drop and adjacent tools.",
    "descriptionHtml": "<p>The team building Drop and adjacent tools.</p>",
    "linkedSpace": null,
    "slackHandle": null,
    "slackId": null,
    "createdAt": "2026-05-20T10:00:00.000Z",
    "updatedAt": "2026-05-28T09:00:00.000Z",
    "createdBy": {
      "id": "usr_alex",
      "email": "alex@himarley.com",
      "name": "Alex Chen"
    }
  },
  "members": [
    {
      "userId": "usr_alex",
      "email": "alex@himarley.com",
      "name": "Alex Chen",
      "title": "Engineering",
      "addedAt": "2026-05-20T10:00:00.000Z",
      "addedBy": "usr_alex"
    }
  ],
  "activity": [],
  "dropletGrants": [
    {
      "slug": "team-roadmap",
      "name": "Team Roadmap",
      "sharedAt": "2026-05-22T12:00:00.000Z"
    }
  ],
  "isPeopleAdmin": false
}
PATCH

Update a User Group

/api/user-groups/{slug}

People Admin only. Patch name, slug (via newSlug), description, tags, visibility, or linkedSpaceId. Slug renames park the old slug as a 30-day soft redirect. System role groups (systemRole: true) reject visibility changes off ADMIN_ONLY (400 SYSTEM_ROLE_ADMIN_ONLY) and reject slug renames outright (400 SYSTEM_ROLE_SLUG_LOCKED); name / description / tags remain editable.

Parameters

slug
string· path· required
Current Group slug.
name
string· body
New display name.
newSlug
string· body
New slug — records a 30-day redirect on the old one.
description
string | null· body
Markdown description; null to clear.
tags
string[]· body
Replace tag list (max 10).
linkedSpaceId
string | null· body
Cross-reference Space id, or null to clear.
visibility
enum· body
PUBLIC | MEMBER_ONLY | ADMIN_ONLY.
PATCH/api/user-groups/{slug}
drop groups update engineering --description "The team building Drop." --visibility MEMBER_ONLY
Response200
{
  "id": "clxgrp2...",
  "slug": "engineering",
  "name": "Engineering",
  "descriptionPreview": "The team building Drop.",
  "memberCount": 24,
  "tags": [
    "team"
  ],
  "source": "MANUAL",
  "visibility": "MEMBER_ONLY",
  "systemRole": false,
  "description": "The team building Drop.",
  "descriptionHtml": "<p>The team building Drop.</p>",
  "linkedSpace": null,
  "slackHandle": null,
  "slackId": null,
  "createdAt": "2026-05-20T10:00:00.000Z",
  "updatedAt": "2026-06-03T11:00:00.000Z",
  "createdBy": {
    "id": "usr_alex",
    "email": "alex@himarley.com",
    "name": "Alex Chen"
  }
}
DELETE

Delete a User Group

/api/user-groups/{slug}

People Admin only. Before the Group is dropped, every AppAccess row that grants via this Group is rewritten to individual email grants for the Group's current members — access is never silently revoked. Returns a summary of the expansion. System role groups reject with 403 SYSTEM_ROLE_PROTECTED.

Parameters

slug
string· path· required
The Group slug.
DELETE/api/user-groups/{slug}
drop groups delete engineering
Response200
{
  "grantsExpanded": 2,
  "dropletSlugs": [
    "team-roadmap",
    "engineering-handbook"
  ]
}
GET

List members

/api/user-groups/{slug}/members

Return every member of the Group. For MEMBER_ONLY groups, non-members get an empty roster; admins always see everything.

Parameters

slug
string· path· required
The Group slug.
GET/api/user-groups/{slug}/members
drop groups members list engineering
Response200
[
  {
    "userId": "usr_alex",
    "email": "alex@himarley.com",
    "name": "Alex Chen",
    "title": "Engineering",
    "addedAt": "2026-05-20T10:00:00.000Z",
    "addedBy": "usr_alex"
  }
]
POST

Add member(s)

/api/user-groups/{slug}/members

People Admin only (or member of a system role group when curating it). Bulk-add members. Idempotent — already-members and unknown ids are silently skipped. New members immediately gain access to every Droplet shared with this Group (CloudFront allowlists re-synced fire-and-forget).

Parameters

slug
string· path· required
The Group slug.
userIds
string[]· body· required
1–50 user ids to add.
POST/api/user-groups/{slug}/members
drop groups members add engineering --user-ids usr_kai,usr_jane
Response200
{
  "added": 2,
  "members": [
    {
      "userId": "usr_alex",
      "email": "alex@himarley.com",
      "name": "Alex Chen",
      "title": "Engineering",
      "addedAt": "2026-05-20T10:00:00.000Z",
      "addedBy": "usr_alex"
    },
    {
      "userId": "usr_kai",
      "email": "kai@himarley.com",
      "name": "Kai Mitchell",
      "title": null,
      "addedAt": "2026-05-31T09:15:00.000Z",
      "addedBy": "usr_alex"
    },
    {
      "userId": "usr_jane",
      "email": "jane@himarley.com",
      "name": "Jane Doe",
      "title": null,
      "addedAt": "2026-05-31T09:15:00.000Z",
      "addedBy": "usr_alex"
    }
  ]
}
DELETE

Remove member

/api/user-groups/{slug}/members/{userId}

People Admin only. Remove a single member. For system role groups, a last-admin guard runs inside the same transaction as the delete + audit: removing the only remaining member rejects with 400 LAST_SYSTEM_ROLE_MEMBER. Site admins (User.isAdmin) bypass the guard.

Parameters

slug
string· path· required
The Group slug.
userId
string· path· required
The user id to remove.
DELETE/api/user-groups/{slug}/members/{userId}
drop groups members remove engineering usr_kai
Response200
{
  "ok": true
}
POST

Bootstrap a Space

/api/user-groups/{slug}/bootstrap-space

Convenience: create a new Space seeded from this Group's current roster, then set linkedGroupId / linkedSpaceId on both sides so the cross-reference is in place. People Admin only. No ongoing membership sync — the seed is one-shot.

Parameters

slug
string· path· required
The Group slug.
POST/api/user-groups/{slug}/bootstrap-space
drop groups bootstrap-space engineering
Response201
{
  "spaceSlug": "engineering",
  "memberCount": 24
}