Skip to main content

API reference

Finns

Create, read, update and delete voice agents.

Beta. Every endpoint on this page is live and documented as shipped. Shapes can still change before general availability.

Overview

A Finn is a voice agent: its instructions, a voice, a language, call settings, an optional transfer number and the questions post-call analysis answers. The Finns API creates, reads, updates and deletes Finns in the organization that owns your API key.

A Finn speaks with the voice in its voice field. Pick one from api-voices: Finn puts the Finn on that voice's provider for you. Speech-to-text and the language model are set by Finn for your organization and are not part of the API.

A Finn created through the API is a draft. It does not take or place calls until you deploy it with api-deployments; deploying sets its status to live, and stopping the deployment sets it back to draft. You can also place a single call with any Finn, draft or live, through api-calls.

Base URL: https://api.hirefinn.ai/api/v1. Auth: Authorization: Bearer finn_live_.... See authentication.

Call flows, tools, the knowledge base and variables are configured in the dashboard. They are not readable or writable through this API.

Endpoints

MethodPathRate limit (per API key)
GET/finns300 per minute, shared with GET /finns/{finn_id}, GET /calls/{call_uuid} and GET /wallet
GET/finns/{finn_id}Same bucket as above
POST/finns60 per minute, shared by all three write endpoints
PATCH/finns/{finn_id}Same write bucket
DELETE/finns/{finn_id}Same write bucket

The Finn object

Every endpoint that returns a Finn (list, retrieve, create and update) returns the same full object.

{
  "id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
  "name": "Renewal reminders",
  "status": "draft",
  "voice": "21m00Tcm4TlvDq8ikWAM",
  "language": "English",
  "language_group": "global",
  "time_zone": "America/New_York",
  "use_case": "Remind customers about their plan renewal",
  "identity_text": "You are Maya, calling from Acme about the customer's plan renewal.",
  "style_guardrails": "Never quote a price that is not in the prompt.",
  "response_guidelines": "Keep each answer under two sentences.",
  "begin_message": "Hi {{ name }}, this is Maya from Acme. Do you have a minute to talk about your renewal?",
  "system_prompt": "Confirm the customer still wants the plan. If they do, confirm the renewal date. If they want to cancel, offer a transfer to the account team.",
  "max_call_duration_minutes": 5,
  "idle_reminder_message": "Are you still there?",
  "idle_reminder_after_seconds": 10,
  "end_call_on_silence_seconds": 30,
  "handoff_number": { "country_code": "+1", "phone_number": "4155550100" },
  "post_call_analysis": [
    { "id": 1759742062000, "name": "Wants to renew", "type": "Yes/No", "description": "Did the customer confirm the renewal?" }
  ],
  "created_at": "2026-09-30T09:14:22.000Z",
  "updated_at": "2026-09-30T09:14:22.000Z"
}
FieldTypeNotes
idstringUUID.
namestringDisplay name. Not required to be unique.
statusstringdraft or live. Set by deployments, not writable.
voicestringVoice ID from api-voices.
languagestring or nullLanguage label, for example English.
language_groupstringSet from the voice's provider. Not writable.
time_zonestring or nullIANA time zone, for example Asia/Kolkata.
use_casestring or nullShort description of what the Finn is for.
identity_textstring or nullWho the Finn is.
style_guardrailsstring or nullTone and boundaries.
response_guidelinesstring or nullHow to phrase answers.
begin_messagestring or nullFirst line the Finn speaks on every call.
system_promptstring or nullThe Finn's main instructions: objective, flow and rules. The same text as System prompt in the dashboard editor.
max_call_duration_minutesinteger or nullHard limit on call length. null means Finn's default limit of about 8 minutes.
idle_reminder_messagestring or nullWhat the Finn says when the caller goes quiet.
idle_reminder_after_secondsinteger or nullSeconds of silence before the Finn says the idle reminder.
end_call_on_silence_secondsinteger or nullSeconds of silence after which the Finn hangs up.
handoff_numberobject or nullTransfer number { "country_code": "+91", "phone_number": "9876543210" }.
post_call_analysisarrayQuestions answered after each call. Empty array when there are none. See Post-call analysis questions.
created_atstringISO 8601.
updated_atstringISO 8601.

Writable fields

POST and PATCH accept only these keys. Any other key returns 400 invalid_request with Unknown field(s): ....

FieldTypeRequired on createRules
namestringYes1 to 100 characters. Can't be null or empty.
voicestringYesA voice_id from api-voices. Up to 200 characters. Can't be null or empty. See Choosing a voice.
languagestringNoUp to 50 characters. Defaults to English.
time_zonestringNoA valid IANA time zone.
use_casestringNoUp to 500 characters.
identity_textstringNoUp to 20,000 characters.
style_guardrailsstringNoUp to 20,000 characters.
response_guidelinesstringNoUp to 20,000 characters.
begin_messagestringNoUp to 1,000 characters. Defaults to Hello, this is a call from Finn.
system_promptstringNoUp to 100,000 characters.
max_call_duration_minutesintegerNoOne of 1, 3, 5, 10, 15, 30, 60.
idle_reminder_messagestringNoUp to 1,000 characters.
idle_reminder_after_secondsintegerNo5 to 600.
end_call_on_silence_secondsintegerNoOne of 15, 20, 30, 45, 60, 90, 120.
handoff_numberobjectNo{ "country_code": "+91", "phone_number": "9876543210" }. country_code is 1 to 4 digits with an optional +; phone_number is 4 to 15 digits without the country code (spaces, dashes and parentheses are stripped).
post_call_analysisarrayNoUp to 50 question objects, each with a non-empty name.

Strings are trimmed. Send null to clear any field except name and voice; null on post_call_analysis clears the list. On the text fields (language, use_case, identity_text, style_guardrails, response_guidelines, begin_message, system_prompt, idle_reminder_message) an empty string also clears the value. A cleared call setting goes back to Finn's default.

Values that break a rule return 400 invalid_request and message names the field, for example max_call_duration_minutes must be one of 1, 3, 5, 10, 15, 30, 60 or null or idle_reminder_after_seconds must be an integer from 5 to 600 or null.

Choosing a voice

voice must be a voice your organization can use: one returned by GET /voices, or one GET /voices/{voice_id} finds. Finn checks it on every POST, and on every PATCH that sends voice:

ResultResponse
The voice is foundThe Finn is saved and switched to that voice's provider.
No provider your organization can use has that voice400 invalid_request, voice is not one of the voices from GET /voices
The voice catalog couldn't be loaded to check it502 voices_unavailable. Retry later.
Your plan includes no voice provider403 feature_not_allowed, with an action_url to upgrade

If your organization is limited to a voice provider whose voices can't be listed through the API, voice is saved without this check. See api-voices.

Call settings and which calls use them

system_prompt, max_call_duration_minutes, idle_reminder_message, idle_reminder_after_seconds and end_call_on_silence_seconds are the same settings as in the dashboard editor. Calls placed with POST /calls don't use all of them:

FieldCalls from deployments (outbound and inbound)Calls placed with POST /calls
system_promptUsed, unless the Finn has a call flow turned on in the dashboard. A call flow runs instead of the system prompt.Used, added after identity_text.
max_call_duration_minutesUsed. null means about 8 minutes.Not used.
idle_reminder_messageUsed.Not used.
idle_reminder_after_secondsUsed.Not used.
end_call_on_silence_secondsUsed.Not used.

When a setting is null, Finn's default applies. max_call_duration_minutes is a hard stop: when it's reached the call ends without a closing line. See agents-conversation for how the reminder and silence settings behave on a call.

Post-call analysis questions

Each item in post_call_analysis is normalized before it is stored:

KeyRequiredNotes
nameYesThe question, also used as question_name in call analysis results.
typeNoYes/No, Selector, Number or Text. Common aliases are accepted (boolean and yes/no become Yes/No; select, dropdown and choice become Selector; integer and numeric become Number). Anything else, or no type, becomes Text.
descriptionNoGuidance for the analyzer. Defaults to an empty string.
choicesNoArray of strings, kept for Selector questions.
formatExamplesNoArray of strings, or one comma-separated string.
idNoNumber. Generated when missing.

Answers appear in the analysis array of api-calls and of the call.completed webhook.

List Finns

GET /finns

Returns Finns in your organization, newest first, as full Finn objects.

Query parameterTypeDefaultNotes
limitinteger501 to 200. Out-of-range values are clamped.
offsetinteger0Number of Finns to skip.
curl "https://api.hirefinn.ai/api/v1/finns?limit=20&offset=0" \
  -H "Authorization: Bearer $FINN_API_KEY"
{
  "success": true,
  "data": [
    {
      "id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
      "name": "Renewal reminders",
      "status": "draft",
      "voice": "21m00Tcm4TlvDq8ikWAM",
      "language": "English",
      "language_group": "global",
      "time_zone": "America/New_York",
      "use_case": "Remind customers about their plan renewal",
      "identity_text": "You are Maya, calling from Acme about the customer's plan renewal.",
      "style_guardrails": null,
      "response_guidelines": null,
      "begin_message": "Hi {{ name }}, this is Maya from Acme. Do you have a minute to talk about your renewal?",
      "system_prompt": "Confirm the customer still wants the plan. If they do, confirm the renewal date.",
      "max_call_duration_minutes": 5,
      "idle_reminder_message": null,
      "idle_reminder_after_seconds": null,
      "end_call_on_silence_seconds": null,
      "handoff_number": null,
      "post_call_analysis": [],
      "created_at": "2026-09-30T09:14:22.000Z",
      "updated_at": "2026-09-30T09:14:22.000Z"
    }
  ],
  "limit": 20,
  "offset": 0
}

The response has no total count. Keep requesting with a larger offset until a page returns fewer than limit items. See api-pagination.

Retrieve a Finn

GET /finns/{finn_id}

curl https://api.hirefinn.ai/api/v1/finns/8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1 \
  -H "Authorization: Bearer $FINN_API_KEY"

Returns { "success": true, "data": { ... } } with one full Finn object.

Create a Finn

POST /finns

Only name and voice are required.

curl -X POST https://api.hirefinn.ai/api/v1/finns \
  -H "Authorization: Bearer $FINN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Renewal reminders",
    "voice": "21m00Tcm4TlvDq8ikWAM",
    "time_zone": "America/New_York",
    "use_case": "Remind customers about their plan renewal",
    "identity_text": "You are Maya, calling from Acme about the customer'\''s plan renewal.",
    "begin_message": "Hi {{ name }}, this is Maya from Acme. Do you have a minute to talk about your renewal?",
    "system_prompt": "Confirm the customer still wants the plan. If they do, confirm the renewal date. If they want to cancel, offer a transfer to the account team.",
    "max_call_duration_minutes": 5,
    "end_call_on_silence_seconds": 30,
    "handoff_number": { "country_code": "+1", "phone_number": "4155550100" },
    "post_call_analysis": [
      { "name": "Wants to renew", "type": "Yes/No", "description": "Did the customer confirm the renewal?" }
    ]
  }'

Response 201 Created with the full Finn object:

{
  "success": true,
  "data": {
    "id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
    "name": "Renewal reminders",
    "status": "draft",
    "voice": "21m00Tcm4TlvDq8ikWAM",
    "language": "English",
    "language_group": "global",
    "time_zone": "America/New_York",
    "use_case": "Remind customers about their plan renewal",
    "identity_text": "You are Maya, calling from Acme about the customer's plan renewal.",
    "style_guardrails": null,
    "response_guidelines": null,
    "begin_message": "Hi {{ name }}, this is Maya from Acme. Do you have a minute to talk about your renewal?",
    "system_prompt": "Confirm the customer still wants the plan. If they do, confirm the renewal date. If they want to cancel, offer a transfer to the account team.",
    "max_call_duration_minutes": 5,
    "idle_reminder_message": null,
    "idle_reminder_after_seconds": null,
    "end_call_on_silence_seconds": 30,
    "handoff_number": { "country_code": "+1", "phone_number": "4155550100" },
    "post_call_analysis": [
      { "id": 1759742062000, "name": "Wants to renew", "type": "Yes/No", "description": "Did the customer confirm the renewal?" }
    ],
    "created_at": "2026-09-30T09:14:22.000Z",
    "updated_at": "2026-09-30T09:14:22.000Z"
  }
}

{{ name }} in begin_message and system_prompt is filled from each contact's audience row on deployment calls. See agents-variables.

The Finn is created with status draft and owned by the user who created the API key. If that user has since been deleted or is no longer a member of your organization, the create returns 409 api_key_owner_required; create a new key from an active account.

There is no idempotency key. A retried create makes a second Finn.

Update a Finn

PATCH /finns/{finn_id}

Send only the fields to change; at least one is required. Fields you don't send keep their current values, including settings made in the dashboard that the API doesn't expose.

curl -X PATCH https://api.hirefinn.ai/api/v1/finns/8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1 \
  -H "Authorization: Bearer $FINN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "voice": "a0e99841-438c-4a64-b679-ae501e7d6091",
    "max_call_duration_minutes": null,
    "idle_reminder_message": "Take your time. I just need the renewal date.",
    "idle_reminder_after_seconds": 8
  }'

Returns 200 with the full Finn object, as on create.

Setting voice checks it as described in Choosing a voice and moves the Finn onto that voice's provider, the same as picking a voice in the dashboard.

Live Finns can be edited. The change applies to the next call placed with the Finn, including the next calls dialed by a running deployment. Calls already in progress are not affected. There is no version history in the API, so keep your own copy of text you overwrite.

Delete a Finn

DELETE /finns/{finn_id}

curl -X DELETE https://api.hirefinn.ai/api/v1/finns/8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1 \
  -H "Authorization: Bearer $FINN_API_KEY"
{
  "success": true,
  "data": { "id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1", "deleted": true }
}

Deletion is permanent and only allowed when:

  • the Finn is a draft, and
  • the Finn has never had a deployment (stopped deployments count), and
  • no phone number is assigned to it.

Otherwise the request returns 409. In practice, a Finn that has been deployed cannot be deleted through the API.

Errors

StatuserrorEndpointWhen
400invalid_requestallfinn_id is not a UUID; the body is not a JSON object; an unknown field; a value fails the rules above; a required field is missing on create; an empty PATCH; voice is not a voice your organization can use. message names the problem.
401api_key_requiredallNo Bearer finn_live_... key.
401invalid_api_keyallKey unknown or revoked.
403feature_not_allowedPOST, PATCH with voiceYour plan includes no voice provider. Upgrade at action_url.
404finn_not_foundGET one, PATCH, DELETENo Finn with that ID in your organization.
409api_key_owner_requiredPOSTThe user who created the API key has been deleted or is no longer a member of your organization.
409finn_not_draftDELETEThe Finn is live.
409finn_in_useDELETEThe Finn has deployments or an assigned number.
429rate_limitedallOver the bucket's per-minute limit.
500internal_errorallUnexpected server error.
502voices_unavailablePOST, PATCH with voiceThe voice couldn't be checked because the voice catalog couldn't be loaded. Nothing was saved; retry later.

Error bodies have the shape { "error": "...", "message": "..." }; internal_error has no message. See api-errors.