Beta. This endpoint is not final. Field names and response shapes can change before general availability.
What a deployment is
A deployment binds a Finn to a phone number and a direction. Outbound deployments pull contacts from an audience and dial them. Inbound deployments answer calls arriving on a number you own. The same Finn can back several deployments at once, so you can run a daytime campaign and a 24/7 receptionist from one agent definition.
Read concepts for the object model and inbound-vs-outbound for how the two directions differ operationally.
Create an outbound deployment
curl -X POST https://api.hirefinn.ai/v1/deployments \
-H "Authorization: Bearer $FINN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: dep-q3-winback-01" \
-d '{
"name": "Q3 winback",
"direction": "outbound",
"finn_id": "finn_8Kd2mQ",
"audience_id": "aud_71xBva",
"from_number": "+14155550123",
"timezone": "America/New_York",
"schedule": {
"days": ["mon", "tue", "wed", "thu", "fri"],
"window": { "start": "09:00", "end": "17:00" }
},
"max_concurrency": 5
}'
timezone governs the schedule window. It is evaluated against the deployment timezone, not the contact's, in the current shape. Whether per-contact local-time dialing lands before general availability is not yet settled.
Create an inbound binding
An inbound deployment has no audience and no schedule. It is a binding: calls to phone_number_id route to finn_id.
curl -X POST https://api.hirefinn.ai/v1/deployments \
-H "Authorization: Bearer $FINN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Main line receptionist",
"direction": "inbound",
"finn_id": "finn_3Rp9tW",
"phone_number_id": "pn_4Ld8ce",
"max_concurrency": 10
}'
One number holds one active inbound binding. Posting a second inbound deployment for a number that already has one returns 409 conflict. Deactivate the existing binding first, or the API will not tell you which deployment is winning. See api-phone-numbers to list numbers and their current bindings.
Fields
| Field | Type | Applies to | Notes |
|---|---|---|---|
name | string | both | Label shown in the dashboard. Not unique. |
direction | enum | both | inbound or outbound. Cannot be changed after creation. |
finn_id | string | both | The agent that handles the call. |
audience_id | string | outbound | Contacts to dial. See api-audiences. |
from_number | string | outbound | E.164. Must be a number on your account. |
phone_number_id | string | inbound | The number to bind. |
timezone | string | outbound | IANA name. Required when schedule is present. |
schedule.days | array | outbound | Lowercase three-letter day names. |
schedule.window | object | outbound | start and end as 24-hour HH:MM. |
max_concurrency | integer | both | Simultaneous calls this deployment may hold. |
status | enum | both | Read-only. See below. |
Passing an outbound-only field on an inbound deployment is rejected rather than ignored. Do not rely on silent field-dropping.
Status
| Status | Meaning |
|---|---|
draft | Created but never activated. No calls. |
active | Dialing, or accepting inbound calls. |
paused | Stopped by you. Contacts not yet dialed stay queued. |
completed | Outbound only. Audience exhausted. |
Creation returns draft. Activate separately:
curl -X POST https://api.hirefinn.ai/v1/deployments/dep_5Yh2nK/activate \
-H "Authorization: Bearer $FINN_API_KEY"
Pause with /deployments/{id}/pause. Pausing does not drop calls already in progress. They finish, then no new calls start.
List and read
curl "https://api.hirefinn.ai/v1/deployments?direction=outbound&status=active&limit=25" \
-H "Authorization: Bearer $FINN_API_KEY"
Filters: direction, status, finn_id. Paging follows api-pagination.
import os, requests
BASE = "https://api.hirefinn.ai/v1"
headers = {"Authorization": f"Bearer {os.environ['FINN_API_KEY']}"}
r = requests.get(f"{BASE}/deployments", headers=headers,
params={"status": "active", "limit": 100})
r.raise_for_status()
for d in r.json()["data"]:
print(d["id"], d["direction"], d["name"], d.get("max_concurrency"))
What will go wrong
- Sum of
max_concurrencyexceeds your account limit. Deployments do not reserve capacity. They compete for it. An outbound campaign can starve your inbound line. Read concurrency before setting these. - Activating with an empty audience. The deployment goes
active, dials nothing, then reportscompleted. Check the audience count first. from_numbernot on your account. Rejected at create time with422, not at dial time.- Editing an active deployment. Changes to
scheduleandmax_concurrencyapply going forward only. Calls in flight keep the old values. - Retrying a create without an idempotency key. You get two deployments and two campaigns. Send
Idempotency-Keyon every create. See api-idempotency.
Per-call results arrive by webhook, not by polling the deployment. Subscribe to call events described in webhook-events.