Skip to main content
This is the step where a real person decides whether to let your application touch their Fluz account. You redirect them to Fluz, they approve, and Fluz redirects them back to you with a short-lived authorization code. Everything before this page is configuration. Everything after it is token handling. This is the only step your user sees.

Where this sits

Note what Fluz absorbs in the middle of that diagram: account creation, sign-in, two-factor authentication, and identity verification if the user hasn’t been verified yet. You don’t build any of it, and you never see the credentials.

Before you start

1

Your app is configured

Permissions selected on the Permissions tab, redirect URI registered on the OAuth tab, client_id and client_secret in your secret store. See Configure OAuth App.
2

Your callback route exists and can reach your session store

It needs to read state and compare it against something you persisted before the redirect.
3

Your Permissions tab holds exactly the scopes this flow needs

The consent screen is built from your app configuration, not from the authorize request. Whatever is selected on the Permissions tab is what the user is asked to approve, so trim it before you send anyone through — this is your highest-drop-off step and its length comes entirely from that tab. See Application Scopes for the catalog.

Step 1 — Build the authorize URL

Direct the user to the /authorize endpoint with the following query parameters.
There is no scopes parameter. Earlier versions of this flow accepted a scopes query parameter and intersected it with your app configuration. That parameter is now ignored: the permissions shown on the consent screen are derived entirely from your app’s Permissions tab. Removing scopes from an existing authorize URL produces an identical consent screen — you can drop it whenever convenient.

Environments

Encoding rules

URL-encode the redirect_uri and external_id values. Build the query string with your language’s URL encoder rather than string concatenation and this takes care of itself. A complete staging example:

What the user sees

Sample OAuth permissions page Your app name, avatar, and description come straight from the Overview tab, and the permission lines are your selected scopes grouped under readable headers. If this screen looks wrong, the fix is on Configure OAuth App, not in your code. The permission list is read-only. The user reviews it and accepts the whole set, or they don’t complete the authorization — there are no per-scope checkboxes to opt out of. Spend-account selection, where your app uses it, is a separate control and still appears.

Account selection

Before the consent screen, the user may be asked which Fluz account this authorization applies to — their personal account, an existing business account, or a new business they want to register. Which of those happens depends on the app’s configuration and on what accounts the user already has. For a consumer-only integration nothing changes: the user has one account, the picker is skipped, and the flow looks exactly as it always did. If you send external_id and it already resolves to an account from a previous authorization, the picker is skipped too. If your app is business-enabled, read Business Accounts in OAuth before you build — the authorize URL is the same, but the account the code resolves to may not be the user’s personal one.

Step 2 — Protect the flow with state

The reference table calls state optional. In a redirect-based authorization flow it is your only defense against having someone else’s authorization code planted in your user’s session, so build it in from the first commit rather than adding it later.
1

Generate an unguessable value

At least 128 bits from a cryptographically secure source. Not a timestamp, not a user ID, not a counter.
2

Store it server-side, bound to the browser session

Session store, signed cookie, or short-TTL cache keyed to the session. Not in a global.
3

Compare on the way back, and reject on mismatch

Missing, unrecognized, or already-used state means abandon the request — do not exchange the code. Use a constant-time comparison.
4

Consume it

Delete it after a successful match so the same callback can’t be replayed.
state travels through the user’s browser. It’s fine to use it to carry a lookup key — which user, which flow, which page to return to — but never put anything sensitive or trusted in the value itself.

Step 3 — Handle the callback

On approval, Fluz redirects the user to your redirect_uri with: If the request was misconfigured, the redirect carries an error message describing what didn’t match.
Exchange the code immediately, once, from your server. It is single-use and short-lived. Make your callback route idempotent — a user refreshing the page, a link prefetcher, or a browser retry will hit it twice, and the second attempt must not corrupt state or surface an error to the user who already succeeded.
Next: Exchange an OAuth authorization code.

Step 4 — Reconcile what you actually got

The exchange response includes the scope array the user approved. That array, not your assumptions, is the truth about what your integration can do. Consent is now all-or-nothing, so the common cause of a missing scope is no longer a user opting out — it’s that the scope isn’t enabled on your app’s Permissions tab, in which case it was never offered. The flow still completes successfully and your API calls fail later. A grant issued before you widened the Permissions tab also won’t carry the newer scopes until that user re-authorizes. Read the returned scopes, persist them alongside the tokens, and gate your features on them. If something essential is missing, tell the user plainly and offer to re-run the flow.

Designing the moment

The consent screen converts far better when the user understands why they’re seeing it.
  • Explain before you redirect. One sentence on your own page — “Connect your Fluz account so we can send your payouts” — outperforms dropping someone cold onto a permissions screen.
  • Trigger it in context. At the point of first payout or first card, not buried in account settings.
  • Full-page redirect over a popup. Popups get blocked, and the flow includes 2FA and possibly identity verification, which is uncomfortable in a small window. If you need to stay in-page, use an embedded widget instead, which is built for exactly that.
  • Handle the return trip. Land the user where they were, with the thing they were trying to do now working. state is how you know where that was.
  • Have a re-authorization path. Refresh tokens expire and users revoke access. Build the “reconnect” flow at the same time as the connect flow, not after the first support ticket.
  • Consider skipping it. If your users don’t already have Fluz accounts, a widget handles registration, verification, and consent in one hosted flow with no redirect. See Embedded Widgets.

Troubleshooting


Next steps

Exchange an authorization code

Turn the code into an access token and refresh token.

Refresh an access token

Stay connected without sending the user back through consent.

Configure OAuth app

Fix anything the consent screen got wrong.

Embedded widgets

Skip the redirect entirely with a hosted in-page flow.