Developers · API
v1API reference
The base URL is https://console-api.livespeech.ai, and every request sends the API key as a Bearer token. The first part of this page is the rules shared by every request; the second part is the endpoint-by-endpoint specification.
Authorization: Bearer ls_sk_0123abcd...
A missing or malformed key answers 401 with a WWW-Authenticate: Bearer header.
Keys
| Item | Detail |
|---|---|
| Format | ls_sk_ + 64 hexadecimal characters |
| Issued | By LiveSpeech when your account is set up |
| Shown | Once, at issue — the server does not keep the plain text |
| Count | One account key covers all agents (no per-agent keys) |
| Earlier keys | Keys issued as ls_partner_… keep working |
Keep the key in a server environment variable or secret store and send requests from your server. In a browser or mobile app it would let anyone control every agent you have.
Scopes
A key carries a set of scopes that says what it can do. Check the ones you have with GET /v1/whoami.
| Scope | Requests that need it |
|---|---|
agents | Create, list, read and terminate agents |
config | Read and update agent settings |
calls | List call results |
webhooks | Register and read the webhook |
Without the scope you get 403 and a message naming the scope you need. Ask support@livespeech.ai for scopes you are missing.
GET /v1/whoami also reports scopes that no request on this page needs: usage, which every account is given, and calls:originate and numbers:issue, which cover outbound dialing and number issuance — an operator grants those per account, and they cannot be requested through the API.
{ "error": "Insufficient scope: calls required" }
Telling refusals apart
| Status | Meaning | What to do |
|---|---|---|
401 | The key is missing, malformed or no longer valid | Check the header and the key value; if it persists, email support@livespeech.ai |
403 | The key is valid but lacks the scope, or the agent belongs to another account | Ask for the scope, or check the agent identifier |
For security, 401 does not say which reason applies. Reading another account's agent answers the same 403 without revealing whether the agent exists.
Rate limit
| Item | Value |
|---|---|
| Counted per | Sending server IP + request path |
| Limit | 30 requests per 5 minutes |
| When exceeded | 429 + {"error":"Too many requests. Please try again later."} |
Different paths are counted separately — agent A's call list and agent B's call list have separate limits. On 429, wait a moment and send again; if it keeps happening, widen the interval. For a bulk sync, tell support@livespeech.ai in advance.
Retrying safely with an idempotency key
Creating an agent (POST /v1/agents) accepts an Idempotency-Key header. If the connection drops before you get the response, sending the same key again returns the first response without creating a second agent.
Idempotency-Key: create-dental-00123-1
| Item | Detail |
|---|---|
| Format | 1–128 printable ASCII characters, no whitespace |
| Kept for | At least 24 hours — it may be deleted after that |
| Same key + same body | The stored first response is replayed |
| Same key + different body | 409 IDEMPOTENCY_KEY_MISMATCH |
| First request still running | 409 IDEMPOTENCY_IN_PROGRESS — try again shortly with the same key |
| Malformed | 400 (the key is not consumed) |
If the first request failed, the key is released — sending it again runs the request anew. Reuse a key only when retrying that same request, and always use a new key for a new creation (for example create-{externalId}-{signupId}). If a key past its retention is still around, you get the earlier response or a 409 back.
Request format
| Item | Detail |
|---|---|
| Body | JSON (Content-Type: application/json) |
| Response | JSON |
| Times | ISO 8601 UTC (2026-09-09T04:15:22.140Z) |
| Characters | UTF-8 |
Endpoints
Base URL: https://console-api.livespeech.ai
Account
Endpoints about the account itself (identity and scopes).
Identify the account (whoami)
/v1/whoamiAuthenticates with the Bearer account key and returns the identity of the account the key belongs to. It needs no agent scope (the delegation gate is not involved), so use it to verify the authentication plumbing.
Responses
200The authenticated account identity.
| Field | Type | Required | Description |
|---|---|---|---|
accountId | string | required | Unique account identifier. |
name | string | required | Account display name. |
scopes | string[] | required | Scopes granted to this account.
calls:originate and numbers:issue are granted by an operator and cannot be requested
through a public path. Accounts that have them see them in this array too.
Values: |
keyPrefix | string | required | Display prefix of the key used to authenticate (not the full key). |
401Authentication failed. The error string is one of two:
Missing or invalid Authorization header— the Authorization header is missing or not inBearer {token}form.Invalid API key— key format/hash mismatch · revoked · inactive (the specific reason is not disclosed).
Example · Authorization header missing / malformed
{
"error": "Missing or invalid Authorization header"
}Example · Key format/hash mismatch · revoked · inactive
{
"error": "Invalid API key"
}403The key is valid but the account is not active (suspended).
Agents
Agent provisioning (agents scope). Works only on delegated agents.
List agents
/v1/agentsParameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer · query | optional | 1–200 · default 50 |
cursor | string · query | optional | nextCursor from the previous response (an opaque token). |
Responses
200A page of the agents delegated to this account.
| Field | Type | Required | Description |
|---|---|---|---|
agents | object[] | required | |
agents[].agentId | string | required | |
agents[].name | string | required | |
agents[].region | string | null | required | Values: |
agents[].status | string | required | Says only whether the agent has been terminated. active does not guarantee that a
number is connected — numbers are assigned separately, so number may be null.
Values: |
agents[].externalId | string | null | required | |
agents[].createdAt | string | null | required | date-time |
nextCursor | string | null | required |
400Malformed request.
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
403Account inactive · missing scope · agent not delegated.
Create an agent
/v1/agentsCreates an agent. The response carries no phone number (the number is connected separately). With an Idempotency-Key header, a repeat request with the same key replays the stored first response (no duplicate creation). externalId is your identifier for the agent (your own record id, say); creating again with the same value is refused with 409.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string · header | optional | Idempotency key for safe retries (1–128 printable ASCII characters, no whitespace). A repeat with the same key and the same body replays the stored first response. The same key with a different body is refused with 409 ( IDEMPOTENCY_KEY_MISMATCH) — so a request never looks successful while its body was ignored.
≤ 128 chars |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | required | Display name of the agent (the business). ≤ 200 chars |
region | string | required | Service (telephony) region. Values: |
externalId | string | required | Your identifier for the agent (the duplicate-creation guard). 1–128 printable ASCII characters, no whitespace. ≤ 128 chars |
settings | object | optional | Agent settings. PUT is a partial patch that updates only the fields sent.
|
settings.persona | string | optional | The agent's persona prompt. The length limit applies to the text you send. On save, a sentence announcing the call context is added in front, so GET and PUT responses are longer than what you sent by that much. Sending that value back does not duplicate the sentence.
≤ 8000 chars |
settings.greeting | string | optional | ≤ 1000 chars |
settings.tone | string[] | optional | ≤ 20 items · each ≤ 60 chars |
settings.businessHours | object | null | optional | {mode: "24/7"|"custom", timezone(IANA), schedule{mon..sun:{enabled,open,close}}}. |
settings.transfer | object | null | optional | {type: take_message|standard_response|transfer_human, message}. |
settings.summaryRecipients | object[] | optional | ≤ 20 items |
settings.summaryRecipients[].email | string | required | |
settings.summaryRecipients[].keywords | string[] | optional | ≤ 20 items · each ≤ 60 chars |
Responses
201The created agent. No number field.
| Field | Type | Required | Description |
|---|---|---|---|
agentId | string | required | |
name | string | required | |
region | string | null | required | Values: |
status | string | required | Says only whether the agent has been terminated. active does not guarantee that a
number is connected — numbers are assigned separately, so number may be null.
Values: |
externalId | string | null | required | |
createdAt | string | null | required | date-time |
400Malformed request.
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
402Agent quota exceeded (code: AGENT_QUOTA_EXCEEDED).
403Account inactive · missing scope · agent not delegated.
409code: EXTERNAL_ID_EXISTS — an agent with this externalId already exists.
code: IDEMPOTENCY_IN_PROGRESS — a request with the same Idempotency-Key is still running.
code: IDEMPOTENCY_KEY_MISMATCH — the same Idempotency-Key was reused with a different body.
Read an agent
/v1/agents/{agentId}Includes the number (null before one is connected), integration status and a plan summary.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
agentId | string · path | required | Agent identifier. 403 if the agent is not delegated to this account (existence is not revealed). |
Responses
200Agent detail.
| Field | Type | Required | Description |
|---|---|---|---|
agentId | string | required | |
name | string | required | |
region | string | null | required | Values: |
status | string | required | Says only whether the agent has been terminated. active does not guarantee that a
number is connected — numbers are assigned separately, so number may be null.
Values: |
externalId | string | null | required | |
createdAt | string | null | required | date-time |
number | string | null | required | The number currently being served (null if none). |
integrations | object | required | Commerce integration status per provider. |
integrations.cafe24 | object | optional | |
integrations.cafe24.connected | boolean | optional | |
integrations.nextengine | object | optional | |
integrations.nextengine.connected | boolean | optional | |
plan | object | null | required | |
plan.tier | string | null | optional | |
plan.status | string | null | optional |
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
403Account inactive · missing scope · agent not delegated.
Terminate an agent
/v1/agents/{agentId}Terminates the agent. An agent that still holds an active number must have it released first; until then termination is held with 502 (CARRIER_RELEASE_FAILED) and the agent stays active. Email support@livespeech.ai with the agentId to release the number.
purge: true also removes what the agent stored, such as the persona and summary recipients.
Once termination completes, the externalId mapping and the delegation link are released: the same externalId can be used to sign up (create) again, the terminated agent no longer counts against the quota, and it disappears from list and read (later access answers 403).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
agentId | string · path | required | Agent identifier. 403 if the agent is not delegated to this account (existence is not revealed). |
Request body(optional)
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | optional | ≤ 500 chars |
purge | boolean | optional | default false |
Responses
200Termination complete.
| Field | Type | Required | Description |
|---|---|---|---|
status | string | required | Values: |
numberReclaimed | boolean | required | |
terminatedAt | string | required | date-time |
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
403Account inactive · missing scope · agent not delegated.
409An agent that was already terminated but whose delegation was not yet released (code: ALREADY_TERMINATED). This retry runs the release again. An agent whose termination and release are fully complete has no delegation, so it answers 403.
502Number release failed — termination held (code: CARRIER_RELEASE_FAILED).
Agent settings
Agent settings for an agent (config scope).
Read agent settings
/v1/agents/{agentId}/settingsParameters
| Name | Type | Required | Description |
|---|---|---|---|
agentId | string · path | required | Agent identifier. 403 if the agent is not delegated to this account (existence is not revealed). |
Responses
200The current agent settings.
| Field | Type | Required | Description |
|---|---|---|---|
persona | string | optional | The agent's persona prompt. The length limit applies to the text you send. On save, a sentence announcing the call context is added in front, so GET and PUT responses are longer than what you sent by that much. Sending that value back does not duplicate the sentence.
≤ 8000 chars |
greeting | string | optional | ≤ 1000 chars |
tone | string[] | optional | ≤ 20 items · each ≤ 60 chars |
businessHours | object | null | optional | {mode: "24/7"|"custom", timezone(IANA), schedule{mon..sun:{enabled,open,close}}}. |
transfer | object | null | optional | {type: take_message|standard_response|transfer_human, message}. |
summaryRecipients | object[] | optional | ≤ 20 items |
summaryRecipients[].email | string | required | |
summaryRecipients[].keywords | string[] | optional | ≤ 20 items · each ≤ 60 chars |
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
403Account inactive · missing scope · agent not delegated.
Update agent settings (partial)
/v1/agents/{agentId}/settingsA partial patch that updates only the fields sent. It can take up to 5 minutes for a change to reach live calls.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
agentId | string · path | required | Agent identifier. 403 if the agent is not delegated to this account (existence is not revealed). |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
persona | string | optional | The agent's persona prompt. The length limit applies to the text you send. On save, a sentence announcing the call context is added in front, so GET and PUT responses are longer than what you sent by that much. Sending that value back does not duplicate the sentence.
≤ 8000 chars |
greeting | string | optional | ≤ 1000 chars |
tone | string[] | optional | ≤ 20 items · each ≤ 60 chars |
businessHours | object | null | optional | {mode: "24/7"|"custom", timezone(IANA), schedule{mon..sun:{enabled,open,close}}}. |
transfer | object | null | optional | {type: take_message|standard_response|transfer_human, message}. |
summaryRecipients | object[] | optional | ≤ 20 items |
summaryRecipients[].email | string | required | |
summaryRecipients[].keywords | string[] | optional | ≤ 20 items · each ≤ 60 chars |
Responses
200The full settings after the update.
| Field | Type | Required | Description |
|---|---|---|---|
persona | string | optional | The agent's persona prompt. The length limit applies to the text you send. On save, a sentence announcing the call context is added in front, so GET and PUT responses are longer than what you sent by that much. Sending that value back does not duplicate the sentence.
≤ 8000 chars |
greeting | string | optional | ≤ 1000 chars |
tone | string[] | optional | ≤ 20 items · each ≤ 60 chars |
businessHours | object | null | optional | {mode: "24/7"|"custom", timezone(IANA), schedule{mon..sun:{enabled,open,close}}}. |
transfer | object | null | optional | {type: take_message|standard_response|transfer_human, message}. |
summaryRecipients | object[] | optional | ≤ 20 items |
summaryRecipients[].email | string | required | |
summaryRecipients[].keywords | string[] | optional | ≤ 20 items · each ≤ 60 chars |
400Malformed request.
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
403Account inactive · missing scope · agent not delegated.
409The agent is terminated (code: AGENT_TERMINATED).
Call results
Call results for an agent (calls scope). Summaries only — no transcripts or recording URLs.
List an agent's call results
/v1/agents/{agentId}/callsCall summaries for a delegated agent, newest first. Returns summary fields only — no full transcript or recording URL, and the caller's number (callerNumber) is masked to its last four digits. Calls the operator deleted in the console do not appear.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
agentId | string · path | required | Agent identifier. 403 if the agent is not delegated to this account (existence is not revealed). |
limit | integer · query | optional | Page size (1–200). 1–200 · default 50 |
cursor | string · query | optional | nextCursor from the previous response. A tampered or damaged value answers 400 (INVALID_CURSOR). |
since | integer · query | optional | Epoch milliseconds. Returns only calls whose summary was stored at or after this time — the same basis as startedAt.≥ 0 |
Responses
200A page of call summaries.
| Field | Type | Required | Description |
|---|---|---|---|
calls | object[] | required | |
calls[].callId | string | required | Call (session) identifier. The same key as the webhook's callId. |
calls[].startedAt | string | null | required | The time the summary was stored (ISO 8601). Not when the call started: it is set after the call ends and the summary is ready. Use durationSecs for the call length.date-time |
calls[].title | string | null | optional | One-line title. |
calls[].summary | string | null | required | The call summary. |
calls[].durationSecs | integer | null | optional | Call length in seconds. |
calls[].callType | string | null | optional | Values: |
calls[].callerNumber | string | null | optional | Caller's number — masked (only the last four digits shown). |
calls[].transferred | boolean | optional | |
calls[].transferTo | string | null | optional | |
calls[].resolution | string | null | optional | Values: |
calls[].sentiment | string | null | optional | Values: |
calls[].topics | string[] | optional | |
nextCursor | string | null | required | Cursor for the next page. null on the last page. |
400Malformed request.
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
403Account inactive · missing scope · agent not delegated.
Webhooks
Push delivery of call completions (webhooks scope). One endpoint per account.
Read the current webhook configuration
/v1/webhooksReturns the current configuration. Only the last four characters of the signing secret are included.
Responses
200The current webhook configuration.
| Field | Type | Required | Description |
|---|---|---|---|
url | string | required | |
events | string[] | required | Values: |
signingSecretHint | string | required | The last four characters of the signing secret (for identification — cannot sign). |
createdAt | string | null | optional | date-time |
updatedAt | string | null | optional | date-time |
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
403Account inactive · missing scope.
404No webhook registered (code: WEBHOOK_NOT_CONFIGURED).
Register or replace the webhook endpoint
/v1/webhooksRegisters (or replaces) the one webhook endpoint per account. The URL must be https; URLs with credentials and private or loopback hosts are refused (422 WEBHOOK_URL_INVALID). Omitting events subscribes to every supported event; if an unsupported event is included, the whole request is refused with 422 UNKNOWN_EVENT.
The signing secret is returned in full only in this response (signingSecret) — it was issued when the account was created, and the registration response is its official hand-over. Later GETs return only a four-character hint.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | required | The https delivery URL. No credentials; no private or loopback hosts. ≤ 2048 chars |
events | string[] | optional | Events to subscribe to. Omit to subscribe to every supported event. Values: ≥ 1 items |
Responses
200The registered configuration plus the full signing secret (this response only).
| Field | Type | Required | Description |
|---|---|---|---|
url | string | required | |
events | string[] | required | Values: |
signingSecret | string | required | The full HMAC signing secret ( whsec_ + hex). Returned only in the PUT response — store it somewhere safe. To verify, drop whsec_ and hex-decode the rest to get the key.
|
createdAt | string | optional | date-time |
updatedAt | string | optional | date-time |
400Malformed request.
401Authentication failed (missing key · format/hash mismatch · revoked — reason not disclosed).
403Account inactive · missing scope.
422URL format or host violation (WEBHOOK_URL_INVALID) · unsupported event (UNKNOWN_EVENT).
Call completed event delivery
call.completed → to your registered webhook URLWhen an agent's call (session) ends and its summary is final, a call.completed event is POSTed to the registered webhook URL. The payload carries agentId so the account can route it to the right agent. Phone numbers, transcripts and recording URLs are not included.
Delivery guarantee: at least once. The same callId can arrive twice on rare occasions, so the receiver must de-duplicate by callId.
Success and retries: a 2xx response counts as success (the body is ignored; respond within 10 seconds; redirects are not followed and count as failure). 5xx · 408 · 429 · no response (timeout, connection failure, redirect) are retried with exponential backoff (starting at 1 minute, doubling, up to 1 hour apart), for 8 attempts in total including the first. Any other 4xx is taken as the receiver refusing the request: no retry, and that delivery ends.
Signature verification (X-LiveSpeech-Signature):
- Parse the header:
t={unix seconds},v1={hex HMAC}. - Drop the
whsec_prefix from the secret and hex-decode it to get the raw key. - Sign
{t}.{raw request body bytes}with HMAC-SHA256 and compare withv1in constant time. - Reject as a replay if
tis more than 5 minutes away from the current time.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
X-LiveSpeech-Event | string · header | required | Event type. Values: |
X-LiveSpeech-Delivery | string · header | required | Delivery identifier (= callId). The same value on retries. |
X-LiveSpeech-Signature | string · header | required | t={unix seconds},v1={hex HMAC-SHA256} — see the verification steps above. |
Request body
call.completed webhook payload. Phone numbers, transcripts and recording URLs are not included. summary, however, is free text summarizing the conversation and may contain personal data mentioned during the call (names, order numbers, addresses) — store and process the payload as personal data.
| Field | Type | Required | Description |
|---|---|---|---|
event | string | required | Values: |
accountId | string | required | The account that registered this webhook. |
agentId | string | required | The agent the call belongs to — your routing key. |
callId | string | required | Call (session) identifier. The same as callId in GET /calls. The de-duplication key. |
occurredAt | string | required | When the call ended (summary finalized). date-time |
summary | string | required | The call summary. |
resolution | string | null | required | Values: |
recordingAvailable | boolean | required | Whether a recording is expected to exist (true for phone calls). Recordings are not provided through the API. |
Responses
2XXReceived (the body is ignored). 5xx · 408 · 429 · no response are retried; any other 4xx is not.