Configuration reference — every key in app_config.json
Every key the app reads from assets/config/app_config.json — type, default, whether it is required, what it does, when a change takes effect, and the server setting it has to agree with. Also the keys older configs carry that the app ignores.
Every key the app reads from assets/config/app_config.json. How the file is
loaded, what happens when it is missing or broken, and what must never go in it
are on App configuration.
Four rules apply to every key:
- Every change needs a new build. The file is packed into the app when it is built. A published app keeps the values it was built with until its users install an update.
- Write JSON types. Text in quotes, switches as bare
true/false, numbers as bare whole numbers. A value of the wrong type stops the app on its Configuration Error screen. - For the name, identifier and colour keys,
""counts as not set: an empty colour gives the default colour, an emptyappNamegivesBiCrypto. For the other keys an empty string is a value, not a default. - Unknown keys are ignored, and keys starting with
_are notes.
All keys at a glance
| Key | Type | When absent | Required |
|---|---|---|---|
baseUrl |
text | — | Yes |
wsBaseUrl |
text | — | Yes |
appName |
text | BiCrypto |
No |
androidApplicationId |
text | com.bicrypto.mobile |
No |
iosBundleId |
text | The Xcode project's id | No |
iosTeamId |
text | Nothing written; the Team chosen in Xcode | No |
primaryColor |
text | #0ECE7A |
No |
buyColor |
text | #0ECE7A |
No |
sellColor |
text | #FF5A5F |
No |
accentColor |
text | #1890FF |
No |
googleServerClientId |
text | "" |
Only for Google sign-in |
googleAuthEnabled |
switch | Shown only if googleServerClientId is set |
No |
stripePublishableKey |
text | "" |
Only for Stripe card deposits |
defaultTradingPair |
text | BTC/USDT |
No |
defaultExchangeProvider |
text | bin |
No |
appVersion |
text | 5.0.0 |
No |
settingsCacheDuration |
whole number | 3600 |
No |
_clientCredentialNotice |
text | — | No (a note) |
Your server
baseUrl
- Required. Missing, blank or not text stops the app on the Configuration
Error screen:
… does not set "baseUrl". - What to write. The address your customers open in a browser, such as
https://exchange.example.com. No/apion the end; the app adds the paths itself. Spaces around it and one trailing/are removed for you. - What it is used for. Every API request (
/api/...), and the images: coin logos (/img/crypto/...) and every image the server gives as a relative path, such as uploaded avatars (/uploads/...), are loaded from this host too. - Release builds accept only an
https://address and stop on the Configuration Error screen otherwise, naming the key and the value:… sets "baseUrl" to "http://…", which is not a https:// address, and this is a release build.The build itself still succeeds, so check first withdart run tool/apply_app_config.dart --check --release. Debug and profile builds also accepthttp://, for testing against a server on your own computer (on the Android emulator,http://10.0.2.2:4000reaches your computer). - Takes effect at the next build.
- Must agree with your server's public address: the host that answers
/api,/img/cryptoand/uploads. Check it with the market list request in Install.
wsBaseUrl
- Required, with the same rule and error as
baseUrl. - What to write. The same host with
wss://, such aswss://exchange.example.com. The app adds the paths, all under/api/...(for example/api/exchange/tickerfor prices). - Release builds accept only
wss://, and stop on the Configuration Error screen namingwsBaseUrlotherwise; debug and profile builds also acceptws://. - Takes effect at the next build.
- Must agree with your reverse proxy. It must pass WebSocket upgrades on
/api/...through to the backend and must not strip the headers the app sends with them (accesstoken,sessionid,csrftoken,platform), or the connections that need a signed-in user fail while prices still work. See Nginx and reverse proxy.
Name, identifiers and colours
These keys are covered in full, with what each platform does at build time, on Branding.
appName
- Format. One line of text. A tab or line break stops the build. Absent or
blank gives
BiCrypto. A value that is not text stops the app on the Configuration Error screen. - Takes effect at the next build, on both platforms, with nothing else to run: Gradle reads it on every Android build, and the Apply app_config.json build phase on every iOS build.
- Server counterpart: none. Your store listing's name is set separately in Play Console and App Store Connect.
androidApplicationId
- Format. Two or more parts separated by dots, each starting with a letter, using only letters, digits and underscores. An invalid value stops the Android build.
- Takes effect at the next Android build. Gradle reads it every time.
- Changing it after release makes a new app. Play will not accept it as an update to your listing.
- Must agree with
android/app/google-services.json(for push), which must come from a Firebase Android app registered with the same id, and with the Android OAuth client you register for Google sign-in.
iosBundleId
- Format. Two or more parts separated by dots, using only letters, digits and hyphens. No underscores.
- Empty or absent means the build keeps whatever id the Xcode project has
(the template's is
com.bicryptto.mobile) and does not check it. The example file ships it as"". - Takes effect after you run
dart run tool/apply_app_config.dart, which writes it into the Xcode project. An iOS build whose project disagrees with a setiosBundleIdstops, with both ways to fix it in the message. - Must agree with the App ID registered in your Apple Developer account,
your provisioning profile, and
ios/Runner/GoogleService-Info.plistif you use push.
iosTeamId
- Format. Exactly 10 characters, upper-case letters and digits, as
developer.apple.com → Account → Membership details shows it. Any
other value is refused by
dart run tool/apply_app_config.dart, which then writes nothing. The app itself never reads this key. - Empty or absent means nothing is written: the Xcode project keeps
whatever Team it has, and you choose yours in Xcode under Runner → Signing
& Capabilities. The template's project carries no Team, and the example
file ships the key as
"". - Takes effect after you run
dart run tool/apply_app_config.dart, which writes it intoDEVELOPMENT_TEAMin every Runner build configuration. With--check, the tool exits 1 while the project's Team differs from it. - The release preflight reports
ios-teamwhile the project names no Team for Release, or still carries52K7QMD7KR(the Team the template's project carried up to 5.3.9), andios-team-mismatchwhen the project's Team differs from this key. - Must agree with the Apple Developer account that owns your bundle id.
primaryColor, buyColor, sellColor, accentColor
| Key | Default | Paints |
|---|---|---|
primaryColor |
#0ECE7A |
Your brand colour: buttons, tabs and links |
buyColor |
#0ECE7A |
Buy, prices going up, positive changes |
sellColor |
#FF5A5F |
Sell, prices going down, negative changes |
accentColor |
#1890FF |
Informational accents |
- Format.
"#RRGGBB"or"#AARRGGBB"(opacity first), in either case. An empty string counts as not set. Any other value, such as"blue"or"0ECE7A"without the#, stops the app on the Configuration Error screen naming the key. - Not configurable. Errors stay red (
#FF5A5F) and success stays green (#0ECE7A) whatever the four keys say, and text on primary-coloured buttons switches to dark on a very light primary. The exception in this release is the Transfer screens and the wallet's Deposit screens, which still colour their own success and error marks withbuyColorandsellColor, and keep white text on some primary-coloured buttons and selected items. - Takes effect at the next build or
flutter run. - Server counterpart: none. The website's colours do not reach the app.
Google sign-in
How to set Google sign-in up on both platforms, and what it can and cannot do, is on Sign-in methods.
googleServerClientId
- Required for Google sign-in. Empty means the app asks Google without a server client ID.
- Takes effect at the next build.
- Must agree with your server's
NEXT_PUBLIC_GOOGLE_CLIENT_ID: the same Web client ID. The server accepts a Google token only if it was issued for that ID, and refuses any other with an error startingGoogle authentication failed:. See Environment variables. - Never validated by the app. A wrong value shows up only when someone taps Continue with Google.
- It is a public identifier, not a secret. Never put a client secret here.
googleAuthEnabled
trueshows the button.falsehides it. Either is obeyed as written.- Absent shows the button only when
googleServerClientIdis set (not blank), so a config with no client ID has no button that could only fail. - Android with
google-services.json. On Android the Google plugin can take the Web client ID fromgoogle-services.jsonwhengoogleServerClientIdis empty. The absent-key rule looks only atgoogleServerClientId, so in that setup write"googleAuthEnabled": true. - Takes effect at the next build.
- Not linked to your server's Google OAuth Login switch. That switch (Admin → System → Platform Settings → Security → Authentication) governs the website's buttons only. Turning it off does not hide the button in the app; this key does.
- The installers (
setup/installers/install.sh,install.bat) leave this key out, so the button follows the Google client ID you give them. The example file ships"googleAuthEnabled": false.
Card deposits
stripePublishableKey
- Required for Stripe card deposits, and used by nothing else in the app.
- Empty means Stripe is not set up in the app. If your server still offers Stripe, customers can choose it and the payment fails when the payment sheet opens. Leave it empty only if your server does not offer Stripe.
- Takes effect at the next build.
- Must agree with your server's
APP_STRIPE_SECRET_KEY: a key pair from the same Stripe account and the same mode —pk_test_…withsk_test_…,pk_live_…withsk_live_…. The server creates each payment with its secret key and the app completes it with this one. - It is a publishable key. Never put the
sk_…secret key here.
Setup, and what the customer sees, is on Payments.
Trading defaults
defaultTradingPair
- Format.
BASE/QUOTEwith a slash, as your markets are named, such asETH/USDT. Leave the key out rather than empty:""is not replaced by the default. - What it does. The Trade tab opens on this market when the customer arrives without tapping a market, and the live trading connection subscribes to it first.
- Takes effect at the next build.
- Must agree with a market that exists and is enabled on your server. If it
does not, the app refuses an order placed from it with
We couldn't confirm which market <symbol> belongs to. Pull down to refresh the market list and try again.
defaultExchangeProvider
Despite its name, this key does not choose the exchange your server trades on, and the app never asks your server which one it uses. Its only effect is which timeframes the chart's timeframe picker offers:
| Value | Timeframes offered |
|---|---|
bin (Binance) |
1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M |
kuc (KuCoin) |
1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 1w |
okx |
1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d, 3d, 1w, 1M |
xt |
1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w |
kra |
1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w |
| Anything else | The Binance list |
- Only the short codes match, in upper or lower case.
"binance"or"kucoin"fall back to the Binance list. xtworks from 5.4.0. Earlier releases had a stray space in the app's XT entry, so"xt"got the Binance list; an XT install that used"kra"as a workaround gets the same list either way.- Takes effect at the next build.
- Must agree with the spot exchange provider that is active on your server
(Binance →
bin, KuCoin →kuc), so the chart does not offer timeframes your exchange does not have. See Exchange provider.
Housekeeping
appVersion
- What it does. The version customers see comes from the build itself, from
the
version:line inpubspec.yaml, shown as<version> (<build number>)on Profile → Open-source Licenses. This key is used only when the phone cannot report the build's version, which does not happen on a normal install. - You do not need to edit it when you release. Change the version in
pubspec.yaml; see Build for Android. - Server counterpart: none. The app does not send its version to your server.
settingsCacheDuration
- What it does, in practice: very little. The app downloads your server's
public settings (
GET /api/settings) when it starts and again every five minutes, and both of those always go to the server. This value only lets one screen that asks without forcing a download reuse a copy younger than this. Offline, the app uses its saved copy whatever its age. - Format. A whole number of seconds, written without quotes.
"3600"or3600.0stops the app on the Configuration Error screen. - Leave it at
3600. It does not make the app pick up your settings faster or slower; the five-minute refresh does that. - Takes effect at the next build.
_clientCredentialNotice
The example file carries: "This distributed file contains placeholders only.
Configure your own public client identifiers before building. Never put
secret/private keys in a mobile app." Every key that starts with _ is
treated the same way, so you can add notes of your own.
Keys the app ignores
Configurations from releases up to 5.3.9, and files written by their installers, carry keys this release does not read. None of them is in the example file any more, and the installers no longer write them.
| Key | What decides it instead |
|---|---|
recaptchaEnabled, recaptchaSiteKey |
Your server's Captcha Provider. The app answers the built-in proof-of-work at sign-up and password reset, and sends customers to the website under a hosted captcha; see Sign-in methods |
walletAuthEnabled, walletConnectProjectId |
Nothing: the app has no wallet sign-in |
twoFactorEnabled, twoFactorSmsEnabled, twoFactorEmailEnabled, twoFactorAppEnabled |
Your server's Two-Factor Authentication settings |
emailVerificationEnabled |
Your server's Email Verification Required setting |
backgroundUpdateInterval |
Nothing: the app's refresh intervals are built in |
cryptoCompareApiKey, _cryptoCompareApiKeyHelp |
Your server: the app reads news from your backend, which holds the provider key |
defaultShowComingSoon |
Nothing: which modules appear is your server's decision |
None of these keys changes anything, whatever its value — a wrongly typed one cannot stop the app — so they are safe to leave or delete.
A complete file
Every key this release reads, with example values. Replace them with your own.
{
"baseUrl": "https://exchange.example.com",
"wsBaseUrl": "wss://exchange.example.com",
"appName": "MyExchange",
"androidApplicationId": "com.myexchange.app",
"iosBundleId": "com.myexchange.app",
"iosTeamId": "",
"primaryColor": "#2F6BFF",
"buyColor": "#16C784",
"sellColor": "#EA3943",
"accentColor": "#F0B90B",
"googleServerClientId": "1234567890-abc123.apps.googleusercontent.com",
"googleAuthEnabled": true,
"stripePublishableKey": "pk_live_...",
"defaultTradingPair": "BTC/USDT",
"defaultExchangeProvider": "bin",
"appVersion": "5.3.9",
"settingsCacheDuration": 3600,
"_clientCredentialNotice": "Public values only. Never put secret or private keys in this file."
}Not in this file
| What | Where it is set |
|---|---|
| Push notifications | android/app/google-services.json and ios/Runner/GoogleService-Info.plist — see Push notifications |
| The icon | assets/icons/app_icon.png, then dart run tool/apply_app_config.dart --icons — see Branding |
| The version and build number | version: in pubspec.yaml |
| Android release signing | android/key.properties — see Build for Android |
| Which modules, sign-in checks and deposit gateways customers get | Your server's settings |