Overview
Finn has official SDKs for TypeScript and Python. Both wrap the REST API at https://api.hirefinn.ai/v1, and their types come from the same OpenAPI spec the API reference shows. They handle the parts that are easy to get wrong by hand: retries with backoff, Idempotency-Key on writes, both error shapes, every pagination style and approval holds.
| Language | Package | Import | Source |
|---|---|---|---|
TypeScript and JavaScript (Node 18+, or any runtime with fetch) | @hirefinn/sdk on npm | import { Finn } from "@hirefinn/sdk" | finn-sdk-ts |
| Python 3.9+ | hirefinn on PyPI | from finn import Finn | finn-sdk-py |
Both are at 0.3.0, a pre-release: method names can still change before 1.0. Pin the version you test with.
Install
npm install @hirefinn/sdk
pip install hirefinn
The Python distribution is called hirefinn because another project owns finn on PyPI. You still import finn.
Place a call
Both clients read the key from FINN_API_KEY when you don't pass one. Any key works: finn_live_, finn_test_, a personal MCP key or an OAuth token; see authentication.
import { Finn, isPendingApproval } from "@hirefinn/sdk";
const finn = new Finn();
const res = await finn.calls.create({ finn_id: FINN_ID, to_number: "4155550142", country_code: "1" });
if (isPendingApproval(res)) {
// 202: the call waits for a person to approve it in Finn.
const outcome = await finn.waitForApproval(res.approval_id);
console.log(outcome.status);
} else {
console.log("Dialing", res.data.call_uuid);
}
from finn import Finn, is_pending_approval
finn = Finn()
res = finn.calls.create({"finn_id": FINN_ID, "to_number": "4155550142", "country_code": "1"})
if is_pending_approval(res):
outcome = finn.wait_for_approval(res.approval_id)
print(outcome.status)
else:
print("Dialing", res.data["call_uuid"])
What the SDKs do for you
| What happens | |
|---|---|
| Retries | Network errors, 408, 429, 5xx and 409 idempotency_in_progress are retried with backoff, honouring Retry-After. |
| Idempotency | Writes get an Idempotency-Key automatically, so a retry never runs a write twice. Pass your own to retry across processes; see api-idempotency. |
| Errors | Every API error raises FinnError with status, code, message, fields, the request id (requestId / request_id) and, on 403 insufficient_scope, the scope the key needs. Branch on code. |
| Pagination | listAll (TypeScript, for await) and list_all (Python) walk every page, whatever style the endpoint uses. |
| Holds | isPendingApproval / is_pending_approval spots a 202 pending_approval, and waitForApproval / wait_for_approval polls until the request is approved and runs, is declined or expires. |
| Organization | Pass org to send Finn-Org with a member token that can act in more than one organization. |
Keep the key on your server
The API sends no CORS headers, so a web page can't call it, and a key in a browser bundle is a leaked key. Use the SDK from your backend, a worker or a script.
Related
- api-quickstart: the same first call with
curl - api-errors: every code
FinnError.codecan hold - mcp: connect an AI app to Finn without writing code