Skip to main content

API reference

Errors

Every error code, its status and what to retry.

How errors are returned

A failed request returns an HTTP status code and a flat JSON body. error is a stable code that your code can check. message explains the problem to a person.

{
  "error": "invalid_request",
  "message": "finn_id must be a valid UUID"
}
FieldTypeNotes
errorstringCode to check in your code. The tables below list every value.
messagestringHuman-readable explanation. It can change at any time, so don't parse it. internal_error responses have no message.

Some errors include extra fields:

FieldOnMeaning
details400 invalid_request from audience contact endpointsContacts that failed validation, up to 50 entries.
deployment_id409 deployment_already_active, 409 audience_in_useThe live deployment that caused the conflict.
errors422 deployment_rejectedThe specific reasons the deployment was rejected, when Finn has them.
required_credits, available_credits402 insufficient_credits on POST /phone_numbersHow many credits the rental needs and how many you have.
transaction_id500 rent_failedThe wallet transaction that was refunded.
action_urlErrors that need setup on the website (see below)The dashboard page where you fix the problem.

Setup that happens on the website

Compliance, billing and API key management are done in the Finn dashboard, not through the API. When a request fails because one of them isn't done, the error includes action_url pointing at the page to finish it. Retry the request once that's done.

{
  "error": "compliance_required",
  "message": "Compliance approval is required. Complete the compliance application in the Finn dashboard (see action_url), then retry.",
  "action_url": "https://www.hirefinn.ai/dashboard/settings?tab=telephony-account"
}
errorWhat to do on the websiteaction_url
compliance_requiredSubmit the compliance application under Settings → Compliance and wait for approval. Required before renting numbers, and before searching India (IN) or Exotel inventory.https://www.hirefinn.ai/dashboard/settings?tab=telephony-account (opens the Compliance tab)
insufficient_creditsTop up the wallet.https://www.hirefinn.ai/dashboard/settings?tab=billing
feature_not_allowedUpgrade the plan to include a voice provider.https://www.hirefinn.ai/dashboard/settings?tab=billing
api_key_owner_requiredThe user who created the key left the organization or was deleted. Sign in as an active member and create a new key.https://www.hirefinn.ai/dashboard/settings?tab=integrations

Team members and roles are also managed only in the dashboard (Settings → Team). Show message and action_url to the person running your integration rather than retrying automatically. These errors don't go away until someone acts.

Responses don't include a request ID. If you contact support, include the method, path, time (UTC) and the full response body.

Status codes

StatusMeaningRetry?
400The request is malformed: a missing or invalid field, an invalid UUID, or a bad query value.No. Fix the request.
401The API key is missing, malformed, unknown or revoked. A body that isn't valid JSON gets 400 instead, even without a key; see Errors outside the normal format.No. See authentication.
402The wallet balance is too low for this action.Only after you top up the wallet.
403Your plan or compliance status doesn't allow this action, or the request came from a browser origin Finn doesn't allow.No.
404The resource doesn't exist or belongs to another organization.No.
409The request conflicts with the current state, for example a deployment that's already running or a number that's in use.Not until that state changes.
422The request is valid but can't be carried out, for example an audience with no contacts uploaded.No.
429You hit the rate limit for this key and endpoint group.Yes, after Retry-After. See api-rate-limits.
500Unexpected error on Finn's side.Reads, yes. Writes, see api-idempotency.
502Finn couldn't complete the request: placing the call, starting or stopping the deployment, the carrier or the voice provider failed or didn't respond.Reads, yes. Writes, see api-idempotency.

A 404 for a resource you're sure exists usually means the API key belongs to a different organization.

Error codes

Every endpoint

StatuserrorCause
400invalid_requestA field, path ID or query value failed validation. message names the problem.
401api_key_requiredNo Authorization header, or the token doesn't start with finn_live_.
401invalid_api_keyThe key is unknown or has been revoked.
403origin_not_allowedThe request has a browser Origin header that Finn doesn't allow. The API is for server-to-server use: call it from your backend, not from a web page.
429rate_limitedToo many requests for this key and endpoint group.
500internal_errorUnexpected failure. No message.

Calls

StatuserrorEndpointCause
404finn_not_foundPOST /callsNo Finn with that finn_id in your organization.
402insufficient_creditsPOST /callsThe wallet balance is too low to place the call.
502call_failedPOST /callsThe call couldn't be placed, or Finn didn't get a call_uuid back. See api-idempotency before you retry.
404call_not_foundGET /calls/{call_uuid}No call with that ID in your organization.

Finns and voices

StatuserrorEndpointCause
404finn_not_foundGET, PATCH, DELETE /finns/{finn_id}No Finn with that ID in your organization.
403feature_not_allowedPOST /finns, PATCH /finns/{finn_id} with voice, GET /voices, GET /voices/{voice_id}Your plan includes no voice provider.
409api_key_owner_requiredPOST /finnsThe user who created this key has been deleted or is no longer in the organization. Create a new key.
409finn_not_draftDELETE /finns/{finn_id}Only draft Finns can be deleted.
409finn_in_useDELETE /finns/{finn_id}The Finn is assigned a number or is used by deployments.
400invalid_requestPOST /finns, PATCH /finns/{finn_id} with voicevoice is not a voice your organization can use. message is voice is not one of the voices from GET /voices.
404voice_not_foundGET /voices/{voice_id}None of your organization's voice providers has a voice with that ID.
502voices_unavailableGET /voices, GET /voices/{voice_id}, POST /finns, PATCH /finns/{finn_id} with voiceThe voice catalog couldn't be loaded, so the voice couldn't be listed or checked. Retry later. On GET /voices, also returned when your organization is limited to a voice provider whose voices can't be listed through the API; retrying doesn't help then.

Deployments

StatuserrorEndpointCause
409api_key_owner_requiredPOST /deployments, POST /deployments/inboundThe key has no creator, or its creator is no longer in the organization. Create a new key. POST /finns returns the same error; see Finns and voices.
404finn_not_foundcreate and inbound stopNo Finn with that ID in your organization.
404phone_number_not_foundPOST /deployments, POST /deployments/inboundNo usable number with that phone_number_id in your organization.
409phone_number_isolatedPOST /deployments, POST /deployments/inboundThe number was isolated after a spam check.
409phone_number_in_usePOST /deployments, POST /deployments/inboundThe number is held by another deployment that has not ended.
404audience_not_foundPOST /deploymentsNo active audience with that ID in your organization.
422audience_not_supportedPOST /deploymentsDynamic audiences can't be deployed through the API.
422audience_not_readyPOST /deploymentsThe audience has no uploaded contact list yet, or its contact list has no contacts.
422schedule_not_supportedPOST /deploymentsScheduled deployments aren't available yet. Leave out schedule.
409deployment_already_activePOST /deployments, POST /deployments/inboundThis Finn already has a live deployment. The response includes its deployment_id.
402insufficient_creditsPOST /deploymentsThe wallet balance is too low.
422deployment_rejectedPOST /deployments, POST /deployments/inboundFinn rejected the deployment. Any reasons are in errors.
409conflictPOST /deployments, POST /deployments/inboundAnother conflict with the current state prevented the deployment.
502deployment_failedPOST /deployments, POST /deployments/inboundThe deployment couldn't be created, or Finn didn't get a deployment ID back.
404deployment_not_foundGET /deployments/{id}, both stop endpointsNo such deployment, or the Finn has no live inbound deployment.
409use_inbound_stopPOST /deployments/{id}/stopThis is an inbound deployment. Use POST /deployments/inbound/{finn_id}/stop.
409deployment_not_activePOST /deployments/{id}/stopThe deployment has already finished or been stopped.
502stop_failedboth stop endpointsFinn couldn't stop the deployment.

Phone numbers

StatuserrorEndpointCause
404phone_number_not_foundGET, DELETE /phone_numbers/{id}No number with that ID in your organization.
409compliance_requiredGET /phone_numbers/availableYou need compliance approval to search this inventory. Applies to India (IN) inventory, to provider=exotel, and to searches without provider when your organization's default carrier is Exotel. Organizations with an approved compliance application or an existing carrier subaccount pass.
502provider_unavailableGET /phone_numbers/available, DELETE /phone_numbers/{id}The carrier couldn't be reached.
409api_key_owner_requiredPOST /phone_numbersThe key's creator is no longer in the organization or has no email on file.
402insufficient_creditsPOST /phone_numbersThe wallet balance is too low. Includes required_credits and available_credits.
403compliance_requiredPOST /phone_numbersYou need compliance approval before you can rent numbers.
403rental_unavailablePOST /phone_numbersThis organization can't rent numbers.
400price_currency_mismatchPOST /phone_numbersThe number's price is in a different currency from your wallet.
502price_unavailablePOST /phone_numbersFinn couldn't confirm the rental price. Check GET /phone_numbers before you retry.
500rent_failedPOST /phone_numbersSetting up the number failed after the charge, and the charge was refunded. Includes transaction_id.
409number_in_useDELETE /phone_numbers/{id}The number is used by a deployment that hasn't ended (as its number or in its number_pool), was ever a deployment's phone_number_id, or is assigned to a Finn or has old assignment records.

compliance_required returns 409 from the search endpoint and 403 from the rental endpoint. Check the error code, not the status.

Audiences

StatuserrorEndpointCause
404audience_not_foundany /audiences/{audience_id} pathNo active audience with that ID in your organization.
404contacts_file_missingcontact list and edit endpointsThe audience's contact file no longer exists.
409audience_not_editablePOST /audiences/{audience_id}/contacts, DELETE /audiences/{audience_id}/contacts/{phone}The audience has no contact file to edit.
409audience_in_usePOST /audiences/{audience_id}/contacts, DELETE /audiences/{audience_id}/contacts/{phone}A running deployment is using the audience: one with status in_progress, active, running or live. pending and paused deployments don't block edits. The response includes its deployment_id.
409audience_limit_exceededPOST /audiences/{audience_id}/contactsThe audience would have more than 50,000 contacts.
404contact_not_foundDELETE /audiences/{audience_id}/contacts/{phone}No contact with that phone number in the audience.
409audience_would_be_emptyDELETE /audiences/{audience_id}/contacts/{phone}You can't remove the last contact. Archive the audience instead.

Errors outside the normal format

A few failures happen before your request reaches an endpoint, so their bodies use a different shape. Check the status code for these.

The body is parsed before the API key is checked. A body that isn't valid JSON gets 400 even when the key is missing or wrong, and it doesn't count toward your rate limits.

StatusWhenBody
400The body isn't valid JSON{"success": false, "error": "<parser message>"}. Here error is a description, not a code.
413The body is larger than 10 MB{"success": false, "error": "request entity too large"}
404Valid key, but the path doesn't exist{"success": false, "message": "Route /api/v1/... not found"}

What to retry

ResponseDo this
429Wait for the number of seconds in Retry-After, then retry. Nothing was done, so retrying is safe.
500 or 502 on a GETRetry with exponential backoff and jitter.
500 or 502 on a write, a timeout, or a dropped connectionThe write may have happened. Check first. See api-idempotency.
402Top up the wallet, then retry.
Any other 4xxDon't retry. The same request gets the same error.

There's no Idempotency-Key header. Retrying a write after an unclear result can do the work twice, for example placing two calls. api-idempotency shows how to check before you retry each write.

Client example

This client retries reads only. Writes go back to the caller so it can decide.

const BASE = "https://api.hirefinn.ai/api/v1";
const RETRYABLE = new Set([429, 500, 502]);

export async function finnGet(path: string, maxAttempts = 4): Promise<Response> {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const res = await fetch(`${BASE}${path}`, {
      headers: { Authorization: `Bearer ${process.env.FINN_API_KEY}` },
    });

    if (!RETRYABLE.has(res.status) || attempt === maxAttempts - 1) return res;

    const retryAfter = res.headers.get("Retry-After");
    const waitMs = retryAfter
      ? Number(retryAfter) * 1000
      : 2 ** attempt * 500 + Math.random() * 250;

    await new Promise((r) => setTimeout(r, waitMs));
  }
  throw new Error("unreachable");
}
import os, time, random, requests

BASE = "https://api.hirefinn.ai/api/v1"
RETRYABLE = {429, 500, 502}

def finn_get(path, max_attempts=4, **kwargs):
    headers = {"Authorization": f"Bearer {os.environ['FINN_API_KEY']}"}

    for attempt in range(max_attempts):
        res = requests.get(f"{BASE}{path}", headers=headers, timeout=30, **kwargs)

        if res.status_code not in RETRYABLE or attempt == max_attempts - 1:
            return res

        retry_after = res.headers.get("Retry-After")
        wait = float(retry_after) if retry_after else 2**attempt * 0.5 + random.random() * 0.25
        time.sleep(wait)

    raise RuntimeError("unreachable")

Failures that aren't HTTP errors

202 from POST /calls means Finn accepted the call and started dialing. It doesn't mean anyone answered. Calls can still fail after that: the line is busy, the carrier rejects the call, or nobody picks up. Those outcomes appear in the call record (GET /calls/{call_uuid}) and in the call.completed webhook. They never come back as an HTTP error.

So handle errors in two layers. An HTTP error means your request was wrong or Finn couldn't process it. A call outcome means the request worked but the call didn't go the way you wanted.

Reporting a problem

Send Finn support the method, path, time (UTC) and the full response body. Never include the API key. If the failure is intermittent, include the times of one failing request and one successful one.