---
name: finbaze-embed-nextjs
description: Integrate FinBaze embedded components (tax calendar, VAT return, booking chip, receipt button, notification banner and more) into a Next.js app with @finbaze/embed-react. Use when the user asks to add FinBaze, FinBaze for Platforms, embedded bookkeeping or tax filing to a Next.js project.
---

# Integrate FinBaze embedded components into a Next.js app

FinBaze for Platforms puts bookkeeping and tax filing inside a partner's product. The browser shows FinBaze components that run in an iframe on FinBaze's origin. The partner's **server** creates a single-use client secret for each session; the browser never sees a secret key.

Work in this order. Do not skip step 2.

## 0. Look things up, do not guess

- Docs index for agents: `https://developers.finbaze.com/llms.txt`. Full text: `/llms-full.txt`. Any page as Markdown: add `.md`.
- If the docs MCP server is available (`https://mcp.finbaze.com/docs/mcp`), use `get_component` for options and events, `get_error_code` for an error, `get_operation` for a GraphQL call.
- Component names: `onboarding`, `tax-authorisation`, `notification-banner`, `action-centre`, `transactions`, `transaction-detail`, `purchase-inbox`, `tax-calendar`, `vat-return`, `icp-return`, `oss-return`, `tax-payments`, `booking-chip`, `receipt-button`, `tax-due-chip`, `accountant-connect`, `company-transfer`, `contact-support`, `support-access`, and, from the full catalogue, `company-settings`, `bank-connections`, `users-and-access`, `data-export`, `dashboard`, `insights`, `bank-rules`, `reconciliation`, `payment-batches`, `sales-invoices`, `invoice-editor`, `quotes`, `recurring-invoices`, `customers`, `products`, `reminders`, `purchase-invoices`, `suppliers`, `document-requests`, `reports`, `general-ledger`, `journal`, `assets`, `loans`, `vehicles`, `projects`, `cost-centers`, `year-end`, `annual-accounts`, `cit-return`, `uk-itsa`, `us-1120`, `employees`, `pay-runs`, `payslip-viewer`, `payroll-filings`, `assistant`, `agent-tasks`, `automations`, `suite`, `receipt-dropzone`, `category-picker`, `ledger-account-picker`, `vat-code-picker`, `invoice-status-badge`, `invoice-pdf-preview`, `file-now-button`, `vat-reservation-meter`, `profit-tile`, `revenue-tile`, `cash-runway-tile`, `period-picker`, `customer-picker`, `product-tax-code-picker`, `requirements-checklist`, `onboarding-progress`, `company-search`, `approval-button`, `accountant-badge`, `engagement-card`, `feed-status`. In React they are `FinBaze` + PascalCase (`FinBazeVatReturn`); in the session API they are camelCase (`vatReturn`). Never invent a component name: check `get_component` or `/components`.

## 1. Preconditions the user must provide

- A FinBaze for Platforms **sandbox** account with a secret or restricted key (`fb_sk_test_…`, `fb_rk_test_…`) and the publishable key (`fb_pk_test_…`). Test keys use `https://sandbox.api.platform.finbaze.com`.
- The page's origin added under **Embedding → Allowed origins** in the developer portal (`http://localhost:3000` for local work).
- A company and a user: see `references/session-route.md`. Use the sandbox demo company if the user has none (`seedTestCompany`).

Ask for what is missing. Never put a key in source code; use environment variables (`FINBAZE_SECRET_KEY`, `NEXT_PUBLIC_FINBAZE_PUBLISHABLE_KEY`, `FINBAZE_COMPANY_ID`).

## 2. The server route (required)

Create `app/api/finbaze/session/route.ts`. It checks the partner's own sign-in, then calls `createEmbedSession` and returns `{ clientSecret }`. Read `references/session-route.md` for the code. Rules:

- The secret key stays on the server. It is never in a `NEXT_PUBLIC_` variable.
- Every call returns a **new** client secret. It is single use and expires after 15 minutes. Never cache it, never reuse it.
- Pass `previousSessionId` through when the browser sends it, so refreshes form one chain.
- `components` lists exactly the components the page mounts. An embed token can run only those components' operations.

## 3. The provider (client component)

Install `@finbaze/embed-react` and wrap the signed-in part of the app once:

```tsx
"use client";
import { FinBazeProvider } from "@finbaze/embed-react";

export function FinBaze({ children }: { children: React.ReactNode }) {
  return (
    <FinBazeProvider
      publishableKey={process.env.NEXT_PUBLIC_FINBAZE_PUBLISHABLE_KEY!}
      appearance={{ theme: "auto" }}
      fetchClientSecret={async ({ previousSessionId }) => {
        const res = await fetch("/api/finbaze/session", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ previousSessionId }) });
        if (!res.ok) throw new Error("Could not create a FinBaze session");
        return (await res.json()).clientSecret as string;
      }}
      onError={(error) => console.error(error.code, error.message)}
    >
      {children}
    </FinBazeProvider>
  );
}
```

One provider per signed-in user. Components render in the browser only: in the App Router put them in client components (`"use client"`).

## 4. Mount components

```tsx
"use client";
import { FinBazeTaxCalendar, FinBazeVatReturn } from "@finbaze/embed-react";

export function Taxes() {
  return (
    <div style={{ height: 640 }}>
      <FinBazeVatReturn period="current" />
    </div>
  );
}
```

- **Views** (`tax-calendar`, `vat-return`, `transactions`, …) fill their parent: give the parent a height.
- **Elements** (`booking-chip`, `receipt-button`, `tax-due-chip`) are inline; place them next to the partner's own UI. `booking-chip` and `receipt-button` take `transactionId`, or `bankAccountId` plus `externalId` (the partner's own transaction id).
- Events are props: `filing.submitted` becomes `onFilingSubmitted`.
- Use `navigation="external"` and handle the `navigate` event when the partner's router should handle moves.

## 5. Only if the product needs them

- Pay a VAT return from the partner's account: `onPaymentRequested` on the provider, a `payments` provider registered for the platform, and the bank transaction pushed back with the line's `endToEndId`. Never report "paid".
- Strong customer authentication: `onStepUp`, signed on the server. See `/concepts/delegated-actions`.
- Camera: `onCameraCapture`. Without it a file input opens the phone's camera.
- Filing needs the company's consent, given by its admin in `onboarding` or `tax-authorisation` (`TAX_AUTHORISATION_REQUIRED` otherwise). The server token cannot give it.

## 6. Verify

Run through `references/checklist.md`. Typical errors and fixes are in `/reference/errors`: `EMBED_ORIGIN_NOT_ALLOWED` (add the origin), `EMBED_SECRET_USED` (a secret was reused), `EMBED_COMPONENT_NOT_ALLOWED` (the component is not in the session), `FEATURE_DISABLED` (the company's features hide it).

## Never

- Never ship a secret or restricted key to the browser or commit it.
- Never cache or reuse a client secret.
- Never call FinBaze's GraphQL API from the browser with a server key.
- Never edit the iframe or inject CSS. Look is controlled with `appearance` (validated variables only).
- Never invent props, events or component names.
