# Groups

> Create, list, get and delete groups.

Source: https://useprimitives.com/docs/api/groups

A group is a collection of primitives. It decides which account owns them and which scoped API keys can reach them. A group has no lifecycle of its own beyond create and delete, and it starts empty. See [Groups](/docs/concepts/groups).

| Method | Path | Description |
| --- | --- | --- |
| `POST` | `/v1/groups` | Create a group |
| `GET` | `/v1/groups` | List groups |
| `GET` | `/v1/groups/{group}` | Get a group and its primitives |
| `DELETE` | `/v1/groups/{group}` | Delete a group |
| `GET` | `/v1/groups/{group}/audit` | Read the group's audit trail |

`{group}` is the group name. Group names are 1–63 characters from `a-z`, `0-9`, `-`, `_` and `.`, starting with a letter or digit, with no `..`, and are unique within your workspace.

Sections marked **Account.** reject scoped API keys with `403 forbidden`. See [Authentication](/docs/api/authentication).

## Create a group

`POST /v1/groups`

**Account.** Creates an empty group. Scoped API keys get `403 forbidden`.

A name that exists answers `409 already_exists`, and an invalid name answers `400 invalid_request`. On Starter, a second group answers `403 plan_limit`.

**Body**

| Parameter | Type | Description |
| --- | --- | --- |
| `name` | string | Required. The group name. |
| `region` | string | Where the group's data lives. Default and, for now, only: `us-east-1`. It can't change later. An unknown region is `400 invalid_request`. |

```bash
curl -X POST https://api.useprimitives.com/v1/groups \
  -H "Authorization: Bearer $PRIMITIVES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "agent-42"}'
```

Returns `201` with the GroupInfo plus `region`:

```json
{
  "id": "01k7c3n2d8f4g6h9j1k3m5p7q9",
  "namespace": "ws_8f3k2m9q4x7d",
  "name": "agent-42",
  "created_at": 1760486400000,
  "primitives": [],
  "region": "us-east-1"
}
```

`namespace` is the workspace the group belongs to: its workspace id (`ws_…`) in the cloud, `default` under `primitives dev`. `id` is the group's own ULID; API calls address a group by `name`.

## List groups

`GET /v1/groups`

**Account.** Lists the workspace's groups as manifests, each with its `region`. Scoped API keys get `403 forbidden`.

Paged; see [Pagination](/docs/api#pagination). The cursor is a group name.

**Query parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `limit` | integer | Page size. Default `50`, max `200`. |
| `cursor` | string | The `next_cursor` of the previous page. |

```bash
curl "https://api.useprimitives.com/v1/groups?limit=2" \
  -H "Authorization: Bearer $PRIMITIVES_API_KEY"
```

```json
{
  "data": [
    {
      "id": "01k7c3n2d8f4g6h9j1k3m5p7q9",
      "namespace": "ws_8f3k2m9q4x7d",
      "name": "agent-42",
      "created_at": 1760486400000,
      "region": "us-east-1"
    },
    {
      "id": "01k7c4a6b8c0d2e4f6g8h0j2k4",
      "namespace": "ws_8f3k2m9q4x7d",
      "name": "agent-43",
      "created_at": 1760486460000,
      "region": "us-east-1"
    }
  ],
  "next_cursor": "agent-43"
}
```

## Get a group

`GET /v1/groups/{group}`

Returns the group's manifest and every primitive in it (GroupInfo). Workspace API keys and keys scoped to this group can call it. A key scoped to another group gets `403 group_mismatch`. A group that doesn't exist answers `404 not_found`.

```bash
curl https://api.useprimitives.com/v1/groups/agent-42 \
  -H "Authorization: Bearer $PRIMITIVES_API_KEY"
```

```json
{
  "id": "01k7c3n2d8f4g6h9j1k3m5p7q9",
  "namespace": "ws_8f3k2m9q4x7d",
  "name": "agent-42",
  "created_at": 1760486400000,
  "primitives": [
    {
      "id": "01jbz3k8m5q2w7x9v4n6t0r1ce",
      "group": "agent-42",
      "type": "db",
      "name": "orders",
      "created_at": 1760486400000,
      "paused": false,
      "size_bytes": 24576,
      "forked_from": null
    }
  ]
}
```

## Delete a group

`DELETE /v1/groups/{group}`

**Account.** Deletes an empty group. Scoped API keys get `403 forbidden`.

Without `force`, a group that still has primitives answers `409 conflict`. With `?force=true`, it also deletes every primitive in the group and their snapshots. This can't be undone, and the name can be reused immediately.

**Query parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `force` | boolean | `true` also deletes every primitive in the group. Default `false`. |

```bash
curl -X DELETE "https://api.useprimitives.com/v1/groups/agent-42?force=true" \
  -H "Authorization: Bearer $PRIMITIVES_API_KEY"
```

Returns `204` with no body.

## Read the group's audit trail

`GET /v1/groups/{group}/audit`

**Account.** Returns the audit trail of the group and every primitive in it, newest first, as a paged list of AuditEntry. It takes the same filters as the workspace trail, needs Pro, Team or Enterprise, and is served by the cloud, not by `primitives dev`. See [Audit](/docs/api/audit#read-a-groups-audit-trail).

```bash
curl "https://api.useprimitives.com/v1/groups/agent-42/audit?action=db.&limit=20" \
  -H "Authorization: Bearer $PRIMITIVES_API_KEY"
```

## Shapes

| Shape | Fields |
| --- | --- |
| Manifest | `{ id, namespace, name, created_at }`. Group listings add `region` |
| GroupInfo | Manifest plus `primitives: PrimitiveInfo[]`. Create adds `region` |

PrimitiveInfo is described on [Primitives](/docs/api/primitives#shapes). AuditEntry is described on [Audit](/docs/api/audit#shapes).
