> ## 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.

# Overview

> How your application gets permission to act on a Fluz user's account — the credentials involved, the permission model, the token lifecycle, and which page to read next.

## Why OAuth exists here

Your application can already do everything the Fluz API offers **on your own account**. You mint a token with your API key and go — see [Authentication](/concepts/authentication) and [API credentials](/get-started/api-credentials).

An OAuth application is what you need when the account isn't yours.

The moment you want to issue a card on a customer's wallet, pull from their linked bank account, read their transactions, or pay them out, you need that person's explicit permission — and you need it in a form Fluz can verify, scope, expire, and revoke. That's what an OAuth application is: **a registered identity for your software, plus a consent mechanism that turns a user's approval into a token your server can use.**

<Info>
  Once you hold a customer-scoped token, the API is identical. `createVirtualCard` on your own token creates a card on your wallet; the same mutation on a customer token creates it on theirs. OAuth changes *whose account you're touching*, not what you can do.
</Info>

***

## Do you need one?

<Tabs>
  <Tab title="No — your own account only">
    You're issuing cards, buying gift cards, or moving money **within your own Fluz account**: a disbursement engine, a bulk card run, an internal spend tool, an ERP sync.

    Use your application API key to call `generateUserAccessToken` directly. No OAuth app, no consent screen, no redirect. Start at [API credentials](/get-started/api-credentials).
  </Tab>

  <Tab title="Yes — customer accounts">
    You're building a platform where **your users have their own Fluz accounts** and you act on their behalf.

    You need an OAuth application, and each user has to grant your app scopes once. Then you hold a refreshable, customer-scoped token. Start with [Create an OAuth App](/create-an-o-auth-app), then see [Build a platform](/build-a-platform).
  </Tab>

  <Tab title="You already have one">
    You're embedding a [Fluz Widget](/developers/widgets).

    A widget **is** an OAuth application — one that ships with a hosted front end for the consent step instead of making you build a redirect flow. The credentials, scopes, and token mechanics on this page all apply to it. See [Configure App Widget](/developers/configure-app-widget).
  </Tab>
</Tabs>

***

## Three sets of credentials, three different jobs

The most common source of confusion in this section is that a Fluz application carries more than one credential pair, and they are not interchangeable.

| Credential                           | Where it lives               | What it's for                                                                                                                                                           | Ever leaves your server?                |
| :----------------------------------- | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------- |
| **API Key** / **API Secret**         | Overview tab of your app     | Identifies your *application*. Mints tokens on your own account (`Authorization: Basic <API_KEY>`) and signs widget pre-approved transaction tokens.                    | Never                                   |
| **Client ID** / **Client Secret**    | Overview tab of your app     | Identifies your app to the *authorization server*. Used in the authorize URL and to exchange or refresh codes (`Authorization: Basic base64(client_id:client_secret)`). | Client ID is public; secret never       |
| **Access Token** / **Refresh Token** | Returned per user, per grant | Acts on one specific user's account with one specific set of scopes.                                                                                                    | Sent as `Authorization: Bearer <token>` |

<Warning>
  Every secret here mints authority. An `apiSecret` leak lets someone sign transactions as your platform; a `client_secret` leak lets someone exchange codes as your app. Keep both server-side, out of browser bundles, out of mobile binaries, out of source control.
</Warning>

***

## The permission model

Fluz enforces permissions at two levels, and an application's effective access is the **intersection** of the two.

<Steps>
  <Step title="App-level grant — the ceiling">
    Set on the **Permissions** tab of your app. This is the maximum your application may ever request, independent of any user. A scope you haven't enabled here is silently ignored if you put it in an authorize URL — the request won't error, the scope simply won't be granted.

    Some scopes are administered by Fluz rather than self-selected. `PCI_COMPLIANCE` is granted at the application level only, to developers who have demonstrated PCI DSS compliance, and cannot be requested when generating a token.
  </Step>

  <Step title="User-level grant — the floor">
    Set by the end user on the consent screen. They see the scopes you requested — grouped under readable top-level headers rather than raw enum values — and approve them. Anything they decline is not granted.
  </Step>

  <Step title="Both must be live">
    Validated at `generateUserAccessToken`, not at call time. Both grants must exist and be unexpired. A revoked or lapsed grant therefore surfaces as a **token generation failure**, not as a permission error midway through a flow — which is usually the first place to look when a previously working integration stops working.
  </Step>
</Steps>

Scopes by capability:

| Area                   | Scopes                                                                              |
| :--------------------- | :---------------------------------------------------------------------------------- |
| Funding sources        | `LIST_PAYMENT`, `MANAGE_PAYMENT`                                                    |
| Deposits & withdrawals | `MAKE_DEPOSIT`, `MAKE_WITHDRAW`                                                     |
| Gift cards             | `LIST_OFFERS`, `PURCHASE_GIFTCARD`, `REVEAL_GIFTCARD`, `LIST_PURCHASES`             |
| Virtual cards          | `CREATE_VIRTUALCARD`, `EDIT_VIRTUALCARD`, `REVEAL_VIRTUALCARD`, `CREATE_SHARE_LINK` |
| Card data              | `PCI_COMPLIANCE` (application-level, Fluz-administered)                             |

Use `getApplicationScopes` to read what's currently granted. Full reference: [Application Scopes](/application-scopes).

<Note>
  **Ask for less.** A shorter consent screen converts better, and a narrower token limits the damage if it leaks. Request what the flow in front of you needs and mint a new token when you need more.
</Note>

***

## The lifecycle, end to end

Each step below is a deep-dive page in this section. This is the map; the pages are the territory.

<Steps>
  <Step title="Create the app">
    From the developer dashboard, choose **Browse templates** and add the **OAuth Integration** template. Name it, subtitle it, describe it — those three fields are what your users will see on the consent screen, so write them for a human, not for your issue tracker.

    → [Create an OAuth App](/create-an-o-auth-app)
  </Step>

  <Step title="Configure it">
    On the **Permissions** tab, select your scope ceiling. On the **OAuth** tab, set your **Redirect URIs** (public, no query parameters, any number of them) and your **Webhook URLs** (each optionally subscribed to specific events; a URL with none selected becomes a catch-all). Add an avatar and logomark on **Overview** — the consent screen looks unfinished without them.

    → [Configure OAuth App](/configure-o-auth-app)
  </Step>

  <Step title="Send the user to authorize">
    Redirect to `/authorize` with `response_type=code`, your `client_id`, a registered `redirect_uri`, a space-delimited `scopes` list, and an optional `state` value you want handed back to you untouched.

    → [Client-facing OAuth grant flow](/client-facing-o-auth-grant-flow)
  </Step>

  <Step title="Receive the code">
    On approval, Fluz redirects to your `redirect_uri` with `code` and your original `state`. On a misconfiguration, the redirect carries an error message describing what didn't match.
  </Step>

  <Step title="Exchange the code for tokens">
    Call `/token/exchange` with the `code` and **the exact same `redirect_uri`**, authenticated with `Authorization: Basic base64(client_id:client_secret)`. You get back an `accessToken`, a `refreshToken`, expiry timestamps, and the confirmed scope array.

    → [Exchanging an OAuth authorization code](/exchanging-an-o-auth-authorization-code)
  </Step>

  <Step title="Refresh, don't re-prompt">
    Call `/token/refresh` with the `refresh_token` and the same Basic auth header. Access tokens are deliberately short-lived — on the order of ten minutes — while refresh tokens last roughly a month. Refresh silently in the background; only send a user back through consent when the refresh token itself has expired or the grant has been revoked.

    → [Refreshing an OAuth accessToken](/refresh-o-auth-access-token)
  </Step>

  <Step title="Go live">
    Staging and production are separate environments with separate applications and separate credentials. Nothing carries over — you register the app, the redirect URIs, and the webhook endpoints again against production hosts.

    → [Deploying to Production](/deploying-to-production)
  </Step>
</Steps>

***

## Rules that bite

Worth internalizing before you start, because each of these fails quietly or confusingly.

<AccordionGroup>
  <Accordion title="Redirect URIs must match exactly — twice">
    The `redirect_uri` you send to `/authorize` must be registered on your app, and the one you send to `/token/exchange` must be byte-identical to the one you used at `/authorize`. Trailing slashes, `http` vs `https`, and host casing all count. Register no query parameters on the URI itself — use `state` to carry context instead.
  </Accordion>

  <Accordion title="Unenabled scopes are ignored, not rejected">
    Request a scope you haven't checked on the Permissions tab and the authorize request still succeeds — the scope is dropped. Always read the `scope` array in the exchange response and treat it, not your request, as the truth about what you can do.
  </Accordion>

  <Accordion title="An authorization code is single-use and short-lived">
    Exchange it immediately, server-side, once. If your redirect handler can be replayed — a user refreshing the callback page, a link prefetcher — make sure a second attempt doesn't corrupt state.
  </Accordion>

  <Accordion title="Basic auth is base64 of the pair, not of each part">
    `Authorization: Basic <base64(client_id + ":" + client_secret)>`. Encode the joined string. Most integration failures at the exchange step are this.
  </Accordion>

  <Accordion title="`state` is your only channel back">
    The redirect is a fresh browser navigation. If you need to know which user, which flow, or which page to return to, put a signed or server-looked-up reference in `state`. Don't put anything sensitive in it — it travels through the user's browser.
  </Accordion>

  <Accordion title="Token failures are usually grant failures">
    If `generateUserAccessToken` starts failing for a user who worked yesterday, check whether the app-level grant or the user-level grant expired or was revoked, before you go looking at your code.
  </Accordion>
</AccordionGroup>

***

## OAuth apps vs. widgets

Both are applications. Both use the permission model above. The difference is who builds the consent surface.

|                                           | OAuth application                         | Widget                                  |
| :---------------------------------------- | :---------------------------------------- | :-------------------------------------- |
| Consent UI                                | You build the redirect flow               | Fluz renders it in a modal on your page |
| User leaves your site                     | Yes, to `/authorize`                      | No                                      |
| Sensitive data (PAN, SSN, documents, PIN) | You handle it, and you're in scope for it | Fluz collects and encrypts it           |
| Registration and KYC                      | Yours to build, or via API                | Included in the flow                    |
| Control over presentation                 | Complete                                  | Limited to branding                     |
| Time to first working flow                | Days                                      | Hours                                   |

You can mix them: register and KYC users over the API, then open a widget only for consent and sensitive capture. See [Embedded Widgets](/developers/widgets) for the hybrid patterns.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Create an OAuth app" icon="plus" href="/create-an-o-auth-app">
    Register your application from the OAuth Integration template.
  </Card>

  <Card title="Configure OAuth app" icon="sliders" href="/configure-o-auth-app">
    Scopes, redirect URIs, webhooks, branding.
  </Card>

  <Card title="Client-facing grant flow" icon="user-check" href="/client-facing-o-auth-grant-flow">
    Build the authorize URL and handle the callback.
  </Card>

  <Card title="Exchange an authorization code" icon="arrow-left-right" href="/exchanging-an-o-auth-authorization-code">
    Turn a code into an access token and refresh token.
  </Card>

  <Card title="Refresh an access token" icon="refresh-cw" href="/refresh-o-auth-access-token">
    Stay authorized without re-prompting the user.
  </Card>

  <Card title="Deploy to production" icon="rocket" href="/deploying-to-production">
    Re-register against live hosts and go live.
  </Card>
</CardGroup>

<Info>
  Building a platform where every one of your customers has a Fluz account? [Build a platform](/build-a-platform) walks the whole pattern end to end, and every capability on [API Features](/features) works identically on a connected account.
</Info>
