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 andIntegration drafts sections of the API Reference.
What each system brings
| Connector | Category | Records you can read | Drafts | Plan |
|---|---|---|---|---|
| Invoice4U | Invoicing | Documents, customers, receivables | ✅ | Starter and above |
| Morning | Invoicing | Documents, customers, receivables | ✅ | Starter and above |
| SUMIT | Invoicing | Documents, customers, receivables | ✅ | Starter and above |
| iCount | Invoicing | Documents, customers, receivables | ✅ | Starter and above |
| Priority | ERP | Documents, customers, receivables, payables, suppliers, bills, ledger, orders, inventory | — | Pro and above |
| Isracard Business | Clearing | Settlements (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
clientIdandclientSecretunder 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 withuserId(403 USER_ID_REQUIREDotherwise). See Authentication.
| Scope | What it allows |
|---|---|
read:integrations | List integrations and the connector catalog |
create:integrations | Create connect links, rename and refresh integrations |
delete:integrations | Disconnect an integration |
read:integration-data | Read records, receivables, payables, the change feed, drafts and operations |
create:integration-actions | Prepare 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": [] }
}| Field | What to do with it |
|---|---|
nextCursor | Pass 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.dataAsOf | When 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.stale | true once the data is more than 26 hours old. |
meta.untrustedFields | JSON 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
| Route | What it returns |
|---|---|
GET /{integrationId}/documents | Tax invoices, receipts, invoice-receipts, credit notes, proformas. Filter by status, type, counterpartyId, issuedFrom, issuedTo, receivableOnly, number |
GET /{integrationId}/documents/{documentId} | One document |
GET /{integrationId}/customers | Customers; search with q |
GET /{integrationId}/receivables | Who owes what: totals and aging buckets per currency, and per customer |
GET /receivables | The 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": [] }
}| Parameter | Values |
|---|---|
status | expected (a future credit) or settled (already paid) |
paymentFrom, paymentTo | YYYY-MM-DD, on the payment date, both ends included |
limit, cursor | Paging, as above |
- There is one record per merchant number (
branch), payment date and currency. A payout keeps itsidwhen it
moves fromexpectedtosettled. - Never add
expectedandsettledtogether. They are different points in time for the same kind of money. - A
settledpayout is already in the bank account ending inpayoutAccount, so it is already part of that account's
balance and transactions.payoutAccountisnullwhen the merchant has more than one payout account. - Deductions are positive numbers, and
netMinoris what reaches the bank.nullmeans unknown, not zero. - Items come back in storage order, not by date. Sort them yourself.
ERP data (Priority)
| Route | What it returns |
|---|---|
GET /{integrationId}/payables | Who the business owes: totals and aging per currency, and per supplier |
GET /{integrationId}/suppliers | Suppliers; search with q |
GET /{integrationId}/bills | Supplier invoices and credits, with what was paid. Filter by status, supplierId, issuedFrom, issuedTo, openOnly |
GET /{integrationId}/ledger | Each general-ledger account per month: debits, credits, closing balance. Filter by periodFrom, periodTo (YYYY-MM), kind, accountCode |
GET /{integrationId}/orders | Sales and purchase orders. Filter by side, status, orderedFrom, orderedTo |
GET /{integrationId}/inventory | Stock 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:
- Call it without
cursor. It first delivers every record asadded, then the changes since. - Keep calling with
nextCursorwhilehasMoreistrue. - Later, call again with the last
nextCursorto 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) orcancelled.GET /operations/{operationId}follows one issued item, andGET /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": "..."
}| Type | Status | What to do |
|---|---|---|
VALIDATION_FAILED | 400 | Fix the request; details.fields names each field |
CURSOR_INVALID | 400 | Start the listing again without cursor |
USER_ID_REQUIRED | 403 | Mint the token with a userId |
UPGRADE_REQUIRED | 403 | The plan does not include this connector or action; action.plan names the plan that does |
INTEGRATION_LIMIT_REACHED | 403 | The organization used up its plan's integrations of this system |
FORBIDDEN_BY_POLICY | 403 | An org admin restricted this key |
IP_NOT_ALLOWED | 403 | Call from an IP in the key's allowlist |
INTEGRATION_NOT_FOUND | 404 | No such integration for this user, or it was disconnected |
INTEGRATION_NEEDS_REAUTH | 409 | The system's credential stopped working; send the user a reconnect link |
CURSOR_EXPIRED | 410 | Restart the change feed without a cursor |
PROPOSAL_INVALID | 422 | Fix the draft; details.items says which item and field |
RATE_LIMITED | 429 | Slow down: 600 requests a minute per API key; honour Retry-After |
REFRESH_COOLDOWN | 429 | An integration can be refreshed once an hour |
PROVIDER_UNAVAILABLE | 503 | The system is not responding; retry later |
WRITES_DISABLED | 503 | This 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.
Updated 26 minutes ago

