# Client accounts

Generated from contract version `1.0.0` — the same document [`/v1/api/openapi.json`](/api/openapi/) serves.

## Prerequisites

- An API key in `x-api-key`, or a browser session cookie
- `X-Organization-Id` naming the workspace to act in, unless the session already has an active one
- The base URL for your deployment, `https://server.hlix.ai` by default

## Operations

| Method | Path | Operation |
| --- | --- | --- |
| `GET` | `/v1/api/client-accounts` | [List the agency's customers](#list-the-agencys-customers) |
| `POST` | `/v1/api/client-accounts` | [Add a customer](#add-a-customer) |
| `DELETE` | `/v1/api/client-accounts/{id}` | [Delete a customer](#delete-a-customer) |
| `GET` | `/v1/api/client-accounts/{id}` | [Get one customer](#get-one-customer) |
| `PATCH` | `/v1/api/client-accounts/{id}` | [Update a customer](#update-a-customer) |
| `GET` | `/v1/api/client-accounts/{id}/projects` | [List one customer's projects](#list-one-customers-projects) |
| `DELETE` | `/v1/api/client-accounts/{id}/projects/{projectId}` | [Unassign a project from a customer](#unassign-a-project-from-a-customer) |
| `PUT` | `/v1/api/client-accounts/{id}/projects/{projectId}` | [Assign a project to a customer](#assign-a-project-to-a-customer) |

## List the agency's customers

`GET /v1/api/client-accounts` · operation ID `listClientAccounts`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ClientAccount[] | Every client account, by display name. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The caller is a client collaborator; the customer roster is agency-only. (Or the caller is not a member of this workspace at all.) |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |

## Add a customer

`POST /v1/api/client-accounts` · operation ID `createClientAccount`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Request body** — `application/json`, `CreateClientAccount` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `billingEmail` | string (email) | null | no |  |
| `branding` | object | no |  |
| `currency` | string | null | no |  |
| `displayName` | string | **yes** | min length 1, max length 200 |
| `legalName` | string | null | no |  |
| `portalSlug` | string | **yes** | min length 1, max length 100 |
| `status` | `prospect` | `active` | no |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `201` | ClientAccount | The stored account. The workspace is taken from the tenant context — a body cannot name the organization it writes into. |
| `400` | ValidationError \| ApiError | The JSON body failed schema validation (`ValidationError`), or the request named no active organization (`ApiError`). |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The caller is a client collaborator; the customer roster is agency-only. (Or the caller is not a member of this workspace at all.) |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |
| `409` | ApiError | That portal slug is already taken in this workspace. |
| `500` | ApiError | The account could not be written. |

## Delete a customer

`DELETE /v1/api/client-accounts/{id}` · operation ID `deleteClientAccount`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | MessageAck | The account is gone. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The caller is a client collaborator; the customer roster is agency-only. (Or the caller is not a member of this workspace at all.) |
| `404` | ApiError | No such client account in this workspace. |
| `409` | ApiError | The account still has projects. It REFUSES rather than orphaning delivery history — unassign them first, or archive the account instead. |

## Get one customer

`GET /v1/api/client-accounts/{id}` · operation ID `getClientAccount`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ClientAccount | The account. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The caller is a client collaborator; the customer roster is agency-only. (Or the caller is not a member of this workspace at all.) |
| `404` | ApiError | No such client account in this workspace. |

## Update a customer

`PATCH /v1/api/client-accounts/{id}` · operation ID `updateClientAccount`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Request body** — `application/json`, `UpdateClientAccount` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `billingEmail` | string (email) | null | no |  |
| `branding` | object | no |  |
| `currency` | string | null | no |  |
| `displayName` | string | no | min length 1, max length 200 |
| `legalName` | string | null | no |  |
| `portalSlug` | string | no | min length 1, max length 100 |
| `status` | `prospect` | `active` | `archived` | no |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ClientAccount | The updated account. |
| `400` | ValidationError \| ApiError | The JSON body failed schema validation (`ValidationError`), or the request named no active organization (`ApiError`). |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The caller is a client collaborator; the customer roster is agency-only. (Or the caller is not a member of this workspace at all.) |
| `404` | ApiError | No such client account in this workspace. |
| `409` | ApiError | That portal slug is already taken in this workspace. |

## List one customer's projects

`GET /v1/api/client-accounts/{id}/projects` · operation ID `listClientAccountProjects`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectSummaryRow[] | The projects assigned to this customer, with the index counts but WITHOUT their repositories — unlike `GET /projects`, which attaches them. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The caller is a client collaborator; the customer roster is agency-only. (Or the caller is not a member of this workspace at all.) |
| `404` | ApiError | No such client account in this workspace. |

## Unassign a project from a customer

`DELETE /v1/api/client-accounts/{id}/projects/{projectId}` · operation ID `unassignProjectFromClientAccount`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `projectId` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectRow | The project row, WITHOUT its repositories. It returns to the unassigned bucket, which is a supported resting state. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The caller is a client collaborator; the customer roster is agency-only. (Or the caller is not a member of this workspace at all.) |
| `404` | ApiError | No such project — or it is not assigned to THIS account, which answers the same 404. |

## Assign a project to a customer

`PUT /v1/api/client-accounts/{id}/projects/{projectId}` · operation ID `assignProjectToClientAccount`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `projectId` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectRow | The project row, WITHOUT its repositories — this answer is the updated row itself, not the composed project read. Assigning twice is the same as assigning once. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The caller is a client collaborator; the customer roster is agency-only. (Or the caller is not a member of this workspace at all.) |
| `404` | ApiError | No such client account, or no such project. |

## If a request fails

These apply to every operation on this page.

- `400` — the body failed schema validation, or the request named no active organization. A `ValidationError` body carries the failing fields; an `ApiError` body carries only `error`.
- `401` — no valid session or API key.
- `403` — authenticated, but not a member of the named organization, or a client collaborator without the required access.
- `404` — no such resource **in this workspace**. Absent and not-visible are deliberately the same answer; do not infer cross-tenant existence from it.
- `409` — the expected revision or state is stale, or another operation currently owns the transition.
- `500` — the operation failed server-side. Retry only where an idempotency key makes that safe.

## Next steps

[API reference](/api/reference/)
  [API & OpenAPI](/api/openapi/)
  [TypeScript SDK](/sdk/typescript/)
  [CLI reference](/reference/cli/)