Webhooks and TradingView signals
Routing a TradingView alert through the same risk gate your own clicks go through.
A Signal is a TradingView alert delivered to an endpoint of your own. It goes through exactly the same gate a click does: it is sized from your rules, checked against your Buffer, and refused for the same reasons in the same words. TradingView alerts to cTrader is the same route told from the alert's side.

Setting one up
- Make an endpoint on Webhooks. You get a URL and a secret. The secret is shown once and stored as a hash - if you lose it, rotate it (below).
- Open Alert message on the endpoint's card. Pick where the alert comes from and what it should do, and the builder writes the exact message - or, for a strategy or a Pine alert() call, the Pine that builds it - checked by the same parser the server uses, with what it would do at today's price. Save a setup as a template to reuse it for the next symbol.
- In TradingView, put the URL in Webhook URL.
- Put the alert body in the alert's Message box - or, from a Pine strategy or an alert() call, the Pine in your script (see From a Pine strategy below).
- A new endpoint starts in observe mode: real alerts are checked against your rules and the gate and logged with what they would have done, and nothing is traded. The first one checked is notified, to say the wiring works; after that only a message that would be refused whatever the mode - one that does not parse, or arrives too late - is. The account, its rules, your plan and the endpoint's limits are logged Observed with the reason, without a notification. When the log reads right, Arm it from its card; Pause puts it back in observe mode without changing the URL or the secret.
Limits on an endpoint
An endpoint can be held to less than your Risk rules allow, so a leaked secret or a script gone wrong can do no more than you meant. Open Limits on its card; the card says them in one line while it is closed - Max 0.5% per trade · XAUUSD, EURUSD · close-only. Each is optional, and Test a message is held to them exactly as an alert is. The alert builder reads them too: a message they would refuse is marked red and cannot be copied, and an alert on the chart's {{ticker}} is marked amber, because it is accepted on the listed symbols alone. In observe mode an alert outside them is logged Observed with the limit as what would have happened, and is notified only if it is the endpoint's first observed alert.
- Max risk per trade - a percentage of the balance, to two decimals. A risk_percent above it is refused as the alert arrives. An order sized by volume, risk_amount or a share of Initial capital is sized first, and refused with nothing sent when what it would risk comes out above the limit; a risk_percent at the limit always fits. It covers an open and a reverse's open.
- Symbols - the symbols this endpoint may act on, as your broker spells them; case is ignored. Every action that names a symbol is held to the list, a close as much as an open, and a symbol off it is not even asked of the broker. A flatten, and a cancel that names no symbol, are governed by the action list alone.
- Actions - which of open, close, modify, cancel, flatten, breakeven and reverse it may take. Close-only is close, cancel, flatten and breakeven: a strategy's exits keep working, and nothing can open a position or widen a stop.
A message can only ask for less: no field of it raises a limit. A refusal names the limit and what the message asked for, is logged and notified like any refusal, and is answered 422, which TradingView does not resend. What the message itself says - its action, its symbol, its risk_percent - is refused before the alert is recorded as received, so its signal_id stays free: the same alert sent after you loosen the limit runs rather than being called a duplicate. A change to the limits holds from the next alert.
Rotating a secret
Rotate on an endpoint's card gives it a new secret, shown once with a Copy button, and keeps the URL. On a plan with Signals, as when an endpoint is made, its alert builder opens under the card with the new secret filled in, so the message you copy from it is ready to paste. The old secret keeps working for 24 hours, so a strategy with many alerts can be moved over one alert at a time without missing a trade. While both work, the card counts down to the old one's end and says how many deliveries since the rotate still carried it and how many the new one. A log row opened from that time says Secret: the old one when its alert still has the old secret in it, beside the alert's symbol, comment and signal_id, so you can find the alert to edit.
- Stop the old secret now ends the 24 hours early - once every alert has the new secret, or at once when the old one leaked. From then on it is refused.
- After its 24 hours, an alert still carrying the old secret is refused as this secret was replaced and has stopped working: put the endpoint's new secret in the alert, rather than as a wrong secret, and answered 401. TradingView does not resend it and shows the refusal only in its own alert log, so check the Webhooks log before the day is out.
- An endpoint rotates again once its old secret has stopped: a second rotate while both work would have to stop one that an alert may still carry. If you lost the new secret before copying it, stop the old one, rotate again, and paste the newest into every alert.
Nobody can read a secret back, Propcockpit included: only its hash is kept, for the old secret as for the new. Revoke stops the URL itself, at once and for good.
Accepting only TradingView's addresses
Open Addresses on an endpoint's card and tick Accept only TradingView's addresses, and a delivery from anywhere else is refused even with the right secret. It narrows who can send; it does not replace the secret, which every delivery still needs. It is off on every endpoint until you turn it on. A refused delivery is logged with the address it came from, as not from TradingView's addresses (1.2.3.4), and answered 403, which TradingView does not resend.
TradingView sends its webhooks from 52.89.214.238, 34.212.75.30, 54.218.53.128 and 52.32.178.7, as listed on TradingView's help page (checked 2026-09-27).
The trade-off: TradingView may change those addresses, and does not say when. If it does, its alerts to an endpoint with this on are refused until Propcockpit updates the list, and TradingView shows those refusals only in its own alert log - so leave it off unless a leaked secret is a risk you want closed this way, and keep an eye on the Webhooks log. A test you send to the URL from your own machine is refused too; Test a message on the card is not, because it never goes through the URL.
The alert body
One message works on every symbol and at every price: the stop is a percentage of the price, the target a multiple of the stop, and the size a share of your balance.
{
"secret": "whsec_...",
"action": "open",
"symbol": "{{ticker}}",
"side": "buy",
"sl_percent": 0.3,
"tp_rr": 2,
"risk_percent": 0.5,
"on_existing": "skip",
"signal_id": "pc-k3f9qz-{{ticker}}-{{time}}-open",
"sent_at": "{{timenow}}"
}- action - open, close, modify, cancel, flatten, breakeven or reverse.
- side - buy or sell; long and short mean the same.
- signal_id - unique per Signal, so a delivery TradingView repeats trades once. {{time}} is the bar's time, which makes each bar's alert its own Signal; the letters in front make it this alert's, so give every alert its own six; and {{ticker}} makes one alert that fires on several symbols in the same bar several Signals.
- sent_at - when TradingView sent it: {{timenow}}. An entry or a modify that arrives more than 60 seconds after it was sent is refused, so a late alert never trades a price that has moved; a close, cancel, flatten or breakeven is run however late, because it only takes risk away, and its log row says how late. An ISO time with its zone or an epoch in seconds or milliseconds works too; a time with no zone is refused.
The stop - exactly one of
- sl_pips - pips from the price the order is judged at: the ask for a buy, the bid for a sell, the entry for a limit or stop order. A pip is what your broker says it is for that instrument - 0.0001 on EURUSD, 0.01 on USDJPY, and on gold and indices it differs from broker to broker - so one pip count is a different distance on every chart. Use it in an alert for one symbol; on {{ticker}} use sl_percent. The alert builder shows what a pip is on each symbol at your broker.
- sl_percent - a percentage of that same price, like 0.5.
- sl - a price, on the losing side of the market: below it for a buy, above it for a sell. A price suits one chart; the two above travel between charts.
A distance becomes a price on the instrument's own decimals, and that price goes through exactly the checks a typed one does - a stop inside the room your broker keeps clear of the market is refused, and says so. The log shows the price each stop came to.
The take profit - at most one of
- tp_rr - a multiple of the stop's distance: 2 puts the target twice as far from the entry as the stop.
- tp_pips - pips from the same price the stop is measured from.
- tp - a price. A take profit that is not a price is refused, never dropped.
The size - one of
- risk_percent - a percentage of the balance, like 0.5, sized to the stop. Add "risk_percent_of": "initial" to take it of your Initial capital instead - what your firm issued the account with, which is how most firms state a per-trade rule.
- risk_amount - money in the account's currency: 50 risks 50 USD on a USD account.
- volume - lots. A size you typed is a decision, so when an alert carries both lots and risk_percent, the lots win, as they always have; risk_amount beside either is refused.
A size that does not fit your Buffer or Max open risk is refused. Add "size": "fit" to have it sized down to the largest size that does fit instead; the log says when it was.
The rest of an open
- type - limit or stop with an entry price, or omit it for a market order. expires_in gives a limit or stop order a life in minutes; without it the order rests until you cancel it.
- on_existing - skip does nothing when this endpoint already holds a position on the symbol or has a limit or stop order of its own resting there, so an alert that fires again does not stack a second trade; add, the default, opens another. Only this endpoint's own positions and orders count - one you opened by hand, or another alert did, does not stop it. On a netted account, which holds one position per symbol whoever opened it, skip is refused.
- comment - up to 64 characters, kept on the log row and sent to the broker as the order's comment. A longer one is cut, and the row says so.
- trailing - true asks the broker to trail the stop. See Managing a trade below.
Worked examples
Risk a fixed amount, stop as a percentage
{
"secret": "whsec_...",
"action": "open",
"symbol": "{{ticker}}",
"side": "sell",
"sl_percent": 0.4,
"tp_rr": 1.5,
"risk_amount": 50,
"signal_id": "pc-r8m2dw-{{ticker}}-{{time}}-open",
"sent_at": "{{timenow}}"
}Sells at the bid with the stop 0.4% above it and the target one and a half times as far below, sized so that reaching the stop loses 50 in the account's currency.
A prop firm's per-trade rule, sized down when the day is short
{
"secret": "whsec_...",
"action": "open",
"symbol": "{{ticker}}",
"side": "long",
"sl_percent": 0.3,
"tp_rr": 2,
"risk_percent": 1,
"risk_percent_of": "initial",
"size": "fit",
"on_existing": "skip",
"signal_id": "pc-t4v7hn-{{ticker}}-{{time}}-open",
"sent_at": "{{timenow}}"
}Risks 1% of the Initial capital - 1,000 USD on a 100,000 account, however the balance has moved - and is sized down to what the Buffer allows rather than refused. When this endpoint already holds the symbol, or has an order resting on it, it does nothing.
A limit order at an indicator's level, for four hours
{
"secret": "whsec_...",
"action": "open",
"symbol": "{{ticker}}",
"side": "buy",
"type": "limit",
"entry": "{{plot_0}}",
"sl_percent": 0.2,
"tp_rr": 2,
"volume": 0.5,
"expires_in": 240,
"comment": "pullback",
"signal_id": "pc-b2x6qa-{{ticker}}-{{time}}-open",
"sent_at": "{{timenow}}"
}Rests a 0.5-lot buy limit at the indicator's first plot with its stop 0.2% under the entry, and withdraws it after four hours if it has not filled. The comment reaches the broker with the order.
Getting out: close, modify, cancel and flatten
Every order a Signal places carries the label pc: followed by the last part of the endpoint's URL. You can see it on the position in cTrader, so you and your firm can tell an alert's trade from a click, and Propcockpit's Journal and positions say via Signal and the endpoint's name.
That label is how an exit knows which positions are its own. A close, modify or cancel touches only what this endpoint opened or placed, and leaves your manual trades and other alerts' positions alone:
- scope - mine, the default, is this endpoint's own; symbol is everything on the symbol, whoever opened it.
- side on a close - buy or long closes only the long positions, sell or short only the shorts. It is the side of the position, not of the order that closes it: a close sent with the other side, when only this side is open, is refused rather than doing nothing.
- percent on a close - how much of each position to close.
- cancel withdraws this endpoint's resting limit and stop orders on the symbol, or on every symbol when the alert names none. scope: symbol withdraws every resting order on the symbol it names.
- flatten always closes the whole account - every position and every resting order, whoever opened it. It takes no scope.
When a close or modify finds nothing of its own on the symbol but other positions are there, it is refused and names them - 3 positions on XAUUSD were not opened by this endpoint - send scope: symbol, or close them by hand. - rather than closing them or quietly doing nothing. A cancel that finds only other resting orders is Nothing to do, so a strategy that withdraws its orders before every entry is not refused while you have a limit of your own resting. Whenever an exit leaves somebody else's positions or orders alone, the log row says how many. On a netted account the one position on a symbol is this endpoint's only when it carries this endpoint's label, which is when this endpoint opened it.
Exits keep working when your plan no longer includes Signals or your Risk rules are not armed: a close, a cancel and a flatten still go through, and an open, a modify, a breakeven or a reverse is refused. When the platform's Signal intake is paused, nothing goes through.
Close this endpoint's longs on the symbol
{
"secret": "whsec_...",
"action": "close",
"symbol": "{{ticker}}",
"side": "long",
"signal_id": "pc-m5c1ve-{{ticker}}-{{time}}-close"
}Closes the long positions this endpoint opened on the symbol and leaves its shorts, other alerts' positions and anything opened by hand. The side is the position's: long closes longs, whatever the closing order is called.
Withdraw this endpoint's resting orders on the symbol
{
"secret": "whsec_...",
"action": "cancel",
"symbol": "{{ticker}}",
"signal_id": "pc-w9h3ku-{{ticker}}-{{time}}-cancel"
}Cancels the limit and stop orders this endpoint placed on the symbol and nothing else. Leave symbol out to withdraw this endpoint's resting orders on every symbol.
Close half of everything on the symbol, whoever opened it
{
"secret": "whsec_...",
"action": "close",
"symbol": "{{ticker}}",
"percent": 50,
"scope": "symbol",
"signal_id": "pc-q2j8nx-{{ticker}}-{{time}}-close"
}scope symbol reaches every position on the symbol - other alerts' and your own included - and closes half of each. Use it only when that is what you mean.
Managing a trade: trailing, breakeven and reverse
These three do what cTrader itself can do, and say so plainly. None of them is a program running on Propcockpit's side: each is one alert, done once, when it arrives.
- trailing on an open or a reverse - true asks the broker to trail the stop, at the stop's own distance: as the price moves your way the stop follows, and it never moves back. cTrader's trailing stop has no trigger and no step, so there are no fields for them. It is sent with the order itself, so the stop trails from the fill, and it keeps trailing while Propcockpit is offline because the broker does it. The log says whether the broker confirmed it trails.
- breakeven - moves the stop of this endpoint's positions on the symbol to the price each was opened at. offset_pips puts it that many pips past the entry, in the position's favour, to lock a little in; a negative one puts it that many pips short of the entry instead - tighter than the stop it replaces, but not quite breakeven - for a position not yet far enough past its entry to hold a stop there. It takes scope and side the way a close does. It never loosens a stop: one already at breakeven or better is left where it is, and the row says so. On a position that is still losing, the entry is on the wrong side of the market, so that position is refused by name - the broker would refuse the stop anyway, without saying which number was wrong. Because a breakeven only ever tightens a stop, it goes through while your account is Locked.
- reverse - closes this endpoint's positions on the symbol (or everything on it, with scope: symbol), waits for the broker to confirm every close, then opens the side you name at market, sized and checked exactly like an open. Every position it may touch is closed, whichever side it is on, so afterwards this endpoint holds one position on the symbol: the new one. It is two steps, not one: every close is tried, and if any is refused, done only in part or not confirmed, nothing is opened, and the log shows each step. If the new position is refused after the closes - a Lock, or a Buffer with no room - you are left flat, and the row says so. A reverse takes everything an open takes except type, entry, expires_in and on_existing. It needs a hedged account: on a netted account, which holds one position per symbol whoever opened it, it is refused - send a close, then an open.
Buy with a trailing stop
{
"secret": "whsec_...",
"action": "open",
"symbol": "{{ticker}}",
"side": "buy",
"sl_percent": 0.3,
"risk_percent": 0.5,
"trailing": true,
"signal_id": "pc-f4t8ro-{{ticker}}-{{time}}-open",
"sent_at": "{{timenow}}"
}Buys with the stop 0.3% under the ask and asks the broker to trail it: as the price rises the stop follows at that distance, and it never moves back down. It keeps trailing if Propcockpit is offline, because the broker does it.
Move this endpoint's stops to breakeven
{
"secret": "whsec_...",
"action": "breakeven",
"symbol": "{{ticker}}",
"signal_id": "pc-e7b2kv-{{ticker}}-{{time}}-breakeven"
}Moves the stop of each position this endpoint opened on the symbol to its entry - no offset, because one pip count is a different distance on every chart (send offset_pips on an alert for one symbol). A stop already there or better is left alone, and a position still losing is refused by name, because a stop at its entry would be past the market.
Reverse a long into a short
{
"secret": "whsec_...",
"action": "reverse",
"symbol": "{{ticker}}",
"side": "sell",
"sl_percent": 0.3,
"tp_rr": 2,
"risk_percent": 0.5,
"signal_id": "pc-v3n9sa-{{ticker}}-{{time}}-reverse",
"sent_at": "{{timenow}}"
}Closes this endpoint's positions on the symbol and, once the broker has confirmed every close, sells at market sized like any open. If a close is refused or not confirmed, nothing is opened and the log says so. Hedged accounts only.
Close on a candle close
On Pro and Premium, a TradingView alert can arm the stop loss on candle close - the same one the order ticket arms. The stop above stays required and rests at your broker as the hard backup; inside it goes a second level that Propcockpit watches on its server, and the trade is closed at market when a candle closes at or through that level. A wick through it does nothing. An open or a reverse reads:
- sl_trigger - bar_close arms it; touch, the default, is the ordinary stop.
- sl_close_percent, sl_close_pips or sl_close - the level, exactly one, measured the way the stop is: from the same price, or as a price. It has to sit strictly inside the stop - above it for a buy, below it for a sell - and one at or past the stop is refused, because the broker would take the trade on the touch before any candle could close.
- sl_timeframe - the candle: 5m, 15m, 1h, 4h or 1d, on your broker's own candle times. TradingView's {{interval}} works too, on a chart of one of those timeframes; any other is refused by name.
Risk is measured to your stop loss, not to the bar-close level - the account is exposed to it for the whole candle. A safety-margin close can still cut a candle short. It keeps working with your browser shut and across a restart of our server. On a plan without it the alert is refused and says so; when a trial ends the watch stops, and the broker stop stays.
Buy, and close on a 15-minute candle close rather than a wick
{
"secret": "whsec_...",
"action": "open",
"symbol": "{{ticker}}",
"side": "buy",
"sl_percent": 0.5,
"sl_trigger": "bar_close",
"sl_close_percent": 0.25,
"sl_timeframe": "15m",
"tp_rr": 2,
"risk_percent": 0.5,
"signal_id": "pc-c6l1bz-{{ticker}}-{{time}}-open",
"sent_at": "{{timenow}}"
}Buys with the broker stop 0.5% under the ask and a bar-close level 0.25% under it. A wick through the level does nothing; a 15-minute candle that closes at or below it closes the trade at market. The broker stop stays at the broker as the hard backup, and the risk is measured to it.
Numbers can be sent bare or in quotes, so "sl": "{{plot_0}}" works. A quoted number has to be a plain dot-decimal like "1.0842": no thousands separator, no decimal comma, no unit. Anything else is refused and the refusal names the field.
A symbol your broker does not offer is refused with the nearest one it does: The broker does not offer GER40 - did you mean GER40.cash? A market order sent while the market is shut is refused with when it opens.
From a Pine strategy
A strategy's alert fires when TradingView fills one of its orders, and each order can carry its own message in alert_message. The builder writes that message as Pine with the action spelled out, so the fill of an exit is sent as a close and is never read as an opposite open. Pick Strategy fills in the builder: for an open it writes the entry and its exit together.
// Propcockpit: this open long and its exit, each order sending its own message when TradingView fills it
pc_secret = input.string("whsec_...", "Propcockpit secret", group = "Propcockpit") // once per script: leave this line out if your script has it already
pc_open_long_message = '{"secret":"' + pc_secret + '","action":"open","symbol":"' + syminfo.ticker + '","side":"buy","sl_percent":0.3,"tp_rr":2,"risk_percent":0.5,"on_existing":"skip","signal_id":"pc-k3f9qz-' + syminfo.ticker + '-' + str.tostring(time, "#") + '-open","sent_at":"' + str.tostring(timenow, "#") + '"}'
pc_open_long_exit_message = '{"secret":"' + pc_secret + '","action":"close","symbol":"' + syminfo.ticker + '","side":"buy","signal_id":"pc-k3f9qz-' + syminfo.ticker + '-' + str.tostring(time, "#") + '-close"}'
// Replace each input.bool(...) with your own entry and exit conditions, or give your own
// strategy.entry("Long") and the strategy.exit or strategy.close that ends it these alert_message arguments.
// One message per bar: signal_id is the bar's open time, so a second entry filled on the same bar (pyramiding) is a duplicate and is not sent.
pc_open_long_condition = input.bool(false, "pc_open_long_condition: replace with your own condition", group = "Propcockpit")
if pc_open_long_condition
strategy.entry("Long", strategy.long, alert_message = pc_open_long_message)
pc_open_long_exit_condition = input.bool(false, "pc_open_long_exit_condition: replace with your own condition", group = "Propcockpit")
if pc_open_long_exit_condition
strategy.close("Long", alert_message = pc_open_long_exit_message)- Paste it at the end of your strategy. Replace each input.bool(...) with your own condition - it is an input, off until you do, because TradingView refuses a strategy whose orders sit behind a plain false - or give the two alert_message arguments to your own strategy.entry and to the strategy.exit or strategy.close that ends it. A strategy.exit given alert_profit or alert_loss sends those instead for its target and stop, so give them the close message too.
- Create the alert on the strategy with the condition Order fills only, and make {{strategy.order.alert_message}} the whole Message. There is no frequency to set: every order TradingView fills in realtime sends its message once. An order without an alert_message sends an empty message, which is refused and logged.
- A strategy that trades both ways flips with one strategy.entry the other way, and that one fill sends only its own message. On a hedged account, build those entries as a reverse, which closes the other side first; with nothing open, a reverse is an open.
- A close of a share closes the same share of the strategy's own position: strategy.close("Long", qty_percent = 50, ...), so TradingView's position and the broker's shrink together.
- Send it at market. The fill has already waited for its price, so the builder refuses a limit or a stop from a strategy: the broker would rest a second order behind it.
- There are no {{placeholders}} in the Pine. TradingView's own pages disagree on whether an alert_message resolves them, so the Pine builds every value itself: the symbol from syminfo.ticker, the signal_id from the bar's open time time - the same bar on every copy of the alert, so one made again never repeats an id - and sent_at from timenow.
- One entry message per bar: a second entry filled on the same bar, which pyramiding can do, carries the same signal_id and is not sent.
From alert() in Pine
An alert() call sends the message its code builds and ignores the Message box - and it resolves no placeholders at all. So the builder writes the message as a Pine string expression, sent from a condition of your own. Pick Pine alert() in the builder:
// Propcockpit: the open long message, sent by alert()
pc_secret = input.string("whsec_...", "Propcockpit secret", group = "Propcockpit") // once per script: leave this line out if your script has it already
pc_open_long_message = '{"secret":"' + pc_secret + '","action":"open","symbol":"' + syminfo.ticker + '","side":"buy","sl_percent":0.3,"tp_rr":2,"risk_percent":0.5,"on_existing":"skip","signal_id":"pc-k3f9qz-' + syminfo.ticker + '-' + str.tostring(time, "#") + '-open","sent_at":"' + str.tostring(timenow, "#") + '"}'
// Send it from your own condition, once the bar closes: replace the input.bool(...) with your condition.
pc_open_long_condition = input.bool(false, "pc_open_long_condition: replace with your own condition", group = "Propcockpit")
if pc_open_long_condition
alert(pc_open_long_message, alert.freq_once_per_bar_close)- Create the alert on the script with the condition Any alert() function call (on a strategy, alert() function calls only) and leave the Message box as it is. There is no frequency to set in the dialog: alert.freq_once_per_bar_close in the call sends it once, when the bar closes.
- Replace the input.bool(...) with your own condition. Until you do, it is an input that is off, so the script sends nothing.
- A level from your chart - a pending order's entry, a modify's stop and target - is a variable you set. It is rounded to the symbol's tick and written as a plain dot-decimal, so it never arrives with a thousands separator.
- Every name the Pine declares starts with its message's own, like pc_open_long_, so a long's Pine and a short's, or an open's and a modify's, sit in one script - in a strategy as in an indicator. Paste the pc_secret line once.
Fields it does not know
A field whose name starts like one that protects, sizes or aims a trade is refused by name, because ignoring stoploss would mean trading without the stop you meant. Those beginnings are sl, tp, risk, vol, size, entry, type, close, percent, trail, break, expire, cancel, scope, action, side, symbol, stop, take, lot, in any case - so a name like slow_ma is refused too, and wants renaming. Any other field is accepted and listed on the log row as ignored, so price or time do no harm. So does close, which is TradingView's bar value rather than a field of ours.
Reading the log
Every delivery is logged with what it asked for, what it was sized at, and - if it was refused - the reason, in the same sentence a refused click would have shown. A refusal names what was actually wrong: a stop on the wrong side of the market says so, and is not reported as a complaint about the size. Fields the alert carried that nothing read are listed under the result.
The status on each row is what the broker actually did:
- Executed - the broker did all of it. For an entry, the size and risk are the ones the order went out with, priced by the last check before it was sent, and the stop and target are the ones the broker says it holds.
- Partly done - the broker confirmed some of it and not the rest, and the row says which: 2 of 3 closed; 1 refused, and why. A close, modify or breakeven runs on every position on the symbol one at a time, so one refusal does not stop the others. An entry the broker filled only part of says how much it filled. A reverse whose closes went and whose open did not is partly done too. You are notified.
- Unconfirmed - it was sent and the broker has not confirmed any of it: no answer yet, or a close it accepted without saying how much closed, or a cancel it never acknowledged. It may be live, so check your positions before sending it again. It is never shown as a refusal; when part of it was refused, the row says so and you are notified.
- Nothing to do - a close, modify or flatten that found nothing open, a cancel that found no resting order of its own, a breakeven whose stops were all already there or better, or an open whose on_existing: skip found a position or a resting order of this endpoint's own on the symbol. It is answered 200 and you are not notified, so a strategy that closes before every entry does not fill your inbox.
- Refused - nothing happened, and the reason is on the row.
- Pending - TradingView was answered 202 before the result was known: either it was still running when the answer had to go, and the row fills in as soon as the broker answers; or the trading gateway's answer was lost on the way back, and the row is settled from the Journal within a minute. Either way it keeps the 202 TradingView was told.
- Conflict - its signal_id was already used by a different alert in the last 24 hours. Nothing was sent, and you are notified: give each alert its own id.
- Unknown - the answer never came back, and a minute later the Journal holds no order from it. Check your positions before sending it again.
What TradingView sees
TradingView waits three seconds for an answer, resends one that failed with a 5xx (except 504) five seconds later up to three times, and never resends a 4xx. Each answer is chosen for what a resend would do:
- 200 - done: executed, partly done, unconfirmed, nothing to do, or the same alert again. A resend changes nothing.
- 202 - accepted, and the result is not known yet: it was still running after 2.5 seconds, or the trading gateway's answer was lost after the order may have gone. The answer carries a reference, the row on the Webhooks page settles when the broker answers or from the Journal within a minute, and there is no resend - one could trade twice. If it then turns out nothing reached the broker, it is released like a 503, but TradingView was told it was accepted and will not resend it: its row and a notification say so, and sending it again is up to you.
- 4xx - something to fix in the alert, the endpoint or the account: a refusal (422), a reused signal_id (409), a wrong or replaced secret (401), a delivery from outside TradingView's addresses on an endpoint that accepts only those (403) or too many deliveries (429). TradingView does not resend these.
- 503 - nothing reached the broker: the trading gateway could not be reached, the order was never sent, or the symbol did not start quoting in time. The alert is released, so TradingView's resend five seconds later is a real second try rather than a duplicate.
A close or modify finds its positions whatever case the symbol is written in, the same way an open does. A modify that only moves the take profit leaves a trailing stop trailing. While your account is Locked, that take-profit-only modify is refused on a trailing position, because it restates the stop and a stop the broker has trailed closer could otherwise move back out.
Symbols you do not have open
A Signal works on any symbol your broker offers, whether or not it is on your watchlist and whether or not Propcockpit is open anywhere. The first alert on a symbol nobody is watching waits a moment for the broker to start quoting it. If the broker has not started within a second and a half, that alert is refused with nothing sent, and the next one normally finds the symbol already quoted. A symbol a Signal traded stays quoted for a day after its last alert.
Opening a log row
Every row on the Webhooks page opens. It shows the alert as Propcockpit read it - the action, the symbol, the side, the order type and entry, the stop and target as you wrote them and the prices they became, and the size you asked for - then how it was sized (lots, the money at risk and what the percentage is of), what the risk gate decided, what the broker filled and holds, each position a close, modify or breakeven touched - for a reverse, each close and then the open, as steps - how long each step took, and the Support reference, which opens the order in the Journal. A delivery answered 202 also shows the reference TradingView was given, and when the alert says it was sent. The secret is never kept: not the field, and not a copy of it pasted anywhere else in the alert.
Every refusal, word for word
These are the sentences the log and the notification use. Words in italics are filled in from your alert.
Before the alert is read
- body too large (limit 8 KiB)
- too many deliveries
- no such endpoint
- invalid secret
- this secret was replaced and has stopped working: put the endpoint's new secret in the alert
- not from TradingView's addresses (value)
- signal ingest is paused
- your plan no longer includes Signals
- this endpoint's Trading account is no longer connected
- this account has no armed Risk rules
- the trading gateway could not be reached
- the trading gateway turned it away before running it
- the trading gateway refused to trade it, because it came marked as an observe-mode rehearsal
- the delivery could not be recorded, so nothing was sent
- sent value s before it arrived, and an entry older than 60 s is refused so a late alert never trades a price that has moved
- sent_at is value s after it arrived: check the sender's clock, or that the time says its zone
- signal_id value was already used for a different alert, so nothing was sent: give each alert its own
A revoked endpoint's URL answers no such endpoint and writes nothing to the log, because there is no endpoint left to write it against. TradingView's own alert log is where those show.
In the alert itself
- the alert body was not JSON
- unresolved TradingView placeholder placeholder in field
- unknown field field: check its spelling, or rename it (a field starting with stem is refused, not ignored)
- no action this endpoint understands
- value is not an action: use open, close, modify, cancel, flatten, breakeven or reverse
- no symbol
- no buy or sell
- value is not a side: use buy, sell, long or short
- missing stop loss: send sl, sl_pips or sl_percent
- value is not an order type: use market, limit or stop
- a limit or stop order needs an entry price
- a market order takes no entry price
- no size: send volume, risk_percent or risk_amount
- field is not a number: value
- field is too large to be a price, a size or a risk: value
- field must be above 0, not value
- risk_percent value is below 0.01, the smallest risk this sizes
- percent must be above 0 and at most 100, not value
- neither a stop loss nor a take profit to move
- field both set the stop loss: send one of sl, sl_pips or sl_percent
- field both set the take profit: send one of tp, tp_pips or tp_rr
- field both size the trade: send one of volume, risk_percent or risk_amount
- sl_percent must be above 0 and below 100, not value
- value is not what risk_percent_of takes: use current or initial
- risk_percent_of is only read with risk_percent
- value is not what size takes: the one word it takes is fit
- expires_in is for limit and stop orders: a market order fills or is refused at once
- expires_in is in minutes and must be at least 1, not value
- value is not what on_existing takes: use add or skip
- field is only read on where, and this alert is a value
- value is not a scope: use mine (this endpoint's own) or symbol (everything on the symbol)
- scope symbol on a cancel needs a symbol: name one, or send scope mine
- scope is read on a close, a modify, a cancel, a breakeven or a reverse, not on an open
- a flatten closes the whole account and takes no scope: to close only this endpoint's positions, send close with scope mine
- sent_at is not a time: value; send {{timenow}}, an ISO time or epoch seconds
- value is not what trailing takes: use true or false
- offset_pips is only read on a breakeven, not on value
- a breakeven sets the stop itself, at the entry: leave out sl and tp, or send modify
- a reverse opens at market: leave out type, entry and expires_in, or send close and then open
- on_existing is not read on a reverse, which closes this endpoint's positions on the symbol before it opens
- value is not what sl_trigger takes: use touch or bar_close
- field is only read with sl_trigger bar_close: add it, or leave field out
- sl_trigger bar_close needs the level a candle must close through: send sl_close, sl_close_pips or sl_close_percent
- field both set the bar-close level: send one of sl_close, sl_close_pips or sl_close_percent
- sl_close_percent must be above 0 and below 100, not value
- sl_trigger bar_close needs sl_timeframe: use 5m, 15m, 1h, 4h or 1d
- value is not a timeframe a bar-close stop watches: use 5m, 15m, 1h, 4h or 1d
- the bar-close level (field value) is not inside the stop loss: where
Against the endpoint's limits
- value is not allowed on this endpoint: its limits allow allowed
- value is not allowed on this endpoint: it is close-only (close, cancel, flatten and breakeven)
- value is not traded by this endpoint: its limits allow allowed
- risk_percent value is above this endpoint's limit of limit per trade
- this order would risk value of the balance, above this endpoint's limit of limit per trade
- this order's risk could not be worked out, so it cannot be held to this endpoint's limit of limit per trade
At the gate and the broker
A Signal the risk rules refuse gets the engine's own sentence, the one a refused click shows - a stop on the wrong side, a Buffer it would break. When the engine gives none, it is one of these:
- that action is not one this endpoint understands
- order placement is paused platform-wide
- trading on this account has been frozen
- this account is no longer protected under those rules, so nothing was sent
- this account cannot be read right now
- the broker does not offer that symbol
- that position is no longer open at the broker, so nothing was sent
- that order is no longer resting at the broker, so nothing was sent
- refused by your risk rules
- this instrument has not ticked for a while, so it cannot be priced and nothing was sent
- the market is closed, and a market order is refused until it opens
- a stop that far from the market would be at or below zero
- a target that far from the market would be at or below zero
- this endpoint requires a stop loss, and the alert sent none
- that size is below the smallest this instrument trades
- this instrument cannot be priced right now
- the broker did not start quoting that symbol in time, so nothing was sent
- the broker's symbol list for this account could not be read in time, so nothing was sent
- it never left Propcockpit, so nothing reached the broker
- the broker refused it
- it would risk more than this endpoint's limit allows
- this endpoint does not trade that symbol
- a request is already running on that position
- a change to that order is already on its way
- the stop moved at the broker while this waited to be sent, so nothing was changed - check it and send again
- the order's stop moved at the broker while this waited to be sent, so nothing was changed - check it and send again
- the order changed at the broker while this waited to be sent, so nothing was changed - check it and send again
- that price is on the wrong side of the market for this order, so nothing was changed
- what is open on that symbol was not opened by this endpoint - send scope: symbol, or deal with it by hand
- this endpoint holds only the other side on that symbol - send the position's side, or leave side out
- this account nets its positions - one per symbol, whoever opened it - so it cannot do what this alert asked: send close, then open
- a reverse needs a hedged account: send close, then open
- a stop at the entry would be past the market, so the broker would refuse it
- a bar-close stop (sl_trigger bar_close) and a trailing stop are on Pro and Premium, and this account's plan does not include them
- the bar-close level is not inside the stop loss, so a candle could never close through it first
- refused by the trading gateway (value)
What changed on 2026-09-27
Signals now label what they open, and exits touch only their own. Every message still reads the way it did, with these changes - each one closes less, or says so where it used to act:
- A close or modify touches only the positions this endpoint opened. It used to touch every position on the symbol, your manual trades included. Send "scope": "symbol" for the old reading.
- Positions a Signal opened before this change carry no label, so an exit that finds only those is refused and names them, rather than closing them. Close them by hand, or send scope: symbol once.
- A side on a close used to be ignored; it now closes only that side's positions. A close whose side matches none of them while the other side is open is refused - that is usually the closing order's side, which is the opposite of the position's.
- on_existing: skip counts only this endpoint's own positions and resting orders on the symbol, and is refused on a netted account.
- An alert still running after 2.5 seconds is answered 202 instead of making TradingView wait and time out. One that reached nothing is answered 503 and released, so TradingView's resend is a real second try; it used to be answered "duplicate".
- A signal_id reused for a different alert is refused as a conflict instead of answered as a duplicate, and an entry with a sent_at more than 60 seconds old is refused.
- breakeven and reverse are new actions, and trailing is read on an open. A message carrying trailing used to be refused as an unknown field; "trailing": true now asks the broker to trail the stop, and any value but true or false is refused by name.
What changed on 2026-09-26
Every message that was accepted before still means what it meant, with these changes, each one louder or safer:
- A take profit that is not a price is refused. It used to be dropped.
- On a modify, a stop that is not a price is refused. It used to be dropped while the take profit moved.
- An unknown field with one of the beginnings above is refused. It used to be ignored.
- A {{ left in any value is refused as an unresolved TradingView placeholder, which means the alert was made where placeholders are not filled in.
- An entry is read as a price on every open. One that is not a price, or a quoted one on a market order, is refused. Both used to be ignored.
- A volume that is not a size is refused. It used to be ignored, and a risk percent beside it sized the trade.
- Numbers in quotes are read. "volume": "0.5" used to be ignored, so a risk percent beside it sized the trade; now the lots do, as they always did for a bare number.
- risk_percent is read as typed: 0.29 is 0.29%, where it used to be sized at 0.28%.
- A close or modify with nothing open is Nothing to do, answered 200 with no notification. It used to be refused with a 422 and a notification.
- A take-profit-only modify keeps a trailing stop trailing. It used to turn the trail off.
- comment is read and sent to the broker. It used to be listed as ignored.
- long and short are sides. They used to be refused.
What it cannot do
A Signal cannot exceed your rules, cannot name an account (the endpoint decides that), cannot place an order while your account is Locked, and cannot close or change a position another endpoint or you opened unless it says scope: symbol.
How many Signals an endpoint takes
Up to 30 at once and 120 a minute, per endpoint - enough for a strategy firing on 30 symbols at the same bar close. Anything beyond that is refused with 429 and never trades. None of it is silent: the invocation log and the endpoint's card count what was refused, and you are notified, at most once an hour, when the refused Signals carried your secret. A strategy that sends more can share its Signals across a second endpoint.