Triggers
Triggers are webhook-based trade automations. You define a trigger with your trade parameters, then fire it from TradingView alerts, custom bots, or any system that can send an HTTP POST request.
How Triggers Work
- You create a trigger in the Crodl UI with your desired trade parameters
- You copy the trigger's JSON payload (which includes your security token) and the webhook URL
- You configure an external source (e.g., TradingView) to POST the trigger payload to the webhook
- Crodl validates the payload, resolves your API key via the security token, and queues the trade for execution on your exchange
- If the payload includes
strategy_dashboard_id, Crodl also groups the execution into that strategy journal for dashboard reporting - You receive a
202 Acceptedresponse immediately — execution happens asynchronously in the background. Results show up in the Executed Triggers tab and via Discord/Telegram notifications if connected
Webhook Endpoint
All triggers are sent to a single endpoint:
POST https://api.crodl.com/webhooks/trading
Content-Type: application/json
The response is immediate:
{
"accepted": true,
"request_id": "a1b2c3d4...",
"accepted_ms": 45
}202 Accepted means the request was validated and queued — not that the order filled. Check the Executed Triggers tab or your Discord/Telegram notifications for the execution result.
Security Token
Every trigger payload requires a security_token. This is a unique key tied to your exchange API key — it authenticates the webhook without exposing your actual API credentials.
To get it, use Copy JSON on any trigger row on the Automations page — the copied payload includes the security_token for that trigger's API key. MCP clients can retrieve it with the single-trigger payload tool using explicit token reveal.
Your security token grants trade access to your exchange account. Treat it like a password — never share it publicly or commit it to version control.
If you are connecting Claude, Codex, or another MCP client, use MCP Access instead of pasting security_token into trade requests. MCP tools use the same payload shape and inject the token server-side. If you explicitly ask for one saved trigger's TradingView payload, MCP can return that trigger's security_token so you can copy it into TradingView.
Open Trigger
An open trigger places a new position. Here's a full example:
{
"name": "BTC Long",
"allocation_id": "btc-breakout-a1",
"strategy_dashboard_id": "btc-momentum-2026",
"exchange": "blofin",
"trading_action": "open",
"security_token": "your_security_token",
"symbol": "BTC-USDT",
"side": "long",
"order_type": "market",
"margin_mode": "cross",
"position_mode": "oneway",
"trading_type": "futures",
"leverage_amount": "10",
"quantity_type": "percentage",
"quantity_amount": "5",
"quantity_quote": "USDT",
"opposite_side_close": false
}Open Trigger Fields
| Field | Required | Values | Description |
|---|---|---|---|
exchange | Yes | blofin, bitunix, bybit, bitget, okx, hyperliquid | Target exchange |
trading_action | Yes | open | Opens a new position |
security_token | Yes | string | Your API key's security token |
symbol | Yes | e.g. BTC-USDT, BTC-USDT-SWAP | Trading pair |
side | Yes | long, short | Trade direction |
order_type | Yes | market, limit, post_only, fok, ioc | Order execution type. There is no chase value — Limit Chaser is Terminal-only |
quantity_type | Yes* | amount, percentage, asset | How quantity is measured |
quantity_amount | Yes* | numeric string | Size of the trade (percentage must be in (0, 100]) |
qty | Yes* | number or numeric string | Shorthand alternative to the two fields above — a raw size in base units. Crodl treats it as quantity_type: "asset" and takes the absolute value. Ignored if quantity_type and quantity_amount are both present |
margin_mode | No | isolated, cross | Margin mode. Defaults to isolated |
position_mode | No | oneway, hedge | Position mode. Defaults to oneway |
leverage_amount | No | numeric string | Leverage multiplier (e.g. "10"), up to the pair's maximum. If omitted, your current leverage on the exchange is kept |
quantity_quote | No | USDT, USDC, USD | Quote currency (auto-derived from the symbol if omitted) |
quantity_mode | No | margin, position | Whether an amount quantity is margin or position notional |
max_notional_usd | No | numeric string | Optional maximum position notional in USD terms |
guard_action | No | reject, clamp | What to do when max_notional_usd is exceeded. Defaults to reject |
name | No | string | Label for this trigger |
allocation_id | No | string | Optional strategy bucket ID for tracking this position independently from other strategies on the same symbol/side. Market orders only |
strategy_dashboard_id | No | string | Optional reporting-only journal ID used to group trades into one strategy dashboard |
trading_type | No | futures | Defaults to futures (the only supported value). market_type is accepted as an alias |
* Send either quantity_type + quantity_amount, or qty. A payload with none of them is rejected with 422.
OKX symbols should use the full exchange-native instId format, for example BTC-USDT-SWAP for linear perpetuals or BTC-USD-SWAP for inverse perpetuals.
Hyperliquid does not support hedge mode — sending position_mode: "hedge" to Hyperliquid fails with position_mode_unsupported. Hyperliquid also has no FOK policy on this order path: order_type: "fok" is rejected at admission on both opens and closes (hyperliquid_fok_unsupported / hyperliquid_close_fok_unsupported). Use ioc or post_only instead.
Optional Trade Guardrails
Open triggers can include optional guard fields to prevent a signal from placing a larger position than expected. They work on every supported exchange: Blofin, Bitunix, Bybit, Bitget, OKX, and Hyperliquid. If these fields are omitted, Crodl keeps the existing behavior and does not enforce a notional cap.
These are payload-only fields — the trigger form does not expose them. Add them to the JSON you send (or paste into TradingView):
"max_notional_usd": "500",
"guard_action": "reject"That snippet is two fields to add to an existing payload, not a payload on its own. Every webhook request must be one complete JSON object containing at minimum exchange, trading_action, security_token, symbol, side, order_type, and the quantity fields — see the full example above.
max_notional_usd is a position notional cap, not the margin spent. For example, a 10 SOL order at $100 is about $1,000 notional even if the trade uses 10x leverage and only requires about $100 margin.
| Field | Behavior |
|---|---|
max_notional_usd | Caps the final computed order notional before Crodl submits the order to the exchange |
guard_action: "reject" | Rejects the trade before exchange submission if the cap is exceeded |
guard_action: "clamp" | Reduces the computed quantity to fit the cap, then submits the smaller order |
If guard_action is omitted but max_notional_usd is present, Crodl defaults to reject.
If clamp reduces the quantity below the exchange minimum order size, Crodl rejects the trade instead of submitting an invalid order.
These guardrails apply only to open triggers. Close triggers ignore these fields so exits are not blocked by notional caps.
Close Trigger
A close trigger reduces or closes an existing position:
{
"name": "BTC Close Half",
"allocation_id": "btc-breakout-a1",
"strategy_dashboard_id": "btc-momentum-2026",
"exchange": "blofin",
"trading_action": "close",
"security_token": "your_security_token",
"symbol": "BTC-USDT",
"side": "close_long",
"order_type": "market",
"trading_type": "futures",
"quantity_type": "percentage",
"quantity_amount": "50",
"cancel_unfilled": "disabled"
}Close Trigger Fields
| Field | Required | Values | Description |
|---|---|---|---|
trading_action | Yes | close | Closes a position |
side | Yes | close_long, close_short | Which position to close (long/short/buy/sell are accepted as aliases) |
quantity_type | Yes | amount, percentage, asset | How quantity is measured |
quantity_amount | Yes | numeric string | Amount to close (e.g. "50" for 50%) |
order_type | Yes | market, limit, post_only, fok, ioc | Order execution type |
allocation_id | No | string | Optional strategy bucket ID to close only one allocation instead of the full net position. Market orders only |
strategy_dashboard_id | No | string | Optional reporting-only journal ID used to group this close with the matching strategy dashboard |
cancel_unfilled | No | disabled, enabled, longs, shorts, both | Cancel pending orders before closing. Defaults to disabled |
Setting quantity_amount to "100" with quantity_type of "percentage" will close the entire position.
Flat Trigger (Blofin only)
trading_action: "flat" is a shorthand for "close everything on the opposite side". Crodl rewrites it into a full close before execution:
side: "buy"or"long"→ closes your short (close_short)side: "sell"or"short"→ closes your long (close_long)quantity_typedefaults topercentageandquantity_amountto100if you omit them
{
"exchange": "blofin",
"trading_action": "flat",
"security_token": "your_security_token",
"symbol": "BTC-USDT",
"side": "sell",
"order_type": "market"
}flat is Blofin-only. Sending it to any other exchange fails with 422 and the message Flat trading_action is only supported for Blofin. On other exchanges use trading_action: "close" with quantity_type: "percentage" and quantity_amount: "100".
allocation_id
allocation_id is an optional strategy bucket ID for running multiple strategies on the same symbol and side without mixing their closes together.
When allocation_id is present:
- Opens are tracked into that allocation bucket
- Multiple opens with the same
allocation_idaccumulate into the same bucket - Closes only consume that bucket's remaining size
- Percentage closes are calculated from that allocation's remainder, not the full net exchange position
When allocation_id is omitted, Crodl keeps the legacy behavior and closes against the normal net position on the exchange.
You can review your allocation buckets (opened and remaining size per ID) in the Allocation IDs tab on the Automations page.
Example flow
Open $100 of notional into the btc-breakout-a1 bucket:
{
"exchange": "blofin",
"trading_action": "open",
"security_token": "your_security_token",
"symbol": "BTC-USDT",
"side": "long",
"order_type": "market",
"allocation_id": "btc-breakout-a1",
"quantity_type": "amount",
"quantity_mode": "position",
"quantity_amount": "100"
}Then close 10% of that bucket:
{
"exchange": "blofin",
"trading_action": "close",
"security_token": "your_security_token",
"symbol": "BTC-USDT",
"side": "close_long",
"order_type": "market",
"allocation_id": "btc-breakout-a1",
"quantity_type": "percentage",
"quantity_amount": "10"
}In that example, Crodl closes 10% of the btc-breakout-a1 allocation only. The rest of that allocation stays open, and any other allocation on BTC-USDT is untouched.
allocation_id requires order_type: "market" — on both opens and closes. Any other order type is rejected with allocation_orders_market_only.
strategy_dashboard_id
strategy_dashboard_id is an optional reporting-only ID for journaling and dashboarding.
Use it when you want to group open and close triggers into a strategy journal without affecting how the trade is executed.
When strategy_dashboard_id is present:
- Crodl records the value for reporting and dashboard views
- Crodl auto-creates the journal the first time that ID is saved or executed for that exchange and API key
- The trade execution path does not change
- Closes still follow the same
allocation_idand position rules as before
You can review those journals in Dashboard → Strategy or directly at /dashboard?view=strategy. In that view, you:
- Pick the exchange first
- Pick the API key
- Choose the exact
strategy_dashboard_idjournal to review - Rename, annotate, end, or delete the journal without changing how trades execute
Choosing a good ID
Use an ID that stays stable across the related opens and closes for one strategy run. Good examples:
btc-breakout-londonturbo-venometh-mean-revert-apr-2026
If you want a different journal, send a different strategy_dashboard_id.
Do not use strategy_dashboard_id as a substitute for allocation_id. allocation_id controls how closes are matched; strategy_dashboard_id only groups trades for reporting.
You can use strategy_dashboard_id without allocation_id if you only want journal reporting. Add both fields when you want isolated close matching and grouped reporting at the same time.
Limit Order Pricing
For non-market order types (limit, post_only, fok, ioc), two additional fields are required:
| Field | Values | Description |
|---|---|---|
limit_price_mode | price, percentage | Absolute price or percentage-based |
limit_order_price | numeric string | The price or percentage value |
Both are fields you add to an open or close payload — not payloads on their own.
Example — limit order at a specific price:
"order_type": "limit",
"limit_price_mode": "price",
"limit_order_price": "95000"Example — limit order 2% below current price:
"order_type": "limit",
"limit_price_mode": "percentage",
"limit_order_price": "98"Sending a non-market order_type without both limit_price_mode and limit_order_price is rejected with 422. For order_type: "market", Crodl ignores and clears both fields.
Take-Profit and Stop-Loss
Attach TP/SL to any open trigger. All TP/SL fields are optional — omit them entirely if you don't want TP/SL. TP and SL are independent: you can send either one, or both.
Take-Profit Fields
| Field | Values | Description |
|---|---|---|
take_profit_type | market, limit | TP execution type — setting this enables the TP |
take_profit_trigger_price | numeric string | Price that activates the TP |
take_profit_mode | price, percentage | Absolute or percentage-based. Defaults to price |
take_profit_order_price | numeric string | Execution price (required if type is limit) |
Stop-Loss Fields
| Field | Values | Description |
|---|---|---|
stop_loss_type | market, limit | SL execution type — setting this enables the SL |
stop_loss_trigger_price | numeric string | Price that activates the SL |
stop_loss_mode | price, percentage | Absolute or percentage-based. Defaults to price |
stop_loss_order_price | numeric string | Execution price (required if type is limit) |
Example with TP/SL
{
"exchange": "blofin",
"trading_action": "open",
"security_token": "your_token",
"symbol": "ETH-USDT",
"side": "long",
"order_type": "market",
"margin_mode": "cross",
"position_mode": "oneway",
"leverage_amount": "5",
"quantity_type": "percentage",
"quantity_amount": "10",
"take_profit_type": "market",
"take_profit_mode": "price",
"take_profit_trigger_price": "4000",
"stop_loss_type": "market",
"stop_loss_mode": "price",
"stop_loss_trigger_price": "3200"
}If you use allocation_id together with TP/SL, the TP/SL remains position-level, not allocation-level. That means one TP/SL can affect every allocation sharing the same symbol and side.
Trade Policies
opposite_side_close
When true, automatically closes any existing position on the opposite side before opening the new one.
Example: You hold a SHORT on BTC-USDT and send an open LONG trigger with "opposite_side_close": true — the SHORT is closed first, then the LONG is opened.
cancel_unfilled
Controls whether pending unfilled orders are cancelled before the trigger executes:
| Value | Behavior |
|---|---|
disabled | Do nothing (default) |
enabled | Cancel all unfilled orders on this symbol |
longs | Cancel only long orders |
shorts | Cancel only short orders |
both | Cancel both long and short orders |
pyramiding_allowed
Controls whether multiple orders or positions can stack on the same symbol and side:
| Value | Behavior |
|---|---|
none | Block if a matching order or position already exists (default) |
orders_and_positions | Allow unlimited stacking |
orders_only | Allow pending orders, block new positions |
positions_only | Allow new positions, block pending orders |
Use pyramiding_allowed: "none" (the default) to prevent accidentally opening duplicate positions from repeated alerts.
TradingView Integration
Connect your triggers to TradingView alerts:
- Create a trigger in Crodl and copy the JSON payload
- In TradingView, create an alert on your desired condition
- In the alert's Notifications tab, enable Webhook URL
- Set the URL to
https://api.crodl.com/webhooks/trading - Paste your trigger JSON into the Message field
- Save the alert
When TradingView fires the alert, it POSTs your payload to Crodl, which executes the trade on your exchange.
You can use TradingView's placeholder variables (like {{ticker}}, {{close}}) in the JSON message to make dynamic triggers that adapt to market conditions.
Execution Flow
When a trigger fires, Crodl processes it in this order:
- Validate payload and resolve security token
- Return
202 Acceptedimmediately - Cancel unfilled orders (if
cancel_unfilledis set) - Close opposite position (if
opposite_side_closeistrue) - Check pyramiding rules (if
pyramiding_allowedrestricts) - Apply optional open-trade guardrails (if
max_notional_usdis set) - Place the order on your exchange
- Record execution result in your trade history
Because execution is asynchronous, the webhook response never contains a fill result. Track outcomes in the Executed Triggers tab, or connect Discord/Telegram in Settings to get a notification per execution.
Response Codes
| Code | Meaning |
|---|---|
202 | Trigger accepted and queued for execution |
400 | Invalid JSON or unsupported exchange |
401 | Invalid security token |
409 | An allocation close for the same bucket is already in progress |
422 | Validation error (missing or invalid fields) |
429 | Rate limited (max 30 requests/second) |
503 | Queue or idempotency backend temporarily unavailable, or the same request is still being claimed — retry |
Idempotency
Crodl supports idempotent trigger execution to prevent duplicate trades from repeated webhook fires.
How It Works
When you send a trigger with an explicit request key, Crodl checks whether that key has already been accepted.
- Explicit keys use a 1-hour TTL window
- If no explicit key is provided, each webhook request is treated as a new request
If a matching request is found, the original request ID is returned instead of executing a new trade.
Providing an Idempotency Key
You can provide an idempotency key in two ways (checked in this order):
1. HTTP Header
Idempotency-Key: your-unique-key-here
2. Body-level key — include any of these fields in your JSON payload:
| Field | Also accepts (camelCase) |
|---|---|
idempotency_key | idempotencyKey |
request_id | requestId |
signal_id | signalId |
alert_id | alertId |
{
"exchange": "blofin",
"trading_action": "open",
"security_token": "your_token",
"idempotency_key": "tv-alert-btc-long-20240115-001",
"symbol": "BTC-USDT",
"side": "long",
"order_type": "market",
"quantity_type": "percentage",
"quantity_amount": "5"
}Response Headers
Responses include idempotency information:
| Header | Description |
|---|---|
X-Idempotency | Idempotency decision (claimed, replay, or bypass) |
X-Idempotency-Source | Where the key came from (header, body, or request when no explicit key was provided) |
Always provide an explicit idempotency key when sending triggers from TradingView or other alert systems that may fire multiple times. This prevents duplicate trades from repeated alerts.
Managing Triggers
On the Automations page (/trading-terminal):
- Create open and close triggers with a visual form
- Edit existing triggers to update parameters
- Delete triggers you no longer need
- Run triggers manually (with a confirmation dialog) before connecting to TradingView
- View execution history in the Executed Triggers tab