Emails and notifications the addon sends
Every message Forex Investment sends a customer — the five email template rows, the three in-app notifications, where each one fires, which ones never fire, and where they are edited.
When a customer says nobody told me, this is the page that answers it. Forex Investment reaches a customer through exactly two channels — an email built from a template row, and an in-app notification composed in code — and there are fewer of both than most operators expect.
Nothing here is SMS or push. The addon only ever enqueues email and only ever writes IN_APP notifications, so the SMS and Push bodies stored on the template rows below are dead text for this product.
The five email templates
These are rows in the notification template table, seeded on install. Their names are what the code asks for, so renaming one breaks the send.
| Template | Fires when | Never fires on |
|---|---|---|
NewForexInvestmentCreated |
The customer opens an investment — POST /api/forex/investment |
An investment an admin creates from /admin/forex/investment |
ForexInvestmentCompleted |
Settlement, from the hourly cron, after the payout has committed | Anything an admin settles by hand |
ForexInvestmentCanceled |
Nothing. The template is seeded and no code path asks for it | Every cancellation, admin or automatic |
ForexDepositConfirmation |
An admin reverses a deposit at /admin/forex/deposit/{id} |
The deposit itself |
ForexWithdrawalConfirmation |
An admin approves or rejects a withdrawal at /admin/forex/withdraw/{id} |
Submission of the withdrawal |
Short codes available to each, if you rewrite the bodies:
| Template | Short codes |
|---|---|
NewForexInvestmentCreated |
FIRSTNAME, PLAN_NAME, AMOUNT, DURATION, TIMEFRAME, STATUS |
ForexInvestmentCompleted |
FIRSTNAME, PLAN_NAME, AMOUNT, PROFIT, STATUS |
ForexInvestmentCanceled |
FIRSTNAME, PLAN_NAME, AMOUNT, STATUS |
ForexDepositConfirmation |
FIRSTNAME, ACCOUNT_ID, TRANSACTION_ID, AMOUNT, CURRENCY, STATUS |
ForexWithdrawalConfirmation |
FIRSTNAME, ACCOUNT_ID, TRANSACTION_ID, AMOUNT, CURRENCY, STATUS |
PLAN_NAME is filled from the plan's title, not its name. PROFIT
carries a leading + on a WIN and - on a LOSS, and no sign on a DRAW.
The three things a customer is actually told about money
This is the part worth reading twice, because four of the five templates are not where an operator assumes they are.
- A deposit that succeeds sends nothing. Funding a forex account settles
instantly and silently.
ForexDepositConfirmation— subject Forex Deposit Confirmation, body Forex Deposit Confirmed! — is sent only from the reversal path, after the transaction has already been flipped toREJECTED. So the one deposit email your customers ever receive is a confirmation of money being taken back, with%STATUS%rendering asREJECTED. - A withdrawal submission sends nothing. The customer's forex account is
debited and a
PENDINGrow appears with no message at all. - The withdrawal decision does send an email, but it cannot tell the customer
what you decided. Approval and rejection both send the same
ForexWithdrawalConfirmation, whose body reads Your withdrawal ... has been successfully processed and Your funds have been transferred to your wallet in either case. Worse, the send is queued before the new status is written to the row, so%STATUS%renders asPENDINGon both.
A customer who was rejected receives a mail saying their funds have been transferred. Treat the decision as in-app and dashboard only, and if rejection matters to your desk, tell the customer yourself.
Two smaller consequences of the same shape:
- The withdrawal email is enqueued inside the database transaction that moves the money. Queueing is not rolled back, so on the rare failure after that point the mail still goes out.
- Because both money emails use
%STATUS%from the transaction row rather than from the decision, editing the template body cannot fix the wording. If you want an accurate rejection notice, the honest option today is to turn the template's email channel off and handle rejections manually.
The in-app notifications
These are composed in code, not from a template row, so nothing on the Notification Templates screen edits them. All three are written to the IN_APP channel only.
| Title | Written when | Type | Link | Action button |
|---|---|---|---|---|
New Forex Investment |
The customer opens an investment | investment |
/forex/investment/{id} |
View Investment |
Forex Investment Completed |
Settlement, from the cron | system |
/forex/investment/{id} |
View Investment |
Forex Investment Cancelled |
The cron gives up on an investment after three retries and refunds the principal | system |
/forex/investment/{id} |
none |
The settlement notification's body is Your Forex investment of {amount}
{currency} has been completed with a status of {result} — the amount is the
principal, the currency is the plan's currency, and the result is WIN,
LOSS or DRAW.
The Forex Investment Cancelled notification is written only by the cron's
terminal-failure path. Cancelling from /admin/forex/investment — the row
action, the edit dialog or the bulk selector — refunds the principal and writes
neither a notification nor an email, and ForexInvestmentCanceled is not sent
by any path at all. The customer's balance changes and nothing announces it.
Where these are edited
Admin → System → Communication Tools → Notification Templates, at
/admin/system/notification/template, gated on
access.notification.template. The five rows above are all editable there;
the three in-app notifications are not.
Each template row carries an independent Email, SMS and Push switch. Only the Email one does anything for this addon.
An email is refused before it is composed if the template row is missing, its
Email channel is off, or its body is empty. The customer gets nothing and the
backend log records Email template not found or email not enabled under
EMAIL. Nothing surfaces on the admin screen that triggered it.
Two platform-wide switches sit above all of this:
MAIL_DISABLED=truein.envat the repository root — there is nobackend/.env; the backend loads the root file — drops every outbound mail on the platform before it reaches the queue. It is read per call, so it takes effect on the next backend restart.- Addresses on the reserved
.invalid,.test,.exampleand.localhostdomains are dropped without an attempt, which is why seeded demo customers never receive anything.
Admin → System → Communication Tools → Notification Service
(/admin/system/notification, access.notification.settings) is the health and
test surface for the delivery channels themselves.
Settlement never fails because mail failed
The cron's post-processing for a settled investment runs in two independent
try/catch blocks after the payout transaction has already committed:
affiliate rewards first, then the email and the in-app notification together.
Both swallow their errors and log under FOREX_INVESTMENT_POST_PROCESS.
That is deliberate — a mail outage must never strand a settled investment — but it has a consequence you have to know about:
If your mail provider is down when processForexInvestments runs, every
investment maturing in that hour settles correctly, credits the customer's
forex account correctly, and sends nothing. The investment reads COMPLETED,
the balance is right, and the only evidence is a log line.
After any mail outage, check the backend log for
FOREX_INVESTMENT_POST_PROCESS and reconcile against investments that moved to
COMPLETED during the window. The emails are not retried once the queue job
has exhausted its three attempts.
The same block is also where a failed affiliate reward disappears — see What the platform earns, and referral rewards.
Signals notify nobody
Creating a signal at /admin/forex/signal, and assigning one to an account
with PUT /api/admin/forex/account/{id}/signal, send no email and write no
notification. Signals are pull-only: a customer sees them when they open
/forex or their account screen, and not before.
If you are running a paid signal service on this, the announcement is your job. See Signals.
How the other forex product differs
Forex & Multi-Asset Trading is a separate addon with a separate notification story, and if you run both, do not assume what you learn on one applies to the other.
| Forex Investment | Forex & Multi-Asset Trading | |
|---|---|---|
| Templates | 5, seeded once at install | 4 — FxTradingDepositConfirmation, FxTradingWithdrawalStatus, FxTradingStopOut, FxTradingMarginCall |
| Missing template row | Silent failure, nothing sent | Re-created automatically on first use, so an upgrade without a re-seed still delivers |
| Withdrawal decision | One template for both outcomes, status stuck at PENDING |
%STATUS% and a %MESSAGE% are passed in, so approval and rejection read differently |
| Operator alerts | None | An alert channel that fans out IN_APP and email to every holder of the Super Admin role |
There is no operator alert channel in Forex Investment. Nothing emails you when
a settlement fails, a reward is dropped or the account pool runs dry — the
backend log and /admin/system/cron are the whole of it.
Next: API reference for the routes each of these hangs off, or Troubleshooting if a customer is reporting something that should have reached them.