API keys
Every request to the Finn API authenticates with an API key sent as a bearer token.
curl https://api.hirefinn.ai/api/v1/finns \
-H "Authorization: Bearer finn_live_YOUR_KEY_HERE"
Create and revoke keys in the dashboard under Settings → Integrations → API keys. Give each key a name that says where it runs, such as "Production server". There is no API endpoint for creating keys: a key can't mint another key.
The full key is shown once, when you create it. Finn stores only its SHA-256 hash, so it can't show the key to you again. If you lose a key, revoke it and create a new one.
Key format
Keys start with finn_live_ followed by 48 hexadecimal characters. The dashboard identifies each key by its first 16 characters (for example finn_live_3f9a1c), the date you created it and when it was last used.
| Situation | Status | error |
|---|---|---|
No Authorization header, or the token does not start with finn_live_ | 401 | api_key_required |
| The key is unknown or has been revoked | 401 | invalid_api_key |
{
"error": "invalid_api_key",
"message": "The key is unknown or has been revoked."
}
These are the responses when the body is valid JSON or there's no body. A body that isn't valid JSON is rejected with 400 before the key is checked, so you get 400, not 401, even with no key. See api-errors.
Finn checks the prefix before it looks the key up, so a dashboard session token or a finn_mcp_ key is rejected with api_key_required. finn_mcp_ keys are personal credentials for the MCP server. They belong to one user, not to the organization.
Every key is live. There are no test keys and no sandbox, so every call you place is a real call and costs real credits. While you're testing, call a number you control.
What a key can reach
Each key belongs to one organization. That's set when you create the key and never changes. A key can read and write only its own organization's data:
- An ID that belongs to another organization returns
404, the same response as an ID that doesn't exist. The API never confirms that another organization's resource exists. - Finn takes the organization from the key, never from the request. Adding an
org_idto a body or query string has no effect.
Keys have no scopes. Every key has full access to the whole /api/v1 API for its organization: it can place calls, rent and release phone numbers, start deployments and spend the wallet balance. Treat every key as an admin credential. To limit the damage if one leaks, give each service or job its own key.
An organization can have up to 20 active keys at a time. Revoked keys don't count toward the limit. To create a 21st key, revoke one first.
When an API key makes a successful write request, Finn records it in your organization's audit log. Reads aren't recorded.
The user who created a key
A key belongs to the organization, not to a person, but Finn remembers who created it. A few endpoints act on that user's behalf:
| Endpoint | Error if the creator has left the organization or been deleted |
|---|---|
POST /finns | 409 api_key_owner_required |
POST /deployments, POST /deployments/inbound | 409 api_key_owner_required |
POST /phone_numbers | 409 api_key_owner_required (it also needs the creator's email on file) |
Reads and every other endpoint keep working. To fix this, have a current member of the organization create a new key in Settings → Integrations → API keys (the error's action_url links there), then switch to it. Team membership is managed only in the dashboard under Settings → Team. To avoid the problem, create production keys from an account that won't be removed from the organization.
Rotation
Keys don't expire. You rotate them by hand, and the overlap step is what prevents an outage.
- Create the new key in Settings → Integrations → API keys.
- Deploy the new key to every service that uses the old one. Don't revoke the old key yet.
- Watch the old key's Last used column. It updates on every request the key authenticates.
- When the old key has been unused for longer than your least frequent job runs, revoke it.
Don't skip step 3. A cron job that runs nightly at 02:00 looks idle all afternoon. If you revoke the key after six quiet hours, the job breaks that night with a 401, and nobody may be watching it.
Revoking a key takes effect on the next request that uses it. There's no grace period and no undo. A revoked key stays in the list, marked Revoked.
Storing keys
Anyone with your key can place calls, spend your wallet balance and change your Finns.
- Keep keys in environment variables or a secrets manager. Never commit them to source control.
- Call the API from your own backend only. A key in browser or mobile code is visible to every user. The API is for server-to-server use: a request from a browser origin Finn doesn't allow is rejected with
403 origin_not_allowed. - Give each service, cron job or integration its own key. Then you can revoke one without breaking the rest.
- Rotate a key right away if it shows up in a log, a screenshot, a support ticket or a public repository.
const res = await fetch(`https://api.hirefinn.ai/api/v1/calls/${callUuid}`, {
headers: { Authorization: `Bearer ${process.env.FINN_API_KEY}` },
});
if (res.status === 401) {
const body = await res.json();
throw new Error(`Finn rejected the key: ${body.error}`);
}
const { data } = await res.json();
import os
import requests
res = requests.get(
f"https://api.hirefinn.ai/api/v1/calls/{call_uuid}",
headers={"Authorization": f"Bearer {os.environ['FINN_API_KEY']}"},
timeout=30,
)
if res.status_code == 401:
raise RuntimeError(f"Finn rejected the key: {res.json()['error']}")
data = res.json()["data"]
Rate limits are per key
Rate limits count requests per key, not per IP address. If you give two jobs separate keys, each gets its own limit, so a batch job that uses up its own key's limit doesn't slow your production traffic. Each group of endpoints has its own limit. See api-rate-limits.
Webhooks use a separate secret
Finn signs webhook deliveries with a secret that belongs to the webhook endpoint. That secret starts with whsec_ and isn't related to your API keys. Never use an API key as a webhook secret. See webhook-security.
Related
- api-conventions: base URL, request and response format
- api-errors: every error code
- api-calls: placing calls and reading results