Skip to main content

API reference

Approvals

Read and decide approvals, within a key's limits.

Beta. Every endpoint on this page is live and documented as shipped. Shapes can still change before general availability.

Overview

A Finn Operator case can stop and ask a person to approve what it is about to do: send a quote, book a slot, refund a fee. The Approvals API lists and reads those approvals and lets a key decide them, within strict limits.

Base URL: https://api.hirefinn.ai/api/v1. Auth: Authorization: Bearer finn_live_.... See authentication and scopes.

Finn passes these requests on to Finn Operator and returns its answer, with no { "success": true, "data": ... } envelope.

Endpoints

MethodPathScopeRate limit (per API key)
GET/approvalscases:read300 per minute, shared by all reads in Records, Cases and Approvals
GET/approvals/{id}cases:readRead bucket
POST/approvals/{id}/decideapprovals:write120 per minute, shared with the Records writes
POST/approvals/{id}/approveapprovals:writeWrite bucket
POST/approvals/{id}/rejectapprovals:writeWrite bucket

What a key may decide

A key with approvals:write decides one approval at a time, as asked:

  • Only approvals asked of the approver role, or of a role that isn't a Finn Operator role (such as finance). Not approvals assigned to a named person.
  • Never a payment. Payment approvals return 403; a person decides them in Finn.
  • Never an edited proposal. The key approves or rejects the proposal as it is. To change it, edit it in Finn.
  • Never in bulk.

The decision is recorded as decided_by: "api_key:<key_id>" and decided_via: "api", so you can tell it apart from a person's.

A key created before scopes existed can read approvals but can't decide them. Create a key with approvals:write.

The approval object

{
  "id": "5d2e8f1a-3b4c-4d6e-9f0a-1b2c3d4e5f6a",
  "case_id": "3c9b1f7e-2a4d-4e6b-8c1d-5f7a9b0c2d3e",
  "case_title": "Renewal follow-up",
  "agent_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
  "agent_name": "Renewals",
  "contact_id": "6f1c2a0e-8d1b-4b7a-9f3e-2c4d5e6f7a8b",
  "question": "Send the renewal quote of 12,000 to Ravi Rao?",
  "allowed_answers": ["approve", "reject"],
  "status": "open",
  "assignee_role": "approver",
  "assignee_user_id": null,
  "proposal": { "summary": "Email the renewal quote" },
  "payment": null,
  "answer": null,
  "comment": null,
  "decided_by": null,
  "decided_via": null,
  "decided_at": null,
  "deadline_at": "2026-11-04T09:00:00.000Z",
  "created_at": "2026-11-03T09:00:00.000Z"
}
FieldNotes
questionWhat the person is asked.
allowed_answersThe answers decide accepts.
statusopen until decided, expired or cancelled.
assignee_role, assignee_user_id, assignee_user_idsWho is asked.
proposalWhat the agent will do. why holds its reason.
paymentSet for a payment approval. A key can't decide these.
finalThe proposal as finally approved, when a person edited it.
answer, comment, decided_by, decided_via, decided_atThe decision.
deadline_at, reminders, escalated_toTiming.

List approvals

curl "https://api.hirefinn.ai/api/v1/approvals?limit=50" \
  -H "Authorization: Bearer $FINN_API_KEY"
QueryNotes
scopemine (default): open approvals this key may answer. team: every approval in the organization; needs approvals:write.
statusall adds decided, expired and cancelled approvals. Default: open only.
contact_idApprovals about one record.
limit1 to 200. Default 50.
cursornext_cursor of the previous page.

Returns { "approvals": [ ... ], "next_cursor": "..." }. next_cursor is null on the last page. Approvals of test cases are not listed.

Retrieve an approval

curl https://api.hirefinn.ai/api/v1/approvals/5d2e8f1a-3b4c-4d6e-9f0a-1b2c3d4e5f6a \
  -H "Authorization: Bearer $FINN_API_KEY"

Approve or reject

curl -X POST https://api.hirefinn.ai/api/v1/approvals/5d2e8f1a-3b4c-4d6e-9f0a-1b2c3d4e5f6a/approve \
  -H "Authorization: Bearer $FINN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "comment": "Checked against the policy." }'
FieldTypeRequiredNotes
commentstringNoKept with the decision. Up to 2,000 characters.

approve picks the approval's own approve answer from allowed_answers (approve, approved, yes, accept or confirm, any case). reject picks reject, rejected, decline, declined, no or deny. If there is none, you get 422 no_such_answer with allowed_answers; use decide.

Returns the decided approval.

Decide with another answer

For approvals that offer a choice between options:

curl -X POST https://api.hirefinn.ai/api/v1/approvals/5d2e8f1a-3b4c-4d6e-9f0a-1b2c3d4e5f6a/decide \
  -H "Authorization: Bearer $FINN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "answer": "tuesday_slot", "comment": "Customer prefers Tuesday." }'
FieldTypeRequiredNotes
answerstringYesOne of allowed_answers.
commentstringNoUp to 2,000 characters.

Retries. Deciding is not repeatable: a second decision on the same approval returns 409 conflict. After a timeout, read the approval and check status before retrying.

Webhook

approval.requested is sent once when an approval opens. Read it with GET /approvals/{id} and decide it here. See webhook-events.

Errors

Errors raised by Finn use { "error", "message" }. Errors from Finn Operator use { "code", "message", "details" }. Read error ?? code.

StatusCodeWhen
400invalid_idThe ID isn't a UUID.
400answer_requireddecide without answer.
400invalidMalformed body or query.
403insufficient_scopeThe key lacks the scope.
403forbiddenThe approval isn't the key's to answer, it is a payment, the key predates scopes, or scope=team without approvals:write.
404not_foundNo such approval in your organization.
409conflictThe approval is no longer open.
422no_such_answerapprove or reject on an approval without that answer.
422invalidThe answer isn't in allowed_answers, the body carries an edited proposal, or the cursor is bad.
429rate_limitedOver the limit. Wait Retry-After seconds.
502, 503operator_unavailableFinn Operator is down or not set up here.