FinBaze for Platforms

Changelog

Every change to the GraphQL schema gets an entry here. Add a file content/changelog/YYYY-MM-DD-<slug>.md; CI fails a pull request that changes schema.gql without one.

  • FinBaze files only with consent. For a platform company the company's own user authorises FinBaze to file each tax (AuthorisedTax: nl_vat, nl_icp, nl_oss, nl_cit, nl_annual_accounts, nl_wage_tax, nl_pension, uk_vat, uk_income_tax). A submit without it fails with TAX_AUTHORISATION_REQUIRED. See Filing consent.
  • New operations. taxAuthorisationStatement(companyId, taxes, locale), taxAuthorisationStatus(companyId), taxAuthorisations(companyId, includeRevoked), grantTaxAuthorisation(input) and revokeTaxAuthorisation(input). Granting is for embed and headless tokens only: a server token or a user token minted by your backend gets FORBIDDEN_TOKEN_TYPE. The session needs the onboarding or tax-authorisation component and an admin of the company. Revoking also works with your server token and is effective on the next request.
  • Hash of the displayed text. grantTaxAuthorisation takes the statementVersion and statementHash of the statement that was shown; a different text is TAX_AUTHORISATION_STATEMENT_OUTDATED. A tax the company has no obligation for is TAX_NOT_REGISTERED.
  • Requirements and events. onboardingRequirements gains tax_<tax>_authorisation (currently_due while a tax is missing or reconfirmation_due). New events tax_authorisation.granted, tax_authorisation.revoked ({ companyId, taxes, channel, stepUp, at }, no IP or user agent) and company.requirements.updated, now available. Message type tax_authorisation.revoked for your message routing.
  • Step-up. The step_up provider accepts the action tax_authorisation.grant.
  • Components. onboarding has a "Filing authorisation" step (features.filingConsent); tax-authorisation shows the status per tax, the history, grant and revoke (features.grant, features.revoke) next to the HMRC connection; vat-return, icp-return and oss-return show a grant panel instead of an error (features.inlineConsent) or "Your accountant files this return". Events tax_authorisation.granted / .revoked carry the webhook payload. Headless: useTaxAuthorisation().
  • SDK. platform.taxAuthorisations.list, .status, .revoke (no grant) and the new events in PlatformControlEvent.
  • Portal. The companies page shows each company's authorisation status and history, read only.

2026-11-06: accountants on platform companies (Wave 5.3)#

  • Engagements. A company connects one accounting firm by name: lookupFirmForConnect (exact KvK number or connect code, 10 lookups an hour per company), firmEngagementStatement, requestFirmConnection, decideFirmConnection, updateFirmEngagementAuthority, endFirmEngagement, companyFirmEngagements. A firm asks for access with requestCompanyAccess (always true: it never says whether a company matched); the company approves or declines. New enums FirmEngagementStatus, FirmEngagementOrigin, FirmFilingAuthority, EngagementSide; new scopes platform_firms:read and platform_firms:write.
  • The firm pays nothing for a platform company: no seat, no filings-only fee, no per-document or AI usage. The firm works the company in the normal dashboard inside its effective features; firm-installed apps and firm API credentials do not reach it.
  • firm_access feature key. Hiding it blocks new connections and gives active ones a notice (default 30 days) before they end (firm.disconnect_scheduled, then firm.disconnected with endedBySide: platform).
  • Events. firm.access_requested, firm.connection_requested, firm.connected, firm.connection_declined, firm.connection_expired, firm.disconnect_scheduled, firm.disconnected, firm.authority_changed; messages firm.access_requested, firm.engagement_company_notice and firm.engagement_firm_notice.
  • Component. accountant-connect (session key accountantConnect, features invite, approve, end, events firm.connected and firm.disconnected); notification-banner points at pending firm requests. See Working with accountants.
  • SDK. platform.firms.listEngagements(companyId) and platform.firms.end(engagementId, { reason }).
  • Profile.platformName and Bookkeeper.connectCode (firm admins; bookkeeperConnectCode, rotateBookkeeperConnectCode). firmEngagementStatement is the one firm… root field that platform tokens may call.

2026-11-03: sandbox tools (Wave 5.5)#

Sandbox only; production answers SANDBOX_ONLY.

  • Test clocks. createTestClock, advanceTestClock, deleteTestClock, testClocks; createPlatformCompany takes testClockId. Events test_clock.ready and test_clock.failed carry testClockTime.
  • Demo companies. seedTestCompany with five templates; magic KvK and Companies House numbers 00000001, 00000002, 99999999.
  • Reset. platformSandboxSettings, updatePlatformSandboxSettings (sandboxReset: none or nightly, sandboxSeedTemplates), resetPlatformSandbox, event sandbox.reset.completed.

2026-10-31: documents, part 2 (Wave 4.3/4.4)#

  • Presets travel with the documents policy. The sandbox bundle carries your active layout presets; confirming the import in production makes them the active versions there (earlier versions are archived, companies follow the new one).
  • Quote, reminder, payslip and jaaropgaaf. Templates and partner rendering now cover them (setPlatformDocumentRenderSetting accepts quote, reminder, payslip, year_document in partner mode). Their data models never contain the BSN; documentDataModelSchema publishes them. A partner payslip must show employer, employee, period, gross, wage tax withheld and the amount payable; a jaaropgaaf employer, employee, year, wage and withholding, or FinBaze falls back to its own layout (document.render_rejected).
  • Large PDFs. POST /v2/platform/documents/uploads (application/pdf) answers an uploadId, valid an hour and usable once: attachRenderedDocument(jobId, uploadId). pdfBase64 stays for small files.
  • UBL link. document.render_requested carries attachments with a signed link (15 minutes) to the invoice's UBL.
  • Company fonts. POST|GET|DELETE /v2/profiles/:profileId/fonts for a company's own WOFF2 brand fonts.

2026-10-30: delegated actions (Wave 4.5)#

  • Providers. upsertPlatformProvider declares what you fulfil yourself: payments, payment_links, tax_reservation, step_up (and camera, embed only), each with its own config and mode (webhook, embed or both). Registration needs the matching delegated.* feature in your allowance, checked again at use. platformProviders lists them.
  • Payment requests. FinBaze asks you to pay: createTaxPaymentRequest(company, taxFilingId) for an accepted NL VAT or wage tax filing, createPaymentRequestFromBatch(company, paymentBatchId) for supplier and wage batches (lines held or blocked by Risk Guard are left out; a pain.001 file is attached through pain001Url). Creditor IBANs are frozen when the request is created; you cannot change them. Put the request's endToEndId (or the line's) in the SEPA EndToEndId. Events: payment_request.created, payment_request.updated, payment_request.paid.
  • Status. reportPaymentRequestStatus accepts sent, accepted, scheduled, failed and cancelled; it never accepts paid. A request becomes paid (or partially_paid) only when the bank transaction is matched, by end-to-end id or, for tax, by the 16-digit betalingskenmerk to the Belastingdienst IBAN with the exact amount. cancelPaymentRequest, paymentRequest, paymentRequests.
  • Payment links. With a payment_links provider FinBaze emits payment_link.requested for an invoice and waits waitSeconds; providePaymentLink(requestId, url, expiresAt) stores an https link on one of your allowedHosts.
  • Tax reservation. With a tax_reservation provider, tax_reservation.advised carries what to set aside for the open VAT period; taxReservationAdvice(company) lists it and reportTaxReservation tells FinBaze what you reserved.
  • Step-up. When your step_up provider lists an action (tax_filing.submit, pay_run.approve, bank_details.change, payment_request.approve), a guarded mutation from a user, embed or headless token fails with STEP_UP_REQUIRED and a challenge (stepUpChallenge, event step_up.required). Sign a JWS (ES256, EdDSA or RS256; claims iss = your platform id, aud = finbaze, sub = the user's external id, challenge_id, nonce, action, amr, iat, exp within maxAgeSeconds) and retry with FinBaze-Step-Up: <challengeId>.<jws>. A challenge is single use and lives five minutes.
  • Automatic requests. A tax payment request is created when an NL VAT or wage tax filing is accepted with an amount due, and a wage request when a pay run batch is created, for platforms with a payments provider. Wage tax advice (nl_wage_tax) comes from approved and paid pay runs.
  • For your users' components. taxFilingPaymentRequests, createTaxFilingPaymentRequest and reportTaxFilingPaymentRequestStatus take a profileId and work for user and embed tokens (scopes tax:read and tax:write; embed: the payment feature of vat-return). loadFinBaze takes onPaymentRequested, onStepUp and onCameraCapture; the vat-return component shows a Pay button when you set the first.
  • Payment links and invoices. PublicSalesInvoice.partnerPaymentUrl carries your link; the invoice email, the PDF and the public page show it, and paymentsEnabled is false for that invoice while replaceFinbazePsp is on.
  • Step-up also covers createOneRelationBankAccount, updateOneRelationBankAccount, deleteOneRelationBankAccount and createManyRelationBankAccounts for bank_details.change.
  • New scopes platform_payments:read and platform_payments:write; the feature keys delegated.payment_requests, delegated.payment_links, delegated.tax_reservation and delegated.step_up.

2026-10-30: policies and extensions (Wave 4.6, 4.7)#

  • Policies. One versioned store for how FinBaze behaves for your companies: category_mapping (MCC, partner category, counterparty IBAN, description regex → ledger and tax code, applied to bank feed transactions before any AI suggestion), chart_template, booking_rules, filing_policy (deadline reminders as tax.deadline_upcoming messages), roles_mapping, company_defaults (including optional modules), quotas, and the messaging, documents and extensions snapshots. policySchemas returns a JSON Schema per kind; createPolicyDraft validates (POLICY_INVALID lists every issue), activatePolicyVersion archives the previous version, platformPolicies lists versions, effectivePolicy(kind, companyId) shows what applies to a company. setCompanyPolicyOverride / clearCompanyPolicyOverride for the kinds that allow it. Event policy.activated.
  • Promotion. In the sandbox promotePlatformPolicies returns a bundle id; in production previewPolicyImport(bundleId) verifies the signed bundle and shows what changes per kind, confirmPolicyImport activates everything together. Ids that only exist in one environment (a company in a booking rule) are left out and reported as warnings.
  • Quotas. quotas.ai.creditsPerCompanyPerMonth / documentsPerCompanyPerMonth with atLimit: block (QUOTA_EXCEEDED) or platform_pays; the first time a company reaches one in a month you get quota.reached.
  • Partner roles. setPlatformUserAccess takes partnerRole (a role from your roles_mapping policy) instead of role; role is now optional, one of the two is required. Only the role's permission set is applied for now.
  • Custom actions and deep links. upsertCustomAction (placements such as booking.detail.header, conditions hasExternalId and customRecordType), customActions(placement, contextType, contextId) resolves what to show, invokeCustomAction emits custom_action.invoked for webhook delivery. upsertLinkTemplate (https, host in your allowed origins; {externalId}, {id}, {companyExternalId}, {metadata.<key>}) and partnerLinks(objectType, objectId): a booking made by a flow template links back to its custom record ("Open shipment 48213 in TMS").
  • Custom fields. upsertCustomFieldDefinition (text, number, boolean, date, select, url; at most 50 per entity type; editableBy), setCustomFieldValues / customFieldValues on sales invoices, transactions, relations and projects. Behind the feature extensions.custom_fields.
  • Field control. setFieldControl hides, locks or presets a field for your users (FIELD_LOCKED on a write); legally required invoice fields cannot be controlled.
  • Slots. Declarative in the embed iframe: your page sends finbaze:slots.set with descriptors for headerActions, rowActions, emptyState, help and paymentButton; a click arrives as finbaze:action.invoked and as custom_action.invoked (invokeSlotItem).
  • Policies in effect. Category mapping and booking rules now decide a bank suggestion before learned rules and AI (a policy match carries autoBook and reviewRequired hints); quotas are checked before AI calls and document processing; applyChartTemplate(companyId) adds your chart template's accounts and hides the ones you listed; filing policy submitRoles and dualApproval are enforced on submitTaxFiling (FILING_SUBMIT_ROLE, FILING_APPROVAL_REQUIRED) with approveTaxFiling for the second person, and autoCreateDrafts creates drafts for ended periods.
  • Partner roles now carry their grants and denies as a role of your platform (denies win; nothing beyond what an admin has).
  • Custom fields show as customFields on sales invoices, transactions, relations and projects, and as custom_fields in the events of those objects (for fields with includeInEvents). Field control is enforced on the create and update mutations of those four objects for your users' tokens.
  • Extension queries (customActions, partnerLinks, customFieldDefinitions, customFieldValues, fieldControls) and invokeCustomAction / invokeSlotItem also work for user, embed and headless tokens of your companies. All policy and extension operations take an optional platformId for the portal.
  • New scopes platform_policies:read|write and platform_extensions:read|write; events policy.activated, quota.reached, custom_action.invoked.

2026-10-30: documents (Wave 4.3 and 4.4)#

  • Layout presets. upsertPlatformDocumentLayout / activatePlatformDocumentLayout / archivePlatformDocumentLayout give your companies presets for invoices, quotes and reminders (and chrome layouts for payslips, year documents, loonstaten, annual accounts and reports). companyEditable is none, unlocked_blocks or full; chooseDocumentLayout lets a company pick one, availableDocumentLayouts lists them, validateDocumentLayout shows the required-content warnings, platformDocumentLayoutPreview renders a sample. Activation fails with DOCUMENT_LAYOUT_INVALID when a rule that can block is violated.
  • Brand fonts. POST /v2/platform/fonts?family=&weight=&style=&attest=true with a WOFF2 body (up to 1 MB, 8 per platform); brandFonts, deleteBrandFont. Fonts are embedded in the PDF, nothing is fetched at render time. Feature documents.brand_fonts.
  • Required content. NL invoice, quote and reminder checks run when a document closes: warnings for direct companies, a refusal (DOCUMENT_REQUIRED_CONTENT_MISSING) for platform companies once a rule is signed off by the adviser. Invoices now print "Btw verlegd" for domestic reverse charge, a VAT exemption reference, and separate wording for intra-EU goods and services (these are visible changes on direct invoices too).
  • Official copies. Closed invoices, sent quotes and reminders keep the PDF that was sent: a layout change no longer rewrites them. documentArtifacts(entityType, entityId) lists the copies with a signed link; event document.official_copy_created.
  • Custom templates. upsertDocumentTemplate, validateDocumentTemplate, activateDocumentTemplate, archiveDocumentTemplate, documentTemplatePreview, documentDataModelSchema (JSON Schema of the data model). A logic-less subset ({{ }}, if, unless, for, raw; filters t default upcase downcase truncate escape newline_to_br size), rendered in an isolated renderer without JavaScript or network. Images must be PNG, JPEG or WebP (asset:<key>). A template activates only after rendering at least 12 fixtures with the required content. Feature documents.custom_templates.
  • Partner-rendered invoices. setPlatformDocumentRenderSetting(documentType: invoice, mode: partner). On close you get document.render_requested with the data model; answer with POST /v2/platform/documents/render-jobs/:jobId/pdf (application/pdf) or attachRenderedDocument. FinBaze checks the PDF (no scripts or attachments, the required content) and stores it as the official copy, or falls back to its own layout (document.render_rejected, document.render_failed). A late PDF answers 409 RENDER_JOB_CLOSED. Feature documents.partner_rendered.
  • Custom domains for public invoice, quote and payment pages: addPlatformCustomDomain, checkPlatformCustomDomain, removePlatformCustomDomain, platformCustomDomains (CNAME to domains.finbaze.com plus a TXT record), event custom_domain.status_changed. Feature public_pages.custom_domain.
  • New scopes platform_documents:read|write, platform_domains:read|write.

2026-10-30: all messages, sender domains and the notification centre (Wave 4.2)#

  • Every mail goes through messages. Customer and employee correspondence, firm and account mail, notifications, automation and assistant mail are now messages with a routing decision. Lifecycle, billing and internal mail never reach a platform company. New types in messageCatalogue: sales.* (invoice, payment received and failed, quote, reminder, recurring), payroll.payslip_available, payroll.year_document_available, custom.notification, custom.automation_email, assistant.inbound_reply, documents.inbound_unsorted and the firm types. Platform default is finbaze_sends for correspondence (from your own sender domain) and partner_delivers for notifications; you can change it unless the catalogue says platformMayChange: false.
  • Sender domains. createPlatformSenderDomain returns the DKIM, SPF and DMARC records to publish; verifyPlatformSenderDomain checks DNS (FinBaze also re-checks every 10 minutes while pending); platformSenderDomains, disablePlatformSenderDomain. Mail about your companies is signed with your key and sent from no-reply@<your domain>, never "via FinBaze". Without a verified domain, correspondence is routed to you (partner_delivers, reason no_sender_domain) and replies to inbound mail are dropped. updatePlatformMessageSettings takes brandName, brandLogoUrl, brandColor and supportEmail for company mail that has no logo or colour of its own.
  • Copy overrides. setPlatformMessageCopy / clearPlatformMessageCopy / platformMessageCopy replace the push, in-app, email subject, email intro and SMS text per type and locale with plain text and {{data.field}} placeholders (checked against the type's fields, HTML refused); previewPlatformMessageCopy. Your wording appears in message.created and in the notification centre.
  • Notification centre and preferences (feature notification_centre): myNotifications, markNotificationsRead, notificationPreferences, setNotificationPreference (account security email cannot be switched off). With routing finbaze_sends and channels: ["in_app"] your users read FinBaze's inbox in the embedded action centre instead of you building one.
  • Push for FinBaze's own app (feature push_notifications, FinBaze direct only): registerPushToken / unregisterPushToken. WhatsApp automation actions need the whatsapp feature.
  • Messages that carry a token in a link (password reset, invites) have the link blanked once they are delivered.

2026-10-29: messages (Wave 4.1)#

  • Messages. FinBaze now routes the notifications it used to email to your users (tax deadlines, filings, documents, agents, thresholds) through a message log. For a platform company with partner_delivers you receive message.created with content variants for push, in-app, email and SMS; critical messages carry ack_required and ack_deadline. acknowledgeMessage / acknowledgeMessages confirm delivery, markMessageRead records that the user saw it. platformMessages lists the log with the routing decision and reason.
  • Unacknowledged. After the deadline message.unacknowledged goes to your endpoints; with criticalEmailFallback on, FinBaze emails critical messages itself an hour later.
  • Routing. messageCatalogue lists every message type with its default and what you may change. setPlatformMessageRouting / clearPlatformMessageRouting choose finbaze_sends, partner_delivers, both or suppress per type, category or for all, platform-wide or per company; lifecycle, billing and account-security messages cannot be changed (ROUTING_NOT_CHANGEABLE). platformMessageSettings / updatePlatformMessageSettings set quiet hours, digest hour, acknowledgement timeouts and the email fallback.
  • Resend. resendPlatformMessage sends message.created again for a message that was routed to you (a lost webhook). From the developer portal every message operation takes the optional platformId, as the feature operations do.
  • New scopes platform_messages:read and platform_messages:write; events message.created and message.unacknowledged. See Messages and the message catalogue.

2026-10-28: credit notes through the data API, operations status#

  • Credit notes. upsertSalesInvoice takes creditOf: { externalId } instead of lines. It credits that finalised invoice (a draft credit invoice with the lines negated; add finalise: true to number and book it). Send either lines or creditOf, not both. Crediting twice, or crediting a credit note, is refused.
  • Operations status. platformOpsStatus (scope platform_usage:read) shows queue depths and oldest-item ages for ingest, events, webhooks, feeds, flows and exports.

2026-10-27: reports, documents and exports (Wave 3.5)#

  • Reports. platformReport(company, input) for profit_loss, balance_sheet, trial_balance, vat_position (with a taxFilingId), cashflow, aged_receivables, aged_payables, kpis, general_ledger and forecast; amounts in minor units, data is JSON per type. platformReportDocument renders profit_loss, balance_sheet, cashflow, forecast and the aged reports as XLSX, and the last three as PDF.
  • Documents. companyDocument(company, kind, object, params) for sales_invoice_pdf, sales_invoice_ubl, credit_note_pdf, quote_pdf, reminder_pdf, report_pdf, report_xlsx and xaf. The result carries a signed downloadUrl valid for 15 minutes (GET /v2/platform/files/{token}); files are kept for 24 hours. The other kinds answer UNSUPPORTED_OPERATION for now.
  • Audit file. requestXafExport writes an Auditfile Financieel 3.2 for a financial year or a period in the background; the run's document has the link when it is ready, with the event export.ready.
  • Scheduled exports to your own bucket: createPlatformExportDestination (S3 and S3-compatible, or an Azure container SAS; verified by writing a marker; credentials are stored encrypted and never returned), createPlatformExportSchedule / updatePlatformExportSchedule, runPlatformExportNow, platformExportRun. Datasets: journal_lines, trial_balance, bank_transactions, sales_invoices, sales_invoice_lines, purchase_invoices, purchase_invoice_lines, relations, custom_records, record_effects, as Parquet, CSV or NDJSON. Incremental datasets send only what changed since the last run (op is upsert or delete). Every run ends with _manifest.json and then _SUCCESS.
  • New scopes reports:read, documents:read, exports:read, exports:write; events export.ready and export.failed; the feature data_export is part of a platform's allowance.

2026-10-26: custom records and flow templates (Wave 3.3)#

  • Record types. definePlatformRecordType describes your own objects (a shipment, a job): a JSON Schema for data, the states and which moves between them are allowed. A change creates a new version; records keep the version they were last written with. platformRecordTypes lists them.
  • Records. upsertCustomRecord, deleteCustomRecord (only without bookings), customRecord, customRecords. Each record keeps its revision, its history of states, the bookings it caused (effects) and its flow runs.
  • Flow templates turn a state change into bookings: YAML or JSON with {{ interpolation }} and JSONata expressions (=), the steps upsert_relation, upsert_project, create_sales_invoice, create_purchase_invoice, create_journal_entry, reverse and fail, and the helpers $lookup, $net, $vatRate, $date, $euCountry. saveFlowTemplate, testFlowTemplate (plans what would be booked, writes nothing), activateFlowTemplate, retireFlowTemplate, flowTemplates.
  • Runs. One transaction per run: any failing step undoes the others and the run is failed with the step, code and path; later runs of that record wait until you retryFlowRun. A trigger can rebook when the data changes in a state it watches. Reversals use what the run recorded, never the current template.
  • Events record.created, record.updated, record.transitioned, flow_run.succeeded, flow_run.failed; scopes custom_records:read and custom_records:write; the feature custom_records.

2026-10-25: data sources and bank feeds (Wave 3.2)#

  • Data sources. registerDataSource, updateDataSource, retireDataSource, attachDataSource (with backfillFrom), pauseDataSourceAttachment, detachDataSource; queries dataSources, dataSource, feedHealth, dataSourceFiles. Sources with autoAttach attach to every new company.
  • Feeds. upsertFeedBankAccount (bank, card or PSP accounts; an existing Lunch Flow account with the same IBAN is taken over, keeping its history and processing), ingestBankTransactions (max 500 per call; booked only, PENDING_NOT_BOOKED otherwise; a changed line is IMMUTABLE_TRANSACTION and a correction is a reversal), reportBankBalances (checked against the transactions; a mismatch changes nothing in the books and raises feed.balance_mismatch), reportFeedWatermark (also the heartbeat), ingestWorkedHours.
  • Statement files. POST /v2/platform/data-sources/{key}/files?format=camt053|camt052|mt940 accepts a file for many companies at once, routed by IBAN. The same booking arriving as camt.052, camt.053 or MT940 is stored once.
  • Feed health (healthy, delayed, gap, balance_mismatch, stopped) with events feed.delayed, feed.gap_detected, feed.balance_mismatch, feed.recovered, bank_account.created, data_source_file.processed, data_source_file.failed and data_source.backfill_completed.
  • Bank transactions and accounts a platform delivered are read-only for users (PARTNER_SOURCED_READ_ONLY); processing them is not.
  • New scopes data_sources:read and data_sources:write.

2026-10-24: return flow (Wave 3.6)#

  • bankTransactionBookings(company, externalIds) (max 100) returns what happened to your bank transactions in the books: status (unbooked, suggested, booked, needs_review), counterpart, ledger legs, VAT code, receipt status and who booked it. Unknown ids and ids of other companies read as booking: null.
  • New events bank_transaction.booked, .unbooked, .needs_receipt and .needs_review, sent once per real change.

2026-10-23: events and webhooks (Wave 3.4)#

  • Events. platformEventCatalog lists the event types; platformEvents pages through them by an ordered cursor, platformEvent reads one. Events carry a snapshot of the object, are tagged with API version 2026-11-01, and are not published for a module that is hidden or off for the company.
  • Webhook endpoints. createPlatformWebhookEndpoint, updatePlatformWebhookEndpoint, rotatePlatformWebhookSecret (old secret also signs for a grace period), deletePlatformWebhookEndpoint, pingPlatformWebhookEndpoint. The signing secret is shown once.
  • Deliveries. platformWebhookDeliveries, replayPlatformWebhookDelivery, replayPlatformWebhookDeliveries, resendPlatformEvent. Failed deliveries retry with backoff (30 s to 24 h, 8 attempts); an endpoint answering 410 or failing persistently is disabled.
  • New scopes: events:read, webhooks:read, webhooks:write.

2026-10-22: data in (Wave 3)#

  • Your ids and metadata. Relations, products, projects, cost centers, sales and purchase invoices, quotes, journal entries, employees and employments can carry your own externalId (unique per company and object type) and up to 50 string metadata values. They are never part of the normal create/update inputs.
  • Upserts. upsertRelation, upsertProduct, upsertProject, upsertCostCenter, upsertSalesInvoice, upsertPurchaseInvoice, upsertJournalEntry, upsertEmployee, upsertEmployment answer created, updated, unchanged or deleted. Sending the same data again writes nothing. Invoices stay drafts until finalise: true; after that only metadata changes. Employments are created as drafts; the company finalises the terms in FinBaze. Worked hours (object=worked_hours) are bulk only for now: drafts can be corrected, approved hours are final. setObjectMetadata and platformObjectByExternalId complete the set.
  • Bulk jobs. POST /v2/platform/bulk-jobs?object=… takes NDJSON (optionally gzip) with an Idempotency-Key, answers 202 and applies the lines in the background. platformBulkJob, platformBulkJobs and cancelPlatformBulkJob report and control it, line by line. New scopes bulk:read and bulk:write.
  • Payments. MonetaryAccountPayment gains valueDate, endToEndId, bankReference and details (read-only in the app; feeds fill them from Wave 3.2).

2026-10-22#

  • Components. Nine more views render: onboarding, action-centre, transactions, transaction-detail, purchase-inbox, tax-calendar and the vat-return, icp-return and oss-return overviews. tax-authorisation connects UK companies to HMRC in a popup (Dutch companies need no authorisation); tax-payments is still a placeholder. startHmrcOrganisationConnection takes an optional redirectTarget (web or embed). The purchase-inbox upload runs under the component feature upload.

  • Embed sessions. createEmbedSession, exchangeEmbedSession, revokeEmbedSession, revokeEmbedSessionsForUser, embedContext.

  • Allowed origins and embed settings for platforms.

  • Booking summaries and onboarding requirements for components and headless hooks: bookingSummaries, onboardingRequirements.

  • SDKs. @finbaze/embed-js, @finbaze/embed-react (components and /headless hooks) and @finbaze/platform-sdk 1.0 (built; npm release pending).