Skip to content

API reference

API: Voice agents

Markdown version

MethodPathWhat it doesScope
GET/v1/voice/agentsVoice agents: persona, voice, language, tools, KB sections, runtimesettings:read
POST/v1/voice/agentsCreate a voice agent (owner)settings:write
DELETE/v1/voice/agents/:idArchive a voice agent (owner)settings:write
GET/v1/voice/agents/:idOne voice agent with its saved versionssettings:read
PATCH/v1/voice/agents/:idChange a voice agent (owner); send the version you edited. Each save is a new versionsettings:write
POST/v1/voice/agents/:id/previewHear the persona: sample text spoken with its voice (BYO TTS key, metered) or text onlysettings:write
GET/v1/voice/catalogThe stock voice catalog (carrier voices; ElevenLabs and Cartesia when the organization brought its key), tone presets, languages and whether cloning is onsettings:read
GET/v1/voice/clipsThe hold clip and your voicemail drop (owners: everyone's)calls:read
POST/v1/voice/clipsSave the hold clip (owners) or your own voicemail drop: your recording, ElevenLabs or Cartesia speech (a stock voice or your own clone), or the carrier's speech (web app only)calls:write
DELETE/v1/voice/clips/:idRetire a clip (your own drop; owners: any)calls:write
GET/v1/voice/clonesVoice-clone consents (owners see all; others their own) and the statement you'd readsettings:read
POST/v1/voice/clonesRecord your own spoken consent to clone your voice (only your own voice; refused while cloning is off) (web app only)calls:write
DELETE/v1/voice/clones/:idWithdraw consent: the clone is deleted at the provider (you, or an owner)calls:write
POST/v1/voice/clones/:id/cloneCreate your voice clone at the provider from your consented recording (web app only)calls:write
POST/v1/voice/previewHear an unsaved persona (owner)settings:write
GET/v1/voice/runtimesVoice runtimes: our own pipeline (on), Retell and Vapi (off until their key is set), with the languages each speaks and the speech vendors' keyssettings:read
GET/v1/voice/sessionsVoice sessions (one per call a voice agent answered), newest first, with p50/p95 end-of-speech → first-audio latencycalls:read
POST/v1/voice/sessionsOpen a voice session for a call (owner; the call layer does this when a call is answered): the media stream URL with its short-lived token, and the Conversation Relay settingssettings:write
GET/v1/voice/sessions/:idOne voice session with every turn: what the caller said, the reply, what they heard before a barge-in, tools, KB used and latencytranscripts:read

Examples

GET /v1/voice/agents

Voice agents: persona, voice, language, tools, KB sections, runtime

curl -s "https://api.closefinch.com/v1/voice/agents" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/agents`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

POST /v1/voice/agents

Create a voice agent (owner)

curl -s -X POST "https://api.closefinch.com/v1/voice/agents" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/voice/agents`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();

DELETE /v1/voice/agents/$ID

Archive a voice agent (owner)

curl -s -X DELETE "https://api.closefinch.com/v1/voice/agents/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/agents/${id}`, {
  method: "DELETE",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

GET /v1/voice/agents/$ID

One voice agent with its saved versions

curl -s "https://api.closefinch.com/v1/voice/agents/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/agents/${id}`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

PATCH /v1/voice/agents/$ID

Change a voice agent (owner); send the version you edited. Each save is a new version

curl -s -X PATCH "https://api.closefinch.com/v1/voice/agents/$ID" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/voice/agents/${id}`, {
  method: "PATCH",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();

POST /v1/voice/agents/$ID/preview

Hear the persona: sample text spoken with its voice (BYO TTS key, metered) or text only

curl -s -X POST "https://api.closefinch.com/v1/voice/agents/$ID/preview" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/voice/agents/${id}/preview`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();

GET /v1/voice/catalog

The stock voice catalog (carrier voices; ElevenLabs and Cartesia when the organization brought its key), tone presets, languages and whether cloning is on

curl -s "https://api.closefinch.com/v1/voice/catalog" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/catalog`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

GET /v1/voice/clips

The hold clip and your voicemail drop (owners: everyone's)

curl -s "https://api.closefinch.com/v1/voice/clips" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/clips`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

DELETE /v1/voice/clips/$ID

Retire a clip (your own drop; owners: any)

curl -s -X DELETE "https://api.closefinch.com/v1/voice/clips/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/clips/${id}`, {
  method: "DELETE",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

GET /v1/voice/clones

Voice-clone consents (owners see all; others their own) and the statement you'd read

curl -s "https://api.closefinch.com/v1/voice/clones" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/clones`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

DELETE /v1/voice/clones/$ID

Withdraw consent: the clone is deleted at the provider (you, or an owner)

curl -s -X DELETE "https://api.closefinch.com/v1/voice/clones/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/clones/${id}`, {
  method: "DELETE",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

POST /v1/voice/preview

Hear an unsaved persona (owner)

curl -s -X POST "https://api.closefinch.com/v1/voice/preview" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/voice/preview`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();

GET /v1/voice/runtimes

Voice runtimes: our own pipeline (on), Retell and Vapi (off until their key is set), with the languages each speaks and the speech vendors' keys

curl -s "https://api.closefinch.com/v1/voice/runtimes" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/runtimes`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

GET /v1/voice/sessions

Voice sessions (one per call a voice agent answered), newest first, with p50/p95 end-of-speech → first-audio latency

curl -s "https://api.closefinch.com/v1/voice/sessions" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/sessions`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

POST /v1/voice/sessions

Open a voice session for a call (owner; the call layer does this when a call is answered): the media stream URL with its short-lived token, and the Conversation Relay settings

curl -s -X POST "https://api.closefinch.com/v1/voice/sessions" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/voice/sessions`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();

GET /v1/voice/sessions/$ID

One voice session with every turn: what the caller said, the reply, what they heard before a barge-in, tools, KB used and latency

curl -s "https://api.closefinch.com/v1/voice/sessions/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/voice/sessions/${id}`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

Request and response schemas: https://www.closefinch.com/openapi.json. All resources: API reference.

Send feedback

Tell us what's broken, confusing or missing on this page, or an idea. It goes to the team that builds the product.

Please don't include passwords or card numbers.
Only to reply about this. Never added to any mailing list.