Skip to main content

API reference

Calls

Placing calls and reading call records.

Beta. Place-a-call and retrieve-a-call are live and documented as shipped. There is no endpoint to list or cancel calls.

Overview

The Calls API places one outbound call and reads the record of a call. A call is tied to a Finn (the agent), which supplies the voice, the prompt, the language and the transfer number. You pass the Finn's ID and a destination number. Everything else comes from that Finn's saved configuration.

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

The Finn must belong to the organization that issued your API key. A Finn in another organization returns 404 finn_not_found, the same response as a Finn that does not exist, so the endpoint cannot be used to discover which IDs are in use elsewhere.

To call a list of contacts, create an outbound deployment with api-deployments instead of looping this endpoint. Single calls are meant for event-driven cases such as a form submission, a CRM webhook or a support escalation.

Endpoints

MethodPathRate limit (per API key)
POST/calls60 per minute
GET/calls/{call_uuid}300 per minute, shared with GET /finns, GET /finns/{finn_id} and GET /wallet

Place a call

POST /calls

curl -X POST https://api.hirefinn.ai/api/v1/calls \
  -H "Authorization: Bearer $FINN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
    "to_number": "4155550142",
    "country_code": "1"
  }'
FieldTypeRequiredNotes
finn_idstringyesUUID of the Finn to run. Must belong to your organization.
to_numberstringyesDestination number. Digits with an optional leading +; spaces, dashes and parentheses are allowed. If it starts with +, it is used as a full international number.
country_codestringyesCalling code such as "1" or "+91". Prepended to to_number when to_number has no leading +. Send it even when to_number starts with +: without it the request returns 400 invalid_request.

Any other field in the body, including metadata, is ignored.

Which Finn settings apply. The call uses the Finn's voice, language, begin_message, identity_text, system_prompt (added after identity_text), style_guardrails, response_guidelines and handoff_number. It does not use max_call_duration_minutes, idle_reminder_message, idle_reminder_after_seconds or end_call_on_silence_seconds: those apply only to calls from deployments, and a single call runs with Finn's defaults for them. See Call settings and which calls use them.

Caller ID. Every call placed with this endpoint comes from the same Finn-owned Indian number (+91), whatever the destination country. You can't choose or change it, and you can't place the call from a number you rented. Outside India, the person you call sees an international Indian number rather than a local one, which can lower answer rates. To show a local caller ID, rent a number in that country (api-phone-numbers) and call through an outbound deployment that uses it (api-deployments).

There is no idempotency key. A retried POST places a second real call, so retry only when you know the first request did not reach the server.

The response is 202 Accepted once Finn has accepted the call for dialing:

{
  "success": true,
  "data": {
    "call_uuid": "b7e3c9a2-4d1f-4e8b-9a6c-2f5d8e1b3c70",
    "finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
    "to_number": "4155550142",
    "status": "initiated"
  }
}

to_number echoes what you sent, trimmed. call_uuid is the ID for every later lookup.

Errors

StatuserrorWhen
400invalid_requestfinn_id is not a UUID, to_number is missing or not a phone number, or country_code is missing or not 1–4 digits.
401api_key_requiredNo Bearer finn_live_... key in the Authorization header.
401invalid_api_keyThe key is unknown or revoked.
402insufficient_creditsThe wallet does not hold enough credits to start a call. Top up in the dashboard.
404finn_not_foundNo Finn with that ID in your organization.
429rate_limitedMore than 60 calls per minute on this key.
500internal_errorUnexpected server error.
502call_failedThe call couldn't be placed, or Finn didn't get a call ID back.

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

Retrieve a call

GET /calls/{call_uuid}

Returns any call in your organization: calls placed with POST /calls, calls dialed by deployments, and inbound calls.

curl https://api.hirefinn.ai/api/v1/calls/b7e3c9a2-4d1f-4e8b-9a6c-2f5d8e1b3c70 \
  -H "Authorization: Bearer $FINN_API_KEY"
{
  "success": true,
  "data": {
    "call_uuid": "b7e3c9a2-4d1f-4e8b-9a6c-2f5d8e1b3c70",
    "finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
    "deployment_id": null,
    "status": "completed",
    "disconnect_reason": "hangup",
    "direction": "outbound",
    "from_number": "+918045550199",
    "to_number": "+14155550142",
    "duration_seconds": 97,
    "user_sentiment": "positive",
    "call_successful": true,
    "created_at": "2026-09-16T09:14:02.000Z",
    "updated_at": "2026-09-16T09:15:41.000Z",
    "recording_url": "https://storage.example.com/recordings/b7e3c9a2-4d1f-4e8b-9a6c-2f5d8e1b3c70.wav?sig=...",
    "billing": {
      "credits_charged": 2,
      "price_per_credit": 7,
      "currency": "INR",
      "estimated_cost": 14
    },
    "analysis": [
      {
        "question_name": "interested",
        "question_type": "Yes/No",
        "answer": "Yes",
        "needs_review": false,
        "reasoning": "Caller asked for a follow-up call on Tuesday.",
        "extracted_at": "2026-09-16T09:16:10.000Z"
      }
    ]
  }
}

The call object

FieldTypeNotes
call_uuidstringUUID of the call.
finn_idstring or nullThe Finn that handled the call.
deployment_idstring or nullThe deployment the call belongs to: the same id that GET /deployments returns, and the same value as deployment_id in the call.completed webhook. null for calls that don't belong to a deployment, such as calls placed with POST /calls.
statusstringSee Call status.
disconnect_reasonstring or nullWhy the call ended. Free-form.
directionstring or nulloutbound or inbound.
from_numberstring or nullCaller number. For calls placed with POST /calls, always Finn's Indian caller ID.
to_numberstring or nullCalled number, in E.164 format.
duration_secondsinteger or nullTalk time, rounded from milliseconds. Null when no duration was recorded.
user_sentimentstring or nullSet by post-call analysis. Null until analysis runs.
call_successfulboolean or nullSet by post-call analysis. Null until analysis runs.
created_atstringISO 8601.
updated_atstringISO 8601.
recording_urlstring or nullSigned link to the recording, valid for 1 hour. Read the call again for a fresh link. Null when recording was off, the call wasn't answered, or the recording is past your retention window.
billingobject or nullEstimated charge, below. Null if your rate could not be looked up.
analysisarrayPost-call analysis answers, newest first. Empty until analysis completes.

billing is an estimate computed when you read the call. It is ceil(duration_seconds / 60) credits at your organization's current price_per_credit, so a 10-second call shows one credit. It does not apply plan multipliers or unanswered-call charges, and nothing is charged by reading it. The wallet ledger is the record of what was actually debited: see GET /wallet/transactions in api-wallet.

billing fieldTypeNotes
credits_chargedintegerceil(duration_seconds / 60), or 0 with no duration.
price_per_creditnumberYour effective rate.
currencystringCurrency of price_per_credit.
estimated_costnumbercredits_charged × price_per_credit.

Each analysis item has question_name, question_type, answer, needs_review (boolean), reasoning and extracted_at. The questions are the Finn's post_call_analysis configuration; see api-finns. Analysis runs after the call ends, so an empty array on a call that has just finished is expected.

The call record isn't created by POST /calls itself. It appears after the call ends, usually within about 5 minutes. Until then, a GET returns 404 call_not_found. Retry with a backoff, or wait for the call.completed webhook.

Errors

StatuserrorWhen
400invalid_requestcall_uuid is not a UUID.
401api_key_required / invalid_api_keyMissing, unknown or revoked key.
404call_not_foundNo call with that ID in your organization, or the record has not been written yet.
429rate_limitedMore than 300 requests per minute in this key's read bucket.
500internal_errorUnexpected server error.

Call status

StatusMeaning
initiatedReturned by POST /calls only: Finn accepted the call for dialing.
pendingQueued, not yet dialed.
calling / ringingBeing dialed.
answeredPicked up.
completedEnded normally. Analysis follows.
voicemailReached voicemail.
no_answerRang out with no pickup.
busyThe carrier returned busy.
failedCould not be placed or dropped with an error.
canceled / cancelledCanceled before it connected. Both spellings occur.

Status values come from the carrier and from how the call went, so this is the common set, not a closed enum. Compare case-insensitively and handle unknown values.

Rate limits

Limits are counted per API key, not per IP. Over the limit returns 429 with {"error": "rate_limited", "message": "..."}, a Retry-After header, and RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers on every response. See api-rate-limits.

TypeScript example

const res = await fetch("https://api.hirefinn.ai/api/v1/calls", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FINN_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    finn_id: "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
    to_number: "+14155550142",
    country_code: "1",
  }),
});

const body = await res.json();
if (!res.ok) {
  throw new Error(`${res.status} ${body.error}: ${body.message ?? ""}`);
}

console.log(body.data.call_uuid, body.data.status);

Getting results

Pull a finished call with GET /calls/{call_uuid}, or register a webhook endpoint and receive a signed call.completed event after the call ends, usually within about 5 minutes. Calls placed with POST /calls and calls nobody answered get the event too. See api-webhooks to register an endpoint, webhook-post-call for the payload and webhook-security to verify signatures.

Not available

There is no endpoint to list calls or cancel a call, and metadata is not stored or returned. For call history in bulk, use the export in the dashboard.