FinBaze for Platforms

Concepts

Your backend pushes data with a server key and the scope of what it touches (relations:write, sales_invoices:write, monetary_accounts:write, …). Every object you create keeps your own id.

Your ids and metadata#

Relations, products, projects, cost centers, sales and purchase invoices, journal entries, employees and employments carry an externalId (unique per company and type) and up to 50 string metadata values. Find an object again with platformObjectByExternalId.

One object: upsert*#

upsertRelation, upsertSalesInvoice, … create the object or update it. The answer is created, updated, unchanged (the same data was already stored; nothing was written and no event is sent) or deleted (you deleted it earlier; add restore: true to bring it back).

  • A finalised invoice (finalise: true numbers and books it) accepts metadata only. Correct it with a credit note: send an invoice with creditOf: { externalId: … } and no lines.
  • expectedTotalMinor makes the call fail with TOTAL_MISMATCH if your prices and tax codes do not add up to what you expect. Tax is derived from the tax code, never taken from the line.
  • Hidden modules still accept data (you may show the module later); a module that is off for the company answers FEATURE_DISABLED.

Many objects: bulk jobs#

POST /v2/platform/bulk-jobs?object=relations with an Idempotency-Key header and an NDJSON body (optionally gzip). The job is answered with 202 and applied in the background; every line is applied on its own, so one bad line fails alone. Read progress and per-line results with platformBulkJob(id); the same file with the same key is the same job.

const job = await finbaze.platform.bulk.create({ object: "relations", company: { externalId: "acme-1" }, lines });
const done = await finbaze.platform.bulk.wait(job.id);

Bank data: data sources#

Register a data source (registerDataSource) per feed, attach it to companies (attachDataSource, or autoAttach for every new company), then deliver:

Call
upsertFeedBankAccountA bank, card or PSP account. An existing Lunch Flow account with the same IBAN is taken over with its history and processing.
ingestBankTransactionsUp to 500 booked transactions. pending lines are skipped. A line that changes after it was booked fails with IMMUTABLE_TRANSACTION; send a reversal (reverses) and a new transaction instead.
reportBankBalancesBalances are checked against the transactions. A mismatch changes nothing in the books and raises feed.balance_mismatch. The first closing balance after attaching sets the opening balance.
reportFeedWatermark"Everything up to this date is delivered." Also the heartbeat of a quiet account; real-time and hourly feeds should send it at least once per gap threshold.

Bank rows you deliver are read-only for the company's users (PARTNER_SOURCED_READ_ONLY); they can still process and split them.

Statement files#

POST /v2/platform/data-sources/{key}/files?format=camt053|camt052|mt940 accepts one file for many companies, routed by account IBAN to the attached companies. Accounts that are not routed are listed in unroutedAccounts. The same booking arriving as camt.052, camt.053 or MT940 is stored once. Follow a file with dataSourceFiles and the events data_source_file.processed / .failed.

Feed health#

feedHealth shows healthy, delayed, gap, balance_mismatch or stopped per account, and the events feed.delayed, feed.gap_detected, feed.balance_mismatch and feed.recovered announce changes.

Your own objects#

definePlatformRecordType describes a shipment, a job or a Z-report; upsertCustomRecord pushes one. A flow template turns a state change into bookings (invoice, journal entry, reversal) with the same rules as the calls above. See Records and flow templates.