API and data model

Every affiliate endpoint with its permission, the five database tables and what is unique about each, the hourly evaluation job, and the settings keys the engine reads.

3 min readUpdated 3 August 2026api, endpoints, tables, cron, settings

Every route in this addon lives under /api/affiliate or /api/admin/affiliate. Nothing is served from /api/mlm — that path exists in the licensing route map but has no handlers behind it.

Member endpoints

Three of these answer without a session. The rest need one, and all of them scope their data to the calling user — there is no way for a member to read somebody else's downline or rewards.

Public programme statistics — affiliate count, total paid out, average monthly earnings, success rate
Everything the public landing page renders: stats, active conditions, top five earners and recent reward activity
Active commission conditions, filtered to the addons this install actually has enabled
The signed-in member's dashboard — referral counts, earnings and growth against the previous period. Accepts period=1m|3m|6m|1y
The member's network tree, rendered according to the configured structure, plus their upline and lifetime rewards
Paginated list of the people the member referred. page max 1000, perPage max 100
One referral with its earnings summary
The member's own node, with the referrals above and below it
Chart and KPI data for the member's referral analytics
The member's rewards, with the condition each came from. Supports the standard list filters
Claims one reward into a wallet. Enforces the withdraw_affiliate KYC feature and the payout threshold

Admin endpoints

Dashboard metrics, charts, the pending queue and stalled affiliates

Referrals

List all referrals
Read one referral
Create a referral by hand. Rejects self-referral and any referred user who already has a sponsor
Update a referral
Approve or reject. ACTIVE places the referral in the tree, REJECTED removes it and re-parents its children
Bulk status change
Soft-delete a referral. Add force=true to remove it permanently, restore=true to bring it back
Bulk delete
Tree node for one referral

Conditions

List conditions, each with its overlap analysis and effective percentage rate
Read one condition
Create a condition. Ignores minAmount; refuses an active condition that overlaps an existing one unless acknowledgeOverlap is true
Update a condition. Enforces the maximum commission rate on PERCENTAGE rewards
Enable or disable a condition. Enabling is refused with a 409 when it competes with an active one
Bulk enable or disable

There is no delete endpoint for conditions.

Rewards

List all rewards
Read one reward
Create a reward by hand against a referrer and a condition
Update a reward amount or its claimed flag. Moves no money
Mark a reward claimed or unclaimed. Moves no money
Bulk claimed-status change
Soft-delete a reward
Bulk delete

Database tables

Five tables, all prefixed mlm_.

mlm_referral

Who referred whom. referrerId, referredId, status of PENDING / ACTIVE / REJECTED. Soft-deleting.

Two unique indexes, and they matter:

  • referredId is unique. A user can be somebody's referral exactly once, ever. Sponsors cannot be re-assigned, and a soft-deleted row still holds the slot.
  • (referrerId, referredId) is unique. A relationship cannot be duplicated.

A row where referrerId equals referredId is a self-referral: it marks the top of a chain and is created automatically for members who need a tree root. The upline walk stops when it reaches one.

mlm_referral_condition

The commission rules. Unique on name. Not soft-deleting and carrying no timestamps — conditions are edited in place and never removed.

Notable columns: type (16-value enum driving the ledger scan), reward and rewardType, rewardWalletType / rewardCurrency / rewardChain (where the payout lands), minAmount, status, and period (DAILY by default, and not settable through any endpoint).

mlm_referral_reward

Earned commissions. referrerId, conditionId, reward (a DOUBLE), isClaimed, and sourceId. Soft-deleting.

sourceId is unique and is the whole of the duplicate protection. The hourly evaluator writes condition_referrer_referred_period; the multi-level event path appends :L1, :L2 and so on. MySQL permits multiple NULLs in a unique index, so rewards created without one are unaffected — those fall back to a 60-second, same-amount duplicate check instead.

Deleting a reward keeps its sourceId, so the evaluator will not recreate it.

mlm_binary_node and mlm_unilevel_node

Tree placement. Both are unique on referralId — one node per referral — and neither carries timestamps.

mlm_binary_node holds parentId, leftChildId and rightChildId. mlm_unilevel_node holds only parentId and allows any number of children.

Both payout paths walk mlm_referral.referrerId. These two tables exist to draw the network diagram. Deleting rows from them corrupts the member-facing tree and changes nobody's earnings.

The scheduled job

One job, registered only while the mlm extension is enabled and deregistered within a scheduler cycle when you switch it off — no restart needed either way.

Property Value
Name processMlmReferralConditions
Title Process MLM Referral Conditions
Category mlm
Interval Hourly
Writes Reward rows, and empty payout wallets for referrers
Reads Active conditions, active referrals, referred users' wallets and completed transactions

It never moves money. It creates reward rows and, alongside a new reward, the referrer's payout wallet if they do not have one — so a member is never blocked at claim time for want of a wallet row.

Each run evaluates both the current calendar period and the one just completed. That double pass is why the deterministic sourceId matters: without it, the re-scan would pay everything twice.

Its log is the addon's best diagnostic. It names each reward it creates, each condition it skipped and why, currencies it could not price, and the case where none of the referred users hold any wallet at all. Configuration warnings are rate-limited to roughly every four hours so one misconfiguration across 37 conditions produces one line rather than seventy.

Settings the engine reads

All stored as text in the platform settings table.

Key Read by
affiliateMlmSystem Structure selection — DIRECT, BINARY, UNILEVEL
affiliateBinaryLevels Level count under BINARY (2–7)
affiliateBinaryLevel1affiliateBinaryLevel7 Per-level share under BINARY
affiliateUnilevelLevels Level count under UNILEVEL (2–7)
affiliateUnilevelLevel1affiliateUnilevelLevel7 Per-level share under UNILEVEL
affiliateRequireApproval Whether new referrals start PENDING
affiliateMaxCommissionRate Ceiling on PERCENTAGE condition rewards
affiliatePayoutThreshold Minimum unclaimed balance before claiming

Two legacy keys are still read as a fallback when the current ones are absent: mlmSystem (the structure) and mlmSettings (a JSON blob containing both level configurations, with the values stored as strings). Saving the settings screen once writes the current keys, which then take precedence.

Full descriptions and consequences: Programme settings.

Where commissions are triggered from

Event-driven conditions are fired by the code that processed the activity, in core and in other addons. Which means: an addon you do not own cannot pay its commission, and disabling an addon stops its rules firing without disabling the conditions themselves. Those conditions stay active and stay invisible to members, because the members' rates page filters by enabled extension.

The one exception to "conditions belong to addons" is the core set — deposits, spot and binary trading, and investments — which is always available.