Quickstart
This guide takes a new account to an agent talking with a live person. It uses fictional data and 555-01xx numbers. With a test key every step works the same and no calls are placed.
Get your API keys
Your test and live keys are in the dashboard once your account is set up. Request API access if you do not have an account yet. Export the key in your shell or your server's environment as LIVELEG_API_KEY. Details are in Authentication.
Load leads
Create a lead list, then add leads. Each lead needs a phone number and a state. The state sets the lead's calling hours. Up to 500 leads per request.
curl https://api.liveleg.dev/v1/lead_lists \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-d id=lst_roofing_tx \
-d name="Roofing, Texas" curl https://api.liveleg.dev/v1/lead_lists/lst_roofing_tx/leads \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"leads": [
{
"phone": "+15125550147",
"state": "TX",
"name": "Dana Ruiz",
"company": "Ridgeline Roofing",
"external_id": "crm_88213"
},
{
"phone": "+15125550163",
"state": "TX",
"name": "Luis Ortega",
"external_id": "crm_88219"
}
]
}'const res = await fetch('https://api.liveleg.dev/v1/lead_lists/lst_roofing_tx/leads', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
leads: [
{
phone: '+15125550147',
state: 'TX',
name: 'Dana Ruiz',
company: 'Ridgeline Roofing',
external_id: 'crm_88213',
},
{
phone: '+15125550163',
state: 'TX',
name: 'Luis Ortega',
external_id: 'crm_88219',
},
],
}),
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/lead_lists/lst_roofing_tx/leads",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"leads": [
{
"phone": "+15125550147",
"state": "TX",
"name": "Dana Ruiz",
"company": "Ridgeline Roofing",
"external_id": "crm_88213",
},
{
"phone": "+15125550163",
"state": "TX",
"name": "Luis Ortega",
"external_id": "crm_88219",
},
],
},
).json() Buy local numbers
Buy numbers in the area codes you call and put them in a caller ID pool. Each call picks a number from the pool by the lead's area code and state.
curl https://api.liveleg.dev/v1/numbers \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-d area_code=512 \
-d quantity=3 \
-d pool_id=pool_local_presenceconst res = await fetch('https://api.liveleg.dev/v1/numbers', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
area_code: '512',
quantity: 3,
pool_id: 'pool_local_presence',
}),
});
const numbers = await res.json();import os, requests
numbers = requests.post(
"https://api.liveleg.dev/v1/numbers",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"area_code": "512",
"quantity": 3,
"pool_id": "pool_local_presence",
},
).json() Add a webhook endpoint
Tell us where to send events. Store the secret from the response to verify deliveries.
curl https://api.liveleg.dev/v1/webhook_endpoints \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/hooks/liveleg",
"events": [
"call.bridged",
"call.completed",
"call.blocked",
"recording.ready"
]
}'const res = await fetch('https://api.liveleg.dev/v1/webhook_endpoints', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://app.example.com/hooks/liveleg',
events: [
'call.bridged',
'call.completed',
'call.blocked',
'recording.ready',
],
}),
});
const endpoint = await res.json();import os, requests
endpoint = requests.post(
"https://api.liveleg.dev/v1/webhook_endpoints",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"url": "https://app.example.com/hooks/liveleg",
"events": [
"call.bridged",
"call.completed",
"call.blocked",
"recording.ready",
],
},
).json() Connect the agent
Your server mints a token for the agent. The page passes it to the Voice SDK, which opens the agent's line for the whole session.
curl https://api.liveleg.dev/v1/agents/agt_114/tokens \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-d ttl_seconds=3600const res = await fetch('https://api.liveleg.dev/v1/agents/agt_114/tokens', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ ttl_seconds: 3600 }),
});
const token = await res.json();import os, requests
token = requests.post(
"https://api.liveleg.dev/v1/agents/agt_114/tokens",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={"ttl_seconds": 3600},
).json() import { Agent } from '@liveleg/voice';
const { token } = await fetch('/api/liveleg-token').then((r) => r.json());
const agent = await Agent.connect(token);
agent.on('call.bridged', (call) => showLead(call.leadId)); Start dialing
One request starts parallel dialing for the agent. The first person to answer is connected, and the agent hears them about 1 second after they finish saying hello.
curl https://api.liveleg.dev/v1/dial_sessions \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Idempotency-Key: agent-114-morning" \
-d agent_id=agt_114 \
-d lead_list_id=lst_roofing_tx \
-d lines=auto \
-d caller_id_pool=pool_local_presence \
-d amd=true \
-d recording=smartconst res = await fetch('https://api.liveleg.dev/v1/dial_sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'agent-114-morning',
},
body: JSON.stringify({
agent_id: 'agt_114',
lead_list_id: 'lst_roofing_tx',
lines: 'auto',
caller_id_pool: 'pool_local_presence',
amd: true,
recording: 'smart',
}),
});
const session = await res.json();import os, requests
session = requests.post(
"https://api.liveleg.dev/v1/dial_sessions",
headers={
"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}",
"Idempotency-Key": "agent-114-morning",
},
json={
"agent_id": "agt_114",
"lead_list_id": "lst_roofing_tx",
"lines": "auto",
"caller_id_pool": "pool_local_presence",
"amd": True,
"recording": "smart",
},
).json() Handle events
Verify the signature, store the event by webhook-id and return 2xx quickly. Do the slow work after you respond.
import { Webhook } from 'standardwebhooks';
const wh = new Webhook(process.env.LIVELEG_WEBHOOK_SECRET);
app.post('/hooks/liveleg', express.raw({ type: 'application/json' }), (req, res) => {
const event = wh.verify(req.body, req.headers); // throws if the signature is wrong
queue.add(event.type, event, { jobId: req.headers['webhook-id'] });
res.sendStatus(204);
}); Next steps
- Dial sessions for lines per agent, pausing and agent pools.
- Compliance for the checks that run before every call.
- Webhooks and events for every event type.