> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fluz.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Embedded Widgets Overview

> Drop a hosted Fluz flow into your own product to collect user permissions, capture sensitive data inside our PCI scope, and confirm money movement — then run everything else server-side over the API.

## What a widget actually is

A Fluz Widget is a hosted, Fluz-rendered flow that you embed in your own site with a few lines of JavaScript. It runs in a modal on top of your page, on your domain, under your branding.

It exists to do three jobs that you should not have to build yourself:

<CardGroup cols={3}>
  <Card title="Collect the user's permission" icon="shield-check">
    The widget is how an end user creates or logs into their Fluz account and **grants your application the scopes it needs to act on that account**. No grant, no API access.
  </Card>

  <Card title="Capture sensitive data" icon="lock">
    Card numbers, SSNs, identity documents, and PINs are collected by Fluz, inside Fluz's PCI DSS environment, and encrypted on our side. They never touch your servers.
  </Card>

  <Card title="Confirm money movement" icon="arrow-left-right">
    The user sees and approves the amount and direction of a transfer in a surface they can trust, which is what turns a pre-authorized token into a completed transaction.
  </Card>
</CardGroup>

Everything else — issuing the card, pulling the funds, checking the balance, reading transactions — is yours to do over the API, on your own schedule, with no user present.

<Info>
  **The mental model:** the widget is a *consent and sensitive-data surface*, not a product. It is the narrow, high-compliance part of the flow. The API is where the work happens.
</Info>

***

## The division of labor

| Job                                         | Widget | API                                                                  |
| :------------------------------------------ | :----- | :------------------------------------------------------------------- |
| Create the user's Fluz account              | ✅      | ✅ [User Registration](/docs/user-registration)                       |
| Verify identity (KYC)                       | ✅      | ✅ [User KYC Verification](/docs/user-kyc-verification)               |
| Verify a business (KYB)                     | —      | ✅ [Business Registration](/docs/business-registration)               |
| Obtain scope grants from the user           | ✅      | ✅ [OAuth grant flow](/docs/grant-widget-user-permissions-todo)       |
| Collect a card PAN / CVV                    | ✅      | Tokenized only                                                       |
| Collect an SSN or ID document               | ✅      | Pass-through if you already hold it                                  |
| Set a transaction PIN                       | ✅      | —                                                                    |
| Confirm a specific transfer amount          | ✅      | —                                                                    |
| Link a bank account via Plaid               | ✅      | ✅ [Funding Sources](/features/funding-sources)                       |
| Issue, edit, lock, or reveal a virtual card | —      | ✅ [Virtual Cards](/features/virtual-cards)                           |
| Deposit, withdraw, transfer, send money     | —      | ✅ [Wallets & Transfers](/features/move-funds-with-external-accounts) |
| Buy gift cards, read the catalog            | —      | ✅ [Merchant Catalog](/merchant-catalog)                              |
| Read transactions, annotate, run approvals  | —      | ✅ [Transactions](/features/get-all-transactions)                     |
| Bulk-issue up to 10,000 cards               | —      | ✅ [Bulk Operations](/features/virtual-cards)                         |

***

## You can run everything behind the scenes

This is the most commonly missed point about the widget: **it is not the only way to use Fluz, and it is not the way most work gets done.**

Once a user has granted your application scopes — whether through the widget or through the standalone [OAuth grant flow](/docs/grant-widget-user-permissions-todo) — your server holds a user access token. From that point, every capability listed on the [API Features](/features) page is available to you programmatically, with no widget open and no user watching:

<CardGroup cols={2}>
  <Card title="Funding sources" icon="coins" href="/features/funding-sources">
    Link bank cards and Plaid bank accounts, then pull funds on demand.
  </Card>

  <Card title="Wallets & transfers" icon="wallet" href="/features/move-funds-with-external-accounts">
    Open spend accounts, deposit, withdraw, and move funds internally or across accounts.
  </Card>

  <Card title="Virtual cards" icon="credit-card" href="/features/virtual-cards">
    Spend controls, lock/unlock, PINs, wallet provisioning, bulk issuance.
  </Card>

  <Card title="Open loop cards" icon="wallet-cards" href="/features/open-loop-cards/send-open-loop-cards">
    Generate hosted card links recipients claim, with full link lifecycle control.
  </Card>

  <Card title="Send money" icon="circle-dollar-sign" href="/features/account-to-account-transfers">
    Look up recipients by phone or email and transfer to other Fluz wallets.
  </Card>

  <Card title="Approvals & authorized users" icon="users" href="/features/approvals-and-requests">
    Add team members, issue them cards, and route approval requests.
  </Card>
</CardGroup>

The widget's job is to get you to the token. What you do after that is entirely server-side.

***

## Pick how much of the flow you hand to us

You do not have to choose "all widget" or "all API." Most integrations land somewhere in the middle, and the deciding factor is usually **what sensitive data you already hold and want to keep holding.**

<Tabs>
  <Tab title="Full widget">
    **You hand us the whole user journey.**

    The widget handles account creation, phone + 2FA login, KYC, PIN setup, the permissions grant, and the transaction confirmation. You render a button and generate a signed token.

    * Fastest path to production — measured in hours, not sprints.
    * Zero PCI scope, zero CIP data handling on your side.
    * Least control over look and feel between the click and the callback.

    **Good fit:** payout and withdrawal flows, marketplaces, gig platforms, rewards programs — anywhere you want money to leave your system without you becoming a financial institution.
  </Tab>

  <Tab title="Hybrid (most common)">
    **You own the parts you already own; we own the parts you'd rather not.**

    Register the user yourself with [`registerUser`](/docs/user-registration) using the profile data you already collected at signup. Run KYC yourself with [`verifyUserInformation`](/docs/user-kyc-verification) if you already hold the SSN and address. Then open the widget only for the steps that genuinely need it:

    * the permissions grant,
    * document upload when KYC comes back `DECLINED` or needs review,
    * card PAN capture,
    * PIN setup,
    * the transaction confirmation screen.

    Your onboarding stays yours. The user never re-types information you already have. The widget appears for a narrow, obviously-financial moment and then gets out of the way.

    **Good fit:** platforms with an existing KYC'd user base, fintechs, anyone who has already done identity verification and does not want to make the user do it twice.
  </Tab>

  <Tab title="Headless / API only">
    **No widget at all.**

    Register users, verify them, link funding sources, issue cards, and move money entirely over the API. Obtain scope grants through the standalone [OAuth authorization flow](/docs/grant-widget-user-permissions-todo) — a redirect, not an embed — or operate on your own platform account.

    * Full control over every pixel.
    * **You** are responsible for PCI DSS scope if you collect card data, and for the security of any CIP data you handle.
    * Some flows still require a hosted surface: revealing full card details to an end user and collecting identity documents are the usual holdouts.

    **Good fit:** bulk issuance, back-office operations, disbursement runs, ERP and accounting integrations, and any flow with no end user in the loop.
  </Tab>
</Tabs>

<Note>
  **On registering users via API:** if you register and KYC a user yourself and *then* open the widget, pass `externalId` in the pre-approved transaction token so we can match the session to the account you already created rather than starting a new one. You can also pass `phoneNumber`, `firstName`, `lastName`, `email`, and `username` to skip the corresponding steps in the widget. See [Set Up Your Server](/developers/setting-up-your-server).
</Note>

***

## How widgets relate to OAuth applications

A widget **is** an OAuth application. It is not a separate object with a separate permission model — it is an OAuth app that ships with an embeddable front end.

<Steps>
  <Step title="You define the ceiling (app scopes)">
    On the **Permissions** tab of your app, you select the scopes your application is allowed to request. This is the maximum your app can ever ask for, regardless of what any individual user agrees to. Scopes that a given widget type cannot function without are grouped at the bottom of the tab and cannot be unselected.

    See [Application Scopes](/docs/application-scopes) for the full list — `MAKE_DEPOSIT`, `MAKE_WITHDRAW`, `LIST_PAYMENT`, `CREATE_VIRTUALCARD`, `REVEAL_VIRTUALCARD`, `PURCHASE_GIFTCARD`, and the rest.
  </Step>

  <Step title="You configure where the grant can go (OAuth tab)">
    **Origin** — the domain hosting the widget. **Redirect URIs** — where our authorization server may send the user back, no query parameters, and it must match exactly at exchange time. **Webhook URLs** — one or many REST endpoints, each optionally subscribed to specific events; a URL with no events selected becomes a catch-all.

    See [Configure App Widget](/developers/configure-app-widget).
  </Step>

  <Step title="The user sets the floor (user scopes)">
    When the widget opens, the user is shown the scopes you requested — grouped under readable top-level headers rather than listed as raw enum values — and approves them. Anything they decline is simply not granted.
  </Step>

  <Step title="Both grants must be live">
    An application's effective permissions are the **intersection** of the app-level grant and the user-level grant, and both must be unexpired. This is enforced at `generateUserAccessToken`, not at call time — so a revoked or lapsed grant surfaces as a token failure, not a mysterious mid-flow error.
  </Step>

  <Step title="Code becomes tokens">
    The grant produces an authorization `code` at your redirect URI. Exchange it at `/token/exchange` with a Basic auth header of `client_id:client_secret` for an `accessToken`, a `refreshToken`, and the confirmed `scope` array. See [Exchanging an authorization code](/docs/exchanging-an-oauth-authorization-code) and [Refreshing an access token](/docs/refreshing-an-oauth-access-token).
  </Step>
</Steps>

<Warning>
  The pre-approved transaction token (`patToken`) and the OAuth access token are **different things** and do different jobs. The `patToken` is a short-lived, single-transaction JWT signed with your `apiSecret` that authorizes *one* movement of *one* amount. The OAuth `accessToken` is what lets your server act on a user's account over time. A widget session typically involves both.
</Warning>

***

## PCI compliance and sensitive data

When the widget is open, the sensitive fields inside it are Fluz's, not yours. The user is typing into our iframe, posting to our servers, under our compliance program.

That means Fluz takes responsibility for:

* **Card data.** PANs, expiration dates, and CVVs are captured and stored in accordance with PCI DSS requirements and encrypted at rest on our side. Your page never sees them, your logs never contain them, and your infrastructure stays out of PCI scope for these flows.
* **Full card reveal.** Showing an end user their own virtual card number is a hosted Fluz surface for the same reason.
* **CIP and identity data.** SSNs, dates of birth, addresses, and uploaded identity documents are collected and retained inside our verification environment.
* **PINs.** Set and stored by us, never transmitted to you.
* **Bank credentials.** Plaid link flows run inside the widget; you never handle the user's banking login.

What stays your responsibility: your `apiSecret` and `client_secret`. The Installation tab renders working snippets that contain your real credentials, which is convenient and also a hazard — **generate the `patToken` on your server, never in browser JavaScript.** Anything in your page source is public.

<Info>
  Fluz maintains SOC 2 Type II controls and handles card data in accordance with PCI DSS requirements. If your compliance team needs documentation for a vendor review, contact your Fluz account manager.
</Info>

***

## Getting your embed code

You do not hand-write the integration. The **Installation** tab of your app generates it for you, pre-filled with your app's real `apiKey`, and gives you two selectors:

**Transaction Type** — choose the direction of money movement:

| Installation tab label | `transactionType` in the JWT | What happens                                                                                                |
| :--------------------- | :--------------------------- | :---------------------------------------------------------------------------------------------------------- |
| **Pay-In**             | `DEPOSIT`                    | The user moves funds into Fluz and on into your operator spend account. Money flows *toward* your platform. |
| **Payout**             | `WITHDRAW`                   | Funds move from your operator spend account to the user's Fluz account. Money flows *toward* your user.     |

**Server Language** — the snippet that generates the signed pre-approved transaction token, in the language your backend actually uses:

<CardGroup cols={4}>
  <Card title="JavaScript" icon="square-code" />

  <Card title="Ruby" icon="gem" />

  <Card title="Python" icon="code" />

  <Card title="Go" icon="code" />

  <Card title="Java" icon="coffee" />

  <Card title="PHP" icon="code" />

  <Card title="C# / .NET" icon="code" />

  <Card title="More" icon="ellipsis" />
</CardGroup>

Switch the selector and the code block rewrites itself — correct JWT library, correct claim names, correct HS256 signing, correct one-day expiry. Copy it, drop in your `apiSecret` from your secret store, and you have a working token generator. Every variant is also documented in full at [Set Up Your Server](/developers/setting-up-your-server).

The client-side half is a single script tag plus a `FluzEmbedded.init(...)` call. You can let us render the button, or bind the modal to a button you already have. See [Adding the JS Widget to Your Page](/developers/adding-the-js-widget-to-your-page).

Your app's configuration lives at:

```text theme={null}
https://fluz.app/for-developers/overview/{appId}
```

for example `https://fluz.app/for-developers/overview/19be9561-a6a1-4e02-8243-10ede908ef33`. The tabs across the top — **Overview**, **Permissions**, **OAuth**, **Installation** — map exactly to the steps above.

***

## Start from a template

You do not start from a blank app. From the developer dashboard, choose **Browse templates** and pick the one closest to what you are building. A template pre-configures the app type, the required scopes, the transaction direction, and the sequence of screens the user will see — so a new app is functional the moment you finish naming it.

Templates available today include:

| Template                             | What it sets up                                                                                                       |
| :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
| **Withdraw to a virtual Mastercard** | A `Payout` flow ending in an instantly usable Fluz virtual card. Pre-selects the withdrawal and card-creation scopes. |
| **OAuth Integration**                | A permissions-only app with no embedded UI — for headless and redirect-based integrations.                            |

<Note>
  **Treat the template as a starting point, not a specification.** After it is created, go to the **Permissions** tab and shape the app around what you are actually trying to do — add the scopes your use case needs, remove the ones it does not. A payout widget that will later issue cards on the user's behalf needs `CREATE_VIRTUALCARD`; one that only moves cash does not. Requesting fewer scopes means a shorter consent screen and a higher completion rate, so ask for what you need and nothing more.
</Note>

Creating an app: [Add a New App Widget](/developers/add-a-widget) · Configuring it: [Configure App Widget](/developers/configure-app-widget) · Turning it off: [Disable or Delete Your App](/developers/disable-or-delete-your-app)

***

## What the end user sees

Once a user reaches a page hosting your widget and takes the action that opens the modal:

<Steps>
  <Step title="Log in or register">
    The user authenticates to their Fluz account with a 2FA code sent to their phone. If they do not have an account, they create one here. Passing `phoneNumber` in the `patToken` skips straight to the code entry step.
  </Step>

  <Step title="KYC">
    If you already hold the user's SSN, pass it to us and we validate it. If not, the widget runs the full KYC flow. Responses are `APPROVED`, `DECLINED`, `DUPLICATE`, or `ERROR` — see [User KYC Verification](/docs/user-kyc-verification) for what each means and how many attempts a user gets.
  </Step>

  <Step title="Grant permissions">
    The user reviews and approves the scopes your app requested.
  </Step>

  <Step title="Set a PIN">
    A Fluz-wide security measure, prompted again later for actions requiring elevated confirmation.
  </Step>

  <Step title="Confirm the transaction">
    The user sees the amount and direction and either approves or dismisses. Either way, you get an event.
  </Step>
</Steps>

### Pay-In: funds into your platform

<Info>
  Check the user's Fluz balance first to confirm they can cover the transaction.
</Info>

1. The user enters a deposit amount and clicks your button.
2. The widget presents a confirmation screen.
   * **Confirmed** → we initiate the transfer from the user's spend account to yours.
   * **Denied or dismissed** → we send an event.
3. You receive a completion or failure event.
4. Verify your own Fluz balance to confirm settlement.

### Payout: funds out to your user

<Info>
  Check your account's Fluz balance first. If you cannot cover the transfer, initiate a deposit from your funding source. Quarantine or hold the user's funds on your side to prevent double-spend while the transfer is in flight.
</Info>

1. The user enters a withdrawal amount and clicks your button.
2. The widget presents a confirmation screen.
   * **Confirmed** → we initiate the transfer from your operator spend account to the user's.
   * **Denied or dismissed** → we send an event.
3. You receive a completion or failure event.
4. The widget shows the user their withdrawal is complete and gives them direct access to their Fluz virtual card.

<Note>
  Every money-moving call needs a unique `jti` in the token for idempotency, and a unique `idempotencyKey` on the API side. See [Idempotency](/docs/idempotency-requests).
</Note>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Add a widget" icon="plus" href="/developers/add-a-widget">
    Create your first app from a template.
  </Card>

  <Card title="Configure OAuth & permissions" icon="shield" href="/developers/configure-app-widget">
    Scopes, origins, redirect URIs, webhooks.
  </Card>

  <Card title="Set up your server" icon="server" href="/developers/setting-up-your-server">
    Generate the pre-approved transaction token in your language.
  </Card>

  <Card title="Embed the widget" icon="code" href="/developers/adding-the-js-widget-to-your-page">
    Script tag, init call, button binding.
  </Card>

  <Card title="Everything the API can do" icon="sparkles" href="/features">
    The full capability surface, all of it available server-side.
  </Card>

  <Card title="Build a platform" icon="building-2" href="/build-a-platform">
    Run every capability on connected accounts with customer-scoped tokens.
  </Card>
</CardGroup>
