Skip to main content

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

Do you need one?

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.

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

The permission model

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

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

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

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.
Scopes by capability: Use getApplicationScopes to read what’s currently granted. Full reference: Application Scopes.
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.

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

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
2

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
3

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
4

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

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
6

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
7

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

Rules that bite

Worth internalizing before you start, because each of these fails quietly or confusingly.
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.
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.
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.
Authorization: Basic <base64(client_id + ":" + client_secret)>. Encode the joined string. Most integration failures at the exchange step are this.
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.
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.

OAuth apps vs. widgets

Both are applications. Both use the permission model above. The difference is who builds the consent surface. You can mix them: register and KYC users over the API, then open a widget only for consent and sensitive capture. See Embedded Widgets for the hybrid patterns.

Next steps

Create an OAuth app

Register your application from the OAuth Integration template.

Configure OAuth app

Scopes, redirect URIs, webhooks, branding.

Client-facing grant flow

Build the authorize URL and handle the callback.

Exchange an authorization code

Turn a code into an access token and refresh token.

Refresh an access token

Stay authorized without re-prompting the user.

Deploy to production

Re-register against live hosts and go live.
Building a platform where every one of your customers has a Fluz account? Build a platform walks the whole pattern end to end, and every capability on API Features works identically on a connected account.