The Speak AI API exposes 9 voice agents 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.
Create and manage voice agents. Mutations (create/update/delete) require the OWNER or ADMIN role.
What can you do with the voice agents endpoints?
Speak AI groups these 9 endpoints under the voice agents resource. Each entry below links to the full reference for that endpoint further down this page.
Agents
Avatars
Voices
Generation
Agents
List Voice Agents
/voice/agentsList Agents
Returns every non-deleted agent in your company, newest first, each annotated with its conversation count.
Response
agents is an array of agent objects, each with an extra conversationCount field: the number of conversations recorded against that agent in your company.
curl -X GET 'https://api.speakai.co/v1/voice/agents' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.agents |
object[] |
data.agents – 25 more fields
| Field | Type | Description |
|---|---|---|
_id |
string | MongoDB document id, included because every document has one; use agentId to identify it. |
agentId |
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. Sending your own value on create or update is silently dropped; you cannot re-parent an agent. |
userId |
string | Id of the user who created the agent. |
name |
string | Required on create. Trimmed, non-empty. |
personality |
string | Required on create. Free text describing the agent’s tone. Can be cleared to an empty string on update. |
instructions |
string | Required on create. The agent’s system instructions. Can be cleared to an empty string on update. |
voice |
object | Required on create. |
voice.provider |
string, required | TTS provider, e.g. elevenlabs or openai. |
voice.voiceId |
string, required | |
voice.model |
string | |
stt |
object | Speech-to-text provider config. Shape is not validated beyond being an object. |
llm |
object | |
llm.provider |
string | Must be one of the voice-agent LLM providers if sent. |
llm.model |
string | Must be one of the voice-agent model ids if sent. |
liveVoice |
string | The voice id used when llm.model is a Live (speech-to-speech) model. Rejected with a 400 listing the valid ids if it is not one of the voices the resolved Live model offers. Cleared by sending an empty string or null. |
avatar |
object | Set avatar.avatarId to attach an avatar; the server looks it up in your avatar catalog and derives avatarUrl, provider, and any provider-specific ids (e.g. Tavus faceId/palId) from that catalog row. Any of those fields sent in the request body are ignored, they cannot be set directly. An avatarId with no matching catalog row is rejected with 400, not saved. |
avatar.avatarId |
string | Required to set an avatar. Must match a row in your company’s avatar catalog (your own uploads or the shared system catalog). |
avatar.avatarUrl |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.provider |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.tavusFaceId |
string | Read-only: derived server-side from the catalog row’s tavus.faceId. Cannot be set by the client. |
avatar.tavusPalId |
string | Read-only: derived server-side from the catalog row’s tavus.palId. Cannot be set by the client. |
conversationMode |
string | One of: voice, avatar . |
status |
string | One of the agent status values. |
chatSettings |
object | |
websiteUrl |
string | |
enableWebSearch |
boolean | |
creativityLevel |
number | |
folderId |
string | |
telephony |
object | |
telephony.enabled |
boolean | |
telephony.phoneNumberId |
string | |
telephony.handoff |
object | |
telephony.settings |
object | |
isDeleted |
boolean | DELETE soft-deletes: this flips to true and the agent stops appearing in list/get, but the document is not removed. |
deletedAt |
string (date-time) | |
createdAt |
string (date-time) | |
updatedAt |
string (date-time) | |
conversationCount |
integer | Count of conversations recorded against this agent. |
chatSettings – 7 more fields
| Field | Type | Description |
|---|---|---|
welcomeMessage |
string | |
conversationStarters |
string[] | |
maxResponseLength |
integer | |
topicsToAvoid |
string[] | |
maxSessionLength |
integer | |
language |
string | |
responsePace |
string | One of the response-pace presets, if set. |
telephony.handoff – 3 more fields
| Field | Type |
|---|---|
enabled |
boolean |
targetNumber |
string |
summaryPrompt |
string |
telephony.settings – 1 more field
| Field | Type |
|---|---|
recordCalls |
boolean |
Example response, application/json.
{
"status": "success",
"data": {
"agents": [
{
"agentId": "6a1b2c3d4e5f60718293a4b5",
"companyId": "6650a1b2c3d4e5f60000000a",
"userId": "6650a1b2c3d4e5f60000001a",
"name": "Support Agent",
"personality": "Friendly and concise",
"instructions": "Help callers troubleshoot billing questions.",
"voice": {
"provider": "elevenlabs",
"voiceId": "21m00Tcm4TlvDq8ikWAM"
},
"llm": {
"provider": "openai",
"model": "gpt-5.4"
},
"conversationMode": "voice",
"status": "active",
"chatSettings": {
"language": "en",
"maxResponseLength": 500,
"maxSessionLength": 1800,
"conversationStarters": [],
"topicsToAvoid": []
},
"creativityLevel": 0.7,
"isDeleted": false,
"createdAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-23T19:33:24.232Z",
"conversationCount": 42
}
]
}
}Create Voice Agent
/voice/agentsCreate Agent
Requires the OWNER or ADMIN role.
Request Body
name, personality, instructions, and voice (with voice.provider and voice.voiceId) are required. Every other agent field can be set on create.
Set avatar.avatarId to attach an avatar from your catalog, the server resolves avatarUrl, provider, and any Tavus ids from that catalog row itself; you cannot set those directly.
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 |
|---|---|---|
_id |
string | MongoDB document id, included because every document has one; use agentId to identify it. |
agentId |
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. Sending your own value on create or update is silently dropped; you cannot re-parent an agent. |
userId |
string | Id of the user who created the agent. |
name |
string, required | Required on create. Trimmed, non-empty. |
personality |
string, required | Required on create. Free text describing the agent’s tone. Can be cleared to an empty string on update. |
instructions |
string, required | Required on create. The agent’s system instructions. Can be cleared to an empty string on update. |
voice |
object, required | Required on create. |
voice.provider |
string, required | TTS provider, e.g. elevenlabs or openai. |
voice.voiceId |
string, required | |
voice.model |
string | |
stt |
object | Speech-to-text provider config. Shape is not validated beyond being an object. |
llm |
object | |
llm.provider |
string | Must be one of the voice-agent LLM providers if sent. |
llm.model |
string | Must be one of the voice-agent model ids if sent. |
liveVoice |
string | The voice id used when llm.model is a Live (speech-to-speech) model. Rejected with a 400 listing the valid ids if it is not one of the voices the resolved Live model offers. Cleared by sending an empty string or null. |
avatar |
object | Set avatar.avatarId to attach an avatar; the server looks it up in your avatar catalog and derives avatarUrl, provider, and any provider-specific ids (e.g. Tavus faceId/palId) from that catalog row. Any of those fields sent in the request body are ignored, they cannot be set directly. An avatarId with no matching catalog row is rejected with 400, not saved. |
avatar.avatarId |
string | Required to set an avatar. Must match a row in your company’s avatar catalog (your own uploads or the shared system catalog). |
avatar.avatarUrl |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.provider |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.tavusFaceId |
string | Read-only: derived server-side from the catalog row’s tavus.faceId. Cannot be set by the client. |
avatar.tavusPalId |
string | Read-only: derived server-side from the catalog row’s tavus.palId. Cannot be set by the client. |
conversationMode |
string | One of: voice, avatar . |
status |
string | One of the agent status values. |
chatSettings |
object | |
websiteUrl |
string | |
enableWebSearch |
boolean | |
creativityLevel |
number | |
folderId |
string | |
telephony |
object | Accepted on create and, unlike most other server-managed fields, NOT stripped on update – but its shape is not validated by the request schema (any object is accepted as-is), and no other endpoint in this API populates it from a real phone number assignment (that relationship lives on the phone number’s own agentId field – see Phone Numbers). Treat this as a legacy field: writing to it has no confirmed effect. |
telephony.enabled |
boolean | |
telephony.phoneNumberId |
string | |
telephony.handoff |
object | |
telephony.settings |
object | |
isDeleted |
boolean | DELETE soft-deletes: this flips to true and the agent stops appearing in list/get, but the document is not removed. |
deletedAt |
string (date-time) | |
createdAt |
string (date-time) | |
updatedAt |
string (date-time) |
chatSettings – 7 more fields
| Field | Type | Description |
|---|---|---|
welcomeMessage |
string | |
conversationStarters |
string[] | |
maxResponseLength |
integer | |
topicsToAvoid |
string[] | |
maxSessionLength |
integer | |
language |
string | |
responsePace |
string | One of the response-pace presets, if set. |
telephony.handoff – 3 more fields
| Field | Type |
|---|---|
enabled |
boolean |
targetNumber |
string |
summaryPrompt |
string |
telephony.settings – 1 more field
| Field | Type |
|---|---|
recordCalls |
boolean |
curl -X POST 'https://api.speakai.co/v1/voice/agents' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
-H 'Content-Type: application/json' \
-d '{}'201 Created
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.agent |
object |
data.agent – 24 more fields
| Field | Type | Description |
|---|---|---|
_id |
string | MongoDB document id, included because every document has one; use agentId to identify it. |
agentId |
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. Sending your own value on create or update is silently dropped; you cannot re-parent an agent. |
userId |
string | Id of the user who created the agent. |
name |
string | Required on create. Trimmed, non-empty. |
personality |
string | Required on create. Free text describing the agent’s tone. Can be cleared to an empty string on update. |
instructions |
string | Required on create. The agent’s system instructions. Can be cleared to an empty string on update. |
voice |
object | Required on create. |
voice.provider |
string, required | TTS provider, e.g. elevenlabs or openai. |
voice.voiceId |
string, required | |
voice.model |
string | |
stt |
object | Speech-to-text provider config. Shape is not validated beyond being an object. |
llm |
object | |
llm.provider |
string | Must be one of the voice-agent LLM providers if sent. |
llm.model |
string | Must be one of the voice-agent model ids if sent. |
liveVoice |
string | The voice id used when llm.model is a Live (speech-to-speech) model. Rejected with a 400 listing the valid ids if it is not one of the voices the resolved Live model offers. Cleared by sending an empty string or null. |
avatar |
object | Set avatar.avatarId to attach an avatar; the server looks it up in your avatar catalog and derives avatarUrl, provider, and any provider-specific ids (e.g. Tavus faceId/palId) from that catalog row. Any of those fields sent in the request body are ignored, they cannot be set directly. An avatarId with no matching catalog row is rejected with 400, not saved. |
avatar.avatarId |
string | Required to set an avatar. Must match a row in your company’s avatar catalog (your own uploads or the shared system catalog). |
avatar.avatarUrl |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.provider |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.tavusFaceId |
string | Read-only: derived server-side from the catalog row’s tavus.faceId. Cannot be set by the client. |
avatar.tavusPalId |
string | Read-only: derived server-side from the catalog row’s tavus.palId. Cannot be set by the client. |
conversationMode |
string | One of: voice, avatar . |
status |
string | One of the agent status values. |
chatSettings |
object | |
websiteUrl |
string | |
enableWebSearch |
boolean | |
creativityLevel |
number | |
folderId |
string | |
telephony |
object | |
telephony.enabled |
boolean | |
telephony.phoneNumberId |
string | |
telephony.handoff |
object | |
telephony.settings |
object | |
isDeleted |
boolean | DELETE soft-deletes: this flips to true and the agent stops appearing in list/get, but the document is not removed. |
deletedAt |
string (date-time) | |
createdAt |
string (date-time) | |
updatedAt |
string (date-time) |
chatSettings – 7 more fields
| Field | Type | Description |
|---|---|---|
welcomeMessage |
string | |
conversationStarters |
string[] | |
maxResponseLength |
integer | |
topicsToAvoid |
string[] | |
maxSessionLength |
integer | |
language |
string | |
responsePace |
string | One of the response-pace presets, if set. |
telephony.handoff – 3 more fields
| Field | Type |
|---|---|
enabled |
boolean |
targetNumber |
string |
summaryPrompt |
string |
telephony.settings – 1 more field
| Field | Type |
|---|---|
recordCalls |
boolean |
Example response, application/json.
{
"status": "success",
"data": {
"agent": {
"agentId": "6a1b2c3d4e5f60718293a4b5",
"companyId": "6650a1b2c3d4e5f60000000a",
"userId": "6650a1b2c3d4e5f60000001a",
"name": "Support Agent",
"personality": "Friendly and concise",
"instructions": "Help callers troubleshoot billing questions.",
"voice": {
"provider": "elevenlabs",
"voiceId": "21m00Tcm4TlvDq8ikWAM"
},
"llm": {
"provider": "openai",
"model": "gpt-5.4"
},
"conversationMode": "voice",
"status": "active",
"chatSettings": {
"language": "en",
"maxResponseLength": 500,
"maxSessionLength": 1800,
"conversationStarters": [],
"topicsToAvoid": []
},
"creativityLevel": 0.7,
"isDeleted": false,
"createdAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-23T19:33:24.232Z"
}
}
}400 Validation failed, or avatar.avatarId does not match a catalog row
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
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.
{
"status": "failed",
"message": "You do not have permission to manage agents"
}Get Voice Agent
/voice/agents/{agentId}Get Agent
Fetches a single agent by id, scoped to your company. Returns 404 for an agent belonging to another company or one that has been deleted, the response does not distinguish between the two.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
agentId |
path | string | Yes |
curl -X GET 'https://api.speakai.co/v1/voice/agents/AGENT_ID' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.agent |
object |
data.agent – 24 more fields
| Field | Type | Description |
|---|---|---|
_id |
string | MongoDB document id, included because every document has one; use agentId to identify it. |
agentId |
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. Sending your own value on create or update is silently dropped; you cannot re-parent an agent. |
userId |
string | Id of the user who created the agent. |
name |
string | Required on create. Trimmed, non-empty. |
personality |
string | Required on create. Free text describing the agent’s tone. Can be cleared to an empty string on update. |
instructions |
string | Required on create. The agent’s system instructions. Can be cleared to an empty string on update. |
voice |
object | Required on create. |
voice.provider |
string, required | TTS provider, e.g. elevenlabs or openai. |
voice.voiceId |
string, required | |
voice.model |
string | |
stt |
object | Speech-to-text provider config. Shape is not validated beyond being an object. |
llm |
object | |
llm.provider |
string | Must be one of the voice-agent LLM providers if sent. |
llm.model |
string | Must be one of the voice-agent model ids if sent. |
liveVoice |
string | The voice id used when llm.model is a Live (speech-to-speech) model. Rejected with a 400 listing the valid ids if it is not one of the voices the resolved Live model offers. Cleared by sending an empty string or null. |
avatar |
object | Set avatar.avatarId to attach an avatar; the server looks it up in your avatar catalog and derives avatarUrl, provider, and any provider-specific ids (e.g. Tavus faceId/palId) from that catalog row. Any of those fields sent in the request body are ignored, they cannot be set directly. An avatarId with no matching catalog row is rejected with 400, not saved. |
avatar.avatarId |
string | Required to set an avatar. Must match a row in your company’s avatar catalog (your own uploads or the shared system catalog). |
avatar.avatarUrl |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.provider |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.tavusFaceId |
string | Read-only: derived server-side from the catalog row’s tavus.faceId. Cannot be set by the client. |
avatar.tavusPalId |
string | Read-only: derived server-side from the catalog row’s tavus.palId. Cannot be set by the client. |
conversationMode |
string | One of: voice, avatar . |
status |
string | One of the agent status values. |
chatSettings |
object | |
websiteUrl |
string | |
enableWebSearch |
boolean | |
creativityLevel |
number | |
folderId |
string | |
telephony |
object | |
telephony.enabled |
boolean | |
telephony.phoneNumberId |
string | |
telephony.handoff |
object | |
telephony.settings |
object | |
isDeleted |
boolean | DELETE soft-deletes: this flips to true and the agent stops appearing in list/get, but the document is not removed. |
deletedAt |
string (date-time) | |
createdAt |
string (date-time) | |
updatedAt |
string (date-time) |
chatSettings – 7 more fields
| Field | Type | Description |
|---|---|---|
welcomeMessage |
string | |
conversationStarters |
string[] | |
maxResponseLength |
integer | |
topicsToAvoid |
string[] | |
maxSessionLength |
integer | |
language |
string | |
responsePace |
string | One of the response-pace presets, if set. |
telephony.handoff – 3 more fields
| Field | Type |
|---|---|
enabled |
boolean |
targetNumber |
string |
summaryPrompt |
string |
telephony.settings – 1 more field
| Field | Type |
|---|---|
recordCalls |
boolean |
Example response, application/json.
{
"status": "success",
"data": {
"agent": {
"agentId": "6a1b2c3d4e5f60718293a4b5",
"companyId": "6650a1b2c3d4e5f60000000a",
"userId": "6650a1b2c3d4e5f60000001a",
"name": "Support Agent",
"personality": "Friendly and concise",
"instructions": "Help callers troubleshoot billing questions.",
"voice": {
"provider": "elevenlabs",
"voiceId": "21m00Tcm4TlvDq8ikWAM"
},
"llm": {
"provider": "openai",
"model": "gpt-5.4"
},
"conversationMode": "voice",
"status": "active",
"chatSettings": {
"language": "en",
"maxResponseLength": 500,
"maxSessionLength": 1800,
"conversationStarters": [],
"topicsToAvoid": []
},
"creativityLevel": 0.7,
"isDeleted": false,
"createdAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-23T19:33:24.232Z"
}
}
}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.
{
"status": "failed",
"code": 404,
"message": "Agent not found",
"hints": []
}Update Voice Agent
/voice/agents/{agentId}Update Agent
Requires the OWNER or ADMIN role. Partial update, send only the fields you want to change; fields left out of the body are left untouched, including avatar. companyId, agentId, and other immutable fields are silently dropped if sent.
If the body sets avatar.avatarId, the server re-resolves avatarUrl/provider/Tavus ids from the catalog row and overwrites the agent’s whole avatar sub-object with that resolved value; an unresolvable avatarId is rejected with 400 rather than saved.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
agentId |
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 |
|---|---|---|
_id |
string | MongoDB document id, included because every document has one; use agentId to identify it. |
agentId |
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. Sending your own value on create or update is silently dropped; you cannot re-parent an agent. |
userId |
string | Id of the user who created the agent. |
name |
string | Required on create. Trimmed, non-empty. |
personality |
string | Required on create. Free text describing the agent’s tone. Can be cleared to an empty string on update. |
instructions |
string | Required on create. The agent’s system instructions. Can be cleared to an empty string on update. |
voice |
object | Required on create. |
voice.provider |
string, required | TTS provider, e.g. elevenlabs or openai. |
voice.voiceId |
string, required | |
voice.model |
string | |
stt |
object | Speech-to-text provider config. Shape is not validated beyond being an object. |
llm |
object | |
llm.provider |
string | Must be one of the voice-agent LLM providers if sent. |
llm.model |
string | Must be one of the voice-agent model ids if sent. |
liveVoice |
string | The voice id used when llm.model is a Live (speech-to-speech) model. Rejected with a 400 listing the valid ids if it is not one of the voices the resolved Live model offers. Cleared by sending an empty string or null. |
avatar |
object | Set avatar.avatarId to attach an avatar; the server looks it up in your avatar catalog and derives avatarUrl, provider, and any provider-specific ids (e.g. Tavus faceId/palId) from that catalog row. Any of those fields sent in the request body are ignored, they cannot be set directly. An avatarId with no matching catalog row is rejected with 400, not saved. |
avatar.avatarId |
string | Required to set an avatar. Must match a row in your company’s avatar catalog (your own uploads or the shared system catalog). |
avatar.avatarUrl |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.provider |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.tavusFaceId |
string | Read-only: derived server-side from the catalog row’s tavus.faceId. Cannot be set by the client. |
avatar.tavusPalId |
string | Read-only: derived server-side from the catalog row’s tavus.palId. Cannot be set by the client. |
conversationMode |
string | One of: voice, avatar . |
status |
string | One of the agent status values. |
chatSettings |
object | |
websiteUrl |
string | |
enableWebSearch |
boolean | |
creativityLevel |
number | |
folderId |
string | |
telephony |
object | Accepted on create and, unlike most other server-managed fields, NOT stripped on update – but its shape is not validated by the request schema (any object is accepted as-is), and no other endpoint in this API populates it from a real phone number assignment (that relationship lives on the phone number’s own agentId field – see Phone Numbers). Treat this as a legacy field: writing to it has no confirmed effect. |
telephony.enabled |
boolean | |
telephony.phoneNumberId |
string | |
telephony.handoff |
object | |
telephony.settings |
object | |
isDeleted |
boolean | DELETE soft-deletes: this flips to true and the agent stops appearing in list/get, but the document is not removed. |
deletedAt |
string (date-time) | |
createdAt |
string (date-time) | |
updatedAt |
string (date-time) |
chatSettings – 7 more fields
| Field | Type | Description |
|---|---|---|
welcomeMessage |
string | |
conversationStarters |
string[] | |
maxResponseLength |
integer | |
topicsToAvoid |
string[] | |
maxSessionLength |
integer | |
language |
string | |
responsePace |
string | One of the response-pace presets, if set. |
telephony.handoff – 3 more fields
| Field | Type |
|---|---|
enabled |
boolean |
targetNumber |
string |
summaryPrompt |
string |
telephony.settings – 1 more field
| Field | Type |
|---|---|
recordCalls |
boolean |
curl -X PUT 'https://api.speakai.co/v1/voice/agents/AGENT_ID' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
-H 'Content-Type: application/json' \
-d '{}'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.agent |
object |
data.agent – 24 more fields
| Field | Type | Description |
|---|---|---|
_id |
string | MongoDB document id, included because every document has one; use agentId to identify it. |
agentId |
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. Sending your own value on create or update is silently dropped; you cannot re-parent an agent. |
userId |
string | Id of the user who created the agent. |
name |
string | Required on create. Trimmed, non-empty. |
personality |
string | Required on create. Free text describing the agent’s tone. Can be cleared to an empty string on update. |
instructions |
string | Required on create. The agent’s system instructions. Can be cleared to an empty string on update. |
voice |
object | Required on create. |
voice.provider |
string, required | TTS provider, e.g. elevenlabs or openai. |
voice.voiceId |
string, required | |
voice.model |
string | |
stt |
object | Speech-to-text provider config. Shape is not validated beyond being an object. |
llm |
object | |
llm.provider |
string | Must be one of the voice-agent LLM providers if sent. |
llm.model |
string | Must be one of the voice-agent model ids if sent. |
liveVoice |
string | The voice id used when llm.model is a Live (speech-to-speech) model. Rejected with a 400 listing the valid ids if it is not one of the voices the resolved Live model offers. Cleared by sending an empty string or null. |
avatar |
object | Set avatar.avatarId to attach an avatar; the server looks it up in your avatar catalog and derives avatarUrl, provider, and any provider-specific ids (e.g. Tavus faceId/palId) from that catalog row. Any of those fields sent in the request body are ignored, they cannot be set directly. An avatarId with no matching catalog row is rejected with 400, not saved. |
avatar.avatarId |
string | Required to set an avatar. Must match a row in your company’s avatar catalog (your own uploads or the shared system catalog). |
avatar.avatarUrl |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.provider |
string | Read-only in practice: always overwritten from the matched catalog row. |
avatar.tavusFaceId |
string | Read-only: derived server-side from the catalog row’s tavus.faceId. Cannot be set by the client. |
avatar.tavusPalId |
string | Read-only: derived server-side from the catalog row’s tavus.palId. Cannot be set by the client. |
conversationMode |
string | One of: voice, avatar . |
status |
string | One of the agent status values. |
chatSettings |
object | |
websiteUrl |
string | |
enableWebSearch |
boolean | |
creativityLevel |
number | |
folderId |
string | |
telephony |
object | |
telephony.enabled |
boolean | |
telephony.phoneNumberId |
string | |
telephony.handoff |
object | |
telephony.settings |
object | |
isDeleted |
boolean | DELETE soft-deletes: this flips to true and the agent stops appearing in list/get, but the document is not removed. |
deletedAt |
string (date-time) | |
createdAt |
string (date-time) | |
updatedAt |
string (date-time) |
chatSettings – 7 more fields
| Field | Type | Description |
|---|---|---|
welcomeMessage |
string | |
conversationStarters |
string[] | |
maxResponseLength |
integer | |
topicsToAvoid |
string[] | |
maxSessionLength |
integer | |
language |
string | |
responsePace |
string | One of the response-pace presets, if set. |
telephony.handoff – 3 more fields
| Field | Type |
|---|---|
enabled |
boolean |
targetNumber |
string |
summaryPrompt |
string |
telephony.settings – 1 more field
| Field | Type |
|---|---|
recordCalls |
boolean |
Example response, application/json.
{
"status": "success",
"data": {
"agent": {
"agentId": "6a1b2c3d4e5f60718293a4b5",
"companyId": "6650a1b2c3d4e5f60000000a",
"userId": "6650a1b2c3d4e5f60000001a",
"name": "Support Agent",
"personality": "Friendly and concise",
"instructions": "Help callers troubleshoot billing questions.",
"voice": {
"provider": "elevenlabs",
"voiceId": "21m00Tcm4TlvDq8ikWAM"
},
"llm": {
"provider": "openai",
"model": "gpt-5.4"
},
"conversationMode": "voice",
"status": "active",
"chatSettings": {
"language": "en",
"maxResponseLength": 500,
"maxSessionLength": 1800,
"conversationStarters": [],
"topicsToAvoid": []
},
"creativityLevel": 0.7,
"isDeleted": false,
"createdAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-23T19:33:24.232Z"
}
}
}400 Validation failed, or avatar.avatarId does not match a catalog row visible to your company
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
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.
{
"status": "failed",
"message": "You do not have permission to manage agents"
}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.
{
"status": "failed",
"code": 404,
"message": "Agent not found",
"hints": []
}Delete Voice Agent
/voice/agents/{agentId}Delete Agent
Requires the OWNER or ADMIN role. Soft-deletes: the agent is flagged isDeleted and stops appearing in list/get, but the record and its conversation history are retained.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
agentId |
path | string | Yes |
curl -X DELETE 'https://api.speakai.co/v1/voice/agents/AGENT_ID' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
message |
string |
Example response, application/json.
{
"status": "success",
"message": "Agent deleted successfully"
}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.
{
"status": "failed",
"message": "You do not have permission to manage agents"
}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.
{
"status": "failed",
"code": 404,
"message": "Agent not found",
"hints": []
}Avatars
List Voice Avatars
/voice/avatarsList Avatars
Returns the shared system avatar catalog plus any avatars your company has uploaded, newest first. Another company’s private uploads are never included.
curl -X GET 'https://api.speakai.co/v1/voice/avatars' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.avatars |
object[] |
data.avatars – 22 more fields
| Field | Type | Description |
|---|---|---|
avatarId |
string | Server-generated UUID on create. Immutable. |
companyId |
string | Absent on a shared system-catalog avatar, which every company can read but only Speak can write. Present on a company’s own uploaded avatar. |
name |
string | Required on create. |
provider |
string | Required on create. One of: bey, tavus, heygen, synthesia, d-id . |
imageUrl |
string | |
videoUrl |
string | |
localImagePath |
string | Local file path used by internal upload flows. |
localVideoPath |
string | Local file path used by internal upload flows. |
tavus |
object | Set when provider is tavus. |
bey |
object | Set when provider is bey. |
custom |
object | Set when provider is a custom/self-hosted avatar. |
custom.uploadedBy |
string | |
custom.originalFileName |
string | |
custom.fileSize |
number | |
custom.mimeType |
string | |
category |
string | Defaults to professional if not sent. One of: professional, casual, stock, custom, premium . |
tags |
string[] | |
isPremium |
boolean | Defaults to false. |
status |
string | Defaults to active. One of: active, inactive, processing, error . |
isPublic |
boolean | Defaults to true. |
description |
string | |
metadata |
object | Arbitrary metadata object. Shape is not validated. |
createdBy |
string | Id of the user who created this avatar. |
lastSyncedAt |
string (date-time) | Set by the system-catalog sync job for shared avatars. |
createdAt |
string (date-time) | |
updatedAt |
string (date-time) |
tavus – 7 more fields
| Field | Type | Description |
|---|---|---|
faceId |
string | Required within this object if tavus is sent at all. |
palId |
string | |
status |
string | One of: started, completed, error . |
isRecommended |
boolean | |
trainingProgress |
string | |
faceType |
string | One of: user, system . |
modelType |
string |
bey – 8 more fields
| Field | Type | Description |
|---|---|---|
avatarId |
string | Required within this object if bey is sent at all. |
gender |
string | |
ethnicity |
string | |
voiceId |
string | |
ageRange |
string | |
style |
string | |
isDefault |
boolean | |
languageCode |
string |
Example response, application/json.
{
"status": "success",
"data": {
"avatars": [
{
"avatarId": "r55e6793f10f",
"name": "Rae",
"provider": "tavus",
"imageUrl": "https://cdn.replica.tavus.io/still.png",
"tavus": {
"faceId": "r55e6793f10f",
"palId": "p135a0c8e542"
},
"category": "professional",
"isPremium": false,
"status": "active",
"isPublic": true,
"createdAt": "2026-08-01T12:00:00.000Z",
"updatedAt": "2026-08-01T12:00:00.000Z"
}
]
}
}Voices
List Voices
/voice/voicesList Voices
Lists the voices visible to your company: Speak’s shared system catalog, plus your own company’s uploaded voices, whether public or not.
curl -X GET 'https://api.speakai.co/v1/voice/voices' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.voices |
object[] | |
data.total |
integer |
data.voices – 23 more fields
| Field | Type | Description |
|---|---|---|
_id |
string | MongoDB document id, included because every document has one. Not used to identify a voice anywhere in this API – use voiceId for that. |
voiceId |
string | Client-supplied on create if you send one, otherwise server-generated. The identifier for this voice everywhere in this API (unlike most resources, this catalog has no separate server-generated UUID). |
companyId |
string | Absent on a shared system-catalog voice, which every company can read but only Speak can write. Present on a company’s own uploaded voice. Not settable directly. |
createdBy |
string | Id of the user who created this voice. Not settable directly. |
name |
string | Required on create. |
provider |
string | Required on create. One of: elevenlabs, openai, deepgram, cartesia, google, azure . |
language |
string | Defaults to English. |
languageCode |
string | Defaults to en. |
gender |
string | One of: male, female, neutral . |
age |
string | One of: young, middle-aged, old . |
accent |
string | |
description |
string | |
previewUrl |
string | |
category |
string | Defaults to premade. One of: premade, cloned, custom, professional, conversational . |
tags |
string[] | |
isPremium |
boolean | Defaults to false. |
isMultilingual |
boolean | Defaults to false. |
isPublic |
boolean | Defaults to true. |
status |
string | Defaults to active. One of: active, inactive, processing, error . |
providerData |
object | Arbitrary provider-specific data. Shape is not validated. |
metadata |
object | Arbitrary metadata object. Shape is not validated. |
createdAt |
string (date-time) | |
updatedAt |
string (date-time) |
Example response, application/json.
{
"status": "success",
"data": {
"voices": [
{
"_id": "6650a1b2c3d4e5f678901234",
"voiceId": "b2c3d4e5-f678-9012-abcd-ef1234567890",
"createdBy": "user-1",
"name": "Rachel",
"provider": "elevenlabs",
"language": "English",
"languageCode": "en",
"gender": "female",
"category": "premade",
"tags": [],
"isPremium": false,
"isMultilingual": true,
"isPublic": true,
"status": "active",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"total": 1
}
}Generation
Create Agent From Prompt
/voice/agents/generationCreate Agent From Prompt
Requires the OWNER or ADMIN role. Prompt-first creation: creates a new agent server-side with placeholder config, then runs the same generation flow as Generate Agent Config against it. Response always includes the new agentId, plus either an agent key (generation succeeded) or needsFollowUp: true (prompt needs more detail) – see Generate Agent Config for both shapes.
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 |
|---|---|---|
prompt |
string, required | Required. Describe the agent you want in a sentence or two. |
name |
string | |
manualInstructions |
string | Skips the thin-prompt follow-up question and generates immediately, folding this in as additional context. |
curl -X POST 'https://api.speakai.co/v1/voice/agents/generation' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
-H 'Content-Type: application/json' \
-d '{}'201 Created
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object |
Example response, application/json.
{
"status": "success",
"data": {
"agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent": {
"agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Support Agent"
}
}
}400 Validation failed
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
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.
{
"status": "failed",
"message": "You do not have permission to manage agents"
}Generate Agent Config
/voice/agents/{agentId}/generation/generateGenerate Agent Config
Requires the OWNER or ADMIN role. Runs the prompt-to-config pipeline against an existing agent: extracts a config from prompt via the LLM, and either persists it onto the agent (response has an agent key), or, when the prompt is too thin to act on and manualInstructions was not sent, returns a follow-up question instead (response has needsFollowUp: true) so you can call this again with more detail.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
agentId |
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 |
|---|---|---|
prompt |
string, required | Required. Describe the agent you want in a sentence or two. |
name |
string | |
manualInstructions |
string | Skips the thin-prompt follow-up question and generates immediately, folding this in as additional context. |
curl -X POST 'https://api.speakai.co/v1/voice/agents/AGENT_ID/generation/generate' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
-H 'Content-Type: application/json' \
-d '{}'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object |
Example response, application/json.
{
"status": "success",
"data": {
"agent": {
"agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Support Agent"
}
}
}400 Validation failed
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
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.
{
"status": "failed",
"message": "You do not have permission to manage agents"
}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.
{
"status": "failed",
"code": 404,
"message": "Agent not found",
"hints": []
}Related pages
- API reference for the base URL, authentication, and the error format.
- Authenticate with the Speak AI API using access tokens
- Upload audio and video to Speak AI and read insights
- Create and update live transcription sessions
- Analyze text notes with the Speak AI text endpoints
Get an API key on the Speak AI developer page.