Webhook API Reference
Complete reference for the TradeStaq webhook API used to trigger Signal Bot trades.
Overview
The Webhook API allows external services (like TradingView) to send trading signals to your Signal Bots. The webhook accepts a signal and queues it; the trade then runs straight away in the background, and its result appears in the bot's Signal Logs tab.
┌──────────────┐ POST ┌──────────────┐ Execute ┌──────────────┐
│ TradingView │──────────────▶│ TradeStaq │────────────────▶│ Exchange │
│ or Script │ /webhooks │ Server │ Trade │ (API) │
└──────────────┘ └──────────────┘ └──────────────┘
Endpoint
POST https://www.tradestaq.com/api/webhooks/trade/{webhookId}
| Component | Description |
|---|---|
| Base URL | https://www.tradestaq.com |
| Path | /api/webhooks/trade/{webhookId} |
| Method | POST |
| Content-Type | application/json |
Finding Your Webhook ID
- Go to Dashboard → Signal Bots
- Click your bot
- Open the Webhook Setup tab
- Copy the webhook URL
- The ID is the last segment of the URL
Note: Anyone with this URL can send signals to your bot. Keep it private.
Request Format
The body is one JSON object. Every signal needs ticker and position; every other field is optional and falls back to the bot's settings.
Basic Signal
{
"ticker": "BTCUSDT",
"position": "long"
}
Full Signal
{
"ticker": "BTCUSDT",
"position": "long",
"size": "0.01",
"leverage": "5",
"sl": "95000",
"tp": "105000",
"currentPrice": "97500",
"comment": "RSI oversold entry"
}
Close Signal
{
"ticker": "BTCUSDT",
"position": "flat"
}
Limit Entry
{
"ticker": "BTCUSDT",
"position": "long",
"price": "96000"
}
Request Fields
| Field | Required | Description |
|---|---|---|
ticker | Yes | The trading pair (pair also works) |
position | Yes | What to do: open, close, or update a position (see below). action works when position is absent |
price | No | Limit price for a long / short entry, or the price of a close_limit |
size | No | Amount in the base coin, e.g. 0.01 BTC |
leverage | No | Leverage for this entry, up to the bot's setting |
sl | No | Stop loss price |
tp | No | Take profit price |
currentPrice | No | Current price, a hint for sizing (marketPrice also works) |
comment | No | Note shown in Telegram trade notifications |
exchange | No | Run the signal on matching exchange connections only |
timeframe | No | Chart timeframe, checked against your plan |
closePercent | No | Close only this % of the position |
ticker (required)
The trading pair. pair is accepted in its place; when both are sent, pair is used.
| Format | Example |
|---|---|
| Without slash | BTCUSDT |
| With slash | BTC/USDT |
| With dash | BTC-USDT |
| TradingView perpetual | BTCUSDT.P (the part after the dot is dropped) |
The pair is matched to your exchange's market, so BTC/USDT becomes BTC/USDT:USDT for futures on most exchanges.
Note: A bot with a fixed trading pair always trades its own pair; the signal still needs a
ticker, but its value does not change the pair. On a Dynamic Pair bot, thetickerpicks the pair.
position (required)
What the signal does. Values are not case-sensitive.
action is accepted in place of position: when a signal has no position (or an empty one), its action is used, with the same values and aliases. When both are sent, position wins.
| Value | Aliases | What happens |
|---|---|---|
long | buy | Futures: opens a long. If the bot holds a short, it closes the short first. Spot: buys. |
short | sell | Futures: opens a short. If the bot holds a long, it closes the long first. Spot: sells the coins the bot holds (spot never opens a short). |
flat | close, exit, flatten | Closes the bot's open position without opening a new one |
close_long | exit_long | Closes the long only |
close_short | exit_short | Closes the short only |
update_sl | Moves the stop loss of the open position to sl (required) | |
update_tp | Moves the take profit of the open position to tp (required) | |
close_limit | Closes the open position with a limit order at price (required) | |
close_trail | Puts a trailing stop on the open position | |
close_on_candle | Closes the open position when the current candle of the bot's timeframe closes |
Any other value (for example hold, breakeven or a typo) is refused: nothing is sent to the exchange, and Signal Logs shows Unsupported signal position. So is update_sl without a valid sl price, and update_tp without a valid tp price.
On futures, a long while the bot already holds a long (or a short while it holds a short) is ignored, unless DCA is on (see DCA below).
Note: On futures,
sellis an entry: while the bot is long, it closes the long and opens a short. TradingView's{{strategy.order.action}}sendssellfor a strategy's exit orders too, so use{{strategy.market_position}}(it sendsflatfor an exit) unless you want every exit to reverse.
price (optional)
With long or short, a price makes the entry a limit order at that price instead of a market order. A limit order may fill late, partly, or not at all.
- With
buyorsell, the entry goes out at market: the price is not used as a limit. - On spot, a
shortorsellwith a price still sells the holding at market. close_limitneeds aprice: it is the price of its closing limit order.
size (optional)
The amount to trade, in the coin you are buying or selling (the base currency): "0.01" on BTCUSDT trades 0.01 BTC. On exchanges that trade in contracts, TradeStaq converts it to contracts. The bot's Max Position Size, when set, caps it.
| Format | Example | Description |
|---|---|---|
| String number | "0.01" | 0.01 coins |
| Number | 0.5 | 0.5 coins |
Percentages are not supported: size is never a share of your balance, and a signal whose size is not a plain number (for example "25%") is refused. To trade a share of your balance, leave size out and set the bot's position sizing to a percentage.
contracts and amount are accepted in place of size (TradingView's {{strategy.order.contracts}}).
On a close (flat, close, close_long, close_short), a size closes that many coins of the position instead of all of it.
Default: the bot's position sizing.
leverage (optional)
The leverage for this entry, up to the bot's own leverage setting (default 1x).
| Format | Example | Description |
|---|---|---|
| String number | "5" | 5x leverage |
| Number | 10 | 10x leverage |
- A value above the bot's setting trades at the setting: an alert can lower the leverage, never raise it. To trade at a higher leverage, change the bot's leverage setting.
- No entry goes above the exchange's maximum for that coin: when that is lower, the entry goes out at the exchange's maximum.
- An entry that adds to a position the bot already holds goes out at that position's leverage, so the open position is not re-margined.
Default: the bot's leverage setting.
Note: Only applies to futures exchanges. Ignored for spot trading.
sl (optional)
Stop loss price for the position this signal opens, as an absolute price ("95000"). With update_sl, the new stop loss price.
TradeStaq watches the price and closes the position at market when it reaches the stop; the stop is not placed as an order on the exchange.
Note: Percentages are not supported:
"2%"is read as a price of 2.
tp (optional)
Take profit price for the position this signal opens, as an absolute price ("105000"). With update_tp, the new take profit price.
Like the stop loss, TradeStaq watches it and closes the position at market when the price reaches it.
currentPrice (optional)
The current price of the coin, for example TradingView's {{close}}. TradeStaq uses it to size the order instead of fetching the price first. marketPrice is accepted in its place; when both are sent, currentPrice is used.
It is only a hint: it never makes a limit order (that is price).
comment (optional)
A note shown in the bot's Telegram trade notifications, for example "RSI oversold entry".
exchange (optional)
For a bot on several exchange connections: run this signal only on the connections whose exchange name or account label contains this text (not case-sensitive), for example "binance" or "Main Account". When nothing matches, the signal fails with No active authorized exchanges.
Default: every exchange connection on the bot.
timeframe (optional)
The chart timeframe the signal came from (interval and tf also work). When your plan limits timeframes, an entry from a timeframe it does not allow is refused.
closePercent (optional)
With flat, close, close_long or close_short: close this percentage of the position (above 0 and below 100) instead of all of it. A part below the exchange's minimum order is skipped, and the whole position closes when what would remain is below the minimum.
Trailing stop fields (close_trail)
| Field | Description |
|---|---|
trailPercent | Trail distance in %. Default: the bot's trailing stop setting, else 1% |
activationPercent | Start trailing once the position is this % in profit |
activationPrice | Start trailing once the price reaches this level (used instead of activationPercent) |
DCA
There are no DCA fields in the signal. With DCA enabled in the bot's settings, each entry signal is the next DCA level: the first opens the position (level 1), and each further entry on the same side adds a level instead of being ignored. A level's size is the signal's size (or the bot's position sizing) times that level's size percentage. TradeStaq does not place level orders by itself: your signal source decides when each level goes in.
DCA Example Flow
// Level 1 - Initial entry
{"ticker": "BTCUSDT", "position": "long"}
// Level 2 - Price dropped 3%, add more
{"ticker": "BTCUSDT", "position": "long"}
// Level 3 - Price dropped 6%, add more
{"ticker": "BTCUSDT", "position": "long"}
// Close - Exit full position
{"ticker": "BTCUSDT", "position": "flat"}
Once the bot's max levels are in, a further entry fails with Max DCA levels reached. See DCA.
Response Codes
The reply only says whether the signal was accepted. Whether a trade was placed shows in Signal Logs.
Accepted (202)
The signal was received and queued for the bot.
{
"message": "Trade Queued",
"bot": "BTC RSI Strategy"
}
A 202 does not mean a trade was placed: the signal can still be ignored or refused when it runs (see Signal Results below). A signal sent to a paused or stopped bot is accepted too, and then refused with Bot is not active.
Client Errors (4xx)
| Code | Reply message | Common Causes |
|---|---|---|
| 400 | Invalid JSON format. Ensure you are sending a valid JSON string. | The body is not valid JSON |
| 401 | Invalid signature | Only for a bot with a signing secret: the signature header is missing or wrong (see below) |
| 404 | Invalid Webhook ID | No Signal Bot has this webhook ID (a typo, or the bot was deleted) |
Example Error Response:
{
"message": "Invalid Webhook ID"
}
Server Errors (5xx)
| Code | Meaning | Action |
|---|---|---|
| 500 | Internal Error | Retry after delay |
| 502 | Bad Gateway | Retry after delay |
| 503 | Service Unavailable | Retry after delay |
Signed Requests
A bot with a signing secret checks every request for an X-Signature (or X-Webhook-Signature) header: the HMAC-SHA256 of the raw request body with the secret, in hex. A request without a valid signature gets 401. A bot without a signing secret skips this check, and its webhook URL is the only key, so keep it private.
TradingView Integration
Alert Message Format
In TradingView, set your alert message to JSON:
{
"ticker": "{{ticker}}",
"position": "{{strategy.market_position}}",
"currentPrice": "{{close}}"
}
Using TradingView Variables
| Variable | Description | Example |
|---|---|---|
{{strategy.market_position}} | Strategy position after the order | long, short, flat |
{{strategy.order.action}} | Order direction | buy, sell |
{{strategy.order.contracts}} | Order size | 0.1 |
{{ticker}} | Symbol | BTCUSDT |
{{close}} | Current close price | 50000 |
{{open}} | Current open price | 49800 |
{{high}} | Current high price | 50100 |
{{low}} | Current low price | 49700 |
{{volume}} | Current volume | 1234.56 |
{{interval}} | Chart timeframe | 60 |
{{time}} | Candle timestamp | 2024-01-01T00:00:00Z |
{{timenow}} | Current timestamp | 2024-01-01T00:00:00Z |
Pine Script Example
//@version=5
strategy("My Strategy", overlay=true)
// Your strategy logic
longCondition = ta.crossover(ta.sma(close, 10), ta.sma(close, 20))
shortCondition = ta.crossunder(ta.sma(close, 10), ta.sma(close, 20))
if longCondition
strategy.entry("Long", strategy.long,
alert_message='{"ticker":"{{ticker}}","position":"long","currentPrice":"{{close}}"}')
if shortCondition
strategy.close("Long",
alert_message='{"ticker":"{{ticker}}","position":"flat"}')
Setting Up the Alert
- Create alert on your strategy/indicator
- Set Webhook URL to your TradeStaq webhook URL
- Set Message to your JSON payload
- Enable the alert
Code Examples
cURL
curl -X POST https://www.tradestaq.com/api/webhooks/trade/your-webhook-id \
-H "Content-Type: application/json" \
-d '{"ticker":"BTCUSDT","position":"long"}'
Python
import requests
webhook_url = "https://www.tradestaq.com/api/webhooks/trade/your-webhook-id"
signal = {
"ticker": "BTCUSDT",
"position": "long",
"size": "0.01",
"sl": "95000",
"tp": "105000"
}
response = requests.post(webhook_url, json=signal)
print(response.status_code, response.json())
JavaScript/Node.js
const axios = require('axios');
const webhookUrl = 'https://www.tradestaq.com/api/webhooks/trade/your-webhook-id';
const signal = {
ticker: 'BTCUSDT',
position: 'long',
size: '0.01',
sl: '95000',
tp: '105000'
};
axios.post(webhookUrl, signal)
.then(response => console.log(response.status, response.data))
.catch(error => console.error(error.response?.status, error.response?.data));
Signal Processing
Execution Flow
- Receive - The webhook finds the bot by its webhook ID and checks the JSON
- Queue - The signal is queued and the webhook replies 202
- Check - The bot's status, your plan, the bot's risk limits and the position checks run
- Execute - The order goes to the exchange
- Log - The result is written to the bot's Signal Logs
Signal Results
What Signal Logs can show for a signal that was accepted but did not trade:
| Message | What it means |
|---|---|
Missing ticker or position | The signal has no ticker, or neither position nor action |
Unsupported signal position "..." | position is not one of the values above, or an update_sl / update_tp has no valid price |
Bot is not active | The bot is paused or stopped: start it |
Ignored: Already LONG (or SHORT) | Futures: the bot already holds that side and DCA is off |
Ignored: new entry skipped (bot is close-only: a risk limit was reached) | A risk limit was reached: the bot only closes until it lifts (see Risk Management) |
Rate limit: ...s remaining before next allowed signal for your tier. | Your plan sets a minimum time between entry signals |
No active authorized exchanges | The bot has no active exchange connection, or none matched exchange |
Max DCA levels reached (...) | DCA is on and its last level is already in |
Signal expired after ...s (max: ...s) | An entry waited too long in the queue and was dropped; closes are never dropped for age |
A repeat of the same signal (same bot, ticker, exchange and position) within 3 seconds is dropped as a duplicate.
Best Practices
Security
- Keep your webhook URL private
- If it is exposed, create a new bot (it gets a new URL) and delete the old one
- Use HTTPS only (HTTP not supported)
Reliability
- Retry on 5xx errors, not on 4xx (fix the request instead)
- Check Signal Logs to see what each signal did
- Monitor webhook health in dashboard
Performance
- Send only necessary fields
- Avoid duplicate signals
- Send
currentPriceto save a price lookup
Next Steps
- Authentication - API authentication methods
- Rate Limits - Understanding limits
- TradingView Integration - Detailed setup guide