Sarathi developer docs: webhook contract
Use the webhook to turn a TradingView alert into a Sarathi order card. The card still needs a Confirm or Skip decision. The webhook receiver is hosted at https://server-production-d3f7.up.railway.app.
Webhook URL
Send an HTTP POST to your personal URL:
POST https://server-production-d3f7.up.railway.app/hook/<token>
Treat the token and the full URL as secrets. The request body limit is 16 KiB.
JSON body
The canonical alert fields are:
| Field | Accepted type | Meaning |
|---|---|---|
sym | string | Required instrument symbol. symbol and ticker are aliases. |
side | string | Required. BUY, SELL, LONG, and SHORT are accepted case-insensitively. action is an alias. |
type | string | Optional strategy label. strategy is an alias. |
price | number or numeric string | Optional entry reference. close and entry are aliases. |
sl | number or numeric string | Optional stop. stop and stoploss are aliases. |
tp | number or numeric string | Optional target. target and takeprofit are aliases. |
qty | number or numeric string | Optional quantity. quantity and size are aliases. |
time | string recommended | Optional TradingView time. When present, it participates in deduplication. |
id | string recommended | Optional explicit deduplication key. It takes precedence over time. |
Unknown JSON fields are ignored. Numeric values are parsed with parseFloat; non-finite or absent values become null. The current parser does not runtime-validate a non-null type, time, or id, so send strings for a stable contract.
Example:
{
"sym": "{{ticker}}",
"side": "BUY",
"type": "ORB",
"price": "{{close}}",
"sl": 57400,
"tp": 57790,
"qty": 30,
"time": "{{timenow}}"
}In a TradingView strategy alert, set the alert dialog message to {{strategy.order.alert_message}} so the order call's alert_message is posted.
Plain-text body
The parser also accepts this shape:
BUY BANKNIFTY 30 sl=57400 tp=57790
The first word can be BUY, SELL, LONG, or SHORT; the second is the symbol; the optional third value is an integer quantity. sl= and tp= may appear later in the text. Plain text does not carry strategy, price, time, or an explicit dedupe id.
Symbol normalisation and universe gate
Symbols are trimmed and uppercased. A leading NSE:, BSE:, or MCX: prefix and a trailing continuous-contract suffix 1! or 2! are removed. NIFTY50 becomes NIFTY, GOLD becomes GOLDM, and SILVER becomes SILVERM.
After normalisation, the symbol must resolve to an active member of Sarathi's universe. Tier 1 is NIFTY, BANKNIFTY, MIDCPNIFTY, SENSEX, GOLDM, SILVERM, RELIANCE, and TCS. Tier 2 contains supported NIFTY 50 F&O names. A miss returns HTTP 400:
{
"error": "symbol not in universe",
"hint": "Tier-1: NIFTY, BANKNIFTY, MIDCPNIFTY, SENSEX, GOLDM, SILVERM, RELIANCE, TCS; tier-2: NIFTY 50 F&O names",
"symbol": "NORMALISED_SYMBOL"
}Deduplication and expiry
If id is present, it is the dedupe key. Otherwise, a truthy time produces symbol|side|type|time. The uniqueness check is per Sarathi user. A duplicate returns HTTP 200 with {"duplicate":true,"ok":true}. Without id or time, the alert has no dedupe key.
Accepted signals expire from the pending state after SIGNAL_EXPIRY_MIN. The server default is 15 minutes. An expired card cannot be confirmed later and remains in the journal.
Webhook responses
| Status | Meaning |
|---|---|
| 200 | Accepted as pending or held, ignored because the body was empty or an unexpanded placeholder, or recognised as a duplicate. |
| 400 | The body is malformed, required fields are missing, the side is unknown, or the symbol is outside the universe. |
| 404 | The webhook token is unknown. Body: {"error":"unknown token"}. |
| 413 | The request body is larger than 16 KiB. |
| 429 | A rate limiter in front of the hook rejected the request. The current hook route itself does not emit 429. |
Pine v6 alert pattern
Build one message and reuse it for both the order's alert_message and the bar-close alert() call. Keep TradingView placeholders outside str.format(). Use {0,number,#.##} or str.tostring(value, "#.##") so grouped thousands do not make the JSON invalid.
alertMsg = '{"sym":"{{ticker}}",' +
str.format('"side":"{0}","type":"{1}",', side, stratType) +
'"price":{{close}},' +
str.format('"sl":{0,number,#.##},"tp":{1,number,#.##},"qty":{2,number,#}', slLvl, tpLvl, qty) +
',"time":"{{timenow}}"}'
if barstate.isconfirmed and entryCondition
strategy.entry("Long", strategy.long, qty = qty, alert_message = alertMsg)
alert(alertMsg, alert.freq_once_per_bar_close)API
Sarathi has no public API. The web app talks to the server over an authenticated RPC channel that is not a supported integration surface. The only inbound integration is the per-user webhook described above. Outbound webhooks are planned; see webhook connectors.