Finero Tasks API
The Tasks API from Finero — 2 operation(s) for tasks.
The Tasks API from Finero — 2 operation(s) for tasks.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/getfinero-com:getfinero-com-tasks-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Finero Public Tasks API
version: 1.0.0
description: 'Tenant-scoped public API for Finero — AR / cash-flow automation. Authenticate every request with `Authorization: Bearer ` (keys are created in Settings → API and are tenant-owned). All data belongs to the key''s tenant; no request input can address another tenant. Two permissions exist: **admin** keys can call every operation; **readonly** keys only operations without side effects (`x-permission: readonly`). Lists are cursor-paginated (limit 1–100, follow pagination.next_cursor). Request bodies are capped at 64 KiB. Rate limits: 120/min per key and 600/min per tenant, in fixed windows of 60 s and 60 s. The per-tenant budget is ONE budget per workspace, shared by all its API keys AND its MCP agent connections (each agent connection has its own per-credential limit, the key''s). Heavy operations (`x-heavy: true`: summarizeAging, listCustomerAging, listAgingInstallments, summarizeInvoices) read the whole book on every call and also count against 10 per 10 seconds per tenant (60 a minute, in a short window so they cannot arrive in a large burst). EVERY response that reached the limiter carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (seconds until the window resets) and `RateLimit-Policy` (every quota that applies to the operation — the heavy one too on a heavy operation; static per operation), plus the same figures as the newer combined `RateLimit` field. Pace against them rather than waiting to be refused. The triple describes whichever quota is CLOSEST TO REFUSING (on a 429, the one that refused), so a key that is fine may still show little headroom when the tenant is busy. The counters are shared server-side, not per-instance, so the numbers are exact rather than approximate. Requests rejected BEFORE the limiter — unknown path, wrong method, oversized body, missing/invalid/revoked credentials, a workspace without API access — consume no quota and carry no rate-limit fields. 429 adds `Retry-After` in seconds (RFC 9110); waiting that long is enough, and retrying sooner is refused again. Missing and invalid credentials are limited separately per source IP; that quota is deliberately not published. The windows are FIXED rather than sliding, so a burst spanning a boundary can pass up to twice the limit before being refused — do not treat the limit as a hard ceiling over less than two windows. `RateLimit-Remaining` RISES when a window rolls, for the same reason; an increase is the quota refilling, not an error. Watch `RateLimit-Reset` jump upward to tell a roll from a miscount. A 503 is not the limiter: under extreme concurrency the platform sheds load before the request reaches Finero, so it carries neither rate-limit fields nor Retry-After. Back off and retry. Decimal money amounts are exact strings (never floats); *_minor amounts are integers in the currency''s minor unit. Timestamps are ISO 8601 UTC. **There is a second way in.** This API is for systems and unattended automation, authenticated by a tenant API key that acts as the WORKSPACE. If instead a person is present and connecting an AI assistant to their own workspace, use the Finero MCP server at `https://app.getfinero.com/mcp` — Model Context Protocol over the Streamable HTTP transport (JSON-RPC 2.0, `MCP-Protocol-Version` 2026-07-28, 2025-11-25), authorized with OAuth 2.1 so the session acts as THAT PERSON. Its `server.json` manifest — the MCP registry''s published format — is at `https://app.getfinero.com/mcp/server.json`. It is read-mostly — 24 tools (list_tasks, dismiss_task, summarize_aging, list_customer_aging, list_aging_installments, list_invoices, summarize_invoices, get_invoice, list_payment_integrations, list_payments, list_workflows, list_workflow_executions, list_erp_connections, list_sync_runs, get_sync_run, start_erp_sync, list_payment_links, get_payment_link, create_payment_links, list_scheduled_emails, update_scheduled_email, delete_scheduled_email, push_payment_receipts, deactivate_payment_link).'
servers:
- url: https://api.getfinero.com/functions/v1/api
security:
- apiKey: []
tags:
- name: Tasks
paths:
/v1/tasks:
get:
operationId: listTasks
summary: List tasks
description: 'Lists this workspace''s tasks, newest first. A task is work Finero asks a person to do on an invoice; today the one type is payment_arrangement: the Payment Arrangement workflow matched the invoice, so a person sets up its payment links. Filter state=open for the invoices still waiting for their first payment link; each row names its invoice (`invoice_number`). Cursor-paginated: pass `limit` (1–100) and send pagination.next_cursor back as `cursor` until has_more is false. To learn HOW MANY rows match without walking every page, pass `include_total=true` once and read pagination.total_count.
Available to **admin and readonly** API keys (no side effects).'
tags:
- Tasks
security:
- apiKey: []
x-permission: readonly
x-side-effects: false
parameters:
- name: limit
in: query
required: false
description: Page size, 1–100. Default 25.
schema:
type: integer
minimum: 1
maximum: 100
- name: cursor
in: query
required: false
description: Opaque pagination cursor from a previous response's pagination.next_cursor.
schema:
type: string
minLength: 1
maxLength: 400
- name: order
in: query
required: false
description: 'Sort direction. Ordering is ALWAYS by when the record was CREATED in Finero, never by when it changed — `updated_since` narrows the set but does not reorder it, so the most recently updated row is not necessarily first. Creation time is immutable, which is what lets the cursor stay stable: ordering by a value that changes would move a row mid-walk and make a page skip or repeat it. Default desc (newest first by creation time).'
schema:
type: string
enum:
- asc
- desc
- name: include_total
in: query
required: false
description: 'Return pagination.total_count — the number of rows matching the filters, ignoring paging. It is the SAME number on every page, including alongside a cursor. Off by default: an exact count scans the whole filtered set, so asking on each page pays repeatedly for an answer that does not change — ask on the first request and keep it.'
schema:
type: boolean
- name: invoice_id
in: query
required: false
description: Filter by invoice.
schema:
type: string
description: Optional invoice ID.
format: uuid
- name: state
in: query
required: false
description: Filter by task state.
schema:
type: string
enum:
- open
- configured
- dismissed
- not_needed
- name: type
in: query
required: false
description: Filter by task type.
schema:
type: string
enum:
- payment_arrangement
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
data:
type: array
description: Tasks, newest first.
items:
type: object
properties:
id:
type: string
description: Finero task ID.
format: uuid
type:
type: string
description: 'payment_arrangement: the Payment Arrangement workflow matched this invoice, so it needs payment links set up by a person.'
enum:
- payment_arrangement
invoice_id:
type: string
description: Invoice; use get_invoice to load its balances and collection targets.
format: uuid
invoice_number:
type:
- string
- 'null'
description: The invoice's human-readable number (local or ERP), so a person can be told which invoice without loading it; null when the invoice has none.
assignee_id:
type:
- string
- 'null'
description: The workspace member who was notified; null if that member was removed.
format: uuid
state:
type: string
description: 'open: no payment link yet. configured: the invoice got its first payment link (it stays configured unless dismissed; later links change nothing). dismissed: closed by a person or the API. not_needed: the invoice was retired or nothing is owed in the ERP. Auto Generate does not create links for an invoice with a task, and does not come back to it after a dismissal.'
enum:
- open
- configured
- dismissed
- not_needed
configured_at:
type:
- string
- 'null'
description: When the invoice got its first payment link.
format: date-time
dismissed_at:
type:
- string
- 'null'
description: When the task was dismissed.
format: date-time
created_at:
type: string
description: When a match opened the task.
format: date-time
required:
- id
- type
- invoice_id
- invoice_number
- assignee_id
- state
- configured_at
- dismissed_at
- created_at
additionalProperties: false
pagination:
type: object
properties:
next_cursor:
type:
- string
- 'null'
description: Opaque cursor for the next page — pass as ?cursor=. Null when there are no further results.
has_more:
type: boolean
description: Whether another page exists.
limit:
type: integer
description: The page size that was applied.
total_count:
type: integer
description: Total rows matching the filters, ignoring paging. Present ONLY when the request passed ?include_total=true.
required:
- next_cursor
- has_more
- limit
additionalProperties: false
required:
- data
- pagination
additionalProperties: false
example:
data:
- id: 0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b
type: payment_arrangement
invoice_id: 8f14e45f-ceea-4a5b-9d2c-167ce7de1a10
invoice_number: INV-1042
assignee_id: 5b0f6a7e-2f5d-4d24-9e6b-3c1a2b4d5e6f
state: open
configured_at: null
dismissed_at: null
created_at: '2026-09-29T12:00:00Z'
pagination:
next_cursor: null
has_more: false
limit: 25
'400':
description: '`invalid_cursor` — The cursor is malformed, was issued for a different query, or (aging) the report changed. Restart without cursor, keeping the filters identical. `validation_failed` — The request failed schema validation. See error.details for field messages.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: '`invalid_credentials` — The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. `missing_credentials` — No Authorization: Bearer header was sent. `revoked_credentials` — The presented key was revoked. Create a new key in Settings → API.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: '`feature_unavailable` — The tenant''s tier does not currently include API access. Feature rollout flags cannot grant this entitlement. `permission_denied` — The key''s permission does not allow this operation (readonly keys cannot call admin-only operations).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: '`not_found` — No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: '`rate_limit_exceeded` — Too many requests — for this key, for the workspace (one budget shared by all its API keys and MCP agent connections), or, on a heavy operation, for the workspace''s heavy budget. Honor the Retry-After header (seconds) before retrying — waiting that long is enough. The RateLimit-* fields on every response say how much headroom is left before this happens; pace against them instead of retrying into a refusal.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: '`internal_error` — Unexpected server error. Safe to retry with the same Idempotency-Key.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/v1/tasks/{taskId}/dismiss:
post:
operationId: dismissTask
summary: Dismiss a task
description: 'Dismisses the task: it closes, its unsent notices are cancelled, and nothing else changes. Auto Generate acts on an invoice only when it first arrives, so it does not come back to this one; its payment links are still created by a person. Existing links and payments are not changed. A dismissed task never comes back, so obtain the user''s approval before calling. Dismissing a dismissed task changes nothing.
Requires an **admin** API key (has side effects).'
tags:
- Tasks
security:
- apiKey: []
x-permission: admin
x-side-effects: true
parameters:
- name: taskId
in: path
required: true
description: Task ID.
schema:
type: string
description: Task ID.
format: uuid
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
required:
- ok
additionalProperties: false
example:
ok: true
'400':
description: '`validation_failed` — The request failed schema validation. See error.details for field messages.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: '`invalid_credentials` — The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. `missing_credentials` — No Authorization: Bearer header was sent. `revoked_credentials` — The presented key was revoked. Create a new key in Settings → API.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: '`feature_unavailable` — The tenant''s tier does not currently include API access. Feature rollout flags cannot grant this entitlement. `permission_denied` — The key''s permission does not allow this operation (readonly keys cannot call admin-only operations).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: '`not_found` — No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: '`conflict` — A transient state conflict: an identical request is still in flight, a scheduled email is being sent, or a sync is already running. Retry after a short wait (honour Retry-After when present).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'413':
description: '`payload_too_large` — The request body exceeds the 64 KiB limit.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: '`rate_limit_exceeded` — Too many requests — for this key, for the workspace (one budget shared by all its API keys and MCP agent connections), or, on a heavy operation, for the workspace''s heavy budget. Honor the Retry-After header (seconds) before retrying — waiting that long is enough. The RateLimit-* fields on every response say how much headroom is left before this happens; pace against them instead of retrying into a refusal.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: '`internal_error` — Unexpected server error. Safe to retry with the same Idempotency-Key.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Error:
type: object
required:
- error
additionalProperties: false
properties:
error:
type: object
required:
- code
- message
- request_id
additionalProperties: false
properties:
code:
type: string
description: Stable machine-readable error code.
enum:
- missing_credentials
- invalid_credentials
- revoked_credentials
- feature_unavailable
- permission_denied
- not_found
- invalid_cursor
- validation_failed
- conflict
- link_already_covered
- link_not_withdrawable
- payment_link_deactivated
- idempotency_key_required
- idempotency_conflict
- rate_limit_exceeded
- payload_too_large
- method_not_allowed
- installment_not_collectible
- amount_exceeds_available
- sync_disabled
- connection_disconnected
- integration_unavailable
- currency_not_supported
- internal_error
message:
type: string
description: Human-readable explanation.
request_id:
type: string
description: Echo of X-Request-Id — include it when reporting problems.
details:
type: array
description: Field-level validation messages (validation_failed only).
items:
type: object
required:
- field
- message
properties:
field:
type: string
message:
type: string
securitySchemes:
apiKey:
type: http
scheme: bearer
description: 'A Finero API key (`fnr_…`), created by a tenant admin in Settings → API. Sent as `Authorization: Bearer fnr_…`. Never place keys in URLs.'
x-permissions:
supported:
- admin
- readonly
operationField: x-permission
description: 'Each operation declares the LEAST key permission that may call it in `x-permission`. An `admin` key satisfies every operation; a `readonly` key satisfies only operations whose `x-permission` is `readonly`. The router enforces this from the same classification this document is generated from — the check is never inferred from the HTTP method. These are key PERMISSIONS, not OAuth scopes: this scheme is HTTP bearer, and OpenAPI declares `scopes` only on `oauth2`/`openIdConnect` schemes. Finero''s OAuth surface is the MCP server, which publishes RFC 9728 protected-resource metadata at `https://app.getfinero.com/mcp/.well-known/oauth-protected-resource`. Access there is granted on a consent screen, where a person lets the agent act as them, and every call is checked against that person''s own permissions — not by OAuth scope values; the authorization server accepts only `openid`.'
externalDocs:
description: Agent-facing orientation, including the MCP server and how to connect one.
url: https://app.getfinero.com/llms.txt