---
title: "Configure questions a Speak AI voice agent asks"
description: "Attach question templates to an agent, reorder them, and map a question's answer onto a company Field. Manage the shared question-template library too."
---

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

# Configure questions a Speak AI voice agent asks


import EndpointIndex from "@/components/api/EndpointIndex.astro";

The Speak AI API exposes 8 questions endpoints under the base URL `https://api.speakai.co/v1`. Every request needs the `x-speakai-key` and `x-access-token` headers described in [Authentication](/api/authentication/).

## What can you do with the questions endpoints?

Speak AI groups these 8 endpoints under the questions resource. Each entry below links to the full reference for that endpoint further down this page.

### Questions

| Method | Path | What it does |
| --- | --- | --- |
| `GET` | [`/voice/questions`](#get-voice-questions) | List Questions |
| `POST` | [`/voice/questions`](#post-voice-questions) | Create Question |
| `GET` | [`/voice/questions/{fieldId}`](#get-voice-questions-field-id) | Get Question |
| `PUT` | [`/voice/questions/{fieldId}`](#put-voice-questions-field-id) | Update Question |
| `DELETE` | [`/voice/questions/{fieldId}`](#delete-voice-questions-field-id) | Delete Question |
| `PUT` | [`/voice/questions/reorder`](#put-voice-questions-reorder) | Reorder Questions |

### Question Templates

| Method | Path | What it does |
| --- | --- | --- |
| `GET` | [`/voice/question-templates`](#get-voice-question-templates) | List Question Templates |
| `POST` | [`/voice/question-templates`](#post-voice-question-templates) | Create Question Template |

## Questions

<h2 id="get-voice-questions">List Questions</h2>

#### List Questions

Lists the questions configured on one agent, in the order the agent asks them. Requires agentId -- returns 404 if that agent does not exist or does not belong to your company.

These are agent-level instances of a template (see Question Templates below); each carries its own required/attempts/no-response settings and, optionally, a mappedFieldId that writes the caller's answer onto a company Field after the call ends.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `agentId` | query | string | Yes | Required. The agent whose questions to list. |
| `enabledOnly` | query | boolean | No | When true, only returns questions with enabled=true. |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X GET 'https://api.speakai.co/v1/voice/questions' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="get-voice-questions" statuses={[{"code":"200","description":"OK"},{"code":"404","description":"Agent not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.fields` | object[] |  |
| `data.total` | integer |  |

<details>
<summary>`data.fields` -- 16 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `_id` | string | MongoDB document id, included because every document has one; use fieldId to identify it. |
| `fieldId` | string | Server-generated UUID. Immutable -- sent in the body on create or update is silently dropped. |
| `companyId` | string | Stamped from the caller's authenticated company. Sent in the body is silently dropped. |
| `userId` | string | Id of the user who created this question. Sent in the body is silently dropped. |
| `agentId` | string | Required on create. Immutable after create -- sent on update is silently dropped. |
| `templateId` | string | Required on create. The question template this question is based on. Immutable after create -- sent on update is silently dropped. |
| `customConfig` | object | Agent-level override of the template's defaultConfig. Only the keys you send are overridden; the rest fall back to the template. |
| `customConfig.displayLabel` | string |  |
| `customConfig.question` | string | The prompt text the agent speaks to ask this question. Renamed from promptText. |
| `customConfig.confirmationText` | string |  |
| `customConfig.validationPrompt` | string |  |
| `customConfig.validation` | object |  |
| `required` | boolean | Defaults to false. Agent must collect this before ending the conversation. |
| `maxPromptAttempts` | number | Defaults to 2. Hard maximum of 3. Joi validates this as Joi.number().integer(), which this spec generator reports as "number". |
| `noResponseBehavior` | string | Defaults to move_to_next_question. One of: `move_to_next_question`, `end_conversation` . |
| `triggerCondition` | string |  |
| `order` | number | Defaults to 0. Use PUT /voice/questions/reorder to reorder in bulk. |
| `enabled` | boolean | Defaults to true. |
| `mappedFieldId` | string | Id of a general company Field (see the Fields API) this question's collected answer is written onto after each call ends. Send null or an empty string to clear the mapping. |
| `createdAt` | string (date-time) |  |
| `updatedAt` | string (date-time) |  |

<details>
<summary>`customConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"fields": [
  {
    "fieldId": "q-8f2a1c4e",
    "agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "templateId": "tpl-email",
    "required": true,
    "maxPromptAttempts": 2,
    "noResponseBehavior": "move_to_next_question",
    "order": 0,
    "enabled": true,
    "mappedFieldId": "6650a1b2c3d4e5f678901234",
    "createdAt": "2026-08-01T12:00:00.000Z",
    "updatedAt": "2026-08-01T12:00:00.000Z"
  }
],
"total": 1
  }
}
```

</div>

<div slot="status-404">

**`404` Agent not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Agent not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

<h2 id="post-voice-questions">Create Question</h2>

#### Create Question

Requires the OWNER or ADMIN role. Attaches a question template to an agent. agentId and templateId are both required; both must belong to your company (or, for templateId, be a public system template). Returns 404 if either is not found.

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `agentId` | string, **required** | Required. |
| `templateId` | string, **required** | Required. Must reference a template your company can see (its own, or a system template). |
| `customConfig` | object | Agent-level override of the template's defaultConfig. Only the keys you send are overridden; the rest fall back to the template. |
| `customConfig.displayLabel` | string |  |
| `customConfig.question` | string | The prompt text the agent speaks to ask this question. Renamed from promptText. |
| `customConfig.confirmationText` | string |  |
| `customConfig.validationPrompt` | string |  |
| `customConfig.validation` | object |  |
| `required` | boolean |  |
| `maxPromptAttempts` | number |  |
| `noResponseBehavior` | string | One of: `move_to_next_question`, `end_conversation` . |
| `triggerCondition` | string |  |
| `order` | number |  |
| `enabled` | boolean |  |
| `mappedFieldId` | string | Id of an existing company Field to write this question's collected answer onto after each call. |

<details>
<summary>`customConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X POST 'https://api.speakai.co/v1/voice/questions' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

</CodePanel>

<ResponsePanel id="post-voice-questions" statuses={[{"code":"201","description":"Created"},{"code":"400","description":"Validation failed"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"},{"code":"404","description":"Agent or template not found"}]}>

<div slot="status-201">

**`201` Created**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.field` | object |  |

<details>
<summary>`data.field` -- 16 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `_id` | string | MongoDB document id, included because every document has one; use fieldId to identify it. |
| `fieldId` | string | Server-generated UUID. Immutable -- sent in the body on create or update is silently dropped. |
| `companyId` | string | Stamped from the caller's authenticated company. Sent in the body is silently dropped. |
| `userId` | string | Id of the user who created this question. Sent in the body is silently dropped. |
| `agentId` | string | Required on create. Immutable after create -- sent on update is silently dropped. |
| `templateId` | string | Required on create. The question template this question is based on. Immutable after create -- sent on update is silently dropped. |
| `customConfig` | object | Agent-level override of the template's defaultConfig. Only the keys you send are overridden; the rest fall back to the template. |
| `customConfig.displayLabel` | string |  |
| `customConfig.question` | string | The prompt text the agent speaks to ask this question. Renamed from promptText. |
| `customConfig.confirmationText` | string |  |
| `customConfig.validationPrompt` | string |  |
| `customConfig.validation` | object |  |
| `required` | boolean | Defaults to false. Agent must collect this before ending the conversation. |
| `maxPromptAttempts` | number | Defaults to 2. Hard maximum of 3. Joi validates this as Joi.number().integer(), which this spec generator reports as "number". |
| `noResponseBehavior` | string | Defaults to move_to_next_question. One of: `move_to_next_question`, `end_conversation` . |
| `triggerCondition` | string |  |
| `order` | number | Defaults to 0. Use PUT /voice/questions/reorder to reorder in bulk. |
| `enabled` | boolean | Defaults to true. |
| `mappedFieldId` | string | Id of a general company Field (see the Fields API) this question's collected answer is written onto after each call ends. Send null or an empty string to clear the mapping. |
| `createdAt` | string (date-time) |  |
| `updatedAt` | string (date-time) |  |

<details>
<summary>`customConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"field": {
  "fieldId": "q-8f2a1c4e",
  "agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "templateId": "tpl-email",
  "required": true,
  "maxPromptAttempts": 2,
  "noResponseBehavior": "move_to_next_question",
  "order": 0,
  "enabled": true,
  "mappedFieldId": "6650a1b2c3d4e5f678901234",
  "createdAt": "2026-08-01T12:00:00.000Z",
  "updatedAt": "2026-08-01T12:00:00.000Z"
}
  }
}
```

</div>

<div slot="status-400">

**`400` Validation failed**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage questions"
}
```

</div>

<div slot="status-404">

**`404` Agent or template not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Agent not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

<h2 id="get-voice-questions-field-id">Get Question</h2>

#### Get Question

Fetches a single question by id, scoped to your company.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `fieldId` | path | string | Yes |  |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X GET 'https://api.speakai.co/v1/voice/questions/FIELD_ID' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="get-voice-questions-field-id" statuses={[{"code":"200","description":"OK"},{"code":"404","description":"Field not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.field` | object |  |

<details>
<summary>`data.field` -- 16 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `_id` | string | MongoDB document id, included because every document has one; use fieldId to identify it. |
| `fieldId` | string | Server-generated UUID. Immutable -- sent in the body on create or update is silently dropped. |
| `companyId` | string | Stamped from the caller's authenticated company. Sent in the body is silently dropped. |
| `userId` | string | Id of the user who created this question. Sent in the body is silently dropped. |
| `agentId` | string | Required on create. Immutable after create -- sent on update is silently dropped. |
| `templateId` | string | Required on create. The question template this question is based on. Immutable after create -- sent on update is silently dropped. |
| `customConfig` | object | Agent-level override of the template's defaultConfig. Only the keys you send are overridden; the rest fall back to the template. |
| `customConfig.displayLabel` | string |  |
| `customConfig.question` | string | The prompt text the agent speaks to ask this question. Renamed from promptText. |
| `customConfig.confirmationText` | string |  |
| `customConfig.validationPrompt` | string |  |
| `customConfig.validation` | object |  |
| `required` | boolean | Defaults to false. Agent must collect this before ending the conversation. |
| `maxPromptAttempts` | number | Defaults to 2. Hard maximum of 3. Joi validates this as Joi.number().integer(), which this spec generator reports as "number". |
| `noResponseBehavior` | string | Defaults to move_to_next_question. One of: `move_to_next_question`, `end_conversation` . |
| `triggerCondition` | string |  |
| `order` | number | Defaults to 0. Use PUT /voice/questions/reorder to reorder in bulk. |
| `enabled` | boolean | Defaults to true. |
| `mappedFieldId` | string | Id of a general company Field (see the Fields API) this question's collected answer is written onto after each call ends. Send null or an empty string to clear the mapping. |
| `createdAt` | string (date-time) |  |
| `updatedAt` | string (date-time) |  |

<details>
<summary>`customConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"field": {
  "fieldId": "q-8f2a1c4e",
  "agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "templateId": "tpl-email",
  "required": true,
  "maxPromptAttempts": 2,
  "noResponseBehavior": "move_to_next_question",
  "order": 0,
  "enabled": true,
  "mappedFieldId": "6650a1b2c3d4e5f678901234",
  "createdAt": "2026-08-01T12:00:00.000Z",
  "updatedAt": "2026-08-01T12:00:00.000Z"
}
  }
}
```

</div>

<div slot="status-404">

**`404` Field not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Field not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

<h2 id="put-voice-questions-field-id">Update Question</h2>

#### Update Question

Requires the OWNER or ADMIN role. Partial update -- only the keys you send are changed. agentId and templateId cannot be changed after create; sending them is silently dropped.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `fieldId` | path | string | Yes |  |

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `customConfig` | object | Agent-level override of the template's defaultConfig. Only the keys you send are overridden; the rest fall back to the template. |
| `customConfig.displayLabel` | string |  |
| `customConfig.question` | string | The prompt text the agent speaks to ask this question. Renamed from promptText. |
| `customConfig.confirmationText` | string |  |
| `customConfig.validationPrompt` | string |  |
| `customConfig.validation` | object |  |
| `required` | boolean |  |
| `maxPromptAttempts` | number |  |
| `noResponseBehavior` | string | One of: `move_to_next_question`, `end_conversation` . |
| `triggerCondition` | string |  |
| `order` | number |  |
| `enabled` | boolean |  |
| `mappedFieldId` | string | Id of an existing company Field to write this question's collected answer onto after each call. |

<details>
<summary>`customConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X PUT 'https://api.speakai.co/v1/voice/questions/FIELD_ID' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

</CodePanel>

<ResponsePanel id="put-voice-questions-field-id" statuses={[{"code":"200","description":"OK"},{"code":"400","description":"Validation failed"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"},{"code":"404","description":"Field not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.field` | object |  |

<details>
<summary>`data.field` -- 16 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `_id` | string | MongoDB document id, included because every document has one; use fieldId to identify it. |
| `fieldId` | string | Server-generated UUID. Immutable -- sent in the body on create or update is silently dropped. |
| `companyId` | string | Stamped from the caller's authenticated company. Sent in the body is silently dropped. |
| `userId` | string | Id of the user who created this question. Sent in the body is silently dropped. |
| `agentId` | string | Required on create. Immutable after create -- sent on update is silently dropped. |
| `templateId` | string | Required on create. The question template this question is based on. Immutable after create -- sent on update is silently dropped. |
| `customConfig` | object | Agent-level override of the template's defaultConfig. Only the keys you send are overridden; the rest fall back to the template. |
| `customConfig.displayLabel` | string |  |
| `customConfig.question` | string | The prompt text the agent speaks to ask this question. Renamed from promptText. |
| `customConfig.confirmationText` | string |  |
| `customConfig.validationPrompt` | string |  |
| `customConfig.validation` | object |  |
| `required` | boolean | Defaults to false. Agent must collect this before ending the conversation. |
| `maxPromptAttempts` | number | Defaults to 2. Hard maximum of 3. Joi validates this as Joi.number().integer(), which this spec generator reports as "number". |
| `noResponseBehavior` | string | Defaults to move_to_next_question. One of: `move_to_next_question`, `end_conversation` . |
| `triggerCondition` | string |  |
| `order` | number | Defaults to 0. Use PUT /voice/questions/reorder to reorder in bulk. |
| `enabled` | boolean | Defaults to true. |
| `mappedFieldId` | string | Id of a general company Field (see the Fields API) this question's collected answer is written onto after each call ends. Send null or an empty string to clear the mapping. |
| `createdAt` | string (date-time) |  |
| `updatedAt` | string (date-time) |  |

<details>
<summary>`customConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"field": {
  "fieldId": "q-8f2a1c4e",
  "agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "templateId": "tpl-email",
  "required": true,
  "maxPromptAttempts": 2,
  "noResponseBehavior": "move_to_next_question",
  "order": 0,
  "enabled": true,
  "mappedFieldId": "6650a1b2c3d4e5f678901234",
  "createdAt": "2026-08-01T12:00:00.000Z",
  "updatedAt": "2026-08-01T12:00:00.000Z"
}
  }
}
```

</div>

<div slot="status-400">

**`400` Validation failed**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage questions"
}
```

</div>

<div slot="status-404">

**`404` Field not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Field not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

<h2 id="delete-voice-questions-field-id">Delete Question</h2>

#### Delete Question

Requires the OWNER or ADMIN role. Permanently removes the question from the agent and decrements the template's usageCount.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `fieldId` | path | string | Yes |  |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X DELETE 'https://api.speakai.co/v1/voice/questions/FIELD_ID' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="delete-voice-questions-field-id" statuses={[{"code":"200","description":"OK"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"},{"code":"404","description":"Field not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "success",
  "message": "Field deleted successfully"
}
```

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage questions"
}
```

</div>

<div slot="status-404">

**`404` Field not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Field not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

<h2 id="put-voice-questions-reorder">Reorder Questions</h2>

#### Reorder Questions

Requires the OWNER or ADMIN role. Bulk-writes the order field on each listed question, then returns the agent's full question list in its new order. Entries for a fieldId that does not belong to the given agentId are silently skipped (no error).

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `agentId` | string, **required** | Required. |
| `fieldOrders` | object[], **required** | Required. The new order for some or all of the agent's questions; each entry's order value is written directly (not resequenced). |
| `fieldOrders[].fieldId` | string, **required** |  |
| `fieldOrders[].order` | number, **required** |  |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X PUT 'https://api.speakai.co/v1/voice/questions/reorder' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

</CodePanel>

<ResponsePanel id="put-voice-questions-reorder" statuses={[{"code":"200","description":"OK"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"},{"code":"404","description":"Agent not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.fields` | object[] |  |

<details>
<summary>`data.fields` -- 16 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `_id` | string | MongoDB document id, included because every document has one; use fieldId to identify it. |
| `fieldId` | string | Server-generated UUID. Immutable -- sent in the body on create or update is silently dropped. |
| `companyId` | string | Stamped from the caller's authenticated company. Sent in the body is silently dropped. |
| `userId` | string | Id of the user who created this question. Sent in the body is silently dropped. |
| `agentId` | string | Required on create. Immutable after create -- sent on update is silently dropped. |
| `templateId` | string | Required on create. The question template this question is based on. Immutable after create -- sent on update is silently dropped. |
| `customConfig` | object | Agent-level override of the template's defaultConfig. Only the keys you send are overridden; the rest fall back to the template. |
| `customConfig.displayLabel` | string |  |
| `customConfig.question` | string | The prompt text the agent speaks to ask this question. Renamed from promptText. |
| `customConfig.confirmationText` | string |  |
| `customConfig.validationPrompt` | string |  |
| `customConfig.validation` | object |  |
| `required` | boolean | Defaults to false. Agent must collect this before ending the conversation. |
| `maxPromptAttempts` | number | Defaults to 2. Hard maximum of 3. Joi validates this as Joi.number().integer(), which this spec generator reports as "number". |
| `noResponseBehavior` | string | Defaults to move_to_next_question. One of: `move_to_next_question`, `end_conversation` . |
| `triggerCondition` | string |  |
| `order` | number | Defaults to 0. Use PUT /voice/questions/reorder to reorder in bulk. |
| `enabled` | boolean | Defaults to true. |
| `mappedFieldId` | string | Id of a general company Field (see the Fields API) this question's collected answer is written onto after each call ends. Send null or an empty string to clear the mapping. |
| `createdAt` | string (date-time) |  |
| `updatedAt` | string (date-time) |  |

<details>
<summary>`customConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"fields": [
  {
    "fieldId": "q-8f2a1c4e",
    "agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "templateId": "tpl-email",
    "required": true,
    "maxPromptAttempts": 2,
    "noResponseBehavior": "move_to_next_question",
    "order": 0,
    "enabled": true,
    "mappedFieldId": "6650a1b2c3d4e5f678901234",
    "createdAt": "2026-08-01T12:00:00.000Z",
    "updatedAt": "2026-08-01T12:00:00.000Z"
  }
]
  }
}
```

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage questions"
}
```

</div>

<div slot="status-404">

**`404` Agent not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Agent not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

## Question Templates

<h2 id="get-voice-question-templates">List Question Templates</h2>

#### List Question Templates

Lists the question templates visible to your company: Speak's shared system templates, plus your own company's templates.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `category` | query | string | No | Filter to one category. |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X GET 'https://api.speakai.co/v1/voice/question-templates' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="get-voice-question-templates" statuses={[{"code":"200","description":"OK"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.templates` | object[] |  |
| `data.total` | integer |  |

<details>
<summary>`data.templates` -- 14 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `_id` | string | MongoDB document id, included because every document has one; use templateId to identify it. |
| `templateId` | string | Server-generated UUID. Immutable. |
| `userId` | string | Id of the user who created this template. Absent on a system template. |
| `name` | string | Required on create. |
| `description` | string | Required on create. |
| `category` | string | Required on create. One of: `contact`, `booking`, `qualification`, `payment`, `custom` . |
| `fieldType` | string | Required on create. One of: `email`, `phone`, `date`, `time`, `datetime`, `text`, `number`, `boolean`, `choice`, `url` . |
| `defaultConfig` | object | Required on create. displayLabel and question are both required within this object. |
| `defaultConfig.displayLabel` | string |  |
| `defaultConfig.question` | string | The prompt text the agent speaks to ask this. Renamed from promptText. |
| `defaultConfig.confirmationText` | string |  |
| `defaultConfig.validationPrompt` | string |  |
| `defaultConfig.validation` | object |  |
| `isSystemTemplate` | boolean | Always false for a template your company creates. Not settable directly. |
| `isPublic` | boolean | Defaults to false. |
| `tags` | string[] |  |
| `usageCount` | integer | Incremented when a question is created from this template, decremented when that question is deleted. Not settable directly. |
| `createdAt` | string (date-time) |  |
| `updatedAt` | string (date-time) |  |

<details>
<summary>`defaultConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"templates": [
  {
    "templateId": "tpl-email",
    "name": "Email",
    "description": "The caller's email address",
    "category": "contact",
    "fieldType": "email",
    "defaultConfig": {
      "displayLabel": "Email",
      "question": "What is your email address?"
    },
    "isSystemTemplate": true,
    "isPublic": true,
    "tags": [],
    "usageCount": 12,
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  }
],
"total": 1
  }
}
```

</div>

</ResponsePanel>

</div>

<h2 id="post-voice-question-templates">Create Question Template</h2>

#### Create Question Template

Requires the OWNER or ADMIN role. Creates a company-scoped template -- the "custom question" a user names themselves rather than picking from Speak's shared library. The server stamps companyId and forces isSystemTemplate to false, so this template is only ever visible to your company.

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `name` | string, **required** | Required. |
| `description` | string, **required** | Required. |
| `category` | string, **required** | Required. One of: `contact`, `booking`, `qualification`, `payment`, `custom` . |
| `fieldType` | string, **required** | Required. One of: `email`, `phone`, `date`, `time`, `datetime`, `text`, `number`, `boolean`, `choice`, `url` . |
| `defaultConfig` | object, **required** | Required on create. displayLabel and question are both required within this object. |
| `defaultConfig.displayLabel` | string |  |
| `defaultConfig.question` | string | The prompt text the agent speaks to ask this. Renamed from promptText. |
| `defaultConfig.confirmationText` | string |  |
| `defaultConfig.validationPrompt` | string |  |
| `defaultConfig.validation` | object |  |
| `isPublic` | boolean |  |
| `tags` | string[] |  |

<details>
<summary>`defaultConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X POST 'https://api.speakai.co/v1/voice/question-templates' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

</CodePanel>

<ResponsePanel id="post-voice-question-templates" statuses={[{"code":"201","description":"Created"},{"code":"400","description":"Validation failed"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"}]}>

<div slot="status-201">

**`201` Created**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.template` | object |  |

<details>
<summary>`data.template` -- 14 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `_id` | string | MongoDB document id, included because every document has one; use templateId to identify it. |
| `templateId` | string | Server-generated UUID. Immutable. |
| `userId` | string | Id of the user who created this template. Absent on a system template. |
| `name` | string | Required on create. |
| `description` | string | Required on create. |
| `category` | string | Required on create. One of: `contact`, `booking`, `qualification`, `payment`, `custom` . |
| `fieldType` | string | Required on create. One of: `email`, `phone`, `date`, `time`, `datetime`, `text`, `number`, `boolean`, `choice`, `url` . |
| `defaultConfig` | object | Required on create. displayLabel and question are both required within this object. |
| `defaultConfig.displayLabel` | string |  |
| `defaultConfig.question` | string | The prompt text the agent speaks to ask this. Renamed from promptText. |
| `defaultConfig.confirmationText` | string |  |
| `defaultConfig.validationPrompt` | string |  |
| `defaultConfig.validation` | object |  |
| `isSystemTemplate` | boolean | Always false for a template your company creates. Not settable directly. |
| `isPublic` | boolean | Defaults to false. |
| `tags` | string[] |  |
| `usageCount` | integer | Incremented when a question is created from this template, decremented when that question is deleted. Not settable directly. |
| `createdAt` | string (date-time) |  |
| `updatedAt` | string (date-time) |  |

<details>
<summary>`defaultConfig.validation` -- 6 more fields</summary>

| Field | Type |
| --- | --- |
| `pattern` | string |
| `minLength` | number |
| `maxLength` | number |
| `min` | number |
| `max` | number |
| `allowedValues` | string[] |

</details>

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"template": {
  "templateId": "tpl-email",
  "name": "Email",
  "description": "The caller's email address",
  "category": "contact",
  "fieldType": "email",
  "defaultConfig": {
    "displayLabel": "Email",
    "question": "What is your email address?"
  },
  "isSystemTemplate": true,
  "isPublic": true,
  "tags": [],
  "usageCount": 12,
  "createdAt": "2026-01-01T00:00:00.000Z",
  "updatedAt": "2026-01-01T00:00:00.000Z"
}
  }
}
```

</div>

<div slot="status-400">

**`400` Validation failed**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage questions"
}
```

</div>

</ResponsePanel>

</div>

## Related pages

- [API reference](/api/) for the base URL, authentication, and the error format.
- [Authenticate with the Speak AI API using access tokens](/api/authentication/)
- [Upload audio and video to Speak AI and read insights](/api/media/)
- [Create and update live transcription sessions](/api/live-transcription/)
- [Analyze text notes with the Speak AI text endpoints](/api/text/)

Get an API key on the [Speak AI developer page](https://app.speakai.co/developers?utm_source=docs&utm_medium=referral&utm_campaign=api-reference&utm_content=api-voice-agents/questions).

Source: https://docs.speakai.co/api/voice-agents/questions/index.mdx
