Endpoint map
The GraphQL host and the OAuth host are different services with different domains. Swapping one and not the other is the single most common cutover mistake — and it fails in a confusing way, because authorization succeeds and then every API call is rejected.
Reaching each portal
The live portal is athttps://fluz.app/for-developers. From there, the developer tab has an ‘Open Staging’ link into the staging portal — your login credentials are the same in both. Each portal shows only that environment’s applications and API keys.
Make the environment a variable
If any host, key, or redirect URI is a string literal in your codebase, fix that before cutting over. Everything environment-specific belongs in configuration.Default to staging, never to production. If a deployment loses its environment variable, you want it hitting the test environment, not moving real money.
Rebuild your app in the live portal
Work through Create an OAuth App again againsthttps://fluz.app/for-developers. Then check every one of these — each is something people forget:
1
Reissue every credential
Production
client_id, client_secret, apiKey, and apiSecret are all new values. Put them in your production secret store. Confirm no staging value survives in a production config.2
Re-register redirect URIs against production hosts
Your production callback URL, in its exact canonical form. Then remove any
localhost or staging URIs — a production app should not accept a redirect to a developer’s laptop.3
Re-point webhook URLs
At production endpoints that are publicly reachable, monitored, and alerting. Re-do the per-event subscriptions; they don’t copy across. If you used a catch-all URL in staging, decide whether you actually want that in production.
4
Set Origin to your production domain
For embedded widgets, this must match the domain actually serving the page, or the widget won’t load.
5
Re-select your scopes
Scope selections don’t transfer. Walk your integration’s API calls and confirm each one’s scope is checked on the production app’s Permissions tab. A missing scope is silently dropped, not rejected.
6
Finish the Overview tab
Name, subtitle, description, avatar, and logomark are what real users now see on a real consent screen deciding whether to give you access to their money. Placeholder text ships to production if you let it.
7
Confirm your app's status
Applications carry a status in the dashboard — an app sitting in review is not yet an app your customers can use. Confirm your production app is active before you announce anything.
What behaves differently in production
Staging mirrors production’s capabilities and transaction flows without real money. That’s most of the surface, but not all of it.
Three consequences worth planning for:
- Idempotency stops being optional. Every money-moving call needs a unique
idempotencyKey, and widget tokens need a uniquejti. In staging a duplicate is a nuisance; in production it’s a double payment. See Idempotency. - Your error handling gets exercised. Test users don’t get declined for insufficient funds or fail KYC in ways you didn’t script. Every failure path needs a defined user-facing outcome before launch, not after.
- Reconciliation matters. Verify balances on both sides of a transfer rather than assuming success from a 200 response.
Data hygiene, both directions
Never put production data in staging. No real customer details, no real financial information, no PII. Staging is for data created explicitly for testing. The reverse also holds: don’t carry test users, test funding sources, or test webhook payloads into production. Test artifacts in a live ledger are hard to unpick later, and some of them can’t be deleted.Widget cutover
If you’re shipping an embedded widget, the same rules apply plus these:- Regenerate the embed code from the production app’s Installation tab. The
apiKeybaked into the snippet is environment-specific. - Sign
patTokens with your productionapiSecret, server-side. Confirm the secret is loaded from your production secret store and that the token generator is not still pointing at a staging value. - Confirm
Originmatches your production domain exactly. - Re-check the transaction type. Pay-In and Payout move money in opposite directions; verify the direction against a real transfer before you open it to users.
Pre-launch checklist
Configuration
Configuration
- Production app created, configured, and active
- All four credentials reissued and stored in the production secret store
- No staging or localhost redirect URIs remain on the production app
- Webhook URLs point at production endpoints, with event subscriptions re-selected
- Scopes re-selected and matched against your actual API calls
- Overview tab complete — name, subtitle, description, avatar, logomark
Code
Code
- No hard-coded hosts, keys, or redirect URIs anywhere in the codebase
- Environment resolution defaults to staging
- Both the OAuth host and the GraphQL host swapped
- Idempotency keys generated per operation, not per session
- Callback route is idempotent and validates
state - Token refresh runs before expiry rather than reacting to a failure
Operations
Operations
- Webhook endpoint monitored, with alerting on failure to deliver or process
- Logging captures request identifiers and idempotency keys, and never captures secrets, PANs, or PII
- Someone owns the “user can’t connect” and “transfer stuck” runbooks
- You’ve run one real end-to-end transaction at the smallest possible amount, in both directions, and reconciled both ledgers
Rollout
Rollout
- Internal users first, then a small cohort, then general availability
- You can disable the integration without a code deploy — a feature flag, not a rollback
- Re-authorization path built and tested, for when refresh tokens expire or users revoke
Common cutover failures
Next steps
Create an OAuth app
Re-run every tab against your production app.
Grant flow
Verify the consent screen on production hosts.
Refresh an access token
Keep production connections alive without re-prompting.
API features
Everything now running against real money.