alert() vs alertcondition(): which one fires your webhook
The four combinations of alert(), alertcondition(), strategy and indicator in Pine Script, what each can put in a webhook body, and which frequency to choose.
Updated 30 Aug 2026
Pine has two functions with "alert" in the name and two kinds of script that can call them. Which pair you pick decides what your webhook receives: a fixed sentence, a runtime string, or a message that only exists when the broker emulator fills an order. This page lays out the four combinations and what a bridge on the other end can and cannot get from each.
The two functions
alertcondition() is the older one. It works in indicators only, sits in the global scope, and registers a named condition that appears as an item in the Create Alert dialog. Its message argument must be a constant string. The only way to get live values into it is TradingView's own placeholders, such as {{close}} or {{ticker}}, which are substituted after the fact (TradingView help: How to use a variable value in alert).
alert() is the newer one and, in the docs' words, more or less supersedes it. It works in indicators and strategies, can be called from inside an if so it only runs when your condition holds, takes a series string message built at runtime, and lets the script choose its own frequency (Pine Script docs: Alerts).
alert(message, freq)The freq argument is one of:
| Frequency | Fires |
|---|---|
alert.freq_once_per_bar (default) | First call that executes on each live bar, possibly mid bar |
alert.freq_once_per_bar_close | Only on the closing update of the live bar |
alert.freq_all | Every call, on every price update |
Neither function creates a running alert on its own. Both create alert events, and a person still has to open the Create Alert dialog, pick the script, and turn the events into an alert. That alert is a snapshot of the script and its inputs; edit the script and the alert keeps running the old copy until you recreate it.
The two kinds of script
An indicator runs on every price update of the live bar. A strategy, unless calc_on_every_tick = true, runs once per bar on the closing tick. That difference reshapes how the alert functions behave:
- In an indicator,
alert()with the default frequency fires on the first tick that satisfies the condition, before the bar is confirmed. - In a strategy without
calc_on_every_tick, everyalert()call effectively usesalert.freq_once_per_bar_closeregardless of the argument, because the script only runs at bar close (Pine Script docs: Alerts, In strategies). - Only a strategy has order fill events. When the broker emulator fills a simulated order, TradingView can fire the alert and substitute
{{strategy.order.alert_message}}with whatever the order call passed asalert_message.
The four combinations
| Indicator | Strategy | |
|---|---|---|
alertcondition() | Works. Constant message plus TradingView placeholders. One dialog entry per condition. | Not available. Compile error. |
alert() | Works. Runtime message. Fires on first matching tick unless freq_once_per_bar_close. | Works. Runtime message. Always at bar close unless calc_on_every_tick. |
Order fill with alert_message | Not available. Indicators place no orders. | Works. Message set per order call, delivered when the emulator fills. |
Order fill without alert_message | Not available. | Fires, but the body is the literal text {{strategy.order.alert_message}}. |
Indicator with alertcondition()
Use it when the payload is fixed apart from price and time. The body below is valid JSON after substitution because TradingView's numeric placeholders expand without thousands separators.
//@version=6indicator("RSI cross", overlay = false)r = ta.rsi(close, 14)alertcondition(ta.crossover(r, 50) and barstate.isconfirmed, "RSI up", '{"sym":"{{ticker}}","side":"BUY","type":"RSI","price":{{close}},"time":"{{timenow}}"}')What it cannot do: include a stop computed in Pine, switch the side inside one condition, or choose the frequency. Frequency is set in the dialog, and the safe choice there is Once Per Bar Close.
Indicator with alert()
Use it when the payload needs runtime values. Build the string with str.format() and the {0,number,#.##} pattern so numbers arrive ungrouped, keep TradingView placeholders outside the format string, and gate on barstate.isconfirmed. The webhook alerts page explains the thousands-separator trap in full.
//@version=6indicator("ORB signal", overlay = true)orHigh = ta.highest(high, 3)[1]orLow = ta.lowest(low, 3)[1]fired = ta.crossover(close, orHigh) and barstate.isconfirmedbody = '{"sym":"{{ticker}}","side":"BUY","type":"ORB","price":{{close}},' + str.format('"sl":{0,number,#.##}', orLow) + ',"time":"{{timenow}}"}'if fired alert(body, alert.freq_once_per_bar_close)In the dialog, choose the condition "Any alert() function call". The frequency you passed in code wins; the dialog's own frequency setting does not apply to alert() events.
Strategy with alert_message on orders
Use it when you want the alert to mean "the emulator filled this order", with the message you attached to that order. Every order function accepts alert_message: strategy.entry(), strategy.order(), strategy.exit(), strategy.close() and strategy.close_all(). In the dialog, choose "Order fills only" or "Order fills and alert() function calls", and put {{strategy.order.alert_message}} in the message box, or the custom text never reaches the webhook.
if fired strategy.entry("L", strategy.long, alert_message = body)strategy.exit("X", "L", stop = orLow, limit = target, alert_message = exitBody)The catch is fills the script did not see. Stops and targets fill inside a bar in the emulator, and the exit's message is whatever alert_message held when the exit call last ran. If a strategy.close() or a strategy.exit() has no alert_message, the fill still fires and the body is the unexpanded placeholder.
Strategy with alert()
Use it alongside alert_message, not instead of it. alert() in a strategy fires when the script decides, at bar close, and does not wait for a fill. That makes it the right carrier for a signal a human is going to confirm, because the human is the fill. Passing the same string to both keeps the two paths in step:
if fired strategy.entry("L", strategy.long, alert_message = body) alert(body, alert.freq_once_per_bar_close)In the dialog, "alert() function calls only" gives you one message per decision. Adding order fills gives you a second message per fill, which a bridge has to dedupe.
What a bridge can receive from each
| Source | Body | Timing | Dynamic values | Risk to watch |
|---|---|---|---|---|
Indicator alertcondition() | Constant plus placeholders | Per dialog frequency | Only {{close}} style placeholders | Wrong frequency in the dialog fires mid bar |
Indicator alert() | Runtime string | Per freq in code | Anything in Pine | Default frequency is once per bar, not bar close |
| Strategy order fill | alert_message of the filled order | When the emulator fills | Anything in Pine at order time | Exits without alert_message send the raw placeholder |
Strategy alert() | Runtime string | Bar close | Anything in Pine | Duplicate of the fill message if both are enabled |
A bridge that treats every POST as a fresh order will double count a strategy alert configured for both fills and alert() calls. Include a stable key in the body, either an id or the combination of symbol, side, strategy name and bar time, so the receiver can collapse the pair. Also decide what an unexpanded {{strategy.order.alert_message}} means. It is not malformed, it is an exit the script did not annotate, and the safe behaviour is to log it and do nothing.
Choosing
- Fixed message, indicator, no computed levels:
alertcondition(), dialog set to Once Per Bar Close. - Computed levels, indicator:
alert()withalert.freq_once_per_bar_closeandbarstate.isconfirmed. - Strategy, you want the backtest and the live alerts to be the same events:
alert()at bar close,alert_messageon every order call with the same string,calc_on_every_tick = false. - Strategy, you want fills rather than decisions: order fills with
alert_messageon every call, including exits, plus a dedupe key.
The webhook alerts page covers what happens to the body after it leaves TradingView, and the repainting page explains why the bar close gate matters.
Try it in Sarathi
Sarathi's parser accepts all four paths as long as the body carries a symbol and a side, dedupes on id or on symbol, side, strategy and time, and treats an unexpanded {{strategy.order.alert_message}} as ignorable rather than as an error. Live The Studio writes Pine v6 strategies that pass one payload string to both alert_message and alert() at bar close. Each post becomes an order card in Telegram and on the web dashboard, where you tap Confirm at ½, 1× or 2× size, or Skip; nothing is placed without that tap. The paper broker fills confirmed orders today; Dhan and Zerodha adapters are planned. Planned Open the Studio.
Questions
Only through TradingView's own placeholders such as {{close}} and {{ticker}}. Its message must be a constant string, so a stop level computed in Pine cannot be inserted. Use alert() or alert_message for that.
Not strictly, but having both lets the same alert fire on order fills and on the script's own conditions, and it lets you send a message from an indicator style condition inside a strategy.
An order fill happened for a call that had no alert_message argument, usually a strategy.exit() or strategy.close(). TradingView sends the placeholder unexpanded. Add alert_message to every order call or have the bridge ignore that literal.
alert.freq_once_per_bar_close, with the condition gated on barstate.isconfirmed. In a strategy without calc_on_every_tick, every alert() call already behaves that way.
Related
- How TradingView webhook alerts workWhat TradingView posts when a webhook alert fires: placeholders, JSON rules, the 3 second timeout, secrets in the URL, and the failures that break a bridge.
- Repainting, lookahead and calc_on_every_tick explainedWhy a Pine strategy trades differently live than in the backtest: lookahead, request.security offsets, calc_on_every_tick, barstate.isconfirmed, how to test.
- Webhooks: in (live) and out (planned)The per-user inbound webhook Sarathi runs today, how TradingView alerts are parsed, and a planned outbound webhook on decision and fill with a proposed payload.