How this changelog works
Entries are newest first. Each entry gives a date, a change type, and what you need to do. Breaking changes are called out first inside each date block.
Change types:
| Type | Meaning | Action needed |
|---|---|---|
| Breaking | Existing behavior changes or is removed | Yes, before the stated date |
| Added | New endpoint, field, or dashboard capability | No |
| Changed | Behavior differs but old calls still work | Read and confirm |
| Deprecated | Still works, removal date announced | Plan migration |
| Fixed | Defect corrected | No |
API versioning is covered in api-conventions. The version is part of the path (/api/v1). If a change below refers to a request or response shape, that page defines the shape.
What counts as breaking
These are treated as breaking and get advance notice:
- Removing a field from a response, or removing an endpoint.
- Changing the type of an existing field.
- Adding a required request parameter.
- Changing the meaning of an existing enum value.
- Changing an HTTP status code returned for an existing condition.
These are not treated as breaking, and can ship without notice:
- Adding a new field to a response body.
- Adding a new value to an existing enum.
- Adding a new optional request parameter.
- Adding a new webhook event type.
- Changing the wording of an error
messagestring. Thecodeis stable, themessageis not. See api-errors.
The consequence for you: your JSON parsing must tolerate unknown fields, and your webhook handler must ignore event types it does not recognize rather than erroring. A handler that throws on an unknown event type will start failing and get retried on a schedule you did not choose. See webhook-events and webhook-retries.
A tolerant handler looks like this:
import express from "express";
const app = express();
app.post("/finn/webhook", express.json(), (req, res) => {
const event = req.body;
switch (event.event) {
case "call.completed":
handleCallCompleted(event);
break;
default:
// Unknown or newly added event type. Acknowledge, do not throw.
console.log("unhandled event", event.event);
}
res.status(200).send();
});
function handleCallCompleted(event: unknown) {
// your logic
}
app.listen(3000);
Entries
2026-11-04
| Type | Change | Action |
|---|---|---|
| Breaking | Removing a do-not-call number (DELETE /do_not_call/{phone}) needs the new dnc:write scope. voice alone, and keys created before scopes, get 403 insufficient_scope. | To remove numbers by API, create a key with dnc:write. Listing and adding numbers still need only voice. |
| Breaking | Deciding approvals by API key (decide, approve, reject) needs your organization's opt-in, off by default. Without it the API returns 403 approvals_by_api_off. | Ask Finn support to turn it on if an integration decides approvals. |
| Changed | A revoked API key stops working on the Chat API within 30 seconds (was 5 minutes). | None. |
| Added | API key scopes chat:read and chat:write for the Chat API (https://chat.hirefinn.ai/finn-api/public/v1). chat:write includes chat:read. A scoped key without the needed chat scope gets 403 insufficient_scope there. See api-scopes. | If you call the Chat API with a key that has scopes, create a new key with chat:read or chat:write. Keys created before scopes keep full access. |
| Added | POST /calls, POST /deployments, POST /deployments/inbound and POST /phone_numbers accept an Idempotency-Key header. A retry with the same key and body within 24 hours returns the first response with Idempotent-Replayed: true; the same key with a different body returns 422 idempotency_key_reused, and a retry while the first is still running returns 409 idempotency_in_progress. See api-idempotency. | Optional. Send a key on these writes so a retry after a timeout can't place a second call or start or rent twice. |
| Fixed | Rate limits are now counted once across all of Finn's servers. Before, each server kept its own counters, so you could sometimes send more than the documented limit, and counters reset when Finn released a new version. The documented limits are unchanged. See api-rate-limits. | If your client relied on getting more than the documented limit, follow the RateLimit-* headers. |
2026-11-03
| Type | Change | Action |
|---|---|---|
| Added | API keys have scopes: voice, crm:read, crm:sensitive:read, crm:write, cases:read, cases:write, approvals:write. A missing scope returns 403 insufficient_scope. See api-scopes. | None for existing keys: a key created before scopes keeps access to everything except deciding approvals. New keys get voice unless you pick others. |
| Added | Records endpoints: list, read, create and update CRM records, their kinds, fields and timeline, and add notes. See api-records. | None |
| Added | Cases endpoints: list and read Finn Operator cases and their events, and start a case (with Idempotency-Key). See api-cases. | None |
| Added | Approvals endpoints: list, read, and decide single approvals with an approvals:write key, never payments or edited proposals. See api-approvals. | None |
| Added | Webhook events contact.updated, case.created, case.completed and approval.requested. The catalog in webhook-events now lists all 14 events. | Pick them on a new endpoint to receive them. |
2026-10-28
| Type | Change | Action |
|---|---|---|
| Added | Webhook endpoints can get contact and lead events: contact.created, contact.assigned, contact.stage_changed, lead.created and lead.status_changed. Each has the contact as it is when sent and, for a change, who made it. Pick them when you add an endpoint. | None. To get them on an existing endpoint, add a new one and delete the old one. See webhook-events. |
2026-10-27
| Type | Change | Action |
|---|---|---|
| Breaking | An unsupported language on POST or PATCH /finns now returns 400 invalid_request instead of being saved as sent. | Send a supported language. See Language. |
| Fixed | POST /calls sends the Finn's system_prompt the way deployment calls do. Before this, a single call placed through the API did not follow the system prompt. | None |
| Fixed | language accepts a name or code in any case, with or without a region (Hindi, hi, hi-IN), and saves the name. A code with a region, such as hi-IN, used to be saved as sent and the call stayed in English. | Read language back as a name. |
2026-10-17
| Type | Change | Action |
|---|---|---|
| Added | call_flow field on Finns: read and write the call flow, including End Call, Call Transfer and Cal.com tools. Tool API keys are returned as "[redacted]". See Call flow. | None. Tolerate the new response field. |
| Added | POST and GET /finns/{finn_id}/knowledge_base: add documents (text, URL or file) and list them. See Knowledge base. | None |
| Changed | POST /calls now runs the Finn's call flow when it is turned on, as deployment calls do. | Test single calls placed with Finns that have a call flow. |
How to find out before something breaks
Polling this page is the weakest option. Better:
- Keep a staging workspace with a separate API key and run your integration against it before you promote. See local-development for running against your own machine.
- Write your client to tolerate unknown fields and unknown webhook event types, as shown above, so additive changes cannot break it.
Things that change without a changelog entry
Some behavior is per-workspace and will not appear here at all, because it is configuration rather than platform change:
| Area | Where it changes | Page |
|---|---|---|
| Concurrency limits | Your plan and workspace settings | concurrency |
| Rate limits | Per key, per endpoint | api-rate-limits |
| Retention windows | Settings → Data, dashboard only | retention |
| Phone number allocation | Dashboard phone number settings | phone-numbers |
| Agent prompt and voice | Dashboard agent editor | agents-overview |
If a call started failing and nothing on this page explains it, the cause is usually one of the above rather than a platform change. Start at troubleshooting, then check api-errors for the specific code you got back. If neither resolves it, support covers what to include in a report.