FinBaze for Platforms

Guides

Companies on your platform move through active, suspended, closed and archived, and can move to another platform or to direct FinBaze. Books, filings, documents and Digipoort or HMRC history are never deleted by any of this. Company lifecycle has the state rules; this guide is the runbook.

Customer stops paying: suspend#

mutation { suspendPlatformCompany(companyId: "…", reason: "non-payment", displayMessage: "Your account is on hold.") { id lifecycle { state } } }

The company becomes read-only (COMPANY_SUSPENDED on changes); cron work such as reminders and bank sync skips it. Reactivate with reactivatePlatformCompany. A company FinBaze suspended cannot be reactivated by you (SUSPENDED_BY_FINBAZE).

Customer stops trading: close, then archive#

query { companyClosingChecklist(companyId: "…", effectiveDate: "2026-10-31") { key label blocking done filingId } }

Close with closePlatformCompany(companyId: "…", effectiveDate: "2026-10-31"). Final returns can still be filed and exported. reopenPlatformCompany works within the reopen window (90 days by default). When every blocking checklist item is done, archivePlatformCompany detaches the company from your platform. Only exports work after that, and the books are kept until retentionUntil (7 years in the Netherlands by default). An open blocking item gives ARCHIVE_BLOCKED.

Events: company.suspended, company.reactivated, company.closed, company.reopened, company.archived.

Customer moves platform or to FinBaze#

The company's admin consents in the company-transfer element (it shows a versioned statement). Mount it with components: { companyTransfer: { enabled: true } }.

  • To another platform: the element shows a one-time code, once. The receiving platform's backend calls acceptCompanyTransfer(code: "…", externalId: "their-id").
  • To direct FinBaze: the admin enters an email and FinBaze emails a claim link; a FinBaze account claims the company and picks a plan.

On completion your platform loses access (users, limits, data sources, sessions) and you receive company.transferred_out; the receiving side gets company.transferred_in. You never see the new owner's id. cancelCompanyTransfer(transferId: …) cancels a pending one. Errors: TRANSFER_CODE_INVALID, TRANSFER_CODE_EXPIRED, TRANSFER_ALREADY_PENDING, TRANSFER_STATEMENT_OUTDATED.

Getting the books out#

Before a company leaves, give it its data.

mutation { requestXafExport(company: { externalId: "biz-1001" }, financialYear: 2025) { id status } }

Poll platformExportRun(id: …); when ready its document carries a downloadUrl (valid 15 minutes), and export.ready is sent. The audit file is Auditfile Financieel 4.0 (validated against the official XSD, VAT per line); pass version: "3.2" for a receiving system that still needs 3.2. Reports (platformReport), documents (companyDocument) and scheduled exports to your own bucket are in Getting data out.

Ending a user#

deprovisionPlatformUser(userId: …) revokes every token and session of the user at once and removes the user from all companies. A later upsertPlatformUser with the same externalUserId brings the user back with a new identity; old tokens stay dead.

Audit#

query { platformAuditLogs(first: 50) { edges { id at action actorKind requestId } nextCursor } }

Needs the audit:read scope and an admin role, and shows only your own companies. requestAuditLogExport starts an NDJSON export.

Try it in the sandbox#

Seed a company, suspendPlatformCompany, try a write and see COMPANY_SUSPENDED, reactivate, then close it with the checklist. The sandbox reset removes companies not marked preserve every night (Sandbox reset).

Pitfalls#

  • Archiving too early: blocking checklist items are the final returns.
  • Telling the company its books are deleted: they are kept until the retention date.
  • Handing a transfer code over by email: it works once and shows once; deliver it inside your product.