MT5 ships with built-in alerts (popups, sounds, email, push) but no webhook support. With a few lines of MQL5 and the WebRequest function, you can pipe alerts to Slack, Discord, Teams, or any other webhook-accepting service. Here is the working pattern.
https://hooks.slack.com) to the "Allow WebRequest for listed URL" field. Without this, every WebRequest call fails silently with error 4060.
MQL5 exposes WebRequest for outbound HTTP calls. Two function signatures exist; the one used for sending JSON to webhook services:
int WebRequest(
const string method, // "POST" or "GET"
const string url, // Full URL
const string headers, // HTTP headers
int timeout, // ms
const char &data[], // Request body as byte array
char &result[], // Response body
string &result_headers // Response headers
);
The function returns the HTTP status code (200 for success) or -1 for transport errors. Check GetLastError() for specific failure codes; the common ones are 4060 (URL not in allowed list) and 4014 (function not allowed).
Slack incoming webhooks are the simplest case. Get a webhook URL from Slack: Apps → Incoming Webhooks → Add to workspace → choose a channel. The URL looks like https://hooks.slack.com/services/T0XXXXX/B0XXXXX/XXXXXXXXX.
The payload is JSON with a single text field for plain text:
void SendSlackAlert(string message)
{
string url = "https://hooks.slack.com/services/T0XXXXX/B0XXXXX/XXXXXXXXX";
string headers = "Content-Type: application/json\r\n";
string payload = "{\"text\": \"" + message + "\"}";
char data[], result[];
string result_headers;
StringToCharArray(payload, data, 0, StringLen(payload));
int res = WebRequest("POST", url, headers, 5000, data, result, result_headers);
if(res != 200)
Print("Slack webhook failed: HTTP ", res, " error ", GetLastError());
}
Call SendSlackAlert("Trade opened: XAUUSD long at 2380.50") wherever you want a notification.
Discord webhooks work similarly. In Discord: Server Settings → Integrations → Webhooks → New Webhook. The URL format is https://discord.com/api/webhooks/XXXXX/XXXXX.
Discord accepts a content field for plain text and an embeds array for richer formatting:
void SendDiscordAlert(string message)
{
string url = "https://discord.com/api/webhooks/XXXXX/XXXXX";
string headers = "Content-Type: application/json\r\n";
string payload = "{\"content\": \"" + message + "\"}";
char data[], result[];
string result_headers;
StringToCharArray(payload, data, 0, StringLen(payload));
int res = WebRequest("POST", url, headers, 5000, data, result, result_headers);
if(res != 204) // Discord returns 204 No Content on success
Print("Discord webhook failed: HTTP ", res, " error ", GetLastError());
}
Note Discord's success code is 204, not 200. The function signature is otherwise identical.
Teams webhooks expect a slightly different payload structure using the MessageCard schema or the newer Adaptive Card format. Get a webhook URL from Teams: channel → Connectors → Incoming Webhook → Configure.
void SendTeamsAlert(string title, string message)
{
string url = "https://outlook.office.com/webhook/XXXXX/IncomingWebhook/XXXXX/XXXXX";
string headers = "Content-Type: application/json\r\n";
string payload = "{\"@type\":\"MessageCard\",\"@context\":\"http://schema.org/extensions\","
+ "\"summary\":\"" + title + "\","
+ "\"title\":\"" + title + "\","
+ "\"text\":\"" + message + "\"}";
char data[], result[];
string result_headers;
StringToCharArray(payload, data, 0, StringLen(payload));
int res = WebRequest("POST", url, headers, 5000, data, result, result_headers);
if(res != 200)
Print("Teams webhook failed: HTTP ", res, " error ", GetLastError());
}
The interesting question is not how to send a webhook but what to send. Useful alert content for trading:
| Event | Alert content |
|---|---|
| Trade opened | Symbol, direction, lot size, entry price, stop loss, take profit |
| Trade closed | Symbol, exit price, profit/loss in deposit currency, hold time |
| Stop loss hit | Symbol, lot size, loss amount, account equity remaining |
| Daily loss limit reached | Account equity, drawdown percentage, EA disabled status |
| Connection lost | Timestamp, last successful tick, alert priority |
| EA error | Function name, error code, suggested action |
The pattern: build a message string in OnTrade or wherever the event occurs, then call your SendXxxAlert function.
The cleanest way to notify on trades is the OnTradeTransaction callback. MQL5 calls this function whenever any trade-related event occurs:
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
{
string msg = StringFormat("Deal: %s %.2f lots %s at %.5f",
(trans.deal_type == DEAL_TYPE_BUY ? "BUY" : "SELL"),
trans.volume,
trans.symbol,
trans.price);
SendSlackAlert(msg);
}
}
This fires on every executed deal. Filter by symbol or magic number if you want to limit notifications.
Webhook services impose rate limits to prevent abuse:
For a normal EA sending a few alerts per day, you will not hit limits. If you ever loop and send many messages quickly (e.g. on EA startup looping through historical trades), batch them or add small delays.
Webhook URLs are themselves secrets. Anyone with the URL can post to your channel. Treat them as you would a password:
Beyond plain text, each service supports richer formatting:
Slack supports markdown-like formatting in the text field: *bold*, _italic_, ~strike~, ` + "`code`" + `. For richer layouts use the blocks array with structured Block Kit JSON.
Discord supports markdown (**bold**, *italic*, __underline__, code blocks with triple backticks). Embeds let you add structured fields, colours, thumbnails, and footers. Embed example:
{
"embeds": [{
"title": "Trade Opened",
"description": "XAUUSD long",
"color": 5763719,
"fields": [
{"name": "Entry", "value": "2380.50", "inline": true},
{"name": "SL", "value": "2376.50", "inline": true}
]
}]
}
Teams MessageCard supports sections, facts (key/value pairs), images, and action buttons. The newer Adaptive Card format is more flexible but requires the AdaptiveCard schema.
When alerts do not arrive, check in this order:
No. WebRequest is disabled in the Strategy Tester. Alerts are only sent in live or demo trading. For testing, mock the alert function to just Print the message.
Not directly. WebRequest is outbound only. To receive external commands (e.g. close all trades from a Slack command), you need a polling architecture: the EA periodically WebRequests a status endpoint, the external system posts commands to that endpoint, the EA acts on what it reads.
Typically 200ms to 1 second from WebRequest call to message appearing in the destination. Slow internet or service degradation can push this to several seconds. Not real-time, but fast enough for trade notifications.
Slack and Discord support image attachments via webhook, but you must upload the file to a publicly-accessible URL first and reference that URL in the payload. MT5 cannot directly upload binary files; you would need an intermediate server. For chart screenshots, taking and uploading is more involved than typical retail traders attempt.
Not from MetaQuotes. Community libraries exist on MQL5 Code Base for Slack and Discord integration. They wrap the raw WebRequest pattern into nicer interfaces. Code quality varies; review before using.