Finero public API
A versioned, tenant-scoped REST API for Finero — invoices, payment links, confirmed payments, ERP sync, and automation history. Built for external systems, automation platforms, and AI agents. All requests and responses are JSON. Timestamps are ISO 8601 UTC. Decimal money amounts are exact strings (never floats); *_minor amounts are integers in the currency's minor unit with an explicit ISO 4217 currency.
Base URL
https://api.getfinero.com/functions/v1/apiYour first call
A workspace admin creates an API key in Settings → API (the full key is shown once — store it safely). Then confirm it works — this read is safe for both key types and returns your workspace context:
curl "https://api.getfinero.com/functions/v1/api/v1/me" \
-H "Authorization: Bearer $FINERO_API_KEY"Versioning. All paths live under /v1. Changes within v1 are additive and backward-compatible (new fields, new operations). A breaking change would ship as /v2 — existing v1 behavior is never silently changed. The machine-readable spec lives at https://api.getfinero.com/functions/v1/api/openapi.json.
Authentication
Every request needs a Finero API key, created by a workspace admin in Settings → API. Keys are tenant-owned: all data access is scoped to the key's workspace, and nothing in a request can address another workspace. Send the key as a bearer token:
curl "https://api.getfinero.com/functions/v1/api/v1/me" \
-H "Authorization: Bearer fnr_0123456789abcdef_your43charSecretPart..."- Keep keys in a secrets manager. Never commit them, never put them in URLs, client code, or logs.
- The full key is shown exactly once at creation and can never be retrieved.
- To rotate: create a new key, switch your integration, then revoke the old key (revocation is immediate).
- Authentication failures return a generic 401 — the API never confirms whether a key identifier exists.
Permissions
A key has exactly one permission, chosen at creation:
- Read-only — may call only operations guaranteed to have no side effects (marked Admin or Read-only key below). It can never create, change, trigger, or send anything.
- Admin — everything Read-only can, plus the small set of deliberately exposed mutations (marked Admin key required): creating and deactivating payment links. An Admin key does not get write access to other resources — invoices, installments, payments, connections, sync runs, and workflow history stay read-only for every key.
Enforcement is per-operation (each operation is classified in the spec via x-permission), not by HTTP method. Calling an admin-only operation with a read-only key returns 403 permission_denied.
Pagination, filtering & sorting
List endpoints return { data: [...], pagination: { next_cursor, has_more, limit } }. Pass ?limit= (1–100, default 25) and follow pagination.next_cursor via ?cursor= until has_more is false. Cursors are opaque — don't parse or construct them. Results are ordered by creation time (newest first by default; ?order=asc for oldest first).
Filters are per-endpoint allowlists (documented on each endpoint) applied as exact matches or since-timestamps. Unknown query parameters are rejected with 400 validation_failed — there is no generic column filtering.
Idempotency
Operations marked Idempotency-Key required must send a unique Idempotency-Key header (1–255 printable ASCII characters, e.g. a UUID). Retrying with the same key and same body returns the original result (with Idempotency-Replayed: true) instead of executing again — safe for network retries. Reusing a key with a different body returns 409 idempotency_conflict. Keys expire after 24 hours. Retry guidance for agents: on a timeout or 5xx, retry the identical request with the identical key; on 4xx, fix the request and use a fresh key.
Rate limits
- 120 requests/minute per API key
- 600 requests/minute per workspace (all its keys)
- Repeated failed authentication is limited per source address
Exceeding a limit returns 429 rate_limit_exceeded with a Retry-After header (seconds). Request bodies are capped at 64 KiB (413 payload_too_large).
End-to-end examples
A common flow: find a collectible invoice, read its installments, create a payment link for one installment, then poll its status. All data is fictional; replace $FINERO_API_KEY and the ids with real values from earlier responses.
1. List invoices (Admin or Read-only key, no side effects)
List invoices (add ?collection_ready=true to find collectible ones).
curl "https://api.getfinero.com/functions/v1/api/v1/invoices" \
-H "Authorization: Bearer $FINERO_API_KEY"2. Get an invoice with its installments (Admin or Read-only key, no side effects)
Read one invoice with its installments; take a collectible installment's id for the next step.
curl "https://api.getfinero.com/functions/v1/api/v1/invoices/<invoiceId>" \
-H "Authorization: Bearer $FINERO_API_KEY"3. Create a payment link for an installment (Admin key, has side effects)
Create a hosted payment link for that installment. The response's url is the customer-facing checkout page.
curl -X POST "https://api.getfinero.com/functions/v1/api/v1/payment-links" \
-H "Authorization: Bearer $FINERO_API_KEY" \
-H "Idempotency-Key: <unique-key>" \
-H "Content-Type: application/json" \
-d '{"invoice_id":"8f14e45f-ceea-4a5b-9d2c-167ce7de1a10","installment_id":"a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70"}'4. Get a payment link (Admin or Read-only key, no side effects)
Poll the link until its status is paid.
curl "https://api.getfinero.com/functions/v1/api/v1/payment-links/<paymentLinkId>" \
-H "Authorization: Bearer $FINERO_API_KEY"Step 3 is the only mutation and requires an Idempotency-Key: if the response is lost, retry with the same key to get the original link back instead of creating a second one. There is no standalone customers resource — customer display fields live on the invoice, and confirmed payments are read via /v1/payments.
Errors
Errors share one shape. request_id echoes the X-Request-Id response header — include it when reporting a problem. Validation errors add a details array of { field, message }.
{
"error": {
"code": "permission_denied",
"message": "The API key does not have permission to perform this operation.",
"request_id": "req_2f7c1a9b3d5e4f60718293a4"
}
}| Code | Status | Retry? | Meaning |
|---|---|---|---|
| missing_credentials | 401 | fix request | No Authorization: Bearer header was sent. |
| invalid_credentials | 401 | fix request | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. |
| revoked_credentials | 401 | fix request | The presented key was revoked. Create a new key in Settings → API. |
| feature_unavailable | 403 | fix request | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. |
| permission_denied | 403 | fix request | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). |
| not_found | 404 | fix request | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. |
| validation_failed | 400 | fix request | The request failed schema validation. See error.details for field messages. |
| conflict | 409 | retry | State conflict — e.g. an active payment link already exists for the installment, a sync is already running, or a concurrent identical request is in flight. |
| idempotency_key_required | 400 | fix request | This operation requires the Idempotency-Key header (1–255 characters). |
| idempotency_conflict | 409 | fix request | The Idempotency-Key was already used with a DIFFERENT request body. Use a fresh key for a new request. |
| rate_limit_exceeded | 429 | retry | Too many requests. Honor the Retry-After header (seconds) before retrying. |
| payload_too_large | 413 | fix request | The request body exceeds the 64 KiB limit. |
| method_not_allowed | 405 | fix request | The path exists but not with this HTTP method. |
| installment_not_collectible | 422 | fix request | The installment cannot be collected right now — it is already paid, disputed, excluded from collections, has no outstanding balance, or its invoice is not collection-ready. GET /v1/invoices/{invoiceId} reports each installment's payment_state, collectible flag, and block reasons. |
| integration_unavailable | 422 | fix request | 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. |
| currency_not_supported | 422 | fix request | 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 | 500 | retry | Unexpected server error. Safe to retry with the same Idempotency-Key. |
Endpoint reference
All examples use fictional data. Replace path placeholders like <invoiceId> with real ids from list responses.
API context
/v1/meIdentify the calling API key
Returns the tenant and key metadata for the presented credential. Useful as a connectivity/permission check before other calls. No side effects.
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/me" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"tenant_id": "5b0f6a7e-2f5d-4d24-9e6b-3c1a2b4d5e6f",
"tenant_name": "Acme Industries",
"api_key": {
"id": "4d3c2b1a-0f9e-48d7-b6c5-a4b3c2d1e0f9",
"name": "Zapier integration",
"permission": "readonly",
"created_at": "2026-07-01T08:00:00+00:00",
"last_used_at": "2026-07-14T06:59:31+00:00"
}
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| tenant_id | uuid | yes | The tenant this API key belongs to. All data is scoped to it. |
| tenant_name | string | yes | Tenant display name. |
| api_key.id | uuid | yes | API key id. |
| api_key.name | string | yes | Key name given at creation. |
| api_key.permission | "admin" | "readonly" | yes | admin can call every operation; readonly only side-effect-free ones. |
| api_key.created_at | date-time | yes | Key creation time (ISO 8601 UTC). |
| api_key.last_used_at | date-time | null | yes | Previous use of this key (ISO 8601 UTC). |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
Invoices
/v1/invoicesList invoices
Lists the tenant's synced invoices, newest first. Cursor-paginated: pass ?limit= (1–100) and follow pagination.next_cursor until has_more is false. Filters are allowlisted; combine freely.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| limit (query) | integer | no | Page size, 1–100. Default 25. |
| cursor (query) | string | no | Opaque pagination cursor from a previous response's pagination.next_cursor. |
| order (query) | "asc" | "desc" | no | Sort direction by creation time. Default desc (newest first). |
| finalization_state (query) | "draft" | "final" | "cancelled" | "unknown" | no | Filter by the ERP document-lifecycle state (final/draft/cancelled/unknown). |
| collection_ready (query) | boolean | no | Filter by collection readiness. |
| currency (query) | string | no | Filter by ISO 4217 currency code. |
| invoice_number (query) | string | no | Exact-match filter on the ERP invoice number. |
| connection_id (query) | uuid | no | Filter by ERP connection. |
| updated_since (query) | date-time | no | Only invoices updated at or after this ISO 8601 timestamp. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/invoices" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"data": [
{
"id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"invoice_number": "INV-10421",
"finalization_state": "final",
"currency": "USD",
"total_amount": "1250.0000",
"open_balance": "1250.0000",
"invoice_date": "2026-07-01",
"due_date": "2026-08-01",
"collection_ready": true,
"customer": {
"number": "CUST-2201",
"name": "Acme Industries Ltd",
"email": "[email protected]"
},
"origin": "erp",
"connection_id": "1a2b3c4d-5e6f-4a80-91b2-c3d4e5f60718",
"created_at": "2026-07-01T09:15:00+00:00",
"updated_at": "2026-07-10T06:30:00+00:00"
}
],
"pagination": {
"next_cursor": null,
"has_more": false,
"limit": 25
}
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| data | array of object | yes | Invoices, newest first. |
| data[].id | uuid | yes | Invoice id (stable Finero identifier). |
| data[].invoice_number | string | null | yes | Human-readable local or ERP invoice number. |
| data[].finalization_state | "draft" | "final" | "cancelled" | "unknown" | yes | ERP document-lifecycle state: final = finalized in the ERP, draft = not yet finalized, cancelled = voided in the ERP, unknown = unmappable source value. Only final invoices are collectible. Not a payment state. |
| data[].currency | string | null | yes | Invoice currency. ISO 4217 alphabetic code, e.g. "USD". |
| data[].total_amount | string | null | yes | Invoice total. Decimal amount as a string (exact, 4 dp) — never parse as float for arithmetic. |
| data[].open_balance | string | null | yes | Outstanding balance across the invoice. Decimal amount as a string (exact, 4 dp) — never parse as float for arithmetic. |
| data[].invoice_date | date | null | yes | Invoice date (YYYY-MM-DD). |
| data[].due_date | date | null | yes | Invoice due date (YYYY-MM-DD). |
| data[].collection_ready | boolean | yes | Whether the invoice is eligible for payment-link collection (authoritative readiness flag). |
| data[].customer.number | string | null | yes | Bill-to customer number from the ERP. |
| data[].customer.name | string | null | yes | Bill-to customer name. |
| data[].customer.email | string | null | yes | Billing email the payment-link email is sent to. |
| data[].origin | "local" | "erp" | yes | Provenance / system of record: local = the invoice exists only in Finero; erp = an ERP connection owns (or will own) the record. |
| data[].connection_id | uuid | null | yes | The exact ERP connection associated with this invoice. |
| data[].created_at | date-time | yes | Creation time (ISO 8601 UTC). |
| data[].updated_at | date-time | yes | Last update time (ISO 8601 UTC). |
| pagination.next_cursor | string | null | yes | Opaque cursor for the next page — pass as ?cursor=. Null when there are no further results. |
| pagination.has_more | boolean | yes | Whether another page exists. |
| pagination.limit | integer | yes | The page size that was applied. |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
/v1/invoices/{invoiceId}Get an invoice with its installments
Returns one invoice including its installments (ordered by sequence). Use an installment's id to create a payment link. Returns not_found for ids outside your tenant.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| invoiceId (path) | uuid | yes | Invoice id. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/invoices/<invoiceId>" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"invoice_number": "INV-10421",
"finalization_state": "final",
"currency": "USD",
"total_amount": "1250.0000",
"open_balance": "1250.0000",
"invoice_date": "2026-07-01",
"due_date": "2026-08-01",
"collection_ready": true,
"customer": {
"number": "CUST-2201",
"name": "Acme Industries Ltd",
"email": "[email protected]"
},
"origin": "erp",
"connection_id": "1a2b3c4d-5e6f-4a80-91b2-c3d4e5f60718",
"created_at": "2026-07-01T09:15:00+00:00",
"updated_at": "2026-07-10T06:30:00+00:00",
"installments": [
{
"id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70",
"sequence": 1,
"original_amount": "1250.0000",
"outstanding_amount": "1250.0000",
"disputed_amount": "0.0000",
"due_date": "2026-08-01",
"source_status": "OPEN",
"payment_state": "open",
"collectible": true,
"excluded_from_collections": false,
"created_at": "2026-07-01T09:15:00+00:00",
"updated_at": "2026-07-10T06:30:00+00:00"
}
]
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | uuid | yes | Invoice id (stable Finero identifier). |
| invoice_number | string | null | yes | Human-readable local or ERP invoice number. |
| finalization_state | "draft" | "final" | "cancelled" | "unknown" | yes | ERP document-lifecycle state: final = finalized in the ERP, draft = not yet finalized, cancelled = voided in the ERP, unknown = unmappable source value. Only final invoices are collectible. Not a payment state. |
| currency | string | null | yes | Invoice currency. ISO 4217 alphabetic code, e.g. "USD". |
| total_amount | string | null | yes | Invoice total. Decimal amount as a string (exact, 4 dp) — never parse as float for arithmetic. |
| open_balance | string | null | yes | Outstanding balance across the invoice. Decimal amount as a string (exact, 4 dp) — never parse as float for arithmetic. |
| invoice_date | date | null | yes | Invoice date (YYYY-MM-DD). |
| due_date | date | null | yes | Invoice due date (YYYY-MM-DD). |
| collection_ready | boolean | yes | Whether the invoice is eligible for payment-link collection (authoritative readiness flag). |
| customer.number | string | null | yes | Bill-to customer number from the ERP. |
| customer.name | string | null | yes | Bill-to customer name. |
| customer.email | string | null | yes | Billing email the payment-link email is sent to. |
| origin | "local" | "erp" | yes | Provenance / system of record: local = the invoice exists only in Finero; erp = an ERP connection owns (or will own) the record. |
| connection_id | uuid | null | yes | The exact ERP connection associated with this invoice. |
| created_at | date-time | yes | Creation time (ISO 8601 UTC). |
| updated_at | date-time | yes | Last update time (ISO 8601 UTC). |
| installments | array of object | yes | The invoice's installments, ordered by sequence. |
| installments[].id | uuid | yes | Installment id — use as installment_id when creating a payment link. |
| installments[].sequence | integer | yes | 1-based installment sequence within the invoice. |
| installments[].original_amount | string | yes | Original installment amount. Decimal amount as a string (exact, 4 dp) — never parse as float for arithmetic. |
| installments[].outstanding_amount | string | yes | Outstanding (still payable) amount. Decimal amount as a string (exact, 4 dp) — never parse as float for arithmetic. |
| installments[].disputed_amount | string | yes | Amount under dispute (blocks collection when > 0). Decimal amount as a string (exact, 4 dp) — never parse as float for arithmetic. |
| installments[].due_date | date | null | yes | Installment due date (YYYY-MM-DD). |
| installments[].source_status | string | yes | Raw installment status from the ERP. |
| installments[].payment_state | "paid" | "open" | "inactive" | yes | Whether the money is in: `paid` once the installment is settled (in the ERP, or by a Finero payment the ERP has not synced back yet); `inactive` when payment is no longer expected because the receivable ended — cancelled, voided, credited or written off — so do NOT count it as revenue; otherwise `open`. Note `inactive` is about the MONEY and is distinct from the invoice's `finalization_state: cancelled`, which is the ERP document's own lifecycle. Independent of `collectible` — an open installment can still be uncollectible, and a due date being in the past does not change this field. |
| installments[].collectible | boolean | yes | Whether Finero considers this installment collectible right now. False whenever payment_state is `paid`, and also for open installments blocked by a dispute, an exclusion, a non-final invoice, or a missing billing email. |
| installments[].excluded_from_collections | boolean | yes | Manually excluded from collections. |
| installments[].created_at | date-time | yes | Creation time (ISO 8601 UTC). |
| installments[].updated_at | date-time | yes | Last update time (ISO 8601 UTC). |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
Payment links
/v1/payment-linksList payment links
Lists the tenant's payment links, newest first. Cursor-paginated: pass ?limit= (1–100) and follow pagination.next_cursor until has_more is false. The url field is the live hosted checkout page.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| limit (query) | integer | no | Page size, 1–100. Default 25. |
| cursor (query) | string | no | Opaque pagination cursor from a previous response's pagination.next_cursor. |
| order (query) | "asc" | "desc" | no | Sort direction by creation time. Default desc (newest first). |
| status (query) | "active" | "processing" | "paid" | "inactive" | "needs_replacement" | "action_required" | no | Filter by lifecycle status. |
| invoice_id (query) | uuid | no | Filter by invoice. |
| installment_id (query) | uuid | no | Filter by installment. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/payment-links" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"data": [
{
"id": "0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b",
"invoice_id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"installment_id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70",
"status": "active",
"provider": "stripe",
"mode": "live",
"amount_minor": 125000,
"currency": "USD",
"url": "https://app.getfinero.com/pay/EXAMPLEtokenEXAMPLEtokenEXAMPLEtokenEXAMPL",
"paid_at": null,
"paid_amount_minor": null,
"paid_currency": null,
"deactivated_at": null,
"created_at": "2026-07-10T06:31:00+00:00",
"updated_at": "2026-07-10T06:31:00+00:00"
}
],
"pagination": {
"next_cursor": null,
"has_more": false,
"limit": 25
}
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| data | array of object | yes | Payment links, newest first. |
| data[].id | uuid | yes | Payment link id. |
| data[].invoice_id | uuid | yes | Invoice the link collects. |
| data[].installment_id | uuid | yes | Installment the link collects (one active link per installment). |
| data[].status | "active" | "processing" | "paid" | "inactive" | "needs_replacement" | "action_required" | yes | Lifecycle status. active = payable now; processing = a payment is being confirmed; paid = settled; inactive = deactivated. |
| data[].provider | "stripe" | "wix" | yes | Payment provider hosting the checkout. |
| data[].mode | "test" | "live" | yes | Provider environment. |
| data[].amount_minor | integer | yes | Amount the link collects. Integer amount in the currency's MINOR unit (e.g. cents). |
| data[].currency | string | yes | Link currency. ISO 4217 alphabetic code, e.g. "USD". |
| data[].url | string | null | yes | Hosted customer-facing pay-page URL. Treat as a capability: anyone with the URL can open the checkout. |
| data[].paid_at | date-time | null | yes | When the link was paid (ISO 8601 UTC). |
| data[].paid_amount_minor | integer | null | yes | Settled amount. Integer amount in the currency's MINOR unit (e.g. cents). |
| data[].paid_currency | string | null | yes | Settled currency. ISO 4217 alphabetic code, e.g. "USD". |
| data[].deactivated_at | date-time | null | yes | When the link was deactivated (ISO 8601 UTC). |
| data[].created_at | date-time | yes | Creation time (ISO 8601 UTC). |
| data[].updated_at | date-time | yes | Last update time (ISO 8601 UTC). |
| pagination.next_cursor | string | null | yes | Opaque cursor for the next page — pass as ?cursor=. Null when there are no further results. |
| pagination.has_more | boolean | yes | Whether another page exists. |
| pagination.limit | integer | yes | The page size that was applied. |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
/v1/payment-links/{paymentLinkId}Get a payment link
Returns one payment link, including its hosted checkout URL and settlement state.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| paymentLinkId (path) | uuid | yes | Payment link id. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/payment-links/<paymentLinkId>" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"id": "0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b",
"invoice_id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"installment_id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70",
"status": "active",
"provider": "stripe",
"mode": "live",
"amount_minor": 125000,
"currency": "USD",
"url": "https://app.getfinero.com/pay/EXAMPLEtokenEXAMPLEtokenEXAMPLEtokenEXAMPL",
"paid_at": null,
"paid_amount_minor": null,
"paid_currency": null,
"deactivated_at": null,
"created_at": "2026-07-10T06:31:00+00:00",
"updated_at": "2026-07-10T06:31:00+00:00"
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | uuid | yes | Payment link id. |
| invoice_id | uuid | yes | Invoice the link collects. |
| installment_id | uuid | yes | Installment the link collects (one active link per installment). |
| status | "active" | "processing" | "paid" | "inactive" | "needs_replacement" | "action_required" | yes | Lifecycle status. active = payable now; processing = a payment is being confirmed; paid = settled; inactive = deactivated. |
| provider | "stripe" | "wix" | yes | Payment provider hosting the checkout. |
| mode | "test" | "live" | yes | Provider environment. |
| amount_minor | integer | yes | Amount the link collects. Integer amount in the currency's MINOR unit (e.g. cents). |
| currency | string | yes | Link currency. ISO 4217 alphabetic code, e.g. "USD". |
| url | string | null | yes | Hosted customer-facing pay-page URL. Treat as a capability: anyone with the URL can open the checkout. |
| paid_at | date-time | null | yes | When the link was paid (ISO 8601 UTC). |
| paid_amount_minor | integer | null | yes | Settled amount. Integer amount in the currency's MINOR unit (e.g. cents). |
| paid_currency | string | null | yes | Settled currency. ISO 4217 alphabetic code, e.g. "USD". |
| deactivated_at | date-time | null | yes | When the link was deactivated (ISO 8601 UTC). |
| created_at | date-time | yes | Creation time (ISO 8601 UTC). |
| updated_at | date-time | yes | Last update time (ISO 8601 UTC). |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
/v1/payment-linksCreate a payment link for an installment
Creates a hosted payment link for one collectible installment through the tenant's connected payment provider — the same validation and creation path the Finero app uses. At most ONE active link can exist per installment: a duplicate attempt returns 409 conflict. Requires the Idempotency-Key header; retrying with the same key and body returns the original result. If the tenant's email-notification workflow is enabled, the customer payment-link email is sent as a consequence.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| invoice_id | uuid | yes | Invoice to collect. |
| installment_id | uuid | yes | Installment to collect (from GET /v1/invoices/{invoiceId}). |
| integration_id | uuid | no | Optional: a specific connected payments integration. Defaults to the tenant's connected provider. |
Example request
curl -X POST "https://api.getfinero.com/functions/v1/api/v1/payment-links" \
-H "Authorization: Bearer $FINERO_API_KEY" \
-H "Idempotency-Key: <unique-key>" \
-H "Content-Type: application/json" \
-d '{"invoice_id":"8f14e45f-ceea-4a5b-9d2c-167ce7de1a10","installment_id":"a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70"}'Request body example
{
"invoice_id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"installment_id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70"
}Success response (201)
{
"id": "0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b",
"invoice_id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"installment_id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70",
"status": "active",
"provider": "stripe",
"mode": "live",
"amount_minor": 125000,
"currency": "USD",
"url": "https://app.getfinero.com/pay/EXAMPLEtokenEXAMPLEtokenEXAMPLEtokenEXAMPL",
"paid_at": null,
"paid_amount_minor": null,
"paid_currency": null,
"deactivated_at": null,
"created_at": "2026-07-10T06:31:00+00:00",
"updated_at": "2026-07-10T06:31:00+00:00"
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | uuid | yes | Payment link id. |
| invoice_id | uuid | yes | Invoice the link collects. |
| installment_id | uuid | yes | Installment the link collects (one active link per installment). |
| status | "active" | "processing" | "paid" | "inactive" | "needs_replacement" | "action_required" | yes | Lifecycle status. active = payable now; processing = a payment is being confirmed; paid = settled; inactive = deactivated. |
| provider | "stripe" | "wix" | yes | Payment provider hosting the checkout. |
| mode | "test" | "live" | yes | Provider environment. |
| amount_minor | integer | yes | Amount the link collects. Integer amount in the currency's MINOR unit (e.g. cents). |
| currency | string | yes | Link currency. ISO 4217 alphabetic code, e.g. "USD". |
| url | string | null | yes | Hosted customer-facing pay-page URL. Treat as a capability: anyone with the URL can open the checkout. |
| paid_at | date-time | null | yes | When the link was paid (ISO 8601 UTC). |
| paid_amount_minor | integer | null | yes | Settled amount. Integer amount in the currency's MINOR unit (e.g. cents). |
| paid_currency | string | null | yes | Settled currency. ISO 4217 alphabetic code, e.g. "USD". |
| deactivated_at | date-time | null | yes | When the link was deactivated (ISO 8601 UTC). |
| created_at | date-time | yes | Creation time (ISO 8601 UTC). |
| updated_at | date-time | yes | Last update time (ISO 8601 UTC). |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | idempotency_key_required | This operation requires the Idempotency-Key header (1–255 characters). → fix the request |
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 409 | conflict | State conflict — e.g. an active payment link already exists for the installment, a sync is already running, or a concurrent identical request is in flight. → retryable |
| 409 | idempotency_conflict | The Idempotency-Key was already used with a DIFFERENT request body. Use a fresh key for a new request. → fix the request |
| 413 | payload_too_large | The request body exceeds the 64 KiB limit. → fix the request |
| 422 | currency_not_supported | 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. → fix the request |
| 422 | installment_not_collectible | The installment cannot be collected right now — it is already paid, disputed, excluded from collections, has no outstanding balance, or its invoice is not collection-ready. GET /v1/invoices/{invoiceId} reports each installment's payment_state, collectible flag, and block reasons. → fix the request |
| 422 | integration_unavailable | 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. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
/v1/payment-links/{paymentLinkId}/deactivateDeactivate a payment link
Immediately deactivates an active payment link so it can no longer be paid. Naturally idempotent: deactivating an already-inactive link succeeds without change. A paid link cannot be deactivated (409 conflict).
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| paymentLinkId (path) | uuid | yes | Payment link id. |
Example request
curl -X POST "https://api.getfinero.com/functions/v1/api/v1/payment-links/<paymentLinkId>/deactivate" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"id": "0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b",
"invoice_id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"installment_id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70",
"status": "inactive",
"provider": "stripe",
"mode": "live",
"amount_minor": 125000,
"currency": "USD",
"url": null,
"paid_at": null,
"paid_amount_minor": null,
"paid_currency": null,
"deactivated_at": "2026-07-14T07:10:00+00:00",
"created_at": "2026-07-10T06:31:00+00:00",
"updated_at": "2026-07-10T06:31:00+00:00"
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | uuid | yes | Payment link id. |
| invoice_id | uuid | yes | Invoice the link collects. |
| installment_id | uuid | yes | Installment the link collects (one active link per installment). |
| status | "active" | "processing" | "paid" | "inactive" | "needs_replacement" | "action_required" | yes | Lifecycle status. active = payable now; processing = a payment is being confirmed; paid = settled; inactive = deactivated. |
| provider | "stripe" | "wix" | yes | Payment provider hosting the checkout. |
| mode | "test" | "live" | yes | Provider environment. |
| amount_minor | integer | yes | Amount the link collects. Integer amount in the currency's MINOR unit (e.g. cents). |
| currency | string | yes | Link currency. ISO 4217 alphabetic code, e.g. "USD". |
| url | string | null | yes | Hosted customer-facing pay-page URL. Treat as a capability: anyone with the URL can open the checkout. |
| paid_at | date-time | null | yes | When the link was paid (ISO 8601 UTC). |
| paid_amount_minor | integer | null | yes | Settled amount. Integer amount in the currency's MINOR unit (e.g. cents). |
| paid_currency | string | null | yes | Settled currency. ISO 4217 alphabetic code, e.g. "USD". |
| deactivated_at | date-time | null | yes | When the link was deactivated (ISO 8601 UTC). |
| created_at | date-time | yes | Creation time (ISO 8601 UTC). |
| updated_at | date-time | yes | Last update time (ISO 8601 UTC). |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 409 | conflict | State conflict — e.g. an active payment link already exists for the installment, a sync is already running, or a concurrent identical request is in flight. → retryable |
| 413 | payload_too_large | The request body exceeds the 64 KiB limit. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
Payments
/v1/paymentsList confirmed payments
Lists confirmed payments, newest first. A payment appears here only after the payment provider verifies settlement; payments cannot be created through the API. Cursor-paginated: pass ?limit= (1–100) and follow pagination.next_cursor until has_more is false.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| limit (query) | integer | no | Page size, 1–100. Default 25. |
| cursor (query) | string | no | Opaque pagination cursor from a previous response's pagination.next_cursor. |
| order (query) | "asc" | "desc" | no | Sort direction by creation time. Default desc (newest first). |
| invoice_id (query) | uuid | no | Filter by invoice. |
| payment_link_id (query) | uuid | no | Filter by payment link. |
| provider (query) | "stripe" | "wix" | no | Filter by provider. |
| confirmed_since (query) | date-time | no | Only payments confirmed at or after this ISO 8601 timestamp. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/payments" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"data": [
{
"id": "7e6d5c4b-3a29-4180-9f8e-7d6c5b4a3928",
"invoice_id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"installment_id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70",
"payment_link_id": "0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b",
"provider": "stripe",
"provider_payment_id": "pi_ExampleOnly000000000000",
"amount_minor": 125000,
"currency": "USD",
"mode": "live",
"status": "confirmed",
"provider_paid_at": "2026-07-11T14:02:11+00:00",
"confirmed_at": "2026-07-11T14:02:14+00:00"
}
],
"pagination": {
"next_cursor": null,
"has_more": false,
"limit": 25
}
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| data | array of object | yes | Confirmed payments, newest first. |
| data[].id | uuid | yes | Payment id. |
| data[].invoice_id | uuid | yes | Invoice the payment settles. |
| data[].installment_id | uuid | yes | Installment the payment settles. |
| data[].payment_link_id | uuid | yes | The payment link the payer used. |
| data[].provider | "stripe" | "wix" | yes | Payment provider. |
| data[].provider_payment_id | string | yes | The provider's payment/transaction identifier. |
| data[].amount_minor | integer | yes | Settled amount. Integer amount in the currency's MINOR unit (e.g. cents). |
| data[].currency | string | yes | Settled currency. ISO 4217 alphabetic code, e.g. "USD". |
| data[].mode | "test" | "live" | yes | Provider environment. |
| data[].status | "confirmed" | yes | Payments appear here only once verified/confirmed by the provider. |
| data[].provider_paid_at | date-time | null | yes | Provider-reported payment time (ISO 8601 UTC). |
| data[].confirmed_at | date-time | yes | When Finero confirmed the settlement (ISO 8601 UTC). |
| pagination.next_cursor | string | null | yes | Opaque cursor for the next page — pass as ?cursor=. Null when there are no further results. |
| pagination.has_more | boolean | yes | Whether another page exists. |
| pagination.limit | integer | yes | The page size that was applied. |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
/v1/payments/{paymentId}Get a payment
Returns one confirmed payment by id, including the provider reference and settlement timestamps.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| paymentId (path) | uuid | yes | Payment id. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/payments/<paymentId>" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"id": "7e6d5c4b-3a29-4180-9f8e-7d6c5b4a3928",
"invoice_id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"installment_id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70",
"payment_link_id": "0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b",
"provider": "stripe",
"provider_payment_id": "pi_ExampleOnly000000000000",
"amount_minor": 125000,
"currency": "USD",
"mode": "live",
"status": "confirmed",
"provider_paid_at": "2026-07-11T14:02:11+00:00",
"confirmed_at": "2026-07-11T14:02:14+00:00"
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | uuid | yes | Payment id. |
| invoice_id | uuid | yes | Invoice the payment settles. |
| installment_id | uuid | yes | Installment the payment settles. |
| payment_link_id | uuid | yes | The payment link the payer used. |
| provider | "stripe" | "wix" | yes | Payment provider. |
| provider_payment_id | string | yes | The provider's payment/transaction identifier. |
| amount_minor | integer | yes | Settled amount. Integer amount in the currency's MINOR unit (e.g. cents). |
| currency | string | yes | Settled currency. ISO 4217 alphabetic code, e.g. "USD". |
| mode | "test" | "live" | yes | Provider environment. |
| status | "confirmed" | yes | Payments appear here only once verified/confirmed by the provider. |
| provider_paid_at | date-time | null | yes | Provider-reported payment time (ISO 8601 UTC). |
| confirmed_at | date-time | yes | When Finero confirmed the settlement (ISO 8601 UTC). |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
ERP sync
/v1/erp-connectionsList ERP connections
Lists the tenant's ERP connections (safe status metadata only — never credentials, hosts, or configuration). Read-only visibility: connecting, configuring, and syncing are managed inside the Finero app.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| limit (query) | integer | no | Page size, 1–100. Default 25. |
| cursor (query) | string | no | Opaque pagination cursor from a previous response's pagination.next_cursor. |
| order (query) | "asc" | "desc" | no | Sort direction by creation time. Default desc (newest first). |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/erp-connections" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"data": [
{
"id": "1a2b3c4d-5e6f-4a80-91b2-c3d4e5f60718",
"provider": "oracle_fusion",
"status": "connected",
"last_sync_at": "2026-07-14T05:00:04+00:00",
"auto_sync_enabled": true,
"auto_sync_interval_minutes": 60,
"created_at": "2026-06-20T11:00:00+00:00"
}
],
"pagination": {
"next_cursor": null,
"has_more": false,
"limit": 25
}
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| data | array of object | yes | ERP connections. |
| data[].id | uuid | yes | ERP connection id — use as connection_id when starting a sync run. |
| data[].provider | "oracle_fusion" | yes | ERP system this connection syncs from. |
| data[].status | "connected" | "requires_reauthentication" | "temporarily_unavailable" | "disconnected" | yes | Connection health. |
| data[].last_sync_at | date-time | null | yes | Last successful sync completion (ISO 8601 UTC). |
| data[].auto_sync_enabled | boolean | yes | Whether scheduled auto-sync is on. |
| data[].auto_sync_interval_minutes | integer | yes | Scheduled auto-sync cadence in minutes. |
| data[].created_at | date-time | yes | Creation time (ISO 8601 UTC). |
| pagination.next_cursor | string | null | yes | Opaque cursor for the next page — pass as ?cursor=. Null when there are no further results. |
| pagination.has_more | boolean | yes | Whether another page exists. |
| pagination.limit | integer | yes | The page size that was applied. |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
/v1/sync-runsList sync runs
Lists ERP sync runs, newest first. Cursor-paginated: pass ?limit= (1–100) and follow pagination.next_cursor until has_more is false.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| limit (query) | integer | no | Page size, 1–100. Default 25. |
| cursor (query) | string | no | Opaque pagination cursor from a previous response's pagination.next_cursor. |
| order (query) | "asc" | "desc" | no | Sort direction by creation time. Default desc (newest first). |
| connection_id (query) | uuid | no | Filter by ERP connection. |
| status (query) | "running" | "success" | "partial" | "failed" | no | Filter by run status. |
| direction (query) | "pull" | "push" | no | Filter by sync direction. Only `pull` matches new runs; `push` matches historical runs recorded before outbound sync was removed. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/sync-runs" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"data": [
{
"id": "3f2e1d0c-9b8a-4756-8493-21f0e9d8c7b6",
"connection_id": "1a2b3c4d-5e6f-4a80-91b2-c3d4e5f60718",
"direction": "pull",
"trigger": "manual",
"status": "success",
"started_at": "2026-07-14T05:00:04+00:00",
"finished_at": "2026-07-14T05:00:41+00:00",
"items_processed": 36,
"items_failed": 0,
"error_summary": null
}
],
"pagination": {
"next_cursor": null,
"has_more": false,
"limit": 25
}
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| data | array of object | yes | Sync runs, newest first. |
| data[].id | uuid | yes | Sync run id — poll GET /v1/sync-runs/{syncRunId} until status is terminal. |
| data[].connection_id | uuid | yes | The ERP connection that ran. |
| data[].direction | "pull" | "push" | yes | Sync direction. Every new run is `pull` — Finero no longer writes invoices back to an ERP. `push` remains in the enum because historical runs carry it. |
| data[].trigger | "manual" | "scheduled" | yes | How the run started. API-started runs are `manual`. |
| data[].status | "running" | "success" | "partial" | "failed" | yes | running is non-terminal; success/partial/failed are terminal. |
| data[].started_at | date-time | yes | Run start time (ISO 8601 UTC). |
| data[].finished_at | date-time | null | yes | Run finish time (null while running) (ISO 8601 UTC). |
| data[].items_processed | integer | yes | Invoices processed. |
| data[].items_failed | integer | yes | Invoices that failed to process. |
| data[].error_summary | string | null | yes | Categorical failure summary (never raw provider payloads). |
| pagination.next_cursor | string | null | yes | Opaque cursor for the next page — pass as ?cursor=. Null when there are no further results. |
| pagination.has_more | boolean | yes | Whether another page exists. |
| pagination.limit | integer | yes | The page size that was applied. |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
/v1/sync-runs/{syncRunId}Get a sync run
Returns one ERP sync run (read-only operational visibility). Syncs are started from within Finero — the public API cannot trigger synchronization.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| syncRunId (path) | uuid | yes | Sync run id. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/sync-runs/<syncRunId>" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"id": "3f2e1d0c-9b8a-4756-8493-21f0e9d8c7b6",
"connection_id": "1a2b3c4d-5e6f-4a80-91b2-c3d4e5f60718",
"direction": "pull",
"trigger": "manual",
"status": "success",
"started_at": "2026-07-14T05:00:04+00:00",
"finished_at": "2026-07-14T05:00:41+00:00",
"items_processed": 36,
"items_failed": 0,
"error_summary": null
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | uuid | yes | Sync run id — poll GET /v1/sync-runs/{syncRunId} until status is terminal. |
| connection_id | uuid | yes | The ERP connection that ran. |
| direction | "pull" | "push" | yes | Sync direction. Every new run is `pull` — Finero no longer writes invoices back to an ERP. `push` remains in the enum because historical runs carry it. |
| trigger | "manual" | "scheduled" | yes | How the run started. API-started runs are `manual`. |
| status | "running" | "success" | "partial" | "failed" | yes | running is non-terminal; success/partial/failed are terminal. |
| started_at | date-time | yes | Run start time (ISO 8601 UTC). |
| finished_at | date-time | null | yes | Run finish time (null while running) (ISO 8601 UTC). |
| items_processed | integer | yes | Invoices processed. |
| items_failed | integer | yes | Invoices that failed to process. |
| error_summary | string | null | yes | Categorical failure summary (never raw provider payloads). |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |
Workflows
/v1/workflow-executionsList workflow executions
Audit trail of the tenant's automation runs (payment-link automation + email notification): what ran, what was skipped, and why — with stable machine-readable reason codes. Cursor-paginated: pass ?limit= (1–100) and follow pagination.next_cursor until has_more is false.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| limit (query) | integer | no | Page size, 1–100. Default 25. |
| cursor (query) | string | no | Opaque pagination cursor from a previous response's pagination.next_cursor. |
| order (query) | "asc" | "desc" | no | Sort direction by creation time. Default desc (newest first). |
| workflow_type (query) | "payment_link_automation" | "email_notification" | no | Filter by workflow type. |
| status (query) | "pending" | "processing" | "succeeded" | "skipped" | "failed" | no | Filter by outcome. |
| invoice_id (query) | uuid | no | Filter by invoice. |
Example request
curl "https://api.getfinero.com/functions/v1/api/v1/workflow-executions" \
-H "Authorization: Bearer $FINERO_API_KEY"Success response (200)
{
"data": [
{
"id": "6c5d4e3f-2a1b-4c9d-8e7f-0a1b2c3d4e5f",
"workflow_type": "payment_link_automation",
"invoice_id": "8f14e45f-ceea-4a5b-9d2c-167ce7de1a10",
"installment_id": "a3c9d2e1-55b4-4c8e-9f01-2b3c4d5e6f70",
"payment_link_id": "0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b",
"trigger_reason": "invoice_synced",
"status": "succeeded",
"reason_code": null,
"reason_message": null,
"started_at": "2026-07-10T06:31:00+00:00",
"finished_at": "2026-07-10T06:31:01+00:00"
}
],
"pagination": {
"next_cursor": null,
"has_more": false,
"limit": 25
}
}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| data | array of object | yes | Workflow executions, newest first. |
| data[].id | uuid | yes | Workflow execution id. |
| data[].workflow_type | "payment_link_automation" | "email_notification" | yes | Which automation ran. |
| data[].invoice_id | uuid | null | yes | Related invoice. |
| data[].installment_id | uuid | null | yes | Related installment. |
| data[].payment_link_id | uuid | null | yes | Related payment link. |
| data[].trigger_reason | string | yes | What triggered the run. |
| data[].status | "pending" | "processing" | "succeeded" | "skipped" | "failed" | yes | Outcome of this execution. |
| data[].reason_code | string | null | yes | Categorical reason for a skip/failure (stable machine-readable code). |
| data[].reason_message | string | null | yes | Human-readable reason. |
| data[].started_at | date-time | yes | Execution start (ISO 8601 UTC). |
| data[].finished_at | date-time | null | yes | Execution finish (ISO 8601 UTC). |
| pagination.next_cursor | string | null | yes | Opaque cursor for the next page — pass as ?cursor=. Null when there are no further results. |
| pagination.has_more | boolean | yes | Whether another page exists. |
| pagination.limit | integer | yes | The page size that was applied. |
Errors
| Status | Code | When it happens · what to do |
|---|---|---|
| 400 | validation_failed | The request failed schema validation. See error.details for field messages. → fix the request |
| 401 | invalid_credentials | The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. → fix the request |
| 401 | missing_credentials | No Authorization: Bearer header was sent. → fix the request |
| 401 | revoked_credentials | The presented key was revoked. Create a new key in Settings → API. → fix the request |
| 403 | feature_unavailable | The tenant's tier does not currently include API access. Feature rollout flags cannot grant this entitlement. → fix the request |
| 403 | permission_denied | The key's permission does not allow this operation (readonly keys cannot call admin-only operations). → fix the request |
| 404 | not_found | No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones. → fix the request |
| 429 | rate_limit_exceeded | Too many requests. Honor the Retry-After header (seconds) before retrying. → retryable |
| 500 | internal_error | Unexpected server error. Safe to retry with the same Idempotency-Key. → retryable |