# Approvals

See the [OpenAPI document](/openapi.json) for schemas and operation IDs.

## Prerequisites

- Protected operations require an API key in `x-api-key` or a browser session cookie; some actions require a verified user session
- Tenant-scoped operations use `X-Organization-Id`; browser sessions can use their active workspace. Discovery and personal-workspace bootstrap do not require an active workspace
- The base URL for your deployment, `https://server.hlix.ai` by default

## Operations

| Method | Path | Operation |
| --- | --- | --- |
| `GET` | `/v1/api/approvals` | [List requests awaiting a decision](#list-requests-awaiting-a-decision) |
| `GET` | `/v1/api/approvals/{id}` | [Get one request](#get-one-request) |
| `POST` | `/v1/api/approvals/{id}/cancel` | [Cancel a request nobody will decide](#cancel-a-request-nobody-will-decide) |
| `POST` | `/v1/api/approvals/{id}/resolve` | [Approve or deny a request](#approve-or-deny-a-request) |
| `POST` | `/v1/api/approvals/client-question` | [Ask the customer a question](#ask-the-customer-a-question) |

## List requests awaiting a decision

`GET /v1/api/approvals` · operation ID `listApprovals`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `status` | query | no | string | `pending` \| `approved` \| `denied` \| `cancelled`. Omitted, every status is listed. |
| `cycleId` | query | no | string | Only requests raised against this cycle. |
| `taskId` | query | no | string | Only requests raised against this task. |
| `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 | The agency Workspace ID for a signed-in client's portal requests. A verified user session and portal membership are required; API keys cannot authenticate portal access. This header does not grant agency administration access. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ApprovalListItem[] | The matching requests, each with the project it waits in. |
| `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. |

## Get one request

`GET /v1/api/approvals/{id}` · operation ID `getApproval`

**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. |
| `X-Client-Portal` | header | no | string | The agency Workspace ID for a signed-in client's portal requests. A verified user session and portal membership are required; API keys cannot authenticate portal access. This header does not grant agency administration access. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | Approval | The request. |
| `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 | No such request in this workspace — or the caller may not decide it, which answers the same 404 rather than confirming it exists. |

## Cancel a request nobody will decide

`POST /v1/api/approvals/{id}/cancel` · operation ID `cancelApproval`

**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` | Approval | The request was cancelled. A blocking dispatch request is treated as denied, so the cycle fails and the project can continue. |
| `400` | ValidationError \| ApiError | The request is no longer `pending` (it was already approved, denied or cancelled), the JSON body failed validation, or 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 | No such request in this workspace — or the caller may not decide it, which answers the same 404 rather than confirming it exists. |

## Approve or deny a request

`POST /v1/api/approvals/{id}/resolve` · operation ID `resolveApproval`

**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. |
| `X-Client-Portal` | header | no | string | The agency Workspace ID for a signed-in client's portal requests. A verified user session and portal membership are required; API keys cannot authenticate portal access. This header does not grant agency administration access. |

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

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `decidedBy` | string | no | max length 200 |
| `decision` | string | no | max length 5000 |
| `feedback` | string | no | max length 5000 |
| `status` | `approved` \| `denied` | **yes** |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | Approval | The decided request. For client collaborators, `decidedBy` is resolved by the server; a value supplied in the body is ignored. |
| `400` | ValidationError \| ApiError | The request is no longer `pending` (it was already approved, denied or cancelled), the JSON body failed validation, or 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 | No such request in this workspace — or the caller may not decide it, which answers the same 404 rather than confirming it exists. |

## Ask the customer a question

`POST /v1/api/approvals/client-question` · operation ID `askClientQuestion`

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

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `cycleId` | string | **yes** | min length 1 |
| `question` | string | **yes** | min length 1, max length 5000 |
| `title` | string | **yes** | min length 1, max length 200 |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `201` | Approval | The stored nonblocking request. Dispatch does not wait for it. `approvalType` and `requiredActor` are assigned by the server. |
| `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 | Client collaborators cannot create customer approval requests. This action is restricted to agency members. |
| `404` | ApiError | No such cycle in this workspace. |

## If a request fails

The response table for each operation lists its documented errors. Common status codes include:

- `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` — check the resource ID and your access to the selected Workspace.
- `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/)