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.

5 min readUpdated 6 August 2026notifications, emails, templates, settlement, signals

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 to REJECTED. So the one deposit email your customers ever receive is a confirmation of money being taken back, with %STATUS% rendering as REJECTED.
  • A withdrawal submission sends nothing. The customer's forex account is debited and a PENDING row 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 as PENDING on 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=true in .env at the repository root — there is no backend/.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, .example and .localhost domains 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.