> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dbhost.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Database members API: add, change role, remove

> List, add, update and remove DBHost database collaborators with a full-account API key. Roles are admin, developer and viewer.

Members are people you give dashboard access to one of your databases. Roles
control what they can do in the dashboard, not PostgreSQL privileges; see
[Databases](/dashboard/databases#team-access) for what each role allows and
[Database roles](/guides/database-roles) for SQL roles.

* These routes require a **full-account** API key. A selected-database key
  receives `403 KEY_SCOPE_DENIED`. See [API keys](/dashboard/api-keys).
* The key acts as the account owner, so only databases you own are visible.
* API access requires a Pro or Business plan.

Roles are `admin`, `developer` and `viewer`. The owner is listed separately
and cannot be added, changed or removed through these routes.

## List members

`GET /api/v1/databases/{id}/members`

Returns the owner, current members and invitations for the database.

```http theme={null}
GET /api/v1/databases/{id}/members
Authorization: Bearer <api-key>
```

```json 200 OK theme={null}
{
  "data": {
    "owner": {
      "id": "<user-id>",
      "email": "owner@example.com",
      "name": "Owner Name",
      "role": "owner"
    },
    "members": [
      {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "userId": "<user-id>",
        "email": "dev@example.com",
        "name": "Dev Name",
        "role": "developer",
        "createdAt": "2026-10-14T09:00:00.000Z"
      }
    ],
    "invitations": [
      {
        "id": "b2c3d4e5-0000-4000-8000-000000000002",
        "email": "new@example.com",
        "role": "viewer",
        "status": "pending",
        "expiresAt": "2026-10-21T09:00:00.000Z",
        "createdAt": "2026-10-14T09:00:00.000Z"
      }
    ]
  }
}
```

`members[].id` is the membership ID used by the update and remove routes.
`invitations` has the same shape as
[List invitations](/api-reference/collaboration/invitations#list-invitations).

## Add a member

`POST /api/v1/databases/{id}/members`

| Field | Type | Required | Description |
| - | - | - | - |
| `email` | string | Yes | Email address of the person to add |
| `role` | string | Yes | `admin`, `developer` or `viewer` |

```http theme={null}
POST /api/v1/databases/{id}/members
Authorization: Bearer <api-key>
Content-Type: application/json

{"email":"dev@example.com","role":"developer"}
```

What happens depends on whether the email belongs to an existing DBHost user.
Both cases return `201 Created`; `data.kind` tells you which one applied.

**Existing user.** DBHost adds the membership directly. If the user is already
a member, their role is set to the requested role.

```json 201 Created theme={null}
{
  "message": "Database member added.",
  "data": {
    "kind": "membership",
    "membership": {
      "id": "a1b2c3d4-0000-4000-8000-000000000001",
      "databaseId": "550e8400-e29b-41d4-a716-446655440000",
      "userId": "<user-id>",
      "role": "developer",
      "createdByUserId": "<user-id>",
      "createdAt": "2026-10-14T09:00:00.000Z",
      "updatedAt": "2026-10-14T09:00:00.000Z"
    }
  }
}
```

**Unknown email.** DBHost emails an invitation that expires after seven days.
An open invitation for the same email on this database is replaced by the new
one.

```json 201 Created theme={null}
{
  "message": "Database invitation sent.",
  "data": {
    "kind": "invitation",
    "invitation": {
      "id": "b2c3d4e5-0000-4000-8000-000000000002",
      "databaseId": "550e8400-e29b-41d4-a716-446655440000",
      "normalizedEmail": "new@example.com",
      "role": "viewer",
      "invitedByUserId": "<user-id>",
      "clerkInvitationId": "<delivery-id>",
      "status": "pending",
      "expiresAt": "2026-10-21T09:00:00.000Z",
      "acceptedByUserId": null,
      "acceptedAt": null,
      "revokedAt": null,
      "createdAt": "2026-10-14T09:00:00.000Z",
      "updatedAt": "2026-10-14T09:00:00.000Z"
    }
  }
}
```

Manage the invitation afterwards with the
[invitations routes](/api-reference/collaboration/invitations).

## Change a member's role

`PATCH /api/v1/databases/{id}/members/{membershipId}`

Changing the dashboard role does not rotate or revoke a shared SQL password
the member may already have copied.

| Field | Type | Required | Description |
| - | - | - | - |
| `role` | string | Yes | `admin`, `developer` or `viewer` |

```http theme={null}
PATCH /api/v1/databases/{id}/members/{membershipId}
Authorization: Bearer <api-key>
Content-Type: application/json

{"role":"admin"}
```

```json 200 OK theme={null}
{
  "message": "Database member role updated.",
  "data": {
    "id": "a1b2c3d4-0000-4000-8000-000000000001",
    "databaseId": "550e8400-e29b-41d4-a716-446655440000",
    "userId": "<user-id>",
    "role": "admin",
    "createdByUserId": "<user-id>",
    "createdAt": "2026-10-14T09:00:00.000Z",
    "updatedAt": "2026-10-14T09:05:00.000Z"
  }
}
```

## Remove a member

`DELETE /api/v1/databases/{id}/members/{membershipId}`

Removes the membership and dashboard access to this database. It does not
invalidate a shared SQL password the member may have copied. To stop direct
SQL access, the database owner must plan a password rotation and update apps
or other people that use that credential. Returns `204 No Content` with an
empty body.

```http theme={null}
DELETE /api/v1/databases/{id}/members/{membershipId}
Authorization: Bearer <api-key>
```

All changes are recorded in the database's activity log.

## Errors

| Status | Code | Description |
| - | - | - |
| 400 | `VALIDATION_ERROR` | Missing or invalid `email` or `role`, or the email belongs to the database owner |
| 400 | `INVALID_JSON` | Request body is not valid JSON |
| 401 | `UNAUTHORIZED` | Missing or invalid API key |
| 403 | `PLAN_API_ACCESS_REQUIRED` | The account is not on Pro or Business |
| 403 | `KEY_SCOPE_DENIED` | The key does not have full account access |
| 404 | `DATABASE_NOT_FOUND` | Database does not exist or is not owned by this account |
| 404 | `MEMBER_NOT_FOUND` | No membership with this ID on this database |
| 409 | `DATABASE_LIFECYCLE_CONFLICT` | The database is being deleted |
| 409 | `DATABASE_BUSY` | Another change held the database for the whole wait; retry after `Retry-After` seconds |
| 413 | `PAYLOAD_TOO_LARGE` | Request body is larger than 64 KB |
| 429 | `RATE_LIMITED` | Too many requests; retry after `Retry-After` seconds |
| 502 | `INVITATION_DELIVERY_FAILED` | The invitation could not be sent or saved; nothing was created |

The CLI equivalents are `dbhost members list|add|update|remove`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.