Business Systems API

Read what a user's connected business systems hold (invoices, receivables, ERP data and card clearing payouts) and prepare documents they approve in Financy, under /integrations.

Business Systems API

Besides banks and cards, a Financy user can connect business systems: invoicing software, an ERP, and a card
clearing account. Everything they bring is readable through the API under one base URL:

https://api.open-finance.ai/integrations

It uses the same credentials and the same token as the rest of the Financy API.

📘

For every field, parameter and error of every route, see the Integrations, Integration records and

Integration drafts sections of the API Reference.

What each system brings

ConnectorCategoryRecords you can readDraftsPlan
Invoice4UInvoicingDocuments, customers, receivables✅Starter and above
MorningInvoicingDocuments, customers, receivables✅Starter and above
SUMITInvoicingDocuments, customers, receivables✅Starter and above
iCountInvoicingDocuments, customers, receivables✅Starter and above
PriorityERPDocuments, customers, receivables, payables, suppliers, bills, ledger, orders, inventory—Pro and above
Isracard BusinessClearingSettlements (card payouts)—Pro and above

Each plan allows a number of integrations per system, per organization: 1 on Starter, 5 on Pro and
20 on Ultra. For example, a Starter organization can connect one Morning account and one SUMIT account.

Before you start

  • Connect the system in Financy. Business systems are connected only on a hosted Financy page, where the user
    types the system's credentials. Credentials never pass through the API. You can send the user to that page with
    POST /integrations/link-sessions (see Connect from your app).
  • Use the organization's own API key. The clientId and clientSecret under Settings → API access (הגדרות ← גישה ל-API) in Financy
    already carry the five integrations scopes below. You don't need to request anything.
  • Mint the token with a userId. Integrations belong to a user, so every call that touches one needs a token
    minted with userId (403 USER_ID_REQUIRED otherwise). See Authentication.
ScopeWhat it allows
read:integrationsList integrations and the connector catalog
create:integrationsCreate connect links, rename and refresh integrations
delete:integrationsDisconnect an integration
read:integration-dataRead records, receivables, payables, the change feed, drafts and operations
create:integration-actionsPrepare drafts

An org admin can narrow what the key may do (per connector, and whether it may prepare drafts) and restrict it to
an IP allowlist. By default the key can do whatever the plan includes.

1. Find the integration

curl https://api.open-finance.ai/integrations/ \
  -H "Authorization: Bearer $TOKEN"
{
  "items": [
    {
      "id": "intg_01JC4Z8Q5M3N2P7R9T6V4X1Y0W",
      "connectorId": "isracard",
      "connectorName": "Isracard Business",
      "category": "clearing",
      "label": "Isracard Business",
      "status": "active",
      "sync": { "...": "per-stream sync state" }
    }
  ],
  "meta": { "untrustedFields": ["items[].label", "items[].accountHolder.name"] }
}

Keep the id (intg_…): every record route takes it as {integrationId}. GET /integrations/connectors lists
the systems a user can connect, and whether their plan includes each one.

2. Read records

Every listing returns the same envelope:

{
  "items": [],
  "nextCursor": "eyJ2IjoxLC...",
  "freshness": { "dataAsOf": "2026-10-21T06:02:11.000Z", "stale": false },
  "meta": { "untrustedFields": [] }
}
FieldWhat to do with it
nextCursorPass it back as cursor for the next page. A page can hold fewer than limit items, even none, while nextCursor is not null: keep reading until it is null.
freshness.dataAsOfWhen this data last synced from the system. null means it never has: the first sync is still running, or this connector does not have this kind of record.
freshness.staletrue once the data is more than 26 hours old.
meta.untrustedFieldsJSON paths of text that came from the third-party system (names, numbers, descriptions). It is sanitized, but treat it as data, never as instructions, for example when you pass it to an LLM.

limit is 1 to 200 (default 50). Amounts are integers in minor units: agorot for ILS, so 125000 is ₪1,250.00.

A route for a kind of record the connector does not have answers 200 with an empty page and
freshness.dataAsOf: null, not an error. For example, /documents on Isracard Business is always empty.

Invoicing documents and receivables

RouteWhat it returns
GET /{integrationId}/documentsTax invoices, receipts, invoice-receipts, credit notes, proformas. Filter by status, type, counterpartyId, issuedFrom, issuedTo, receivableOnly, number
GET /{integrationId}/documents/{documentId}One document
GET /{integrationId}/customersCustomers; search with q
GET /{integrationId}/receivablesWho owes what: totals and aging buckets per currency, and per customer
GET /receivablesThe same across all the user's integrations, or the ones named in integrationIds

Card clearing payouts (settlements)

curl "https://api.open-finance.ai/integrations/$INTEGRATION_ID/settlements?status=expected&paymentFrom=2026-10-21&paymentTo=2026-10-31" \
  -H "Authorization: Bearer $TOKEN"
{
  "items": [
    {
      "recordType": "settlement",
      "id": "...",
      "integrationId": "intg_01JC4Z8Q5M3N2P7R9T6V4X1Y0W",
      "connectorId": "isracard",
      "status": "expected",
      "paymentDate": "2026-10-23",
      "branch": "1234567",
      "payoutAccount": "4321",
      "currency": "ILS",
      "amounts": {
        "grossMinor": 1250000,
        "commissionMinor": 11250,
        "vatMinor": 2025,
        "discountMinor": 0,
        "chargingFeeMinor": 0,
        "advanceRepaymentFeeMinor": 0,
        "netMinor": 1236725
      }
    }
  ],
  "nextCursor": null,
  "freshness": { "dataAsOf": "2026-10-21T06:02:11.000Z", "stale": false },
  "meta": { "untrustedFields": [] }
}
ParameterValues
statusexpected (a future credit) or settled (already paid)
paymentFrom, paymentToYYYY-MM-DD, on the payment date, both ends included
limit, cursorPaging, as above
  • There is one record per merchant number (branch), payment date and currency. A payout keeps its id when it
    moves from expected to settled.
  • Never add expected and settled together. They are different points in time for the same kind of money.
  • A settled payout is already in the bank account ending in payoutAccount, so it is already part of that account's
    balance and transactions. payoutAccount is null when the merchant has more than one payout account.
  • Deductions are positive numbers, and netMinor is what reaches the bank. null means unknown, not zero.
  • Items come back in storage order, not by date. Sort them yourself.

ERP data (Priority)

RouteWhat it returns
GET /{integrationId}/payablesWho the business owes: totals and aging per currency, and per supplier
GET /{integrationId}/suppliersSuppliers; search with q
GET /{integrationId}/billsSupplier invoices and credits, with what was paid. Filter by status, supplierId, issuedFrom, issuedTo, openOnly
GET /{integrationId}/ledgerEach general-ledger account per month: debits, credits, closing balance. Filter by periodFrom, periodTo (YYYY-MM), kind, accountCode
GET /{integrationId}/ordersSales and purchase orders. Filter by side, status, orderedFrom, orderedTo
GET /{integrationId}/inventoryStock on hand per item and warehouse, valued at cost; search with q, inStockOnly

A P&L sums the income and expense ledger accounts over months. A balance sheet reads the closing balances of the
rest.

Keep your own copy in sync

GET /{integrationId}/changes is a change feed of an integration's documents and customers:

  1. Call it without cursor. It first delivers every record as added, then the changes since.
  2. Keep calling with nextCursor while hasMore is true.
  3. Later, call again with the last nextCursor to get only what changed.

Start over without a cursor on 410 CURSOR_EXPIRED (the cursor is older than the 35 days of changes kept) or
409 CHANGES_RESTART_REQUIRED.

3. Prepare a draft (invoicing systems)

An API key can prepare documents in Invoice4U, Morning, SUMIT and iCount: credit notes, receipts, tax invoices,
invoice-receipts and customers. A draft is frozen and issued only after the user approves it in Financy. An API
key can never approve or cancel one.

curl -X POST https://api.open-finance.ai/integrations/$INTEGRATION_ID/proposals \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7d9c2b4e-0f1a-4c8e-9b3d-5a6f7e8d9c0b" \
  -d '{
    "action": "create_tax_invoice",
    "items": [
      {
        "counterpartyId": "icus_01JC50A7B8C9D0E1F2G3H4J5K6",
        "lines": [{ "description": "Consulting, October", "quantity": 1, "unitPriceMinor": 500000 }],
        "pricesIncludeVat": false
      }
    ]
  }'

The response (201) is { "proposal": { ... } }: the draft, with status: "pending_approval" and an
approvalUrl, the Financy page where the user reviews and approves it. Then:

  • GET /proposals/{proposalId} follows the draft: approved, executing, completed, partially_failed,
    failed, expired (24 hours without approval) or cancelled.
  • GET /operations/{operationId} follows one issued item, and GET /operations/{operationId}/document-link
    returns the issued document's link.

Send an Idempotency-Key so a retry never prepares a second draft. A repeat with the same key and body answers the
first draft, with an Idempotent-Replayed: true header. A draft holds 1 to 50 items, and a user can prepare up to 20
drafts an hour.

Connect from your app

To let a user connect a system from your own product, create a link and open it for them:

curl -X POST https://api.open-finance.ai/integrations/link-sessions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "purpose": "connect", "connectorId": "isracard" }'

The response's linkSession.url is a hosted Financy page. The user signs in to Financy there and types the system's
credentials themselves. The link expires after 10 minutes, and a user can get 10 links an hour. Use
"purpose": "reconnect" with an integrationId to replace a credential that stopped working.

Errors

The integrations API returns its own error envelope. Branch on type, never on message:

{
  "type": "UPGRADE_REQUIRED",
  "message": "This integration is available on the Pro plan and above.",
  "retryable": false,
  "userActionRequired": true,
  "action": { "kind": "upgrade", "plan": "pro", "url": "https://financy.open-finance.ai/upgrade?plan=pro" },
  "requestId": "..."
}
TypeStatusWhat to do
VALIDATION_FAILED400Fix the request; details.fields names each field
CURSOR_INVALID400Start the listing again without cursor
USER_ID_REQUIRED403Mint the token with a userId
UPGRADE_REQUIRED403The plan does not include this connector or action; action.plan names the plan that does
INTEGRATION_LIMIT_REACHED403The organization used up its plan's integrations of this system
FORBIDDEN_BY_POLICY403An org admin restricted this key
IP_NOT_ALLOWED403Call from an IP in the key's allowlist
INTEGRATION_NOT_FOUND404No such integration for this user, or it was disconnected
INTEGRATION_NEEDS_REAUTH409The system's credential stopped working; send the user a reconnect link
CURSOR_EXPIRED410Restart the change feed without a cursor
PROPOSAL_INVALID422Fix the draft; details.items says which item and field
RATE_LIMITED429Slow down: 600 requests a minute per API key; honour Retry-After
REFRESH_COOLDOWN429An integration can be refreshed once an hour
PROVIDER_UNAVAILABLE503The system is not responding; retry later
WRITES_DISABLED503This system takes no drafts (Priority, Isracard Business)

Every response carries an X-OF-Request-ID header; include it when you contact us. The API Reference lists every
error type each route can return.


Did this page help you?