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

# Members API

> List organization members and manage their seats and roles from your own tools.

The Members API lets your scripts and tools manage cubic members. You can list your organizations and their members, read one member, turn a seat on or off, and change a role.

## Get an API key

<Steps>
  <Step title="Open the cubic API integration">
    Go to [Settings > Integrations > cubic API](https://www.cubic.dev/settings?tab=integrations\&integration=api).
  </Step>

  <Step title="Generate a personal key">
    In the **Members API** card, click **Generate API key**. Keys start with `cbk_`, and cubic only shows the full value once.
  </Step>

  <Step title="Store it securely">
    Save the key in your secret manager or local environment.
  </Step>
</Steps>

<Note>
  This is the same personal key that the [cubic MCP server](/ide/mcp-server) and the [cubic CLI](/ide/cli-review) use, and regenerating it replaces the key for all three. The key acts with your own cubic access. You only see organizations you can already view in cubic, and changes need an organization admin. See [Roles and permissions](/account/roles-and-permissions).
</Note>

## Endpoints

| Method | Endpoint | Description |
| - | - | - |
| `GET` | `/api/v1/organizations` | List the organizations you can view. |
| `GET` | `/api/v1/organizations/{org}/members` | List an organization's members. |
| `GET` | `/api/v1/organizations/{org}/members/{githubUserId}` | Read one member. |
| `PATCH` | `/api/v1/organizations/{org}/members/{githubUserId}` | Turn a seat on or off, or change a role. |

The base URL is `https://www.cubic.dev`. `{org}` is the GitHub organization login, such as `acme`. Each organization in the list includes `githubAccountId`, GitHub's numeric account ID, and flags such as `canManageSeats` that show what your access allows.

The [OpenAPI document](https://www.cubic.dev/api/v1/openapi.json) describes every endpoint, parameter, and response. You can generate a client from it.

### Authentication

Send the key in the `Authorization` header as a bearer token.

```http theme={null}
Authorization: Bearer cbk_your_api_key
```

### Example requests

<CodeGroup>
  ```bash cURL theme={null}
  # List members who have a seat
  curl --request GET \
    --url 'https://www.cubic.dev/api/v1/organizations/acme/members?seat=true&limit=100' \
    --header 'Authorization: Bearer cbk_your_api_key'

  # Turn off a member's seat
  curl --request PATCH \
    --url 'https://www.cubic.dev/api/v1/organizations/acme/members/583231' \
    --header 'Authorization: Bearer cbk_your_api_key' \
    --header 'Content-Type: application/json' \
    --data '{"seat": false, "expectedSeat": true}'
  ```

  ```javascript JavaScript theme={null}
  const memberUrl = 'https://www.cubic.dev/api/v1/organizations/acme/members/583231';
  const headers = {
    Authorization: `Bearer ${process.env.CUBIC_API_KEY}`,
    'Content-Type': 'application/json',
  };

  const member = await fetch(memberUrl, { headers }).then((response) => response.json());

  const response = await fetch(memberUrl, {
    method: 'PATCH',
    headers,
    body: JSON.stringify({ seat: false, expectedSeat: member.seat }),
  });
  const result = await response.json();

  if (!response.ok) {
    throw new Error(`${result.error.code}: ${result.error.message}`);
  }
  ```
</CodeGroup>

## Members

```json theme={null}
{
  "githubUserId": "583231",
  "githubLogin": "octocat",
  "workEmail": "octocat@acme.com",
  "role": "member",
  "seat": true,
  "isBot": false
}
```

| Field | Description |
| - | - |
| `githubUserId` | GitHub's numeric user ID, as a string. It never changes, so use it as the member's key. |
| `githubLogin` | GitHub login, or `null`. Logins can change. |
| `workEmail` | Work email, or `null`. An admin sets it in cubic, or cubic syncs it from GitHub SAML or SCIM. cubic never returns a personal email here. |
| `role` | `admin`, `member`, or `viewer`. |
| `seat` | Whether the member has a seat. |
| `isBot` | Whether the account is a bot. Bots cannot be admins. |

A seat is a paid license for cubic reviews. Turning a seat off does not remove the person's access to cubic. To remove someone, remove them from the GitHub organization. cubic removes their access when GitHub notifies it. See [Roles and permissions](/account/roles-and-permissions) for what each role can do.

The member list holds the people cubic tracks for the organization, including members with access to a repository cubic reviews and pull request authors. On paid plans, cubic refreshes it from GitHub every day.

### Filter the member list

| Parameter | Description |
| - | - |
| `search` | Match part of a GitHub login, ignoring case. |
| `role` | Only members with this role. |
| `seat` | `true` for members with a seat, `false` for members without one. |

The list also takes `cursor` and `limit`, described in [Pagination](#pagination). An unknown parameter returns `400 invalid_request`, so a mistyped filter never lists everyone.

## Update a member

Send the fields you want to change, each with the value from your latest read.

| Field | Description |
| - | - |
| `seat` | `true` turns the seat on. `false` turns it off. Send it with `expectedSeat`. |
| `expectedSeat` | The `seat` value from your latest read. |
| `role` | `admin`, `member`, or `viewer`. Send it with `expectedRole`. |
| `expectedRole` | The `role` value from your latest read. |

If the member changed since your read, the request fails with `409 member_state_changed` and changes nothing. Read the member again, then retry. A request that asks for the member's current state succeeds with `"changed": false`, so retrying a completed request is safe.

The response holds the member's `before` and `after` state, `changed`, and `availableSeats`, the number of paid seats still free after the change.

<Note>
  Updates need an organization admin and an active paid subscription. The API never buys seats. When every paid seat is in use, turning a seat on fails with `seat_capacity_exceeded`. Add seats in [subscription settings](https://www.cubic.dev/settings?tab=subscription) first.
</Note>

## Pagination

List endpoints return one page and a `nextCursor`. To get the next page, send `nextCursor` back as `cursor`. `nextCursor` is `null` on the last page. Set the page size with `limit`, from 1 to 100. The default is 20.

## Rate limits

Each API key can make 1,000 requests per 15 minutes. Every response after authentication includes these headers.

| Header | Description |
| - | - |
| `X-RateLimit-Limit` | Requests allowed per 15-minute window. |
| `X-RateLimit-Remaining` | Requests left in the current window. |
| `X-RateLimit-Reset` | When the window resets, in Unix epoch seconds. |

When you run out, cubic answers `429 rate_limited` with a `Retry-After` header in seconds. If cubic cannot check the limit, it answers `503 rate_limiter_unavailable` with `Retry-After: 30` and no `X-RateLimit-*` headers. In both cases, wait for `Retry-After` before you retry.

## Errors

Every error uses the same envelope.

```json theme={null}
{
  "error": {
    "code": "member_state_changed",
    "message": "The member changed since you read it. Fetch the member again and retry.",
    "retriable": false
  }
}
```

Branch on `code`, because messages can change. `retriable` says whether the same request can succeed if you send it again. Some errors include a `correlationId`. Quote it when you contact support. Validation errors include `details`, a list of `path` and `message` pairs for the fields that failed.

| Status | Code | When it happens |
| - | - | - |
| `400` | `invalid_request` | A path, query, or body value is invalid, or a query parameter is unknown. |
| `400` | `invalid_arguments` | The body changes neither `seat` nor `role`, sends a value without its expected value, or sends an expected value without its value. |
| `400` | `invalid_cursor` | The cursor did not come from a previous page. |
| `401` | `unauthorized` | The bearer token is missing, or the account cannot use cubic. |
| `401` | `invalid_api_key` | The key is invalid or expired, or it is not a personal `cbk_` key. |
| `403` | `access_denied` | Your cubic access does not cover this organization. |
| `403` | `admin_required` | Only organization admins can update members. |
| `403` | `paid_plan_required` | The organization is not on a paid plan. |
| `403` | `subscription_not_active` | The subscription is not active. |
| `403` | `unsupported_subscription` | Manage this subscription in cubic. |
| `403` | `billing_provider_not_supported` | The organization is billed through Vercel. Manage its seats in cubic. |
| `404` | `organization_not_found` | The organization does not exist in cubic, or you cannot access it. |
| `404` | `member_not_found` | No member of the organization has this GitHub user ID. |
| `409` | `member_state_changed` | The member changed since your read. |
| `409` | `seat_capacity_exceeded` | Every paid seat is in use. |
| `409` | `last_admin_required` | The change would remove the last active admin. |
| `409` | `billing_state_changed` | Billing changed while the request ran. Send the request again. |
| `422` | `self_demotion_not_allowed` | You cannot remove your own admin role. |
| `422` | `bot_admin_not_allowed` | Bots cannot be admins. |
| `429` | `rate_limited` | You used every request in this window. |
| `500` | `internal_error` | Something failed unexpectedly. Retry the request. |
| `503` | `unavailable` | cubic could not verify the key. Retry shortly. |
| `503` | `rate_limiter_unavailable` | cubic could not check the rate limit. Retry shortly. |


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