# Finero Public API > Finero syncs invoices from an ERP, generates hosted payment links, and tracks > the resulting payments. This API is the programmatic surface for external > systems and AI agents. It is **read-mostly**: everything is readable, and the > writes are deliberately few — create or deactivate a payment link, start an > ERP pull, and set a connection's pull cadence. Nothing else changes state. Base URL: https://api.getfinero.com/functions/v1/api API version: v1 ## Start here - [Full reference](https://app.getfinero.com/api-docs): endpoints, schemas, field-by-field descriptions, worked examples. - [OpenAPI v1 specification](https://api.getfinero.com/functions/v1/api/openapi.json): machine-readable, OpenAPI 3.0.3. Generate a client from this. Fetch the OpenAPI document first. It is authoritative for request/response schemas, enums and required fields; everything below is orientation. ## Authentication Every request needs a tenant API key as a bearer token: ``` Authorization: Bearer fnr_... ``` Keys are created by a workspace administrator in the Finero UI and shown once. A key belongs to exactly one workspace (tenant) and carries one permission: - `readonly` — the read operations below. - `admin` — the read operations plus the two writes. **Receiving a key.** You do not choose the permission — present both and let the operator decide: `readonly` reads every resource in the workspace; `admin` adds the only two writes, creating and deactivating payment links. Ask for the key to be entered into your credential store, **not typed into the conversation** — a key pasted into chat stays in that history; if one is, use it and tell the operator to revoke and replace it. Send it only in the `Authorization` header: never in a URL, a log line, or repeated back to the operator. The key determines the tenant. There is no tenant parameter, header or body field on any endpoint; supplying one does not change what you can reach. Sending no credential, a malformed one, or a revoked one returns the same generic 401 so key existence cannot be probed. ## Verify your access first ```bash curl -s https://api.getfinero.com/functions/v1/api/v1/me \ -H "Authorization: Bearer $FINERO_API_KEY" ``` Returns the workspace this key belongs to and its permission. Do this before anything else — it tells you whether writes are available without attempting one. ## Read operations - `GET /v1/me` — Identify the calling API key (operationId: `getApiContext`, key permission: `readonly`) - `GET /v1/invoices` — List invoices (operationId: `listInvoices`, key permission: `readonly`) - `GET /v1/invoices/{invoiceId}` — Get an invoice with its installments (operationId: `getInvoice`, key permission: `readonly`) - `GET /v1/payment-links` — List payment links (operationId: `listPaymentLinks`, key permission: `readonly`) - `GET /v1/payment-links/{paymentLinkId}` — Get a payment link (operationId: `getPaymentLink`, key permission: `readonly`) - `GET /v1/payments` — List confirmed payments (operationId: `listPayments`, key permission: `readonly`) - `GET /v1/payments/{paymentId}` — Get a payment (operationId: `getPayment`, key permission: `readonly`) - `GET /v1/erp-connections` — List ERP connections (operationId: `listErpConnections`, key permission: `readonly`) - `GET /v1/sync-runs` — List sync runs (operationId: `listSyncRuns`, key permission: `readonly`) - `GET /v1/sync-runs/{syncRunId}` — Get a sync run (operationId: `getSyncRun`, key permission: `readonly`) - `GET /v1/workflow-executions` — List workflow executions (operationId: `listWorkflowExecutions`, key permission: `readonly`) ## Write operations Only these two endpoints change state. Both require an `admin` key. - `POST /v1/payment-links` — Create a payment link for an installment (operationId: `createPaymentLink`, key permission: `admin`) - `POST /v1/payment-links/{paymentLinkId}/deactivate` — Deactivate a payment link (operationId: `deactivatePaymentLink`, key permission: `admin`) - `POST /v1/erp-connections/{connectionId}/sync` — Start a pull from the ERP (operationId: `startErpSync`, key permission: `admin`) - `POST /v1/erp-connections/{connectionId}/sync-schedule` — Set the automatic pull cadence (operationId: `setErpSyncSchedule`, key permission: `admin`) Creating a payment link is idempotent: send an `Idempotency-Key` header. The same key with the same body replays the original result; the same key with a different body returns `idempotency_conflict`. ## Deliberately NOT available Do not attempt these, and do not synthesise endpoints for them. Their absence is a deliberate scope decision, not an omission: - **Customers** — no customer resource. Customer name, number and contact appear as fields ON an invoice. - **Receipts** and **allocations** — no such resources, and no receipt is separately addressable. A settled payment is represented by `GET /v1/payments` — which also reports whether that payment has reached the ERP as a receipt (`erp_push_status`, `erp_receipt_number`, `erp_external_receipt_id`, `erp_pushed_at`, `erp_push_error_category`), so there is nothing to look up elsewhere. Allocation is the ERP's own decision: `posted` means the ERP accepted the receipt, `applied` means it also allocated it, and Finero never chooses which installment a receipt settles. - **Installments** — not separately addressable. They are returned nested inside `GET /v1/invoices/{invoiceId}`. - **Members, API keys, workspace settings, billing** — no endpoints. Managed in the Finero UI only. - **Creating, updating or deleting invoices** — the API never writes invoices. - **Connecting or configuring an ERP** — no endpoints. Adding a connection, credentials, which fields sync, and the invoice date floor are all managed in the Finero app. You CAN start a pull and change the pull cadence: `POST /v1/erp-connections/{connectionId}/sync` and `POST /v1/erp-connections/{connectionId}/sync-schedule`. - **Starting a pull on a sync RUN** — a run is a record of work, not a handle to it. `POST` to a sync-run path returns `405`; start pulls on the CONNECTION. If a task needs one of these, report it as unsupported rather than approximating it. ## Identifiers — four distinct kinds Mixing these up is the most common integration error. - **Workspace (tenant) id** — never appears in a request. It is implied by your API key and returned by `GET /v1/me`. - **Invoice id** (`invoice_id`) — a Finero UUID. Use it in paths and when creating a payment link. - **Installment id** (`installment_id`) — a Finero UUID, unique per installment within an invoice. A payment link is created against an installment, not an invoice. - **External provider ids** — `external_number` (the invoice number in the source ERP) and provider payment ids from the payment processor. These are **display and reconciliation values**. They are not unique across workspaces and must never be used as path parameters. Filter by them, do not address by them. ## Pagination Cursor-based, on every list endpoint. - `limit` — default 25, maximum 100. - Response carries `pagination.next_cursor`. Pass it as `cursor` for the next page. A `null` cursor means the last page. - Do not compute offsets or assume a total count. Loop until `next_cursor` is `null`. ## Limits - 120 requests/minute per key; 600 per workspace. - Request bodies are capped at 65536 bytes. - On `429`, honour the `Retry-After` response header. Do not retry faster. ## Errors Every error has the same shape: ```json { "error": { "code": "not_found", "message": "…", "request_id": "…" } } ``` Branch on `code`, never on `message` — messages may be reworded. Include `request_id` when reporting a problem. - `missing_credentials` — HTTP 401, not retryable. No Authorization: Bearer header was sent. - `invalid_credentials` — HTTP 401, not retryable. The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. - `revoked_credentials` — HTTP 401, not retryable. The presented key was revoked. Create a new key in Settings → API. - `feature_unavailable` — HTTP 403, not retryable. The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. - `permission_denied` — HTTP 403, not retryable. The key's permission does not allow this operation (readonly keys cannot call admin-only operations). - `not_found` — HTTP 404, not retryable. No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. - `validation_failed` — HTTP 400, not retryable. The request failed schema validation. See error.details for field messages. - `conflict` — HTTP 409, retryable. State conflict — e.g. an active payment link already exists for the installment, or a concurrent identical request is in flight. - `idempotency_key_required` — HTTP 400, not retryable. This operation requires the Idempotency-Key header (1–255 characters). - `idempotency_conflict` — HTTP 409, not retryable. The Idempotency-Key was already used with a DIFFERENT request body. Use a fresh key for a new request. - `rate_limit_exceeded` — HTTP 429, retryable. Too many requests. Honor the Retry-After header (seconds) before retrying. - `payload_too_large` — HTTP 413, not retryable. The request body exceeds the 64 KiB limit. - `method_not_allowed` — HTTP 405, not retryable. The path exists but not with this HTTP method. - `installment_not_collectible` — HTTP 422, not retryable. The installment cannot be collected right now — it is already paid, disputed, excluded from collections, has no outstanding balance, its invoice is not collection-ready, or its invoice is no longer present in your ERP. GET /v1/invoices/{invoiceId} reports the invoice's collection_status and each installment's collectible flag. - `integration_unavailable` — HTTP 422, not retryable. The tenant has no connected payments integration able to mint this link, or the one selected is not ready. Connect or repair a payment provider in the Finero app, then retry. - `sync_disabled` — HTTP 422, not retryable. Invoice sync is switched off for this ERP connection, so there is nothing to pull. A workspace administrator turns it back on in the Finero app; retrying will not change the answer. - `currency_not_supported` — HTTP 422, not retryable. The selected payment provider cannot collect the invoice's currency (single-currency providers can only charge in their store currency). Use a provider that supports it, or collect the invoice outside Finero. - `internal_error` — HTTP 500, retryable. Unexpected server error. Safe to retry with the same Idempotency-Key. ## Task recipes **List unpaid invoices.** `GET /v1/invoices?collection_ready=true`, then page until `next_cursor` is null. Each invoice carries `open_balance` and `currency`. **Create a payment link for an installment.** Read the invoice with `GET /v1/invoices/{invoiceId}` to get its installments, choose one, then `POST /v1/payment-links` with `{ "invoice_id": "…", "installment_id": "…" }` and an `Idempotency-Key` header. The response carries the hosted `url` to send to the payer. Requires an `admin` key. **Check whether an invoice was paid.** `GET /v1/payments?invoice_id=…`. A payment row is the authoritative record that money was collected; do not infer payment from an invoice's balance alone, which reflects the last ERP sync. **Confirm a link is still live.** `GET /v1/payment-links/{paymentLinkId}` and read `status`. Only `active` links are payable. **Stop a link being paid.** `POST /v1/payment-links/{paymentLinkId}/deactivate`. Idempotent — deactivating an already-inactive link succeeds. **Check sync freshness.** `GET /v1/sync-runs` shows when data last came from the ERP. Useful for deciding whether an `open_balance` is current. Read-only. ## Recovering from errors - `401` — the key is missing, malformed or revoked. Do not retry; get a new key. - `403` — the key is `readonly` and the operation writes. Do not retry. - `404` — the resource does not exist **or** belongs to another workspace; these are deliberately indistinguishable. Do not retry. - `409 conflict` — a live payment link already exists for that installment. Read it with `GET /v1/payment-links?installment_id=…` rather than creating another. - `422` — the installment cannot be collected (already paid, cancelled, or no payment provider connected). The `code` says which. Do not retry unchanged. - `429` — wait for `Retry-After`, then retry. - `5xx` — retry with backoff, reusing the same `Idempotency-Key` on writes.