Skip to main content

Webhook Trading

Send orders from a script, trading bot, or TradingView alert using CrossTrade's webhook command system. Your requests use the same command validation, rate limiting, trading windows, and other supported webhook options as any external alert.

The same webhook URL accepts two message formats:

FormatContent-TypeBody
JSONapplication/jsonOne flat object using CrossTrade field names
Semicolon texttext/plainkey=value; fields

JSON is also accepted when sent as text/plain. Headers with a charset, such as application/json; charset=utf-8, are supported. Existing semicolon alerts continue to work.

Endpoint and authentication​

Send an HTTP POST to the personal Webhook URL shown on your My Account page:

https://app.crosstrade.io/v1/send/{uid}/{channel}

Copy the complete URL from your account. Include your Secret Key in the body as "key": "YOUR_SECRET_KEY" for JSON or key=YOUR_SECRET_KEY; for semicolon text. No Authorization header is required. For JSON, a Bearer header does not replace the body's key field.

Webhook JSON uses the webhook field names, such as qty, order_type, and stop_loss. The separate REST API uses /v1/api/... routes, Bearer authentication, and its own request schemas. For example, a REST orderType field is not interchangeable with the webhook field order_type.

JSON alert messages​

NinjaTrader market order​

This sends a one-contract buy market order to Sim101 through the CrossTrade NT8 Add-On:

{
"key": "YOUR_SECRET_KEY",
"command": "place",
"account": "Sim101",
"instrument": "MES 12-26",
"action": "buy",
"qty": 1,
"order_type": "market",
"tif": "day",
"destination": "nt8"
}

NinjaTrader is the default destination if destination is omitted. NinjaTrader and the CrossTrade NT8 Add-On must be running and connected.

The equivalent semicolon message is:

key=YOUR_SECRET_KEY;
command=place;
account=Sim101;
instrument=MES 12-26;
action=buy;
qty=1;
order_type=market;
tif=day;
destination=nt8;

Tradovate entry with a target and stop​

Set destination to tradovate and supply a linked Tradovate account. Keep relative prices with units as strings:

{
"key": "YOUR_SECRET_KEY",
"command": "place",
"account": "YOUR_DEMO_ACCOUNT",
"instrument": "MES1!",
"action": "buy",
"qty": 1,
"order_type": "market",
"tif": "day",
"destination": "tradovate",
"take_profit": "40 ticks",
"stop_loss": "20 ticks",
"require_market_position": "flat"
}

For this market buy, CrossTrade calculates the target 40 ticks above the live reference quote and the stop 20 ticks below it, then submits absolute prices. They are not recalculated from the actual fill. See relative-price references for details. require_market_position blocks the entry unless the account is flat on that instrument. Direct Tradovate execution requires a linked account, but no NinjaTrader installation or desktop add-on.

JSON changes how you write the message, not which features each broker supports. See Destinations for the capability matrix and broker-specific fields.

Field names and values​

Use the fields documented under Commands and Advanced Options. Required fields and their meanings are the same in either format.

RuleExample or explanation
One non-empty, flat objectNo batch arrays or nested order objects.
Case-insensitive field namesqty and QTY refer to the same field. Preserve the documented spelling and underscores.
Strings, finite numbers, or booleans"qty": 1, "stop_loss": "20 ticks", "sync_strategy": true. Each field still has its own validation.
String identifiersKeep secrets, account names, instruments, and order IDs as strings. This preserves leading zeros.
Supported aliasesquantity maps to qty; atm_strategy maps to strategy.
No duplicate fieldsqty with QTY, or qty with quantity, is rejected.
No nulls or empty valuesOmit an unused optional field instead of sending null or "".
No unknown fieldsUse CrossTrade's webhook vocabulary.
No delimiters or control characters in valuesValues cannot contain ;, =, or embedded control characters, including escaped newlines or tabs.

Leading and trailing whitespace in field names and string values is trimmed. Pretty-printed JSON with line breaks between fields is supported. Standard JSON syntax applies: double-quoted keys and strings, no comments, and no trailing commas.

For fields that already accept comma-separated lists, keep the list in a string. For example, "account": "Sim101,Sim102" uses the existing multi-account syntax; "account": ["Sim101", "Sim102"] is rejected.

CrossTrade normalizes valid JSON into its semicolon command representation internally. Alert History shows that normalized command, so seeing KEY=...;COMMAND=...; after sending JSON is expected.

Using another platform's JSON?

CrossTrade accepts its own flat JSON schema. Payloads from PickMyTrade or TradersPost may use different names or nested objects, such as ticker or stopLoss. Use the Command Builder's JSON converter and review conversion warnings before sending them. A valid CrossTrade JSON object does not need conversion.

Sending requests​

Replace the webhook URL, secret, account, and instrument in each example with your own values. Test with a simulation or demo account first.

cURL​

curl --request POST 'YOUR_CROSSTRADE_WEBHOOK_URL' \
--header 'Content-Type: application/json' \
--data-raw '{
"key": "YOUR_SECRET_KEY",
"command": "place",
"account": "Sim101",
"instrument": "MES 12-26",
"action": "buy",
"qty": 1,
"order_type": "market",
"tif": "day",
"destination": "nt8"
}'

Code Example (Python)​

This example sends a semicolon command as plain text:

import requests

# Webhook URL
url = "https://app.crosstrade.io/v1/send/abc123/abcdefghijklmnopqrstuvwxyz"

# This example sends a semicolon command as plain text
headers = {
"Content-Type": "text/plain"
}

# Message payload text
data = '''
key=my-secret-key;
command=PLACE;
account=Sim105;
instrument=MES 12-26;
action=BUY;
qty=1;
tif=DAY;
order_type=MARKET;
'''

# Post it to CrossTrade!
try:
response = requests.post(url, headers=headers, data=data)
print(f"Response Code: {response.status_code}, Response Text: {response.text}")
except Exception as e:
print(f"An error occurred: {e}")

JSON example (Python)​

Use json=payload with requests. It serializes the object and sets Content-Type: application/json automatically.

import requests

url = "YOUR_CROSSTRADE_WEBHOOK_URL"
payload = {
"key": "YOUR_SECRET_KEY",
"command": "place",
"account": "Sim101",
"instrument": "MES 12-26",
"action": "buy",
"qty": 1,
"order_type": "market",
"tif": "day",
"destination": "nt8",
}
response = requests.post(url, json=payload, timeout=15)
print(response.status_code, response.text)

For a semicolon message, use data=message with headers={"Content-Type": "text/plain"} instead. Passing a Python dictionary as data=payload sends form-encoded data, which this webhook endpoint does not accept.

JavaScript (Node.js)​

const payload = {
key: 'YOUR_SECRET_KEY',
command: 'place',
account: 'Sim101',
instrument: 'MES 12-26',
action: 'buy',
qty: 1,
order_type: 'market',
tif: 'day',
destination: 'nt8',
};

const response = await fetch('YOUR_CROSSTRADE_WEBHOOK_URL', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(15000),
});
console.log(response.status, await response.text());

Using JSON in TradingView​

Paste the entire JSON object into the alert's Message field and use your existing CrossTrade Webhook URL in Notifications. Do not wrap the object in another string or include Markdown code fences. TradingView chooses the request Content-Type automatically; CrossTrade accepts JSON with either application/json or text/plain.

Strategy placeholders can be used as string values:

{
"key": "YOUR_SECRET_KEY",
"command": "place",
"account": "Sim101",
"instrument": "MES1!",
"action": "{{strategy.order.action}}",
"qty": "{{strategy.order.contracts}}",
"order_type": "market",
"tif": "day"
}

TradingView substitutes these values when the strategy alert fires. Numeric fields such as qty can be supplied as strings, so the placeholders can remain quoted. They do not resolve when sent from cURL or your own script. See TradingView Alerts for alert setup.

Responses and troubleshooting​

Read the response body as well as the HTTP status. A success response is not a broker fill confirmation. TradingView requests are acknowledged before background processing finishes; use Alert History to check processing results and broker orders to verify execution.

ProblemWhat to check
Invalid request format at the HTTP boundaryUse POST with application/json or text/plain, not form encoding.
Invalid JSON or duplicate fieldCheck commas, double quotes, and repeated keys, including case variants and aliases.
Unsupported JSON command fieldUse webhook names such as order_type, not REST names such as orderType or another platform's schema.
Invalid JSON valueRemove nulls, nested objects, arrays, empty values, and forbidden characters.
Missing, invalid, or inactive secretInclude the correct Secret Key inside the JSON object's key field.

A timeout does not establish whether an order was placed. Check Alert History and broker orders before retrying an order-changing request. Changing the message format does not add an exactly-once delivery guarantee.