Commission conditions
The rules that decide what earns a commission — the two engines that pay them, reward types and minimums, the currency trap that stops a rule paying anyone, and the overlap guard that keeps you from paying 8% when you meant 5%.
A condition is one commission rule. It says what activity pays, how much, in what currency, above what minimum, and whether it is switched on. Everything your programme costs is the sum of the conditions you leave active.
They live on Admin → Affiliate → Conditions and in the
mlm_referral_condition table. Thirty-seven ship with the platform; fifteen are
active on a fresh install.
What a condition is made of
| Field | What it does |
|---|---|
name |
The trigger key. Unique, and not free text — see below. |
title / description |
What members see on the public commission-rates page. |
type |
The activity family. One of 16 values; drives which ledger entries the hourly evaluator scans. |
reward |
The number. A percentage, or an absolute amount. |
rewardType |
PERCENTAGE or FIXED. |
rewardWalletType |
FIAT, SPOT or ECO — where the money lands at claim time. |
rewardCurrency |
The unit the commission is denominated and paid in. |
rewardChain |
Optional, for ECO rewards on a specific chain. |
minAmount |
Below this, the activity does not qualify. Denominated in rewardCurrency. |
status |
Active or not. Inactive rules pay nothing and are hidden from members. |
period |
DAILY, WEEKLY or MONTHLY — the evaluation window for cron-driven rules. |
The reward engine looks a condition up by name and refuses any name it does
not recognise. Creating a condition called MY_CUSTOM_BONUS produces a row you
can see on the admin screen, that shows up on the members' commission-rates
page, and that will never pay anybody — no code path triggers it and the
event-driven validator rejects unknown names outright.
If you need a different rate for an existing trigger, edit that condition. Use
title and description for the wording your members read; leave name alone.
Two engines, and which one pays your rule
This is the single most important distinction on this page. It changes how fast
a commission appears, what a percentage is a percentage of, and whether the
period field means anything.
Event-driven — paid at the moment of the transaction
Nineteen conditions are triggered directly by the code that processed the activity. The commission row exists within seconds, and the percentage is applied to that one transaction's amount.
WELCOME_BONUS · ECOMMERCE_PURCHASE · ICO_CONTRIBUTION · STAKING ·
STAKING_LOYALTY · AI_INVESTMENT · INVESTMENT · GENERAL_INVESTMENT ·
FOREX_INVESTMENT · NFT_PURCHASE · NFT_SALE · P2P_TRADE ·
P2P_TRADE_COMPLETION · COPY_TRADING · FUTURES_TRADE · BINARY_WIN ·
BINARY_TRADE_VOLUME · FX_TRADE_COMMISSION · FX_TRADE_VOLUME
For these, period is ignored.
Cron-driven — paid hourly, per calendar period
Everything else is evaluated by Process MLM Referral Conditions, an hourly
job visible in Admin → System → Cron under the mlm category. Each run:
- Loads every active condition and every
ACTIVEreferral. - Skips the nineteen event-driven names, so no activity is ever paid twice.
- For each remaining condition, sums the referred user's qualifying, completed transactions inside the current calendar period — and again inside the previous one, so a transaction landing just before a period boundary is not lost.
- Creates at most one reward per condition, per referrer, per referred user, per period.
The percentage is applied to the summed period volume, not to a single transaction. A 3% monthly rule on a member who deposits ten times is 3% of the month's total, once.
Neither the create nor the update endpoint accepts period, so every condition
sits on the model default of DAILY unless somebody changes the column
directly. If you want weekly or monthly aggregation, that is a database change,
and the effect is significant: DAILY means one reward per referred user per
day, MONTHLY means one per month.
Reward types
PERCENTAGE — reward is a percentage of the qualifying amount. Under
BINARY or UNILEVEL that result is then split by your level percentages.
FIXED — reward is an absolute amount in rewardCurrency, paid once when
the threshold is met. Under a multi-level structure each level receives its
configured percentage of the fixed amount — so a 50 USDT fixed reward with
levels of 50/30/20 pays 25 / 15 / 10, not 50 each.
Two seeded rules do something less obvious. FX_TRADE_VOLUME is stored as
PERCENTAGE with a value of 100, which the Forex Trading addon reads as
1.00 per lot — not 100%. Its partner FX_TRADE_COMMISSION is a genuine
percentage of the commission the trader paid. They are the two halves of one
introducing-broker rebate and are meant to run together.
Minimums and the currency trap
minAmount is expressed in the condition's reward currency, and this is
where most "nobody ever qualifies" reports come from.
Transactions on Bicrypto carry no currency of their own — an amount is denominated by the wallet it sits on. The hourly evaluator therefore builds a conversion table into the reward currency, converts the threshold into each wallet's currency to filter rows in SQL, and converts the resulting sums back before comparing and paying.
Three ways that goes wrong:
- The reward currency cannot be priced. With no rate for the target currency there is no denominator, so nothing can be valued against it — including activity already in that currency. Every condition paying in it stops. The cron log says so explicitly, rate-limited to a few times a day so it does not bury the rest of the log.
- A referred user's wallet currency cannot be priced. That volume is excluded rather than counted 1:1 — counting an unpriced token at parity is how 0.4 BTC becomes 0.40. The job logs which currencies it skipped.
- Nobody holds a wallet yet. A referred user with no wallets has no transactions to sum. The job says so rather than reporting a clean run.
New conditions seed as SPOT / USDT, which matches how a Bicrypto install actually denominates activity. Older installs may still be on the original FIAT / USD default, which also puts the commission into a fiat wallet most members cannot withdraw from. There is a script for that:
# Report only — shows which conditions would move and the unclaimed exposure
node backend/scripts/affiliate-condition-currency.mjs
# Apply
node backend/scripts/affiliate-condition-currency.mjs --applyIt only touches rows still matching the seeded FIAT/USD default, so a rate you have deliberately set is left alone. It does not touch existing reward rows — but because the currency is read from the condition at claim time, retargeting re-denominates unclaimed rewards. The report prints that exposure before you commit.
Overlap — the reason your programme costs more than you think
Several conditions can collect on one piece of activity, and not only when they
share a type. TRADE, SPOT_TRADE, COPY_TRADING and TOKEN_PURCHASE are
four different types that all resolve to the same EXCHANGE_ORDER ledger entry.
Leave all four active and a single spot fill pays all four.
Which ledger entries each type is evaluated against:
| Condition type | Transaction types |
|---|---|
DEPOSIT |
DEPOSIT |
TRADE, SPOT_TRADE, COPY_TRADING, TOKEN_PURCHASE |
EXCHANGE_ORDER |
BINARY_WIN |
BINARY_ORDER |
INVESTMENT |
INVESTMENT, INVESTMENT_ROI |
AI_INVESTMENT |
AI_INVESTMENT, AI_INVESTMENT_ROI |
FOREX_INVESTMENT |
FOREX_INVESTMENT, FOREX_INVESTMENT_ROI |
FOREX_TRADING |
none — settled by the Forex Trading addon at trade time |
ICO_CONTRIBUTION |
ICO_CONTRIBUTION |
STAKING |
STAKING, STAKING_REWARD |
ECOMMERCE_PURCHASE |
ECOMMERCE_PURCHASE |
P2P_TRADE |
P2P_TRADE |
NFT_TRADE |
NFT_PURCHASE, NFT_SALE |
FUTURES_TRADE |
FUTURES_ORDER |
The conditions list computes this for you. Every row carries the other active conditions that compete with it, the ledger types they share, and an effective percentage rate — what a referrer actually earns once every overlapping percentage rule has paid.
Turning on a condition that overlaps an already-active one returns a 409
naming the competitor and quoting the combined rate. Re-send with
acknowledgeOverlap: true — which the admin screen does when you confirm the
warning — to enable it anyway. The same guard runs when you create a condition
already switched on.
Disabling is never guarded. Turning payouts off cannot cost anyone money.
Three pairs are exempt because they are not double payment:
NFT_PURCHASE/NFT_SALE— one trade, two different people paid (the buyer's referrer and the seller's).P2P_TRADE/P2P_TRADE_COMPLETION— the same, on a P2P release.FX_TRADE_COMMISSION/FX_TRADE_VOLUME— one rebate paid off two different bases.
The commission ceiling
Admin → Affiliate → Settings → Maximum Commission Rate is a real limit, not
a display. Saving a PERCENTAGE condition above it is rejected with a message
naming both numbers. Raise the ceiling first if you genuinely want the higher
rate.
It does not apply to FIXED rewards — an absolute amount is not a rate — so a
fixed reward of 10,000 saves happily under a 30% ceiling. Watch those by hand.
What members can see
The public commission-rates page (/affiliate/condition) lists active
conditions only, and filters out any rule belonging to an addon you do not
have enabled. Someone without the NFT addon never sees NFT commissions
advertised. Rules backed by core features — deposits, spot and binary trading,
investments — are always shown.
What you cannot do
- Delete a condition. There is no delete endpoint, and the affiliate family
has no
delete.key for conditions. Disable it instead; the row stays as a record of what your programme once paid. - Set
minAmountwhen creating. The create endpoint ignores it, so a new condition starts at 0 — every qualifying transaction, however small, pays. Save it, then edit it to set the minimum. - Rename
name. The update endpoint's allow-list does not include it.
Next: Rewards and payouts — what happens between a commission being earned and money reaching a wallet.