# Client portals

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/{id}/invitations` | [List customer portal invitations](#list-customer-portal-invitations) |
| `POST` | `/v1/api/client-accounts/{id}/invitations` | [Invite a customer to an agency portal](#invite-a-customer-to-an-agency-portal) |
| `DELETE` | `/v1/api/client-accounts/{id}/invitations/{invitationId}` | [Revoke a pending portal invitation](#revoke-a-pending-portal-invitation) |
| `GET` | `/v1/api/client-accounts/{id}/members` | [List external customer seats](#list-external-customer-seats) |
| `DELETE` | `/v1/api/client-accounts/{id}/members/{userId}` | [Remove a customer's portal access](#remove-a-customers-portal-access) |
| `PATCH` | `/v1/api/client-accounts/{id}/members/{userId}` | [Set customer brainstorming permission](#set-customer-brainstorming-permission) |
| `GET` | `/v1/api/client-accounts/{id}/members/{userId}/projects` | [List a customer seat's portal project grants](#list-a-customer-seats-portal-project-grants) |
| `DELETE` | `/v1/api/client-accounts/{id}/members/{userId}/projects/{projectId}` | [Remove one portal project grant](#remove-one-portal-project-grant) |
| `PUT` | `/v1/api/client-accounts/{id}/members/{userId}/projects/{projectId}` | [Grant view or steering access to one account project](#grant-view-or-steering-access-to-one-account-project) |
| `GET` | `/v1/api/client-portal/invitations` | [List agency client invitations](#list-agency-client-invitations) |
| `POST` | `/v1/api/client-portal/invitations` | [Invite an email directly to the agency client portal](#invite-an-email-directly-to-the-agency-client-portal) |
| `DELETE` | `/v1/api/client-portal/invitations/{invitationId}` | [Revoke an unaccepted client invitation](#revoke-an-unaccepted-client-invitation) |
| `GET` | `/v1/api/client-portal/members` | [List agency portal clients separately from staff](#list-agency-portal-clients-separately-from-staff) |
| `DELETE` | `/v1/api/client-portal/members/{userId}` | [Remove a client's agency portal access](#remove-a-clients-agency-portal-access) |
| `PATCH` | `/v1/api/client-portal/members/{userId}` | [Set or clear a client's permission override](#set-or-clear-a-clients-permission-override) |
| `GET` | `/v1/api/client-portal/members/{userId}/projects` | [List a client's current agency portal project access](#list-a-clients-current-agency-portal-project-access) |
| `DELETE` | `/v1/api/client-portal/members/{userId}/projects/{projectId}` | [Remove one explicit portal project grant](#remove-one-explicit-portal-project-grant) |
| `PUT` | `/v1/api/client-portal/members/{userId}/projects/{projectId}` | [Set view or steering access to one agency project](#set-view-or-steering-access-to-one-agency-project) |
| `GET` | `/v1/api/client-portal/settings` | [Read default client permissions](#read-default-client-permissions) |
| `PATCH` | `/v1/api/client-portal/settings` | [Update default client permissions](#update-default-client-permissions) |
| `GET` | `/v1/api/me/portals` | [Discover your available agency client portals](#discover-your-available-agency-client-portals) |
| `POST` | `/v1/api/me/workspace` | [Initialize your personal Hlix workspace](#initialize-your-personal-hlix-workspace) |
| `POST` | `/v1/api/portal-invitations/{token}/accept` | [Accept an email-bound client portal invitation](#accept-an-email-bound-client-portal-invitation) |
| `POST` | `/v1/api/portal/projects` | [Create a brainstorming project](#create-a-brainstorming-project) |
| `GET` | `/v1/api/portals/{agencySlug}/accounts` | [Resolve your accounts at one agency](#resolve-your-accounts-at-one-agency) |
| `GET` | `/v1/api/portals/{agencySlug}/identity` | [Read the published agency identity](#read-the-published-agency-identity) |
| `POST` | `/v1/api/portals/{agencySlug}/session` | [Enter the agency portal using the signed-in verified email](#enter-the-agency-portal-using-the-signed-in-verified-email) |

## List customer portal invitations

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

**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` | PortalInvitation[] | Sanitized invitation history, owner/admin only. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Invite a customer to an agency portal

`POST /v1/api/client-accounts/{id}/invitations` · operation ID `inviteClientPortalMember`

**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`, `InvitePortal` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `canCreateProjects` | boolean | no |  |
| `email` | string (email) | **yes** | max length 320 |
| `projectGrants` | object[] | no |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `201` | PortalInvitationCreated | Owner/admin only. Pending invitation and raw acceptance link returned once; emailSent reports provider acceptance. Link remains valid if email fails or is not configured. |
| `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 agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |
| `409` | ApiError | Publish the agency portal identity before inviting customers. |

## Revoke a pending portal invitation

`DELETE /v1/api/client-accounts/{id}/invitations/{invitationId}` · operation ID `revokeClientPortalInvitation`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `invitationId` | 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` | PortalInvitation | Revoked invitation. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## List external customer seats

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

**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` | PortalMember[] | Portal seats with person and create-ideas permission, owner/admin only. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Remove a customer's portal access

`DELETE /v1/api/client-accounts/{id}/members/{userId}` · operation ID `removeClientPortalMember`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `userId` | 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 | Seat removed; pending invitations revoked. Existing independent legacy project grants are preserved. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Set customer brainstorming permission

`PATCH /v1/api/client-accounts/{id}/members/{userId}` · operation ID `updateClientPortalPermissions`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `userId` | 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`, `PortalMemberPermissions` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `canCreateProjects` | boolean | **yes** |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | PortalMember | Updated external seat; project steering uses dedicated portal project grants. |
| `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 agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## List a customer seat's portal project grants

`GET /v1/api/client-accounts/{id}/members/{userId}/projects` · operation ID `listClientPortalProjectAccess`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `userId` | 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` | PortalProjectAccess[] | Owner/admin only. Current account-associated portal grants; independent legacy shares excluded. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Remove one portal project grant

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

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `userId` | 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` | MessageAck | Independent legacy project shares are preserved. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Grant view or steering access to one account project

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

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `userId` | 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. |

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

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `canSteer` | boolean | **yes** |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | PortalProjectAccess | Live seat and project must belong to this customer 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 agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## List agency client invitations

`GET /v1/api/client-portal/invitations` · operation ID `listWorkspacePortalInvitations`

**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` | WorkspacePortalInvitation[] | Owner/admin only. No internal account identifier or token hash. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |

## Invite an email directly to the agency client portal

`POST /v1/api/client-portal/invitations` · operation ID `inviteWorkspacePortalClient`

**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`, `InviteWorkspacePortal` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `canCreateProjects` | boolean,null | no |  |
| `email` | string (email) | **yes** | max length 320 |
| `projectGrants` | object[] | no |  |
| `role` | string | **yes** |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `201` | WorkspacePortalInvitationCreated | Client role only; omitted/null permission inherits live workspace default. Manual agency link remains usable if email fails. Explicit project sharing never grants another business account's invoice access. |
| `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 agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |
| `409` | ApiError | Publish the portal identity first. |

## Revoke an unaccepted client invitation

`DELETE /v1/api/client-portal/invitations/{invitationId}` · operation ID `revokeWorkspacePortalInvitation`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `invitationId` | 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 | Revoked. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## List agency portal clients separately from staff

`GET /v1/api/client-portal/members` · operation ID `listWorkspacePortalClients`

**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` | WorkspacePortalClient[] | One row per global user; effective permission and nullable override. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |

## Remove a client's agency portal access

`DELETE /v1/api/client-portal/members/{userId}` · operation ID `removeWorkspacePortalClient`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `userId` | 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 | Workspace relationship plus legacy portal account seats/grants removed; pending invitations revoked. Independent legacy project shares remain. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Set or clear a client's permission override

`PATCH /v1/api/client-portal/members/{userId}` · operation ID `updateWorkspacePortalClient`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `userId` | 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`, `WorkspacePortalMember` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `canCreateProjects` | boolean,null | **yes** |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | WorkspacePortalClient | Null restores live workspace default inheritance. |
| `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 agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## List a client's current agency portal project access

`GET /v1/api/client-portal/members/{userId}/projects` · operation ID `listWorkspacePortalProjectAccess`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `userId` | 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` | PortalGrant[] | Explicit current workspace and legacy portal grants. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Remove one explicit portal project grant

`DELETE /v1/api/client-portal/members/{userId}/projects/{projectId}` · operation ID `removeWorkspacePortalProjectAccess`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `userId` | 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` | MessageAck | Current workspace and legacy portal permissions removed for this user/project. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Set view or steering access to one agency project

`PUT /v1/api/client-portal/members/{userId}/projects/{projectId}` · operation ID `setWorkspacePortalProjectAccess`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `userId` | 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. |

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

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `canSteer` | boolean | **yes** |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | PortalGrant | Replaces same-user legacy portal grant for this project; independent old shares untouched. |
| `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 agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Read default client permissions

`GET /v1/api/client-portal/settings` · operation ID `getWorkspacePortalSettings`

**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` | WorkspacePortalSettings | Owner/admin only; omitted client overrides inherit this live value. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |

## Update default client permissions

`PATCH /v1/api/client-portal/settings` · operation ID `updateWorkspacePortalSettings`

**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`, `WorkspacePortalSettings` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `defaultCanCreateProjects` | boolean | **yes** |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | WorkspacePortalSettings | Updated live default. |
| `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 agency has not granted this action. |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |

## Discover your available agency client portals

`GET /v1/api/me/portals` · operation ID `listMyClientPortals`

**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. |
| `X-Client-Portal` | header | no | string | Agency organization id for one aggregate client dashboard; server derives only this caller's workspace relationship and owned account seats. Legacy account ids remain narrowly compatible. Real verified user session required for agency context; administration denied. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | WorkspacePortal[] | One dashboard per agency. Includes live published invitations bound to a verified email; GET does not accept invitations or change staff/personal workspace membership. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |

## Initialize your personal Hlix workspace

`POST /v1/api/me/workspace` · operation ID `initializePersonalWorkspace`

**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` | PersonalWorkspace | Idempotent Better Auth organization creation; requires a browser/user session. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Accept an email-bound client portal invitation

`POST /v1/api/portal-invitations/{token}/accept` · operation ID `acceptClientPortalInvitation`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `token` | 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` | PortalAccepted | External account seat and explicit selected project grants. No agency organization membership is created. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |
| `409` | ApiError | Invitation revoked, account archived, or selected project reassigned. |

## Create a brainstorming project

`POST /v1/api/portal/projects` · operation ID `createClientPortalIdea`

**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. |
| `X-Client-Portal` | header | no | string | Agency organization id for one aggregate client dashboard; server derives only this caller's workspace relationship and owned account seats. Legacy account ids remain narrowly compatible. Real verified user session required for agency context; administration denied. |

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

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `brief` | string | no | max length 20000 |
| `name` | string | **yes** | min length 1, max length 200 |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `201` | ProjectRow | Draft project with server-derived agency/account and creator collaborator. No coding dispatch. |
| `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. |
| `402` | ApiError | Agency project limit reached. |
| `403` | ApiError | The agency has not granted this action. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Resolve your accounts at one agency

`GET /v1/api/portals/{agencySlug}/accounts` · operation ID `listAgencyPortalAccounts`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `agencySlug` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |
| `X-Client-Portal` | header | no | string | Agency organization id for one aggregate client dashboard; server derives only this caller's workspace relationship and owned account seats. Legacy account ids remain narrowly compatible. Real verified user session required for agency context; administration denied. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | PortalAccount[] | Only the authenticated caller's active external accounts. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | The resource does not exist, or the caller is a task-scoped credential and this endpoint is outside its allowlist. |

## Read the published agency identity

`GET /v1/api/portals/{agencySlug}/identity` · operation ID `getClientPortalIdentity`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `agencySlug` | path | **yes** | string |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | PortalIdentity | Public identity only; no customer names, emails or membership. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## Enter the agency portal using the signed-in verified email

`POST /v1/api/portals/{agencySlug}/session` · operation ID `enterWorkspaceClientPortal`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `agencySlug` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |
| `X-Client-Portal` | header | no | string | Agency organization id for one aggregate client dashboard; server derives only this caller's workspace relationship and owned account seats. Legacy account ids remain narrowly compatible. Real verified user session required for agency context; administration denied. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | WorkspacePortal | Pending email-bound invites activate automatically, including compatible legacy account invites. No token or account choice required. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Email must be verified (EMAIL_VERIFICATION_REQUIRED); scoped automation credentials denied. |
| `404` | ApiError | Entity is not available in this portal/workspace. |

## 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/)