# CRM · AI

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## GET /crm/ai-assistant

**List AI assistants**

`operationId: AIAssistantController_listAssistants`

The assistants configured for the org.

#### Signature

```http
GET /crm/ai-assistant (visibility?: string, status?: string) -> { data, count }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /crm/ai-assistant/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `status` | query | "active" \| "inactive" \| "archived" | — |  |
| `visibility` | query | "owner" \| "team" \| "organization" \| "global" | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { data, count } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /crm/ai-assistant

**Create an AI assistant**

`operationId: AIAssistantController_createAssistant`

Creates a configurable AI assistant — who it is, where it listens, what starts it and the tools it may use.

**The tools you grant are real capabilities.** Leaving `tools` out grants **every** tool; send `[]` for an assistant that can talk but never act. An assistant with a tool that assigns tickets or messages customers will do exactly that, unattended, when its trigger fires.

#### Signature

```http
POST /crm/ai-assistant (body) -> The created assistant
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Notes

- Create it inactive, try it, then activate — an assistant with write tools acts without further confirmation.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | ASSISTANT_INVALID | Give it a name. The handle may only use letters, numbers, dashes and underscores. | Every problem found is listed, joined; the body also carries `problems[]`. Checks: title and handle present, handle characters, status/visibility/channel values, tools a list of `{ key }`, each trigger with at least one event, each when…then complete, each knowledge source typed and pointed, each capability with an id. | — |
| `409` | ASSISTANT_HANDLE_TAKEN | There is already an assistant with the handle "support-triage". Choose another. | Another assistant in the org has that `name`. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /crm/ai-assistant/tools`
- `POST /crm/ai-assistant/{id}/test`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The assistant to create.

```json
{
  "name": "support-triage",
  "title": "Support triage assistant",
  "status": "inactive",
  "visibility": "organization",
  "channels": [
    "email"
  ],
  "tools": [
    {
      "key": "crm.tickets.assign"
    }
  ],
  "triggers": [
    {
      "events": [
        "ticket.created"
      ]
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created assistant |
| `400` | Give it a name. The handle may only use letters, numbers, dashes and underscores. — Every problem found is listed, joined; the body also carries `problems[]`. Checks: title and handle present, handle characters, status/visibility/channel values, tools a list of `{ key }`, each trigger with at least one event, each when…then complete, each knowledge source typed and pointed, each capability with an id. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `409` | There is already an assistant with the handle "support-triage". Choose another. — Another assistant in the org has that `name`. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /crm/ai-assistant/tools

**List available tools**

`operationId: AIAssistantController_getAvailableTools`

The tools an assistant can be granted, by `key`. Read this before configuring one — it is the catalogue of what an assistant can do, and each entry is a real action against org data.

#### Signature

```http
GET /crm/ai-assistant/tools () -> Available tools
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /crm/ai-assistant/capabilities`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Available tools |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /crm/ai-assistant/capabilities

**List built-in capabilities**

`operationId: AIAssistantController_getAvailableCapabilities`

The platform's built-in capabilities an assistant can be given in `capabilities`.

#### Signature

```http
GET /crm/ai-assistant/capabilities () -> Capabilities
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /crm/ai-assistant/tools`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Capabilities |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /crm/ai-assistant/triggers

**List trigger events**

`operationId: AIAssistantController_getAvailableTriggers`

The events that can start an assistant — what goes in `triggers[].events`.

#### Signature

```http
GET /crm/ai-assistant/triggers () -> Trigger events
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /crm/ai-assistant`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Trigger events |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /crm/ai-assistant/activities

**Get AI assistant activity**

`operationId: AIAssistantController_getAllAssistantActivities`

What the org's assistants have been doing, newest first — the audit trail for automated actions.

#### Signature

```http
GET /crm/ai-assistant/activities (limit?: integer) -> { data, count, orgId }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /crm/ai-assistant/{id}/activities`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `limit` | query | integer | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { data, count, orgId } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /crm/ai-assistant/{id}

**Get an AI assistant**

`operationId: AIAssistantController_getAssistant`

One assistant with its full configuration.

#### Signature

```http
GET /crm/ai-assistant/{id} (id: string) -> The assistant
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ASSISTANT_NOT_FOUND | AI Assistant AST-4821 not found | No assistant in this org has that id. | Check the id against `GET /crm/ai-assistant`. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `PUT /crm/ai-assistant/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Assistant id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The assistant |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | AI Assistant AST-4821 not found — No assistant in this org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PUT /crm/ai-assistant/{id}

**Update an AI assistant**

`operationId: AIAssistantController_updateAssistant`

Changes the fields sent; the merged result is validated as a whole and refused if it could not run. Changing `tools` or instructions changes what it does on its very next run — there is no staging.

#### Signature

```http
PUT /crm/ai-assistant/{id} (id: string, body) -> The updated assistant (with `id`)
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ASSISTANT_NOT_FOUND | AI Assistant AST-4821 not found | No assistant in this org has that id. | Check the id against `GET /crm/ai-assistant`. |
| `400` | ASSISTANT_INVALID | Give it a name. The handle may only use letters, numbers, dashes and underscores. | Every problem found is listed, joined; the body also carries `problems[]`. Checks: title and handle present, handle characters, status/visibility/channel values, tools a list of `{ key }`, each trigger with at least one event, each when…then complete, each knowledge source typed and pointed, each capability with an id. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /crm/ai-assistant/{id}/test`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Assistant id. |

### Request body

Fields to change.

```json
{
  "status": "active",
  "behaviorRules": [
    "Assign by topic and set priority from the language used."
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The updated assistant (with `id`) |
| `400` | Give it a name. The handle may only use letters, numbers, dashes and underscores. — Every problem found is listed, joined; the body also carries `problems[]`. Checks: title and handle present, handle characters, status/visibility/channel values, tools a list of `{ key }`, each trigger with at least one event, each when…then complete, each knowledge source typed and pointed, each capability with an id. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | AI Assistant AST-4821 not found — No assistant in this org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## DELETE /crm/ai-assistant/{id}

**Delete an AI assistant**

`operationId: AIAssistantController_deleteAssistant`

Deletes an assistant; it stops running. Setting status `inactive` or `archived` instead keeps the configuration.

#### Signature

```http
DELETE /crm/ai-assistant/{id} (id: string) -> { success, message }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ASSISTANT_NOT_FOUND | AI Assistant AST-4821 not found | No assistant in this org has that id. | Check the id against `GET /crm/ai-assistant`. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `PUT /crm/ai-assistant/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Assistant id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { success, message } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | AI Assistant AST-4821 not found — No assistant in this org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

Example response:

```json
{
  "success": true,
  "message": "AI Assistant deleted successfully"
}
```

## POST /crm/ai-assistant/{id}/execute

**Run an AI assistant**

`operationId: AIAssistantController_executeAssistant`

Runs an assistant now and returns the result when it finishes. Staff can run any active assistant; a **customer** can run one only when its visibility is `global`.

**This performs real actions** — any tool the assistant holds may be called: messages sent, records changed.

#### Signature

```http
POST /crm/ai-assistant/{id}/execute (id: string, body) -> The run result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Side-effecting. Anything the assistant's tools can do, it may do.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ASSISTANT_NOT_FOUND | AI Assistant AST-4821 not found | No assistant in this org has that id. | Check the id against `GET /crm/ai-assistant`. |
| `403` | ASSISTANT_NOT_FOR_CUSTOMERS | This assistant is not available to customers | The caller is a customer (or a system user) and the assistant's visibility is not `global`. | — |
| `400` | ASSISTANT_NOT_ACTIVE | AI Assistant is inactive. Activate this training assistant before running a test. | The assistant's status is not `active`. | Set status to active with `PUT /crm/ai-assistant/{id}`. |

Plus the standard platform errors: `401`, `429`, `500`.

#### See also

- `POST /crm/ai-assistant/{id}/execute/stream`
- `POST /crm/ai-assistant/{id}/test`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Assistant id. |

### Request body

The run.

```json
{
  "task": "Triage ticket TKT-4821",
  "data": {
    "ticketId": "TKT-4821"
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The run result |
| `400` | AI Assistant is inactive. Activate this training assistant before running a test. — The assistant's status is not `active`. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | This assistant is not available to customers — The caller is a customer (or a system user) and the assistant's visibility is not `global`. |
| `404` | AI Assistant AST-4821 not found — No assistant in this org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /crm/ai-assistant/{id}/execute/stream

**Run an AI assistant, streaming**

`operationId: AIAssistantController_executeAssistantStream`

The streaming form of `execute`: Server-Sent Events, each `data:` line a JSON chunk; the last is `{"type":"done"}`, or `{"type":"error","error":"…"}` on failure. Same access rules and real side effects as `execute`.

A refusal before the run starts (not found, not active, not for customers) is an ordinary HTTP error, not an event.

#### Signature

```http
POST /crm/ai-assistant/{id}/execute/stream (id: string, body) -> A stream of run output
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- A dropped connection does not stop the run — the assistant continues acting.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ASSISTANT_NOT_FOUND | AI Assistant AST-4821 not found | No assistant in this org has that id. | Check the id against `GET /crm/ai-assistant`. |
| `403` | ASSISTANT_NOT_FOR_CUSTOMERS | This assistant is not available to customers | The caller is a customer (or a system user) and the assistant's visibility is not `global`. | — |
| `400` | ASSISTANT_NOT_ACTIVE | AI Assistant is inactive. Activate this training assistant before running a test. | The assistant's status is not `active`. | Set status to active with `PUT /crm/ai-assistant/{id}`. |

Plus the standard platform errors: `401`, `429`, `500`.

#### See also

- `POST /crm/ai-assistant/{id}/execute`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Assistant id. |

### Request body

The run.

```json
{
  "task": "Triage ticket TKT-4821"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | A stream of run output |
| `400` | AI Assistant is inactive. Activate this training assistant before running a test. — The assistant's status is not `active`. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | This assistant is not available to customers — The caller is a customer (or a system user) and the assistant's visibility is not `global`. |
| `404` | AI Assistant AST-4821 not found — No assistant in this org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /crm/ai-assistant/voice/templates

**List voice assistant templates**

`operationId: AIAssistantController_getVoiceAssistantTemplates`

Pre-configured voice assistant setups for common uses — example bodies for `POST /crm/ai-assistant/voice/setup`.

#### Signature

```http
GET /crm/ai-assistant/voice/templates () -> { templates, count }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /crm/ai-assistant/voice/setup`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { templates, count } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /crm/ai-assistant/{id}/activities

**Get one assistant's activity**

`operationId: AIAssistantController_getAssistantActivities`

The activity history for a single assistant — what it ran, when, and what it did.

#### Signature

```http
GET /crm/ai-assistant/{id}/activities (id: string, limit?: integer) -> { data, count, ... }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /crm/ai-assistant/activities`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Assistant id. |
| `limit` | query | integer | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { data, count, ... } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /crm/ai-assistant/{id}/test

**Try an AI assistant**

`operationId: AIAssistantController_testAssistant`

Runs the assistant once in a throwaway conversation and returns what it replied, so you can see how it responds. **It is a real run, not a dry run:** the tools it holds act on real data. Try it with `tools: []` or on test records first. The assistant must be active.

#### Signature

```http
POST /crm/ai-assistant/{id}/test (id: string, body) -> `reply` (what it said, as text) and the full `run`
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ASSISTANT_NOT_FOUND | AI Assistant AST-4821 not found | No assistant in this org has that id. | Check the id against `GET /crm/ai-assistant`. |
| `403` | ASSISTANT_NOT_FOR_CUSTOMERS | This assistant is not available to customers | The caller is a customer (or a system user) and the assistant's visibility is not `global`. | — |
| `400` | ASSISTANT_NOT_ACTIVE | AI Assistant is inactive. Activate this training assistant before running a test. | The assistant's status is not `active`. | Set status to active with `PUT /crm/ai-assistant/{id}`. |

Plus the standard platform errors: `401`, `429`, `500`.

#### See also

- `POST /crm/ai-assistant/{id}/execute`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Assistant id. |

### Request body

What a person says to it.

```json
{
  "task": "Customer says their order never arrived"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `reply` (what it said, as text) and the full `run` |
| `400` | AI Assistant is inactive. Activate this training assistant before running a test. — The assistant's status is not `active`. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | This assistant is not available to customers — The caller is a customer (or a system user) and the assistant's visibility is not `global`. |
| `404` | AI Assistant AST-4821 not found — No assistant in this org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /crm/ai-assistant/voice/setup

**Set up a voice assistant**

`operationId: AIAssistantController_setupVoiceAgent`

Creates a voice AI assistant and points a Twilio number at it (an existing number, a specific one, or a newly purchased one in `areaCode`), with the voice and status webhooks configured. It then answers calls itself, so try it before putting a real number on it.

#### Signature

```http
POST /crm/ai-assistant/voice/setup (body) -> { assistant, phone, webhooks: { voice, status } }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff (signed-in users, not customers)`.

#### Notes

- A voice assistant talks to callers unsupervised.
- A Twilio setup failure surfaces as a server error with the reason ("Failed to setup Twilio: …").

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | ASSISTANT_INVALID | Give it a name. The handle may only use letters, numbers, dashes and underscores. | Every problem found is listed, joined; the body also carries `problems[]`. Checks: title and handle present, handle characters, status/visibility/channel values, tools a list of `{ key }`, each trigger with at least one event, each when…then complete, each knowledge source typed and pointed, each capability with an id. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /crm/ai-assistant/voice/templates`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The voice assistant.

```json
{
  "name": "front-desk",
  "title": "Front desk",
  "voiceModel": "nova",
  "purchaseNew": true,
  "areaCode": "415"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | { assistant, phone, webhooks: { voice, status } } |
| `400` | Give it a name. The handle may only use letters, numbers, dashes and underscores. — Every problem found is listed, joined; the body also carries `problems[]`. Checks: title and handle present, handle characters, status/visibility/channel values, tools a list of `{ key }`, each trigger with at least one event, each when…then complete, each knowledge source typed and pointed, each capability with an id. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

