MMashDiv

The module manifest

How GET /api/user/modules decides, for one signed-in customer, which modules the app shows — the checks in the order the server makes them, the reason codes, what the app shows for each, when the app asks again, and how to find out why a screen is missing.

9 min readUpdated 26 September 2026mobile, modules, manifest, attestation, kyc, addons, troubleshooting

The app does not decide for itself which of its screens a customer may use. It asks your server once, right after sign-in, and draws the Futures tab, the Trading Tools tiles, the Help Centre row and the spot order ticket from the answer. That answer is the module manifest: one entry per module, each with a yes or no and, for a no, the reason.

Everything on this page happens on your server. The app has no setting that shows a module the server refuses, and none that hides one it allows — apart from the build flag for Merchant, AI Investment and Affiliate, which decides whether their screens exist at all (see Merchant, AI Investment and Affiliate).

The request

GET/api/user/modules
The modules the signed-in customer may use, with the reason for each one they may not

It needs a signed-in session and answers 401 without one. It answers for the customer who is asking, so two customers on the same install can get different answers. Nothing in it depends on whether the caller is the app or a browser.

A shortened answer:

{
  "user": {
    "roleName": "User",
    "isSuperAdmin": false,
    "kycLevel": 1,
    "kycEnforced": true,
    "residence": "GB",
    "attestationEnforced": false
  },
  "modules": [
    {
      "id": "futures",
      "title": "Futures",
      "extension": "futures",
      "installed": true,
      "enabled": true,
      "licensed": true,
      "kycRequired": true,
      "kycSatisfied": false,
      "attestationRequired": true,
      "attested": true,
      "visible": false,
      "reason": "KYC_REQUIRED"
    }
  ]
}
Field Meaning
user.kycLevel The customer's KYC level (0 when none)
user.kycEnforced True only when KYC Verification and Enforce KYC Feature Access are both on
user.residence The two-letter country the server decided this customer lives in, or null when it could not tell
user.attestationEnforced Whether Enforce Licence Attestations (Mobile App) is on
id The module id the app uses. It is not always the addon's name: faq is the knowledge_base addon, copy-trading is copy_trading
title The name the app puts in its messages — "Spot trading", "Token offerings", "Store", "Bot console"
extension The addon the module needs, or null for a core module
installed The addon is installed. False as well when an addon it depends on is missing
enabled The addon is switched on and no switch of yours turns the module off
licensed The addon's licence is valid
kycRequired, kycSatisfied Whether the module names a KYC feature, and whether this customer's level has it. kycSatisfied is always true while KYC feature enforcement is off
attestationRequired, attested Whether the module is one of the nine licence-gated ones, and whether this customer passes. attested is always true while attestation enforcement is off
visible The one answer the app branches on. True only when reason is OK
reason Why, in one word — see below
mode On the staking entry only: SYNTHETIC (fixed-rate staking) or REAL (on-chain staking)

The list always carries the same 21 modules, including four the app has no screens for (nft, copy-trading, ai-support, ecosystem), because the server answers for the website's addons as well. Binary options, the core Investment plans and forex are not in it and cannot be added, so no answer can ever tell the app to show them.

How each module is decided

The server runs these checks in order and stops at the first one that fails. The reason names the check that failed.

Order Reason What failed
1 NOT_INSTALLED The module's addon is not installed, or an addon it depends on is missing
2 DISABLED The addon is switched off, a switch of yours turns the module off, or an addon it depends on is installed but switched off
3 UNLICENSED The addon's licence is not valid — or the licence check itself failed
4 NOT_ATTESTED Attestation enforcement is on, the customer's country is known, and there is no live licence row for this module in that country
4 RESIDENCE_UNKNOWN Attestation enforcement is on and the server does not know the customer's country
5 KYC_REQUIRED KYC feature enforcement is on and the customer's level does not include the module's feature
— OK Everything passed

Core modules (wallet, markets, trade, account, support, blog, news) have no addon, so they always count as installed and licensed. Only checks 2, 4 and 5 can stop them, and only two core modules have anything for those checks to look at: trade (its switch, attestation and KYC) and wallet (KYC).

Attestation is checked before KYC on purpose. If you are not licensed to serve a customer's country, telling them to complete identity verification would cost them their documents and still show them nothing.

1. Installed

An addon is installed when it has a row in your extension list, whether or not it is switched on. Two modules also need a second addon:

  • Futures needs the Exchange Engine addon (ecosystem). Without it, Futures reports NOT_INSTALLED however healthy the Futures addon is.
  • Staking in on-chain mode needs Exchange Engine and Web3 Wallet & On-Chain Trading (dex). Fixed-rate staking — the default — needs only the Staking addon.

2. Switched on

The addon's own on/off state in System → Extension Manager (/admin/system/extension) is the first half. The second half is a switch of yours, for four modules:

Module The switch Turns the module off when
trade Enable Spot Trading (spotWallets), Finance → Trading Infrastructure → Trading Settings (/admin/trading/settings) It is off. A setting that was never saved counts as on
ico Maintenance Mode (icoMaintenanceMode) in the ICO settings (/admin/ico/settings) It is on
ai-investment Allow AI Investment & MLM in the Mobile App (mobileAllowRestrictedProducts) It is off — which is the default
mlm The same switch It is off — which is the default

An addon that another module depends on counts here too: Futures with Exchange Engine installed but switched off reports DISABLED, and so does on-chain staking when Exchange Engine or Web3 Wallet & On-Chain Trading is switched off or unlicensed.

When staking runs on-chain and one of its two extra addons is missing, switched off or unlicensed, the server does not hide Staking for that reason from a customer who holds an on-chain position that is pending delegation, active, being unstaked, unbonding or ready to withdraw. Hiding it would leave them no way to get their coins out. The Staking addon's own checks, and attestation, still apply to them. Everyone else gets NOT_INSTALLED or DISABLED as usual.

3. Licensed

The addon's licence must be valid. When the licence check throws — an unreachable licence file, say — that one module reports UNLICENSED rather than the whole request failing, and the backend log records Licence check failed for "<addon>". Hiding a module whose licence cannot be confirmed is the safe direction: a tab that then refuses every request is worse than no tab. See Licences and the extension manager.

4. Licence attestation

Only when Enforce Licence Attestations (Mobile App) is on — Admin → System → Platform Settings, tab Features, group Verification, off by default — and only for nine modules: trade, p2p, staking, ico, futures, ecosystem, copy-trading, gateway and ai-investment.

The server works out where the customer lives from their approved KYC applications, oldest first, and falls back to the country on their profile. It never uses the IP address or the phone's settings. Then:

  • a known country with a live (unexpired) licence row for the module passes;
  • a known country without one gets NOT_ATTESTED;
  • no known country gets RESIDENCE_UNKNOWN.

With enforcement on and no rows recorded, all nine modules are closed to every app user: their tiles, the Futures tab and the spot ticket stay on screen, locked, and say why (see the warning under Finding out why a screen is missing). The rows live under System → Compliance → Licence Attestations (/admin/system/attestation) — see Licence attestations. The website is never gated by any of this.

5. KYC feature

Only when KYC Verification (kycStatus) and Enforce KYC Feature Access (kycFeatureEnforcement) are both on — same tab and group as above. A module that names a KYC feature then needs the customer's level to include it:

Module KYC feature
wallet view_wallets
trade trade
ico purchase_ico
ecommerce view_ecommerce
futures futures_trading
gateway use_gateway
ai-investment invest_ai

The features are set per level in the KYC level builder — see KYC. That page also lists the individual actions your server refuses for a missing feature, which go further than this list: a module can be shown while an action inside it is refused.

Every module at a glance

Module Title Addon Also needs Your switch KYC feature Attested
wallet Wallet core — — view_wallets No
markets Markets core — — — No
trade Spot trading core — Enable Spot Trading trade Yes
account Account core — — — No
support Support core — — — No
blog Blog core — — — No
news Market news core — — — No
p2p P2P trading p2p — — — Yes
staking Staking staking On-chain mode: ecosystem and dex — — Yes
ico Token offerings ico — ICO Maintenance Mode purchase_ico Yes
ecommerce Store ecommerce — — view_ecommerce No
nft NFT marketplace nft — — — No
futures Futures futures ecosystem — futures_trading Yes
ecosystem Ecosystem wallets ecosystem — — — Yes
copy-trading Copy trading copy_trading — — — Yes
faq Help centre knowledge_base — — — No
ai-support Support assistant ai_support — — — No
hummingbot Bot console hummingbot — — — No
gateway Merchant gateway — — use_gateway Yes
ai-investment AI Investment ai_investment — Allow AI Investment & MLM in the Mobile App invest_ai Yes
mlm Affiliate mlm — Allow AI Investment & MLM in the Mobile App — No

Which screen each module opens is on What is in the app.

What the app shows for each reason

The app splits the reasons in two. KYC_REQUIRED, NOT_ATTESTED and RESIDENCE_UNKNOWN mean the product exists on your platform but this customer may not start anything in it — they may still hold a futures position or a P2P escrow there, so the way in stays on screen. NOT_INSTALLED, DISABLED and UNLICENSED mean the product is not on your platform at all, so nobody can hold anything in it and the way in disappears.

Reason Trading Tools tile Futures tab Spot order ticket
OK Shown, opens Shown, order form works Shown
KYC_REQUIRED Dimmed; a tap shows the message for five seconds Shown, order form locked Locked, with a Verify identity button
RESIDENCE_UNKNOWN Dimmed, as above Shown, order form locked Locked, with an Add your country button
NOT_ATTESTED Dimmed, as above Shown, order form locked Locked, with "Registered country: " under the message
NOT_INSTALLED, DISABLED, UNLICENSED Absent Absent Locked
Any reason this build does not know Absent Absent Locked
Module missing from the answer Absent Absent Locked

The locked Futures order form adds a second line under the message: "Positions you already hold can still be closed." The spot ticket on the full-screen chart shows the same messages without the buttons.

The Help Centre row in Profile appears only when faq is OK. When it is not, the help-centre shortcut in the support chat answers "This platform has not published a help centre."

The messages

The app writes these itself, with the module's title from the answer in place of title:

Reason The customer sees
KYC_REQUIRED "Complete identity verification to use title."
RESIDENCE_UNKNOWN "Tell us your country of residence to use title. Add it to your profile, or complete identity verification."
NOT_ATTESTED "title is not offered in your registered country, because this platform is not licensed to provide it there. The rest of your account is unaffected."
DISABLED, NOT_INSTALLED, UNLICENSED "title is not available on this platform."
Anything else "title is not available right now."

So a customer without the trade feature reads "Complete identity verification to use Spot trading." When trade is missing from the answer altogether, the spot ticket says "Trading is not available right now."

Only the first two tell the customer to do something, because only those two are theirs to fix. NOT_ATTESTED deliberately does not suggest the website or another device.

When the answer cannot be read

The app fails closed: until it has an answer, every module counts as hidden.

  • While the first request is in flight, the Trading Tools row and the Futures tab are simply not there yet, and the spot ticket shows a spinner.
  • If no answer has ever arrived, Trading Tools shows "We couldn't check which tools are available on your account." with Retry, and the spot ticket shows "Couldn't check what's available on your account." with Retry.
  • If an earlier answer arrived and a later request fails, the app keeps the earlier answer. A dropped connection does not empty somebody's dashboard.
  • A 401 or other refusal from your server counts as no answer, not as an empty list, so an expired session never looks like an unlicensed install.

Hiding is the deliberate direction. Showing a module by default would put something you may not be licensed for in front of a store reviewer, under your developer account.

When the app asks again

The app reads the manifest:

  • at every sign-in — password, Google, and after the two-factor code;
  • every time the app starts with a saved sign-in;
  • when the app comes back to the foreground, only if no answer has loaded yet;
  • on pull-to-refresh on Home, only if no answer has loaded yet;
  • from the Retry buttons above;
  • when the customer comes back from Add your country or Verify identity on the locked spot ticket.

It throws the answer away at sign-out, so the next person to sign in on the phone never sees the previous account's modules.

It does not ask on a timer, and your server does not tell it when something changes. A change you make — an addon switched on, a licence row recorded, a customer's KYC approved — reaches a customer who is already signed in the next time they sign in or fully close and reopen the app.

Taking something away works the other way round. A licence row withdrawn or a KYC feature removed from a level is enforced by your server on the next request, so the order or offer is refused even while an app that read an older answer still shows the screen.

Finding out why a screen is missing

  1. Check the build. Merchant, AI Investment and Affiliate exist only in a build made with --dart-define=STORE_RESTRICTED_MODULES=true. NFT, copy trading and the support assistant have no screens in any build.

  2. Read the answer the customer gets. Sign in to your website as that customer — or as a test account with the same KYC level and country — and, in the same browser, open <your baseUrl>/api/user/modules. The browser sends the website session, and the server answers exactly as it answers the app. Find the module's entry and read reason, then user.residence and user.attestationEnforced.

  3. Act on the reason.

    Reason Where to look
    NOT_INSTALLED The addon — and for Futures, Exchange Engine — in System → Extension Manager
    DISABLED The addon's on/off state, then the switch for that module in the table above
    UNLICENSED The addon's licence; the backend log for Licence check failed
    NOT_ATTESTED A live row for this module and user.residence under Licence Attestations — or whether enforcement should be on at all
    RESIDENCE_UNKNOWN The customer adds a country to their profile, or you approve a KYC application that carries one
    KYC_REQUIRED The customer's KYC level, and whether that level lists the module's feature
    OK The server allows it. Have the customer sign out and in again, so the app reads the answer afresh
  4. Have the customer sign in again after you change anything. The app keeps the answer it read at sign-in.

If the spot ticket, the futures order form and the Futures, P2P, Staking and Launchpad tiles locked for everybody at once — tiles dimmed, order forms showing a message instead — while the website still works, Enforce Licence Attestations (Mobile App) was switched on with no licence rows recorded. The entries read NOT_ATTESTED or RESIDENCE_UNKNOWN. Record your countries or switch it off.

What it deliberately does not do

  • It does not read your website's navigation settings. The app's public settings request (/api/settings) lists which addons are switched on, but carries nothing about licences, KYC or countries, so the app does not use it to decide what to show.
  • It does not hide the core tabs. Nothing in the app reads the wallet, markets, account or support entries: the Wallet and Market tabs, Profile and support tickets are always there. A KYC level without view_wallets hides no tab. What your server refuses inside the Wallet tab — a deposit, a withdrawal, a transfer — follows the other features on the customer's level, action by action.
  • It does not refresh itself. See When the app asks again.
  • It does not show what the server does not list. A module this build knows but the server's answer does not contain is hidden — which is also how an app build newer than your server behaves.