PineHookPineHook

Signal Formatting

How to correctly format webhook signals in CSV, JSON, and minified JSON; field order requirements; all available parameters and their constraints; common formatting errors and fixes.

Overview#

PineHook accepts webhook signals in two interchangeable formats: CSV (plain text) and JSON. The platform auto-detects the format on each request, so you can choose whichever is easier to write in TradingView's alert editor. Every signal requires three core fields: a license key, a command, and (for most commands) a symbol, plus whatever parameters the command needs. Signals that fail validation are rejected immediately, with the reason returned to TradingView and recorded in the Signal Logs dashboard.


How It Works#

  1. TradingView fires an alert and posts the payload to your webhook URL.
  2. The platform detects the format: payloads that begin with { are parsed as JSON; all other payloads are parsed as CSV.
  3. The three core fields (license key, command, and symbol) are extracted and validated first.
  4. Additional parameters are parsed and validated. If any required parameter is missing, out of range, or conflicts with another, the signal is rejected at this step.
  5. On rejection, the platform records the reason in Signal Logs (provided the license key was valid and extractable) and returns an HTTP 400 response to TradingView with the rejection reason in the body.
  6. On success, the signal is queued and delivered to the connected EA. TradingView shows a green result on the alert's execution history.

Duplicate and Repeated Alerts#

TradingView re-sends a webhook when its first attempt is slow to respond or returns a server error, and those retries carry a byte-identical payload. To stop one alert from opening a position twice, the platform collapses identical repeats: if the exact same payload for the same license arrives again within a short window (about 12 seconds), it is treated as a duplicate — the original signal's result is returned, no second copy is queued, and the trade is not executed again.

  • You do not need to worry about TradingView's automatic retries double-filling an order.
  • A strategy that intentionally fires the very same command twice within ~12 seconds (identical symbol, side, size, and parameters) will see the second one collapsed into the first. If you genuinely want two identical entries that fast, vary something in the payload or space them more than ~12 seconds apart.
  • The window is short by design, so normal trading — even many different signals per minute — is unaffected.

Signal Formats#

CSV#

CSV uses plain text with comma-separated fields on a single line.

Structure:

LICENSE_KEY,action,SYMBOL,param1=value1,param2=value2,...

Rules:

  • The first three fields are positional and order-enforced: license key, then command, then symbol. Any other arrangement causes a field-order rejection.
  • Parameters after the symbol are key=value pairs separated by commas. Their order after the symbol is flexible.
  • Each field is trimmed of leading and trailing spaces before parsing.
  • Commands and parameter keys are case-insensitive (BUY, buy, and Buy are treated identically).
  • Symbols are automatically uppercased on receipt (eurusd becomes EURUSD).
  • The payload must be a single line. Embedded newlines and tabs are not permitted.

Multi-symbol parameters in CSV:

Commands that accept a list of symbols (eaoffexcept, eaonexcept) separate values with semicolons within the parameter value:

LICENSE_KEY,eaoffexcept=EURUSD;GBPUSD;USDJPY

Compact multi-target TP/SL in CSV:

Multi-TP and multi-SL targets support a compact shorthand as the value of tp_pips or sl_pips. Each segment follows the pattern pips:close_percentage, separated by semicolons. The last segment may omit the percentage; the platform assigns the remainder to reach 100%. A malformed compact string (a non-numeric segment, pips ≤ 0, a close percentage over 100, or a trailing remainder that works out to 0%) is rejected with an error — it is no longer silently dropped. A complete single level such as tp_pips=30:100 (one TP closing 100%) is accepted.

LICENSE_KEY,buy,EURUSD,size_lots=0.1,tp_pips=30:50;60:30;100

This sets three targets: 30 pips (close 50%), 60 pips (close 30%), 100 pips (close remaining 20%). See the multi-target TP/SL article for full details.


JSON#

JSON uses a standard object payload. Field order is irrelevant; the platform looks up fields by key name.

Structure:

{"license": "LICENSE_KEY", "action": "buy", "symbol": "EURUSD", "size_lots": 0.1}

Rules:

  • Field names for recognized keys are case-insensitive and normalized to lowercase ("license", "action", "symbol"); lowercase is recommended.
  • Parameter values follow standard JSON typing: strings, numbers, and arrays where documented.
  • Symbols are automatically uppercased on receipt, same as CSV.
  • Any valid JSON is accepted: formatted (with whitespace) or minified (no whitespace).

JSON-only features:

  • Multi-target TP/SL can use the tp_targets and sl_targets keys, which accept arrays of objects. Each object specifies one target level using pips, price, or pct plus close_pct or close_lots. The compact string syntax also works in JSON by passing it as a string value for tp_pips or sl_pips.
  • Multi-symbol commands (eaoffexcept, eaonexcept) accept a symbols key that can be a JSON array or a comma/semicolon-separated string.

Minified JSON#

Minified JSON (no whitespace between fields) is fully supported and treated identically to formatted JSON. TradingView's alert editor often produces minified JSON naturally:

{"license":"ABCD-EF01-2345-6789","action":"buy","symbol":"EURUSD","size_lots":0.1,"sl_pips":50,"tp_pips":100}

No special handling or configuration is needed for minified payloads.


Core Required Fields#

License Key#

Identifies your license. Every signal must include it.

  • CSV position: First field (position 1).
  • JSON key: "license"
  • Required format: XXXX-XXXX-XXXX-XXXX: four groups of four hexadecimal characters (0-9, A-F), separated by hyphens. Case-insensitive on input.
  • Error on invalid format: Invalid license key format - expected XXXX-XXXX-XXXX-XXXX (hex characters only)

Critical: If the license key is missing or fails format validation, no Rejected Signals row is created. The rejection is still returned to TradingView, but you will not see it in Signal Logs. Always confirm the key is present and correctly formatted before checking for a Rejected Signals entry.


Command (Action)#

Tells the EA what action to perform. Commands are case-insensitive.

  • CSV position: Second field (position 2).
  • JSON key: "action"
  • Error if missing: Missing 'action' - signals require: license, action, symbol
  • Error if unrecognized (close match found): Unknown command '{command}' - did you mean '{suggestion}'?
  • Error if unrecognized (no close match): Unknown command '{command}' - valid commands include: buy, sell, closelong, closeshort, closeall, etc.
  • Deprecated name: canceldelay was renamed to clearqueue. Sending the old name returns an error pointing to clearqueue; it does not silently execute.

Symbol#

The trading instrument the command applies to.

  • CSV position: Third field (position 3).
  • JSON key: "symbol"
  • Required format: 1 to 20 uppercase alphanumeric characters (A-Z, 0-9). No slashes, dashes, dots, spaces, or other special characters. Automatically uppercased on receipt.
  • Error on invalid format: Invalid symbol '{symbol}'. Symbols must be 1-20 uppercase alphanumeric characters (A-Z, 0-9).
  • Error if missing (when required): Missing 'symbol' - signals require: license, action, symbol

Commands that do not require a symbol in the standard third-field position: eaon, eaoff, eaoffexcept, eaonexcept, closeall, closecancelall, cancelall, clearqueue, closealleaoff, closecancelalleaoff, closeallprofit, closeallloss

eaoffexcept and eaonexcept still require at least one symbol in the parameter value (e.g., eaoffexcept=EURUSD;GBPUSD). Sending either with no symbols is rejected and surfaces in Signal Logs → Rejected Signals with reason "missing_symbol". Use eaoff,ALL or eaon,ALL for the all-symbols cases.

Symbol filter list size limit: the symbol filter (in either blacklist or whitelist mode) is capped at 100 symbols. Any EA control command (eaon, eaoff, eaoffexcept, eaonexcept, or an inline eaon= / eaoff= parameter on a trade command) that would leave the filter with more than 100 entries is rejected with block_reason="filter_list_too_long". The rejection message reports the would-be size and suggests switching to the inverse filter mode (e.g., "you're blacklisting 100+ symbols, consider a whitelist of just the few you want to allow"). The filter is not modified when this happens.


Commands Reference#

CommandCategorySymbol RequiredDescription
buyMarket orderYesOpen a long market position
sellMarket orderYesOpen a short market position
buylimitPending orderYesPlace a buy limit order (entry below current price)
buystopPending orderYesPlace a buy stop order (entry above current price)
selllimitPending orderYesPlace a sell limit order (entry above current price)
sellstopPending orderYesPlace a sell stop order (entry below current price)
buyatPending orderYesAlias: buy limit or stop, direction inferred from price vs. market
sellatPending orderYesAlias: sell limit or stop, direction inferred from price vs. market
closelongCloseYesClose open long positions on the symbol
closeshortCloseYesClose open short positions on the symbol
closeallCloseNoClose all open positions
closeallprofitCloseNoClose all net-profitable positions — P&L incl. swap+commission (optional minprofit price-pip filter)
closealllossCloseNoClose all net-losing positions — P&L incl. swap+commission (optional maxloss price-pip filter)
closecancelallClose + cancelNoClose all positions and cancel all pending orders
cancellongCancelYesCancel pending buy orders on the symbol
cancelshortCancelYesCancel pending sell orders on the symbol
cancelallCancelNoCancel all pending orders
clearqueueQueueNoCancel all queued delayed signals for this license
trailTrade managementYesAdd or update a trailing stop on matching positions
notrailTrade managementYesRemove trailing stop monitors from matching positions
breakevenTrade managementYesAdd or update an auto-breakeven on matching positions
nobreakevenTrade managementYesRemove auto-breakeven monitors from matching positions
modifyTrade managementYesModify the SL or TP of existing positions or pending orders
eaonEA controlNoEnable trading on a symbol (pass symbol as eaon=SYMBOL)
eaoffEA controlNoDisable trading on a symbol (pass symbol as eaoff=SYMBOL)
eaonexceptEA controlNo (third field), but at least one symbol required in the parameter valueEnable trading on all symbols except those listed
eaoffexceptEA controlNo (third field), but at least one symbol required in the parameter valueDisable trading on all symbols except those listed
closealleaoffEmergencyNoClose all positions, then disable the EA
closecancelalleaoffEmergencyNoClose all positions, cancel all pending orders, disable the EA

Parameters Reference#

Position Sizing#

Required for all trade-opening commands (buy, sell, and all pending order commands). Use exactly one.

ParameterDescription and Constraints
size_lotsFixed position volume in lots (e.g., size_lots=0.1). Use any positive value your broker permits.
size_pctRisk-based sizing: percentage of account balance to risk on this trade (greater than 0 and up to 100). Requires a stop loss parameter; without an SL distance, lot size cannot be computed.
  • Using both in the same signal: rejected with Conflicting position sizing parameters: size_lots, size_pct. Use either size_lots (fixed volume) or size_pct (risk-based), not both.
  • Omitting both on a trade-opening command: rejected with Position sizing is required. Specify size_lots (fixed volume, e.g. size_lots=0.1) or size_pct (risk-based, e.g. size_pct=1.5)...
  • size_pct without a stop loss: rejected with size_pct requires a stop loss to calculate position size...; add sl_pips, sl_price, or sl_pct.

Stop Loss#

Optional for most commands, but required when size_pct is used. Use at most one.

ParameterDescription and Constraints
sl_pipsStop loss distance in pips from the entry price
sl_priceStop loss as an absolute price level. Must be positive; sl_price=0 is rejected, and a value above roughly 1,000,000,000 (1e9) is rejected with HTTP 400 (matching the per-target slN_price cap).
sl_pctStop loss distance as a percentage of the entry price

Using more than one format in the same signal: Conflicting stop loss parameters: {params}. Use only one of: sl_pips, sl_price, or sl_pct.


Take Profit#

Optional. Use at most one.

ParameterDescription and Constraints
tp_pipsTake profit distance in pips from the entry price
tp_priceTake profit as an absolute price level. Must be positive; tp_price=0 is rejected, and a value above roughly 1,000,000,000 (1e9) is rejected with HTTP 400 (matching the per-target tpN_price cap).
tp_pctTake profit distance as a percentage of the entry price

Using more than one format: Conflicting take profit parameters: {params}. Use only one of: tp_pips, tp_price, or tp_pct.


Multi-Target TP/SL (Up to 10 Levels)#

Splits the position across multiple take-profit or stop-loss price targets. See the multi-target TP/SL article for full syntax documentation.

Key constraints:

  • Levels must be numbered sequentially from 1 with no gaps (tp1, tp2, tp3, etc.). Skipping from tp1 to tp3 without a tp2 is rejected.
  • Close percentages across all levels must sum to exactly 100%.
  • Cannot mix _close (percentage) and _lots (fixed volume) on the same side.
  • Maximum of 10 targets per side.

Available input modes:

  • Individual parameters: tp1_pips=30, tp1_close=50, tp2_pips=60, tp2_close=30, etc.
  • Compact string (CSV or JSON string value): tp_pips=30:50;60:30;100
  • JSON array (JSON only): "tp_targets": [\{"pips": 30, "close_pct": 50\}, ...]

Pending Order Entry Price#

ParameterDescription and Constraints
priceEntry price for limit and stop orders. Required for all pending order commands (buylimit, buystop, selllimit, sellstop, buyat, sellat). Must be a positive value.

The platform also validates that SL and TP prices are on the correct side of the entry price when absolute prices are used:

  • Buy orders: TP must be above entry; SL must be below entry.
  • Sell orders: TP must be below entry; SL must be above entry.

Trailing Stop#

Use exactly one distance mode. These parameters are also valid on trail commands issued separately after a position opens. See the trailing stop and breakeven article for full details.

ParameterDescription and Constraints
distance_pipsTrail distance in pips. A value above roughly 1,000,000 is rejected with HTTP 400.
distance_pctTrail distance as a percentage of entry price (greater than 0, up to 99)
distance_atrTrail distance as an ATR multiplier (0.1 to 20.0)
trail_triggerPips of profit required before the trail activates. A value above roughly 1,000,000 is rejected with HTTP 400.
trail_trigger_pctPercentage profit required before activation (0–100). A value above 100 is rejected with HTTP 400, on both the standalone trail command and an inline trail on an opening command.
trail_trigger_priceExact price level that triggers trail activation
trail_stepMinimum pips the SL must move per update (must be positive). Use with distance_pips. A value above roughly 1,000,000 is rejected with HTTP 400.
trail_pct_stepMinimum percentage movement per update. Use with distance_pct. Automatically capped at the distance value.
trail_atr_periodATR calculation period in bars (2-500, default 14). Use with distance_atr.
trail_atr_timeframeATR timeframe. Valid values: M1, M5, M15, M30, H1, H4, D1, W1, MN1. An unrecognized value is rejected with HTTP 400 (recorded in Signal Logs -> Rejected Signals).

Trigger modes are mutually exclusive: use at most one of trail_trigger, trail_trigger_pct, trail_trigger_price.

The pips-based trailing fields (distance_pips, trail_step, trail_trigger) reject a finite-but-absurd value above roughly 1,000,000 pips; their _pct/_atr siblings were already bounded. An absurd value placed the trailing stop so far away it never engaged, leaving you with a silently dead trailing stop you believed was active. A legitimately wide trailing distance is unaffected.


Auto-Breakeven#

Use exactly one trigger mode and exactly one offset mode. See the trailing stop and breakeven article.

ParameterDescription and Constraints
be_trigger_pipsPips of profit required before moving the SL to breakeven. be_trigger_pips=0 fires immediately on position open.
be_trigger_pctPercentage profit required. be_trigger_pct=0 fires immediately.
be_trigger_priceExact price level that triggers the breakeven move. be_trigger_price=0 means no trigger.
be_offset_pipsHow far above or below entry to place the SL. Positive values lock in profit; negative values allow a buffer below entry.
be_offset_pctOffset as a percentage of the entry price
be_offset_priceAbsolute SL price to move to

Trigger modes are mutually exclusive; offset modes are mutually exclusive.


Trade Filtering#

These parameters narrow which positions or orders a command affects.

ParameterDescription and Constraints
magicMagic number (integer 0 to 2,147,483,647, or ALL). N restricts to that exact strategy; 0 = magic-0 (manual trades); ALL = every magic (wildcard) on close/cancel/modify commands only, and is rejected on opening commands (buy, sell, pending orders); negatives and values above 2,147,483,647 are rejected. Omit to use your default magic.
close_pctPartial close percentage (greater than 0, up to 100). Cannot combine with close_lots.
close_lotsPartial close in fixed volume (must be positive). Cannot combine with close_pct.
minprofitFor closeallprofit: minimum favourable movement in price pips a position must have to be closed. (Winner/loser is decided on net P&L incl. swap+commission; this is a separate pip filter.) Must be 0 or greater.
maxlossFor closeallloss: maximum adverse movement in price pips a position is allowed to have before it is closed. (Net P&L incl. swap+commission decides "loser"; this caps the pip magnitude.) Must be 0 or greater.
directionFor modify: filter to long or short positions only.
targetFor modify: apply only to sl, tp, or both.
order_typeFor modify: target pending orders of a specific type (buystop, buylimit, sellstop, selllimit). Cannot be combined with direction.

Spread and Slippage Protection#

ParameterDescription and Constraints
maxspread_pipsMaximum allowed spread in pips at execution time. The EA skips execution if the spread exceeds this value at the moment the order is placed.
maxspread_pctMaximum allowed spread as a percentage of price (0 to 100).
marketpriceReference market price from the TradingView alert. Used for slippage comparison at execution. Use the current price variable from TradingView's alert engine.

Timing and Scheduling#

ParameterDescription and Constraints
delaySeconds to wait before executing the signal (paid plans only). Maximum 86,400 seconds (24 hours).
closetimeSeconds after the position opens to automatically close it. Range: 1 to 2,592,000 (30 days).
expirySeconds until a pending order expires. Subject to a tier-based cap; exceeding the cap causes rejection.
expiry_timeISO 8601 datetime for pending order expiration (e.g., 2026-05-01T14:00:00Z). A value with no timezone suffix is treated as UTC. A malformed value is rejected with HTTP 400 (recorded in Signal Logs -> Rejected Signals); omit the field for no expiry.
active_startPer-signal active window start time in HH:MM (24-hour format). Overrides this license's active hours setting for this signal only. A malformed value (e.g. 25:00, or a missing leading zero like 9:00) is rejected with HTTP 400 (recorded in Signal Logs -> Rejected Signals). Must be paired with active_end; active_start and active_end must differ — an equal/zero-length window (incl. 00:00/00:00) is rejected with HTTP 400. Omit both for an always-on window.
active_endPer-signal active window end time in HH:MM (24-hour format). Same pairing and zero-length-window rules as active_start.

Other Parameters#

ParameterDescription and Constraints
authSignal authentication password for this license. When Signal Authentication is enabled on the license, every signal must include it or the signal is blocked; when the feature is off, no signal needs it (a value sent while off is parsed then ignored). Maximum 72 characters. See the signal authentication article for details.
commentAccepted for compatibility but not applied to the order. The broker order comment is reserved for PineHook's internal signal-ID tag (SIG:<id>), so a custom comment has no effect. If sent, it still counts toward the payload size limit.

Payload Size Limit#

All signals must be 2,048 bytes or smaller. Signals exceeding this size return HTTP 413 (payload too large). No Rejected Signals row is created for oversized payloads; the rejection appears only in the TradingView alert response. The most common cause is an unusually long comment value.


Error Messages and Meanings#

Group A: Validation Rejections (HTTP 400)#

Surface: Signal Logs → Rejected Signals tab AND TradingView alert execution history.

A Rejected Signals row is only created when the platform can extract a valid license key from the payload. If the key is absent or fails format validation, no row appears in Signal Logs. The rejection is still returned to TradingView. Very long rejection messages may be shortened.

MessageCauseWhat to do
Invalid license key format - expected XXXX-XXXX-XXXX-XXXX (hex characters only)License key does not match the required four-group hex formatCopy the key directly from the Licenses page in the dashboard. Do not retype it manually.
Missing 'license' - signals require: license, action, symbolNo license key found in the payloadAdd the license key as the first CSV field or as "license" in JSON
Missing 'action' - signals require: license, action, symbolNo command found in the payloadAdd the command as the second CSV field or as "action" in JSON
Missing 'symbol' - signals require: license, action, symbolSymbol field absent for a command that requires oneAdd the symbol as the third CSV field or as "symbol" in JSON
Wrong field order: '{value}' looks like a command but is in the symbol position. Correct order is: LICENSE,action,SYMBOLCSV fields are in the wrong order; a command name appears in the symbol positionReorder the CSV fields: license key first, command second, symbol third
Unknown command '{command}' - did you mean '{suggestion}'?Command name is misspelled; the platform found a close matchUse the suggested correction
Unknown command '{command}' - valid commands include: buy, sell, closelong, closeshort, closeall, etc.Command name is unrecognized with no close matchCheck the Commands Reference in this article and correct the name
canceldelay was renamed to 'clearqueue' for clarity...Deprecated command name usedReplace canceldelay with clearqueue in the alert
Invalid symbol '{symbol}'. Symbols must be 1-20 uppercase alphanumeric characters (A-Z, 0-9).Symbol contains unsupported characters (slash, dash, space, etc.)Remove special characters; use the broker's symbol name without separators (e.g., EURUSD not EUR/USD)
Position sizing is required. Specify size_lots (fixed volume, e.g. size_lots=0.1) or size_pct (risk-based, e.g. size_pct=1.5)...Trade-opening command has no size parameterAdd size_lots=0.1 (or a suitable volume) or size_pct=1.5
Conflicting position sizing parameters: size_lots, size_pct. Use either size_lots (fixed volume) or size_pct (risk-based), not both.Both size_lots and size_pct appear in the same signalRemove one; use only size_lots or only size_pct
Invalid size_pct={value}. Must be > 0 and <= 100 (percentage of account balance to risk on this trade).size_pct is 0, negative, or above 100Use a value between 0 (exclusive) and 100 (inclusive)
Invalid size_lots={value}. Must be greater than 0 (fixed trade volume).size_lots is 0 or negative on a trade-opening commandUse a positive lot size; a negative size_lots would otherwise silently fall back to the EA's default lot
size_pct requires a stop loss to calculate position size...size_pct present but no stop loss parameter includedAdd sl_pips, sl_price, or sl_pct; the EA requires the SL distance to compute lot size
Conflicting stop loss parameters: {params}. Use only one of: sl_pips, sl_price, or sl_pct.More than one SL format type in the same signalRemove all but one SL parameter
Conflicting take profit parameters: {params}. Use only one of: tp_pips, tp_price, or tp_pct.More than one TP format type in the same signalRemove all but one TP parameter
sl_price=0 is not valid. Use a positive price value to set the stop loss.sl_price set to 0Use a positive price level, or omit sl_price to open without a stop loss
tp_price=0 is not valid. Use a positive price value to set the take profit.tp_price set to 0Use a positive price level, or omit tp_price to open without a take profit
Invalid sl_pips={value}. Must be a positive distance — a negative value would open the position with no stop/target...A base stop/target distance or percentage (sl_pips, tp_pips, sl_pct, tp_pct) is negative on a trade-opening commandUse a positive value. A negative stop would otherwise be silently treated as "no stop" and open the position unprotected — the signal is rejected instead
Invalid sl_price={value}. A stop-loss price must be positive. (and tp_price)A negative absolute SL/TP price on a trade-opening commandUse a positive price level; a negative price would silently drop the stop and open the position unprotected
{COMMAND} price={value} is not valid. The entry price must be a positive value...A pending order sent with price=0 or a negative priceProvide a positive entry price (e.g. price=1.0800)
{COMMAND} requires a price parameter specifying the entry level. Example: price=1.0800Pending order command sent without a price parameterAdd price= with the desired entry price
Take profit (tp_price={value}) must be above entry price ({entry}) for BUYLIMIT orders...TP price is below the entry price on a buy orderMove TP above the entry price; for buy orders, TP must be higher than entry
Stop loss (sl_price={value}) must be below entry price ({entry}) for BUYLIMIT orders...SL price is above the entry price on a buy orderMove SL below the entry price; for buy orders, SL must be lower than entry
Take profit (tp_price={value}) must be below entry price ({entry}) for SELLLIMIT orders...TP price is above the entry price on a sell orderMove TP below the entry price; for sell orders, TP must be lower than entry
Stop loss (sl_price={value}) must be above entry price ({entry}) for SELLLIMIT orders...SL price is below the entry price on a sell orderMove SL above the entry price; for sell orders, SL must be higher than entry
Gap detected in TP target numbering. Missing: TP{n}. Found: TP1, TP3. Targets must be numbered sequentially starting from TP1 with no gaps.TP levels skip a numberAdd the missing level, or renumber all levels starting from 1 with no gaps
Too many TP/SL targets in compact syntax: {N} provided, maximum is 10.Compact TP/SL string has more than 10 segmentsReduce to 10 targets or fewer
Multi-TP close percentages must sum to 100% (got {total}%)Close percentages across all TP levels do not total 100Adjust the values until they sum exactly to 100
Conflicting TP{n} level parameters: tp{n}_pips, tp{n}_price. Use only one of: tp{n}_pips, tp{n}_price, or tp{n}_pct.A single TP level uses more than one price formatKeep only one format for that level
trail command requires a distance parameter. Specify one of: distance_pips, distance_pct, or distance_atr.trail command sent without any distance parameterAdd distance_pips, distance_pct, or distance_atr
Conflicting trailing stop parameters: {params}. Use only one of: distance_pips, distance_pct, or distance_atr.Multiple trail distance types in the same signalKeep only one distance type
trail_step={value} is invalid. Must be greater than 0 (minimum SL move in pips).trail_step is 0 or negativeUse a positive value
Conflicting trail trigger parameters: {params}. Use only one of: trail_trigger, trail_trigger_pct, or trail_trigger_price.Multiple trail trigger types in the same signalKeep only one trigger type
Conflicting breakeven trigger parameters: {params}. Use only one of: be_trigger_pips, be_trigger_pct, or be_trigger_price.Multiple breakeven trigger types in the same signalKeep only one trigger type
Conflicting breakeven offset parameters: {params}. Use only one of: be_offset_pips, be_offset_pct, or be_offset_price.Multiple breakeven offset types in the same signalKeep only one offset type
Conflicting expiry parameters: expiry (seconds) and expiry_time (datetime). Use only one.Both expiry and expiry_time are present on the same pending orderKeep only one — the EA would otherwise silently honor expiry and discard expiry_time
`delay={value}` is not valid. Use a positive number of seconds...delay is negativeUse a positive number of seconds, or omit delay for immediate dispatch
Unknown parameter '{param}' for {command} command... Check the spelling, see https://pinehook.io/docs/commands for the full list.Unrecognized parameter name on a trade management commandFix the spelling; trade management commands reject unknown parameters. Even one typo causes rejection.
Unrecognized parameter - did you mean '{suggestion}'?Unknown parameter on a market or close command; a close match was foundFix the parameter name using the suggestion
Invalid value '{value}' - expected a number (non-ASCII characters not allowed)A numeric field contains non-ASCII digits (e.g., Unicode fullwidth numerals)Use standard ASCII numerals only
Invalid value '{value}' - must be a finite numberA numeric field contains NaN or InfinityReplace with a valid real number

Group B: Plan and Rate Limit Rejections#

Surface: TradingView alert response: HTTP 402 for the plan gates (delay requires a paid plan, expiry cap exceeded); HTTP 429 for the queue-full and rate-limit rejections. Rate-limited signals (HTTP 429) are recorded: they appear in Signal Logs as a Blocked row and in Rejected Signals, with reason rate_limited (per-minute) or rate_limited_daily (per-day).

MessageCauseWhat to do
Delayed signals require a paid plan. Upgrade to use delay=.delay parameter used on a Trial accountUpgrade the subscription or remove the delay parameter from the alert
Pending-order expiry exceeds this plan's {minutes}-minute cap. Upgrade for longer expiries.expiry value exceeds what the current plan allowsReduce the expiry duration or upgrade to a plan with a higher cap
Delayed-signal queue is full for this license. Cancel pending signals with clearqueue, remove them individually from Signal Logs on your PineHook dashboard, or wait for them to execute.Too many delayed signals are already queued for this license (returns HTTP 429; this one does appear in Rejected Signals)Send a clearqueue signal to cancel pending delayed signals, then retry

Group C: Payload Size (HTTP 413)#

Surface: TradingView alert response only. No Rejected Signals row is created.

MessageCauseWhat to do
Request entity too largeSignal payload exceeds 2,048 bytesShorten the comment field or remove unusually long string values

Group D: Signal Authentication (HTTP 200, status blocked)#

Surface: Signal Logs → Rejected Signals tab AND the TradingView alert response.

Signal-authentication failures are not validation rejections. The request is well-formed and the license key is valid, so the platform returns HTTP 200 with a body of {"status": "blocked", "reason": ..., "message": ...} rather than a 4xx error. The signal is recorded as blocked and surfaces in the Rejected Signals tab. (An unknown or invalid license key is different: that is an HTTP 401, not a signal-auth block.)

MessageReason codeCauseWhat to do
Authentication requiredmissing_authSignal Authentication is enabled on this license and the auth parameter is missing from the signal (every signal for the license must carry it while the feature is on)Add auth=PASSWORD to the signal; see the signal authentication article
Invalid authenticationinvalid_authSignal Authentication is enabled and the password is incorrect or exceeds 72 bytesVerify the password in the dashboard matches what the alert sends; check for case sensitivity and character count

Where to look#

SituationWhere to look
You see a rejection and want to understand the reasonSignal Logs → Rejected Signals tab in the dashboard
Your signal was rejected but nothing appears in Rejected SignalsThe license key in the payload is missing or malformed. Fix the key format first; once valid, rejections will appear in Signal Logs.
HTTP 429 (rate limit exceeded)Dashboard → Subscription page; signal frequency limits are plan-specific
HTTP 413 (payload too large)The alert payload exceeds 2,048 bytes. Shorten the comment or other long string fields.
"Authentication required" or "Invalid authentication"Signal Logs → Rejected Signals tab AND the signal authentication article
Your signal is not in Rejected Signals and not rate-limited, but TradingView shows a failureConfirm the webhook URL is correct and the request is reaching the platform; check if the EA is connected

Plan and Tier Differences#

Feature or BehaviorTrialStandardProPremium
CSV and JSON signal deliveryAvailableAvailableAvailableAvailable
Multi-target TP/SL (tp1-tp10, sl1-sl10)AvailableAvailableAvailableAvailable
Trailing stop (trail) and auto-breakeven (breakeven) commandsAvailableAvailableAvailableAvailable
delay parameter (queued signal execution)Not availableAvailableAvailableAvailable
Pending order expiry cap (expiry, expiry_time)15-min cap24-hour cap24-hour cap24-hour cap
Signal frequency limitPlan limit appliesPlan limit appliesHigher limitHighest limit

When a Trial period ends with no paid plan active, trade-opening signals are received but not executed. Close and cancel commands remain operational regardless of plan state.


Known Edge Cases and Gotchas#

  • CSV third-field ambiguity: If the third comma-separated value contains an = character, the parser treats it as a key=value parameter rather than the symbol. Always place the symbol as a plain value with no = sign, and put all parameters after it.

  • Automatic symbol uppercasing: Symbols are silently uppercased on receipt. eurusd and EURUSD produce identical results. This is by design but can confuse you if you compare your alert text against the uppercase value you see in Signal Logs.

  • canceldelay is deprecated and does not execute: Sending this old command name returns an error pointing to clearqueue. It does not silently run the equivalent action.

  • size_pct requires a stop loss: Without sl_pips, sl_price, or sl_pct in the same signal, the signal is rejected. The EA needs the SL distance to compute position size from a risk percentage.

  • sl_price=0 and tp_price=0 are invalid: Zero is not accepted as a price value. Omit the field to open a position without a stop loss or take profit.

  • sl_pips/tp_pips have an upper bound: A pip distance above roughly 1,000,000 is rejected with HTTP 400 (sl_pips is implausibly large …), mirroring the size_lots ceiling. This catches an absurd typo such as sl_pips=100000 when you meant 100 — on a SELL that places the stop so far away it never triggers, leaving an effectively naked position you believe is protected. A legitimately wide stop (well under 1,000,000 pips) is unaffected.

  • Compact TP/SL must be valid or the signal is rejected (HTTP 400): The close percentages must sum to exactly 100% (an optional trailing bare-pips segment supplies the remainder). A malformed compact string — non-numeric segment, pips ≤ 0, a close percentage above 100, or a trailing remainder that resolves to 0% because earlier segments already total 100 (e.g. 30:50;60:50;100) — is now rejected with a clear error, not silently discarded. Previously such a string opened a position with no targets and no error message; that footgun is closed. A complete single level like tp_pips=30:100 is accepted.

  • Multi-TP numbering must be sequential with no gaps: Specifying TP1 and TP3 without TP2 is rejected. Always number from 1 upward with no skipped levels.

  • A single TP/SL cannot coexist with a multi-TP/SL ladder: Sending both a single tp_pips/tp_price/tp_pct and a multi-TP ladder (tp1_*tp10_*) in one signal is rejected; use one or the other (the same applies to the SL side).

  • Target indices above 10 are rejected: tp11_*, sl12_*, and higher fail with an error rather than being silently ignored. The maximum is 10 levels per side.

  • Per-target close volumes and distances must be positive, finite, and sane: A tpN_lots/slN_lots (or JSON close_lots) that is negative, zero, NaN, Infinity, or implausibly large (a close volume far above any real position, or an absurd pips/price distance) is rejected at the server before it reaches the EA. In the JSON tp_targets/sl_targets array, a level-distance pct of 0 is also rejected, and a malformed element (a non-object, or a non-numeric value) rejects the whole signal — it no longer silently drops just that level.

  • modify validates per-level format conflicts too: Setting two formats on the same level (e.g. modify,…,tp1_pips=50,tp1_price=…) is rejected on modify, the same as on entry.

  • modify re-validates new levels against the broker freeze zone: A modify that moves a multi-TP/SL level inside the broker's minimum-stop distance (or already past current market) is skipped with a warning instead of being applied — otherwise the EA's tick-by-tick manager would fire it as an instant, unintended partial close. Move the level further from market and resend.

  • Unrecognized parameters cause rejection even on market-order commands: A typo such as sl_pip instead of sl_pips causes the signal to fail with HTTP 400. Trade management commands (trail, breakeven, modify, etc.) are even stricter: they reject any parameter name they do not recognize, including parameters that are valid on other command types.

  • Non-ASCII digits in numeric fields are rejected: Unicode fullwidth numerals (used in some Asian character sets) and other non-ASCII digit substitutes cause an explicit error. Use standard ASCII digits only.

  • Multi-symbol syntax differs between CSV and JSON: In CSV, list symbols semicolon-separated inside the parameter value (e.g., eaoffexcept=EURUSD;GBPUSD). In JSON, use the symbols key with an array or comma/semicolon-separated string. The eaon and eaoff parameters in JSON accept only one symbol; use eaonexcept or eaoffexcept for multiple symbols.

  • Payload limit is enforced at the gateway: Signals over 2,048 bytes return HTTP 413 and do not create a Rejected Signals row. The comment field is the most frequent cause of oversized payloads.

  • trail_atr_timeframe is rejected on unrecognized values: A misspelled or unsupported timeframe value (e.g., 1H instead of H1) is rejected with HTTP 400 and recorded in Signal Logs -> Rejected Signals, rather than silently falling back to a default. Valid values are: M1, M5, M15, M30, H1, H4, D1, W1, MN1.

  • auth is position-independent in CSV: The auth parameter can appear anywhere in the parameter list (before or after other parameters) and is always extracted by key name, not by position.

  • Rate-limited signals are recorded: An HTTP 429 (per-minute or per-day cap) writes a Blocked row in Signal Logs and a row in Rejected Signals, with reason rate_limited or rate_limited_daily. If you're missing signals during a burst, look for these Blocked/Rejected rows to confirm you're hitting your plan's frequency limit. (Close and cancel commands are exempt from the caps.)


Frequently Asked Questions#

Q: Do I have to use JSON, or can I use CSV? A: Either format works for any command. Both CSV and JSON are fully supported everywhere. Choose whichever is easier to write in TradingView's alert message editor.

Q: Does field order matter in JSON? A: No. JSON field order is irrelevant; the platform looks up fields by key name. Field order only matters in CSV, where the first three values must appear as license key, then command, then symbol.

Q: My symbol includes a slash (like EUR/USD). Why is it rejected? A: Symbols must contain only letters (A-Z) and digits (0-9), with no separators. Use EURUSD instead of EUR/USD. Check the exact symbol name in the MT5 Market Watch window; brokers sometimes use suffixes such as EURUSDm or EURUSD.r.

Q: Can I include both size_lots and size_pct in the same signal? A: No. They are mutually exclusive. Use one or the other. A signal containing both is rejected.

Q: I sent a signal and nothing appeared in Rejected Signals. What happened? A: Rejected Signals rows are only created when the platform can extract a valid, correctly formatted license key from the payload. If the key is missing or malformed, no row appears. Rate-limited rejections (HTTP 429) also do not create rows. Check the TradingView alert response for the HTTP status code and body to determine what happened.

Q: What is the difference between tp_pips and tp1_pips? A: tp_pips sets a single take profit target for the whole position. tp1_pips is the first level of a multi-target take profit, where the position is split across multiple price levels. Use tp1_pips, tp2_pips, etc. only when splitting the position; for a single target, use tp_pips.

Q: Why is my delay parameter rejected with "requires a paid plan"? A: The delay parameter is not available on Trial accounts. Upgrade to a Standard, Pro, or Premium plan to queue signals for delayed execution, or remove the delay parameter from the alert.

Q: Can I use minified JSON in TradingView's alert body? A: Yes, fully supported. Minified JSON and formatted JSON are handled identically. TradingView's alert editor often produces minified payloads naturally.

Q: What does the rejection message "did you mean '{suggestion}'?" mean? A: The platform detected that a parameter name in the signal closely resembles a known parameter but is not an exact match, usually a typo. Replace the parameter name with the suggestion provided in the rejection message.

Q: My pending order signal is rejected even though I included all the fields I think are required. What am I missing? A: Three common causes: (1) the price parameter is missing; every pending order command requires an entry price; (2) a TP or SL price is on the wrong side of the entry (TP must be above entry for buy orders, below for sell orders; SL is the reverse); (3) sl_price or tp_price is set to 0, which is not a valid price. Check each against the rejection reason shown in the Rejected Signals tab.