API reference
API: Voice agents
| Method | Path | What it does | Scope |
|---|---|---|---|
| GET | /v1/voice/agents | Voice agents: persona, voice, language, tools, KB sections, runtime | settings:read |
| POST | /v1/voice/agents | Create a voice agent (owner) | settings:write |
| DELETE | /v1/voice/agents/:id | Archive a voice agent (owner) | settings:write |
| GET | /v1/voice/agents/:id | One voice agent with its saved versions | settings:read |
| PATCH | /v1/voice/agents/:id | Change a voice agent (owner); send the version you edited. Each save is a new version | settings:write |
| POST | /v1/voice/agents/:id/preview | Hear the persona: sample text spoken with its voice (BYO TTS key, metered) or text only | settings:write |
| 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 | settings:read |
| GET | /v1/voice/clips | The hold clip and your voicemail drop (owners: everyone's) | calls:read |
| POST | /v1/voice/clips | Save 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/:id | Retire a clip (your own drop; owners: any) | calls:write |
| GET | /v1/voice/clones | Voice-clone consents (owners see all; others their own) and the statement you'd read | settings:read |
| POST | /v1/voice/clones | Record 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/:id | Withdraw consent: the clone is deleted at the provider (you, or an owner) | calls:write |
| POST | /v1/voice/clones/:id/clone | Create your voice clone at the provider from your consented recording (web app only) | calls:write |
| POST | /v1/voice/preview | Hear an unsaved persona (owner) | settings:write |
| 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 | settings:read |
| GET | /v1/voice/sessions | Voice sessions (one per call a voice agent answered), newest first, with p50/p95 end-of-speech → first-audio latency | calls:read |
| 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 | settings:write |
| 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 | transcripts: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.