Beta. Every endpoint on this page is live and documented as shipped. Shapes can still change before general availability.
Overview
A deployment puts a Finn to work on a phone number.
- An outbound deployment dials every contact in an audience from one of your numbers. It starts dialing as soon as it is created.
- An inbound deployment connects a number to a Finn so the Finn answers calls to that number. This is how you assign a number to a Finn; there is no separate assign endpoint.
Deploying sets the Finn's status to live. Stopping the deployment sets it back to draft.
Base URL: https://api.hirefinn.ai/api/v1. Auth: Authorization: Bearer finn_live_.... See authentication.
You need three IDs, all from your organization:
| ID | From |
|---|---|
finn_id | api-finns |
phone_number_id | api-phone-numbers |
audience_id (outbound only) | api-audiences |
Deployments are attributed to the user who created the API key. That user must still be a member of your organization, otherwise creating a deployment returns 409 api_key_owner_required; create a new key from the dashboard.
Scheduling a deployment for later, pausing and resuming are not available in the API. A request with a schedule returns 422 schedule_not_supported.
Endpoints
| Method | Path | Rate limit (per API key) |
|---|---|---|
POST | /deployments | 10 per minute, shared with POST /deployments/inbound |
POST | /deployments/inbound | Same create bucket |
POST | /deployments/{id}/stop | 30 per minute, shared with POST /deployments/inbound/{finn_id}/stop |
POST | /deployments/inbound/{finn_id}/stop | Same stop bucket |
GET | /deployments | 300 per minute, shared with GET /deployments/{id} and GET /wallet/transactions |
GET | /deployments/{id} | Same read bucket |
The deployment object
{
"id": "0e8a4c71-9b2d-4f36-a5e1-7c3d9b2f6a10",
"finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
"audience_id": "c2d9e7a4-51b8-4e0f-8a63-9f1d2b7c5e48",
"phone_number_id": "6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03",
"call_type": "outbound",
"status": "in_progress",
"from_number": "+918035731234",
"number_pool": null,
"max_parallel_calls": 1,
"schedule": null,
"total_calls": 120,
"completed_calls": 37,
"recording_enabled": false,
"created_at": "2026-10-02T10:15:00.000Z",
"updated_at": "2026-10-02T10:41:12.000Z"
}
| Field | Type | Notes |
|---|---|---|
id | string | UUID. |
finn_id | string or null | The Finn. |
audience_id | string or null | The audience. null for inbound. |
phone_number_id | string or null | The number. null when max_parallel_calls is above 1: the number is in number_pool instead. |
call_type | string | outbound or inbound. |
status | string | See Status. |
from_number | string or null | The number dialed from (outbound) or answered on (inbound). null when max_parallel_calls is above 1: the number is in number_pool instead. |
number_pool | string[] or null | Numbers used for parallel calling. Set when max_parallel_calls is above 1, in place of from_number and phone_number_id; null otherwise. A deployment created through the API has one number in the pool: the phone_number_id you sent. |
max_parallel_calls | integer or null | Concurrent calls. 1, or null right after creation, for a deployment dialing one call at a time. |
schedule | object or null | { "date", "start_time", "timezone" } for deployments scheduled in the dashboard. Always null for deployments created through the API. |
total_calls | integer or null | Calls the deployment plans to make, normally the audience size. 0 for inbound. |
completed_calls | integer or null | Calls finished so far. |
recording_enabled | boolean | Whether the deployment's calls are recorded. See Call recording. |
created_at | string | ISO 8601. |
updated_at | string | ISO 8601. |
Status
| Status | Meaning |
|---|---|
pending | Created, starting up. |
in_progress, active, running | Live. |
paused | Paused from the dashboard. |
scheduled | Waiting for a start time (dashboard only). |
completed | An outbound deployment that has dialed its whole audience. |
stopped | Stopped by you or the dashboard. Inbound deployments end as stopped, never completed. |
error | Finn could not run it. |
Treat this as the common set, not a closed enum, and handle unknown values.
Create an outbound deployment
POST /deployments
| Field | Type | Required | Notes |
|---|---|---|---|
finn_id | string | yes | UUID of a Finn in your organization. |
phone_number_id | string | yes | UUID of a number in your organization to dial from. |
audience_id | string | yes | UUID of an audience in your organization. |
max_parallel_calls | integer | no | 1 to 5. Default 1. Above 1, the number places up to that many calls at once. |
recording_enabled | boolean | no | true to turn on the call recording add-on. Default false. Billable; see Call recording. |
schedule | object | no | Not supported. Any non-null value returns 422 schedule_not_supported. |
curl -X POST https://api.hirefinn.ai/api/v1/deployments \
-H "Authorization: Bearer $FINN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
"phone_number_id": "6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03",
"audience_id": "c2d9e7a4-51b8-4e0f-8a63-9f1d2b7c5e48",
"max_parallel_calls": 3,
"recording_enabled": true
}'
Response 201 Created:
{
"success": true,
"data": {
"id": "0e8a4c71-9b2d-4f36-a5e1-7c3d9b2f6a10",
"finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
"audience_id": "c2d9e7a4-51b8-4e0f-8a63-9f1d2b7c5e48",
"phone_number_id": null,
"call_type": "outbound",
"status": "pending",
"from_number": null,
"number_pool": ["+918035731234"],
"max_parallel_calls": 3,
"schedule": null,
"total_calls": 0,
"completed_calls": 0,
"recording_enabled": true,
"created_at": "2026-10-02T10:15:00.000Z",
"updated_at": "2026-10-02T10:15:00.000Z"
}
}
With max_parallel_calls above 1, as in this example, phone_number_id and from_number are null and the number you sent is in number_pool. To match a parallel deployment to a number, compare number_pool with the number's phone_number.
In rare cases data contains only id. Read the full object with GET /deployments/{id}.
This starts real calls immediately. Each call is billed to your wallet. Before creating the deployment, Finn checks that the wallet holds at least half the audience's size in one-minute calls (minimum one), and returns 402 insufficient_credits if it does not. Check your balance with api-wallet.
Other conditions:
- The Finn must not already have a live deployment (
pending,in_progress,activeorrunning, inbound or outbound). Otherwise409 deployment_already_activewith the existingdeployment_id. - The number must not be held by another deployment that has not ended (
409 phone_number_in_use), must not be pending or cancelled (404 phone_number_not_found), and must not be isolated after a spam check (409 phone_number_isolated). A number whose deployment has ended ascompleted,stoppedorerroris freed, so you can deploy it again right away. - The audience must have a contact list with at least one contact (
422 audience_not_ready) and must not be dynamic (422 audience_not_supported).
There is no idempotency key. A retry after a timeout usually returns 409 deployment_already_active because the first request already started the deployment; check with GET /deployments before retrying.
Results arrive per call. Read them with api-calls or receive call.completed webhooks; see api-webhooks.
Create an inbound deployment
POST /deployments/inbound
Connects a number to a Finn so the Finn answers calls to it.
| Field | Type | Required | Notes |
|---|---|---|---|
finn_id | string | yes | UUID of a Finn in your organization. |
phone_number_id | string | yes | UUID of a number in your organization. |
recording_enabled | boolean | no | true to turn on the call recording add-on. Default false. Billable; see Call recording. |
curl -X POST https://api.hirefinn.ai/api/v1/deployments/inbound \
-H "Authorization: Bearer $FINN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
"phone_number_id": "6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03"
}'
Returns 201 Created with the deployment object, call_type inbound and audience_id null.
The number is configured at the carrier to route incoming calls to the Finn. A Finn can have one live inbound deployment, and a number can serve one Finn: otherwise the request returns 409 deployment_already_active or 409 phone_number_in_use. Inbound calls are billed to your wallet as they happen; creating the deployment does not check the balance.
Call recording
Call recording is an add-on you turn on per deployment. It is off for deployments created through the API unless you send "recording_enabled": true when you create an outbound or inbound deployment. The deployment object reports the setting in recording_enabled. The API sets it only at creation; there is no endpoint to change it on an existing deployment.
Recording is a billable add-on. Each answered call on a deployment with recording on is charged once for recording, on top of the call itself, and shows in the wallet ledger as a debit_call_recording entry (api-wallet). Calls that are not answered are not charged for recording. The per-call rate is set for your organization.
Post-call analysis does not depend on the add-on. Answers to the Finn's analysis questions arrive in GET /calls/{call_uuid} (api-calls) and in call.completed webhooks either way. With the add-on on, answered calls are recorded: recording_url in GET /calls/{call_uuid} and in the call.completed webhook is a signed link to the recording, valid for 1 hour. Without it, recording_url is null.
If you record, disclosure rules may apply.
Stop an outbound deployment
POST /deployments/{id}/stop
curl -X POST https://api.hirefinn.ai/api/v1/deployments/0e8a4c71-9b2d-4f36-a5e1-7c3d9b2f6a10/stop \
-H "Authorization: Bearer $FINN_API_KEY"
{
"success": true,
"data": { "id": "0e8a4c71-9b2d-4f36-a5e1-7c3d9b2f6a10", "status": "stopped" }
}
Stops dialing, frees the number and sets the Finn back to draft. The Finn is set to draft even if it still has a live inbound deployment, for example one started in the dashboard. That inbound deployment isn't stopped, but the Finn's status no longer shows live. Works on deployments that are pending, in_progress, active, running, paused or scheduled; a finished deployment returns 409 deployment_not_active. Inbound deployments must be stopped with the inbound endpoint below (409 use_inbound_stop). A stopped deployment cannot be restarted; create a new one.
Stop an inbound deployment
POST /deployments/inbound/{finn_id}/stop
Takes the Finn's ID, not the deployment's, and stops that Finn's live inbound deployment.
curl -X POST https://api.hirefinn.ai/api/v1/deployments/inbound/8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1/stop \
-H "Authorization: Bearer $FINN_API_KEY"
{
"success": true,
"data": {
"id": "5d2c8f19-7a4e-4b03-b6d1-3e9f0a7c2b84",
"finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
"status": "stopped"
}
}
The number stops routing calls to the Finn and is freed. The Finn returns to draft if it has no other live deployment. The number stays rented; release it with api-phone-numbers if you no longer need it.
List deployments
GET /deployments
Newest first.
| Query parameter | Type | Default | Notes |
|---|---|---|---|
status | string | none | One lowercase status, for example in_progress. |
call_type | string | none | inbound or outbound. |
limit | integer | 50 | 1 to 200. Out-of-range values are clamped. |
offset | integer | 0 | Number of items to skip. |
curl "https://api.hirefinn.ai/api/v1/deployments?call_type=outbound&status=in_progress" \
-H "Authorization: Bearer $FINN_API_KEY"
{
"success": true,
"data": [
{
"id": "0e8a4c71-9b2d-4f36-a5e1-7c3d9b2f6a10",
"finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
"audience_id": "c2d9e7a4-51b8-4e0f-8a63-9f1d2b7c5e48",
"phone_number_id": "6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03",
"call_type": "outbound",
"status": "in_progress",
"from_number": "+918035731234",
"number_pool": null,
"max_parallel_calls": 1,
"schedule": null,
"total_calls": 120,
"completed_calls": 37,
"recording_enabled": false,
"created_at": "2026-10-02T10:15:00.000Z",
"updated_at": "2026-10-02T10:41:12.000Z"
}
],
"limit": 50,
"offset": 0
}
There is no total count. Page with offset until a page returns fewer than limit items. Deployments archived in the dashboard are not returned.
Retrieve a deployment
GET /deployments/{id}
curl https://api.hirefinn.ai/api/v1/deployments/0e8a4c71-9b2d-4f36-a5e1-7c3d9b2f6a10 \
-H "Authorization: Bearer $FINN_API_KEY"
Returns { "success": true, "data": { ... } } with one deployment object. Poll it to follow total_calls and completed_calls.
Errors
| Status | error | Endpoint | When |
|---|---|---|---|
| 400 | invalid_request | all | An ID is not a UUID; max_parallel_calls is not an integer from 1 to 5; recording_enabled is not true or false; status or call_type filter is invalid. |
| 401 | api_key_required | all | No Bearer finn_live_... key. |
| 401 | invalid_api_key | all | Key unknown or revoked. |
| 402 | insufficient_credits | outbound create | The wallet does not cover half the audience in one-minute calls. |
| 404 | finn_not_found | create, inbound stop | No Finn with that ID in your organization. |
| 404 | phone_number_not_found | create | No usable number with that ID: not yours, or pending or cancelled. |
| 404 | audience_not_found | outbound create | No active audience with that ID in your organization. |
| 404 | deployment_not_found | get, stop, inbound stop | No deployment with that ID, or the Finn has no live inbound deployment. |
| 409 | api_key_owner_required | create | The key's creator is no longer a member of your organization. |
| 409 | deployment_already_active | create | The Finn already has a live deployment. Body may include deployment_id. |
| 409 | phone_number_in_use | create | The number is held by another deployment that has not ended. Numbers from completed, stopped or error deployments are freed and can be deployed again. |
| 409 | phone_number_isolated | create | The number was isolated after a spam-flag check. Release it in the dashboard first. |
| 409 | conflict | create | Another conflict with the current state; see message. |
| 409 | use_inbound_stop | stop | The deployment is inbound. |
| 409 | deployment_not_active | stop | The deployment has already finished or stopped. |
| 422 | schedule_not_supported | outbound create | A schedule was sent. |
| 422 | audience_not_supported | outbound create | The audience is dynamic. |
| 422 | audience_not_ready | outbound create | The audience has no contact list, or its contact list has no contacts. |
| 422 | deployment_rejected | create | Finn rejected the deployment; see message and, when present, errors. |
| 429 | rate_limited | all | Over the bucket's per-minute limit. |
| 500 | internal_error | all | Unexpected server error. |
| 502 | deployment_failed | create | The deployment could not be created. Check GET /deployments before retrying. |
| 502 | stop_failed | stop, inbound stop | The deployment could not be stopped. Retry. |
Error bodies have the shape { "error": "...", "message": "..." }; internal_error has no message. See api-errors.