Developers · API
v1Quickstart
From receiving your key to creating a first agent and reading its call results takes about ten minutes. Follow the steps in order.
1. Get a key
LiveSpeech issues your API key when your account is set up. It is a string that starts with ls_sk_ and is shown only once — put it in your server's secret store as soon as you receive it. A lost key cannot be shown again; ask support@livespeech.ai for a new one.
The examples below read the key and the base URL from environment variables.
export LS_BASE_URL="https://console-api.livespeech.ai"
export LS_API_KEY="ls_sk_0123abcd..." # the key you received
2. Check that the key works
GET /v1/whoami returns the key's identity and scopes without touching any agent. Use it as the first call to verify your plumbing. Every field is in the API reference.
curl -s "$LS_BASE_URL/v1/whoami" \
-H "Authorization: Bearer $LS_API_KEY"const BASE_URL = process.env.LS_BASE_URL;
const API_KEY = process.env.LS_API_KEY;
const res = await fetch(`${BASE_URL}/v1/whoami`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
BASE_URL = os.environ["LS_BASE_URL"]
API_KEY = os.environ["LS_API_KEY"]
res = requests.get(
f"{BASE_URL}/v1/whoami",
headers={"Authorization": f"Bearer {API_KEY}"},
)
print(res.status_code, res.json()){
"accountId": "acct_0123abcd4567ef89",
"name": "Example Platform Inc.",
"scopes": ["agents", "config", "calls", "webhooks", "usage"],
"keyPrefix": "ls_sk_0123abcd4567"
}
scopes lists what this key can do. If something you need is missing, email support@livespeech.ai.
3. Create an agent
One business is one agent. Put your own identifier for this agent in externalId, and you can match agents to your records later without a mapping table. A business that needs two numbers needs two agents, so give each its own value.
RESPONSE=$(curl -s -X POST "$LS_BASE_URL/v1/agents" \
-H "Authorization: Bearer $LS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-dental-00123-1" \
-d '{
"name": "Riverside Dental",
"region": "kr",
"externalId": "dental_00123"
}')
echo "$RESPONSE"
# Keep the agentId from the response for the next steps.
AGENT_ID=$(printf '%s' "$RESPONSE" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["agentId"])')const BASE_URL = process.env.LS_BASE_URL;
const API_KEY = process.env.LS_API_KEY;
const res = await fetch(`${BASE_URL}/v1/agents`, {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'create-dental-00123-1',
},
body: JSON.stringify({
name: 'Riverside Dental',
region: 'kr',
externalId: 'dental_00123',
}),
});
const agent = await res.json();
console.log(res.status, agent);
// Keep the agentId from the response for the next steps.
const agentId = agent.agentId;import os, requests
BASE_URL = os.environ["LS_BASE_URL"]
API_KEY = os.environ["LS_API_KEY"]
res = requests.post(
f"{BASE_URL}/v1/agents",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"Idempotency-Key": "create-dental-00123-1",
},
json={"name": "Riverside Dental", "region": "kr", "externalId": "dental_00123"},
)
agent = res.json()
print(res.status_code, agent)
# Keep the agentId from the response for the next steps.
agent_id = agent["agentId"]{
"agentId": "8f2c1d40-5b7a-4c31-9e08-1a2b3c4d5e6f",
"name": "Riverside Dental",
"region": "kr",
"status": "active",
"externalId": "dental_00123",
"createdAt": "2026-09-09T04:15:22.140Z"
}
The agentId in the response goes into every agent path from here on — keep it in a variable as the examples do. Every request and response field is in the API reference.
4. Configure the agent
Decide how the agent speaks and what it says. persona is free text describing how to treat callers; greeting is the first thing it says when it picks up.
curl -s -X PUT "$LS_BASE_URL/v1/agents/$AGENT_ID/settings" \
-H "Authorization: Bearer $LS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"persona": "You are the customer service agent for Riverside Dental. Keep answers short and polite. If you are not sure about something, say you will check and call back.",
"greeting": "Hello, this is Riverside Dental customer service. How can I help you?",
"summaryRecipients": [{ "email": "cs@example.com", "keywords": ["refund", "exchange"] }]
}'const BASE_URL = process.env.LS_BASE_URL;
const API_KEY = process.env.LS_API_KEY;
const res = await fetch(
`${BASE_URL}/v1/agents/${agentId}/settings`,
{
method: 'PUT',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
persona:
'You are the customer service agent for Riverside Dental. Keep answers short and polite. If you are not sure about something, say you will check and call back.',
greeting: 'Hello, this is Riverside Dental customer service. How can I help you?',
summaryRecipients: [{ email: 'cs@example.com', keywords: ['refund', 'exchange'] }],
}),
},
);
console.log(res.status, await res.json());import os, requests
BASE_URL = os.environ["LS_BASE_URL"]
API_KEY = os.environ["LS_API_KEY"]
res = requests.put(
f"{BASE_URL}/v1/agents/{agent_id}/settings",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"persona": "You are the customer service agent for Riverside Dental. Keep answers short and polite. If you are not sure about something, say you will check and call back.",
"greeting": "Hello, this is Riverside Dental customer service. How can I help you?",
"summaryRecipients": [{"email": "cs@example.com", "keywords": ["refund", "exchange"]}],
},
)
print(res.status_code, res.json()){
"persona": "You are handling a live phone call.\n\nYou are the customer service agent for Riverside Dental. Keep answers short and polite. If you are not sure about something, say you will check and call back.",
"greeting": "Hello, this is Riverside Dental customer service. How can I help you?",
"tone": [],
"businessHours": null,
"transfer": null,
"summaryRecipients": [{ "email": "cs@example.com", "keywords": ["refund", "exchange"] }]
}
The response's persona starts with a sentence saying this is a live phone call. It is added automatically — you did not send it — and sending the value back as is does not duplicate it. The remaining settings are in Agent settings; every field is in the API reference.
5. Connect a number
Request a number for the agent. During early access the endpoint is being opened, so send the request from the early-access page with the agentId — we connect the number within one business day. The call below is what you integrate against once the endpoint is open; either way, watch number on GET /v1/agents/{agentId} — it stays null until the number is connected.
curl -s -X POST "$LS_BASE_URL/v1/agents/$AGENT_ID/number" \
-H "Authorization: Bearer $LS_API_KEY" \
-H "Idempotency-Key: number-dental-00123-1"const res = await fetch(`${process.env.LS_BASE_URL}/v1/agents/${agentId}/number`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LS_API_KEY}`,
'Idempotency-Key': 'number-dental-00123-1',
},
});
console.log(res.status, await res.json()); // 202 { status: 'queued', ... }import os, requests
res = requests.post(
f"{os.environ['LS_BASE_URL']}/v1/agents/{agent_id}/number",
headers={
"Authorization": f"Bearer {os.environ['LS_API_KEY']}",
"Idempotency-Key": "number-dental-00123-1",
},
)
print(res.status_code, res.json()) # 202 {'status': 'queued', ...}{
"requestId": "num_8f2c1d40",
"agentId": "8f2c1d40-5b7a-4c31-9e08-1a2b3c4d5e6f",
"region": "kr",
"status": "queued",
"number": null,
"requestedAt": "2026-10-08T02:15:00.000Z",
"estimatedBy": "2026-10-09T09:00:00.000Z",
"earlyAccess": true
}
Statuses, polling and failure responses are in Phone numbers.
6. Make a test call and read the result
Call the connected number, talk for a moment, and hang up. Once the call ends and its summary is stored, it appears in the list.
curl -s "$LS_BASE_URL/v1/agents/$AGENT_ID/calls?limit=5" \
-H "Authorization: Bearer $LS_API_KEY"const BASE_URL = process.env.LS_BASE_URL;
const API_KEY = process.env.LS_API_KEY;
const res = await fetch(
`${BASE_URL}/v1/agents/${agentId}/calls?limit=5`,
{ headers: { Authorization: `Bearer ${API_KEY}` } },
);
console.log(res.status, await res.json());import os, requests
BASE_URL = os.environ["LS_BASE_URL"]
API_KEY = os.environ["LS_API_KEY"]
res = requests.get(
f"{BASE_URL}/v1/agents/{agent_id}/calls",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"limit": 5},
)
print(res.status_code, res.json()){
"calls": [
{
"callId": "6f1e0c2a-4b7d-4c1e-9a52-3f8d2f10ab34",
"startedAt": "2026-09-09T04:31:08.000Z",
"title": "Delivery status inquiry",
"summary": "The caller asked about the delivery status of an order and was told it ships today.",
"durationSecs": 74,
"callType": "phone",
"callerNumber": "*******5678",
"transferred": false,
"transferTo": null,
"resolution": "Resolved",
"sentiment": "positive",
"topics": ["delivery"]
}
],
"nextCursor": null
}
A call appears in the list after its summary is stored. If you query right after hanging up it may not be there yet — try again shortly.
startedAt is the time the summary was stored — not when the call started. Use durationSecs for the call length. Every field is in the API reference.
Next
- Want each call pushed to you without polling → Webhooks
- Running many agents → At scale
- How to handle failed responses → Errors & limits