What you'll do
Go from an API key to a finished, analyzed call using only curl: pick a voice, create a Finn, call a list of contacts, get the result pushed to your server, then read the call. It takes about 15 minutes. Every request here is a real request against your organization.
API key → voice → Finn → audience + phone number → deployment → calls → call.completed webhook → GET /calls/{call_uuid}
The objects
| Object | What it is | How you get one |
|---|---|---|
| Finn | A voice agent: its voice, instructions, first line, call settings and the questions answered after each call. Configuration only; it makes no calls on its own. | POST /finns (api-finns) |
| Voice | A voice a Finn can speak with, identified by voice_id. | GET /voices (api-voices) |
| Phone number | A number your organization owns. Outbound deployments dial from it; inbound deployments answer on it. | GET /phone_numbers, or rent one (api-phone-numbers) |
| Audience | A list of contacts to call. Each contact has a phone_number, a country_code, an optional name and any custom fields. | POST /audiences (api-audiences) |
| Deployment | A Finn put to work on a number. Outbound: dials every contact in an audience. Inbound: answers calls to the number. | POST /deployments or POST /deployments/inbound (api-deployments) |
| Call | One conversation, with its status, duration and post-call analysis answers. | GET /calls/{call_uuid} (api-calls) |
| Webhook endpoint | A URL on your server that receives a signed call.completed event after every call. | Dashboard only (api-webhooks) |
There are two ways to make an outbound call:
Single call: POST /calls | Outbound deployment: POST /deployments | |
|---|---|---|
| Who it calls | One number per request | Every contact in an audience |
| Caller ID | A shared Finn number in India (+91), whatever the destination | Your own number |
| Needs | A Finn | A Finn, an audience and a phone number |
| Use it for | Testing, and one-off calls triggered by an event | Campaigns and anything that should come from your number |
Before you start
- An API key. In the dashboard, open Settings → Integrations → API keys and create one. It's shown once. Every key has full access to your organization, so keep it on your server. See authentication.
- Credits. There are no test keys and no sandbox: every call in this guide is a real, billed phone call. Top up in the dashboard if your balance is low, and while you're testing, only call numbers you control.
- A phone number for step 5. If your organization has none, rent one in the dashboard or with
POST /phone_numbers. Renting needs an approved compliance application and is charged to your wallet. You can do steps 1 to 4 without one.
Put the key in an environment variable. Every example below reads it from there:
export FINN_API_KEY="finn_live_..."
1. Check your key and balance
curl https://api.hirefinn.ai/api/v1/wallet \
-H "Authorization: Bearer $FINN_API_KEY"
{
"success": true,
"data": { "credits": 4175, "currency": "INR", "price_per_credit": 7, "monetary_value": 29225, "low_balance": false }
}
Every successful response is { "success": true, "data": ... }. Every error is { "error": "<code>", "message": "..." } with an HTTP status. A 401 with api_key_required means the header is missing or malformed; invalid_api_key means the key is unknown or revoked.
2. Pick a voice
curl "https://api.hirefinn.ai/api/v1/voices?search=rachel" \
-H "Authorization: Bearer $FINN_API_KEY"
{
"success": true,
"data": [
{
"voice_id": "21m00Tcm4TlvDq8ikWAM",
"name": "Rachel",
"description": "Calm, young American female voice.",
"preview_url": "https://example.com/voices/rachel-preview.mp3",
"category": "premade",
"labels": { "accent": "american", "gender": "female", "age": "young" },
"languages": ["en"]
}
]
}
Leave out search to list every voice you can use. Listen to preview_url, then copy the voice_id. Voice IDs aren't UUIDs; use them exactly as returned.
3. Create a Finn
name and voice are the only required fields. This example also sets the first line, the instructions, a call length limit and one question to answer after each call.
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",
"begin_message": "Hi {{ name }}, this is Maya from Acme. Do you have a minute to talk about your renewal?",
"system_prompt": "You are Maya from Acme. Confirm whether the customer wants to renew their plan. Keep answers short. If they want to cancel, thank them and end the call.",
"max_call_duration_minutes": 5,
"post_call_analysis": [
{ "name": "Wants to renew", "type": "Yes/No", "description": "Did the customer confirm they want to renew?" }
]
}'
The response is 201 Created with the full Finn. Save data.id:
export FINN_ID="8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1"
The Finn is a draft. {{ name }} is filled from each contact's name on deployment calls. A voice that isn't one of your voices returns 400 invalid_request. Every field, its limits and its default are in api-finns; what to write in the prompt is in agents-prompting.
4. Place a test call to yourself
Before you call anyone else, hear the Finn on your own phone:
curl -X POST https://api.hirefinn.ai/api/v1/calls \
-H "Authorization: Bearer $FINN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"finn_id": "'"$FINN_ID"'",
"to_number": "4155550142",
"country_code": "1"
}'
{
"success": true,
"data": { "call_uuid": "b7e3c9a2-4d1f-4e8b-9a6c-2f5d8e1b3c70", "finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1", "to_number": "4155550142", "status": "initiated" }
}
202 Accepted means the call was accepted for dialing, not that anyone answered. Your phone rings from an Indian +91 number. A single call doesn't use the Finn's max_call_duration_minutes, idle reminder or end-on-silence settings; deployment calls do. Don't retry a POST /calls whose result you didn't get: it can ring the same phone twice. See api-idempotency.
5. Call a list: audience and deployment
Find a number to call from. List your numbers and pick one whose status is Live and that no running deployment is using. Its id is the phone_number_id.
curl "https://api.hirefinn.ai/api/v1/phone_numbers" \
-H "Authorization: Bearer $FINN_API_KEY"
export PHONE_NUMBER_ID="6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03"
Create an audience. Start with one contact: yourself. country_code and phone_number are required on every contact; extra keys such as plan become custom fields you can use as {{ plan }} in the Finn's begin_message and system_prompt.
curl -X POST https://api.hirefinn.ai/api/v1/audiences \
-H "Authorization: Bearer $FINN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Renewals test",
"contacts": [
{ "name": "Dan", "phone_number": "4155550142", "country_code": "+1", "plan": "gold" }
]
}'
The response is 201 Created with the audience and contacts_added. Save data.id:
export AUDIENCE_ID="c2d9e7a4-51b8-4e0f-8a63-9f1d2b7c5e48"
Start the deployment. This starts dialing straight away.
curl -X POST https://api.hirefinn.ai/api/v1/deployments \
-H "Authorization: Bearer $FINN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"finn_id": "'"$FINN_ID"'",
"phone_number_id": "'"$PHONE_NUMBER_ID"'",
"audience_id": "'"$AUDIENCE_ID"'"
}'
The response is 201 Created with the deployment, status pending, and the Finn becomes live. Save data.id as DEPLOYMENT_ID. Follow progress with GET /deployments/{id}: completed_calls counts up to total_calls, and an outbound deployment ends as completed once it has dialed everyone.
The common rejections: 402 insufficient_credits (the wallet must cover at least half the audience in one-minute calls), 409 deployment_already_active (this Finn already has a live deployment), 409 phone_number_in_use (another deployment holds the number) and 422 audience_not_ready (the audience has no contacts). The full list is in api-deployments.
To have the Finn answer calls on the number instead, use POST /deployments/inbound with finn_id and phone_number_id.
6. Receive results by webhook
Webhook endpoints are added in the dashboard, not through the API:
- Open Settings → Integrations → Webhooks, click Add endpoint and enter a public
https://URL. To receive events on your laptop, use a tunnel; see local-development. - Copy the signing secret (
whsec_...). It's shown once. - In your handler, verify the
X-Finn-Signatureheader against the raw body, store the event, then return2xxwithin 10 seconds. A ready-made handler is in webhook-security.
After each call ends, answered or not, every endpoint receives one call.completed event, usually within about 5 minutes:
{
"event": "call.completed",
"created_at": "2026-09-18T11:14:09.000Z",
"data": {
"call_uuid": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
"finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
"deployment_id": "0e8a4c71-9b2d-4f36-a5e1-7c3d9b2f6a10",
"status": "completed",
"to_number": "+14155550142",
"duration_seconds": 97,
"call_successful": true,
"analysis": [
{ "question_name": "Wants to renew", "question_type": "Yes/No", "answer": "Yes", "needs_review": false, "reasoning": "Customer said they want to keep the plan.", "extracted_at": "2026-09-18T11:14:07.000Z" }
]
}
}
This example is shortened; webhook-post-call lists every field. An endpoint only receives calls that end after you add it, and a delivery that fails 3 times is not sent again, so keep step 7 as your fallback.
7. Read the call
curl https://api.hirefinn.ai/api/v1/calls/3f2504e0-4f89-11d3-9a0c-0305e82c3301 \
-H "Authorization: Bearer $FINN_API_KEY"
This returns the same call with billing added. The record appears only after the call ends, so a GET right after POST /calls returns 404 call_not_found: wait for the webhook, or retry with a backoff. analysis can be empty for a few minutes after the call while it's being worked out. There is no endpoint to list calls: keep the call_uuid values from POST /calls and from webhooks.
8. Stop the deployment
An outbound deployment stops by itself when it has dialed everyone. To stop it early:
curl -X POST "https://api.hirefinn.ai/api/v1/deployments/$DEPLOYMENT_ID/stop" \
-H "Authorization: Bearer $FINN_API_KEY"
Calls already in progress finish. A stopped deployment can't be restarted; create a new one. Inbound deployments stop with POST /deployments/inbound/{finn_id}/stop.
Before you go to production
- Handle errors by
errorcode, notmessage. Don't retry other4xx. Retry429afterRetry-After. Errors that need someone to act in the dashboard, such as topping up or compliance, include anaction_url. See api-errors. - Writes aren't idempotent. A retried create can make a second Finn, audience or call. Check state before retrying. See api-idempotency.
- Dedupe webhook events on
data.call_uuid. See webhook-retries. - Respect calling rules. Finn doesn't enforce calling hours or consent for you. See compliance.
- Mind the limits. Each endpoint group has a per-key rate limit. See api-rate-limits.
Next
- API reference: every endpoint and schema in one place.
- api-finns: every Finn field, and which calls use the call settings.
- webhooks: when events are sent, and the per-Finn data webhook.