Triggers
Alerts, stop-loss, targets, entries, ladders and loss limits the server watches tick by tick.
List v2 triggers #
/v2/triggers scope readEvery v2 trigger of the caller, newest first, with its state.
More about this
Ended triggers stay listed for 8 days.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
All fields (request and response schema)
Loading fields…
curl "https://cirrus.pocketful.in/v2/triggers" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v2/triggers",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": [
{
"trigger_id": "01M5VVFCDFABPK086FBZD11WZ5",
"account": "P1",
"strategy": null,
"kind": "bracket",
"legs": [
{
"side": "BUY",
"quantity": 5,
"product": "MIS",
"instrument_token": "CT:TEST:RELIANCE"
}
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 25",
"trail": {
"step": "5"
}
}
},
"valid_until": null,
"state": "submitted",
"transition_seq": 0,
"reason": null,
"created_at": "2026-10-26T15:48:09.715+05:30",
"updated_at": "2026-10-26T15:48:09.715+05:30",
"protection": {
"stop_loss": {
"held_by": "server",
"broker_order_id": null,
"level": 2475,
"adjusted_by_server": false,
"status": "held_by_server",
"note": null
},
"target": {
"held_by": "server",
"broker_order_id": null,
"level": 2550,
"adjusted_by_server": false,
"status": "held_by_server",
"note": null
},
"trail": {
"held_by": "server",
"broker_order_id": null,
"level": 2475,
"adjusted_by_server": false,
"status": "held_by_server",
"note": null
}
},
"created_via": "app",
"route": "trigger"
}
],
"error": null
}Create a v2 trigger #
/v2/triggers scope triggersCreates a v2 trigger: an alert, a bracket (entry, target, stop-loss, trailing), a ladder (several targets) or a risk_limit (loss / profit limits on an account, the user or a strategy), with conditions on any tick field.
More about this
The trigger is checked first (its conditions compile, the account is the caller's, every leg's instrument exists; bracket and ladder legs need product), then handed to the trigger engine: 202 with the new trigger's id, made by the server. Read it with GET /v2/triggers/{trigger_id}; state is submitted until the engine reports. A bracket protecting a position already held (entry_px, no entry) may set its stop-loss at or beyond entry_px to lock in profit (long: entry_px <= init_sl < the current price; short mirrored); a waiting bracket's stop stays on the protective side of its entry.
Where the protection lives (route, default auto): a simple protection (a bracket on one leg already held, entry_px and no entry, with a plain price stop-loss and / or target and nothing else: no trail, no other condition, no on: close / hold_s, no valid_until, strategy or watch) is placed at the broker by the rules of POST /v1/triggers: a broker GTT for a positional product where the broker has GTTs (a broker whose GTT takes one leg holds the stop-loss and a server-side trigger the target; a CARRYFORWARD GTT never covers more than the broker holds), a broker stop-loss order for an intraday (MIS) position where the broker's stop orders are proven. It is placed before the 202, which then names its display record in trigger_id and says route gtt / stop_order; read, change and cancel it here like any v2 trigger (a change moves it at the broker and keeps the same parts; a cancel cancels it at the broker and it is no longer listed). If the broker refuses it a server-side trigger protects the position at once (route trigger, the broker's reason in route_reason); if the broker does not answer, nothing else is created (504 broker_outcome_unknown). Anything else, and route trigger, is a v2 trigger the server evaluates (route trigger).
Body fields
- account string required
Account id (see
/v1/accounts). - kind string required
The machine that runs a trigger.
One of: alert, bracket, ladder, risk_limit
- spec object required
Kind-specific rules, conditions written in the CEL subset (
ltp <= 120 && bid_qty > ask_qty). Checked before anything is sent: an unknown field or a condition that does not compile is a 422. - strategy string | null
Strategy the trigger belongs to (risk limits at strategy level).
- legs object[]
- valid_until string | null
RFC 3339; the trigger expires then.
- watch object[]
Instruments the conditions read but the trigger never trades (at most 3), for indicator conditions:
[{"name": "nifty", "symbol": "<instrument token>"}]lets a condition saynifty.rsi(14, '5m')ornifty.ltp. Names are[a-z][a-z0-9_]{0,15}, not a variable or function name. Each counts toward the distinct-instrument limit. Fixed at create (a change keeps them). Refused (422) while indicator conditions are not available. - route string
Where a
bracketprotecting a held position lives.auto(default) puts a simple protection (one leg already held, a plain price stop-loss and / or target, nothing else) at the broker with the rules ofPOST /v1/triggers: a broker GTT for a positional product where the broker has GTTs (a one-leg-GTT broker holds the stop-loss, a server-side trigger the target), a broker stop-loss order for an intraday (MIS) position where the broker's stop orders are proven; anything else is a v2 trigger the server evaluates.triggeralways makes the v2 trigger (the earlier behaviour).gtt/stop_orderask for that route and are refused (422) whereautowould not take it;bracket/coverare refused.One of: auto, bracket, cover, gtt, trigger, stop_order
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | malformed_json | The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types). |
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | account_not_found | No linked broker account with this id for the caller. |
| 422 | invalid_request | The request is well-formed but not valid; |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
| 504 | broker_outcome_unknown | The broker did not answer in time: it may or may not have applied the change. Check before retrying. |
All fields (request and response schema)
Loading fields…
curl -X POST "https://cirrus.pocketful.in/v2/triggers" \
-H "Authorization: token $CIRRUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account": "P1",
"kind": "bracket",
"legs": [
{
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"quantity": 5,
"product": "MIS"
}
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 25",
"trail": {
"step": "5"
}
}
}
}'import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v2/triggers",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
json={
"account": "P1",
"kind": "bracket",
"legs": [
{
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"quantity": 5,
"product": "MIS",
},
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02",
},
"stop_loss": {
"init_sl": "entry_px - 25",
"trail": {
"step": "5",
},
},
},
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers', {
method: 'POST',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"account": "P1",
"kind": "bracket",
"legs": [
{
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"quantity": 5,
"product": "MIS"
}
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 25",
"trail": {
"step": "5"
}
}
}
}),
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"trigger_id": "01M5VVFCDFRZFZ0A56Z1VHC8WJ",
"command_id": "01M5VVFCDM23ZD6N7BKFGJ3KZD",
"status": "accepted",
"route": "trigger",
"route_reason": "a trailing stop-loss is evaluated by the server (a GTT or broker stop cannot trail)"
},
"error": null
}One v2 trigger #
/v2/triggers/{trigger_id} scope readOne v2 trigger of the caller.
More about this
Another user's id is not found.
Parameters
- trigger_id path · string required
The trigger's id.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | trigger_not_found | No trigger with this id for the caller. |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
All fields (request and response schema)
Loading fields…
curl "https://cirrus.pocketful.in/v2/triggers/<trigger_id>" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v2/triggers/<trigger_id>",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/<trigger_id>', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"trigger_id": "01M5VVFCDF60NDFJ5SMKG675R2",
"account": "P1",
"strategy": null,
"kind": "bracket",
"legs": [
{
"side": "BUY",
"quantity": 5,
"product": "MIS",
"instrument_token": "CT:TEST:RELIANCE"
}
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 25",
"trail": {
"step": "5"
}
}
},
"valid_until": null,
"state": "active",
"transition_seq": 1,
"reason": null,
"created_at": "2026-10-26T15:48:09.715+05:30",
"updated_at": "2026-10-26T15:48:09.719+05:30",
"protection": {
"stop_loss": {
"held_by": "server",
"broker_order_id": null,
"level": 2480,
"adjusted_by_server": false,
"status": "held_by_server",
"note": null
},
"target": {
"held_by": "server",
"broker_order_id": null,
"level": 2550,
"adjusted_by_server": false,
"status": "held_by_server",
"note": null
},
"trail": {
"held_by": "server",
"broker_order_id": null,
"level": 2475,
"adjusted_by_server": false,
"status": "held_by_server",
"note": null
}
},
"created_via": "app",
"route": "trigger"
},
"error": null
}Change a v2 trigger #
/v2/triggers/{trigger_id} scope triggersReplaces a v2 trigger's spec (and optionally its expiry).
More about this
The new spec is checked like on create; the engine then applies what the trigger's state allows (for example a stop-loss is never loosened, an entry cannot change once active). 202: read the trigger to see the result. Protection the broker holds (route gtt / stop_order) is changed at the broker first and answered once the broker agreed: its spec must stay a plain price stop-loss / target with the same parts, and valid_until cannot be set.
Parameters
- trigger_id path · string required
The trigger's id.
Body fields
- spec object required
The whole new spec (same shape as on create).
- valid_until string | null
New expiry (RFC 3339); unchanged when absent.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | malformed_json | The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types). |
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | trigger_not_found | No trigger with this id for the caller. |
| 404 | account_not_found | No linked broker account with this id for the caller. |
| 409 | trigger_fired | The trigger already fired; its exit is being handled. |
| 409 | broker_held | The protection is held by the broker; change the broker order instead. |
| 422 | invalid_request | The request is well-formed but not valid; |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 502 | broker_refused | The broker refused the change. |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
| 504 | broker_outcome_unknown | The broker did not answer in time: it may or may not have applied the change. Check before retrying. |
All fields (request and response schema)
Loading fields…
curl -X PATCH "https://cirrus.pocketful.in/v2/triggers/<trigger_id>" \
-H "Authorization: token $CIRRUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 20",
"trail": {
"step": "5"
}
}
}
}'import os
import requests
response = requests.patch(
"https://cirrus.pocketful.in/v2/triggers/<trigger_id>",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
json={
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02",
},
"stop_loss": {
"init_sl": "entry_px - 20",
"trail": {
"step": "5",
},
},
},
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/<trigger_id>', {
method: 'PATCH',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 20",
"trail": {
"step": "5"
}
}
}
}),
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"trigger_id": "01M4GF38AQ39WVJZHH8YWWNNQZ",
"command_id": "01M4GF38BEE9Y3N9T9M8EHNXKX",
"status": "accepted"
},
"error": null
}Cancel a v2 trigger #
/v2/triggers/{trigger_id} scope triggersCancels a v2 trigger.
More about this
202: the engine ends it within moments (state cancelled); one that fires first ends as fired instead. Protection the broker holds (route gtt / stop_order) is cancelled at the broker first (a stop order is read back final; one that already went off is exiting and is kept: 502, retry), then removed with its paired target: broker_cancel says how the broker answered. A refusal never removes it on its own: shown ended at the broker it is removed (cancelled), shown fired it ends as fired (409), otherwise it stays live with state cancel_pending (broker_cancel pending) and is re-checked until the broker shows a final state.
Parameters
- trigger_id path · string required
The trigger's id.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | trigger_not_found | No trigger with this id for the caller. |
| 409 | trigger_fired | The trigger already fired; its exit is being handled. |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 502 | broker_refused | The broker refused the change. |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
All fields (request and response schema)
Loading fields…
curl -X DELETE "https://cirrus.pocketful.in/v2/triggers/<trigger_id>" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.delete(
"https://cirrus.pocketful.in/v2/triggers/<trigger_id>",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/<trigger_id>', {
method: 'DELETE',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"trigger_id": "01M4GF38B4NDN7T4SDYK85MKNM",
"command_id": "01M4GF38BK9BQ5Z9NTES6C5R31",
"status": "accepted"
},
"error": null
}Pause a v2 trigger #
/v2/triggers/{trigger_id}/pause scope triggersPauses a v2 trigger: the engine stops evaluating it (state paused) until it is resumed.
More about this
A paused trigger does not act on any condition, its stop-loss included; a part the broker holds stays at the broker. 202: the engine applies it within moments; read the trigger to see the result.
Parameters
- trigger_id path · string required
The trigger's id.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | trigger_not_found | No trigger with this id for the caller. |
| 409 | trigger_fired | The trigger already fired; its exit is being handled. |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
All fields (request and response schema)
Loading fields…
curl -X POST "https://cirrus.pocketful.in/v2/triggers/<trigger_id>/pause" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v2/triggers/<trigger_id>/pause",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/<trigger_id>/pause', {
method: 'POST',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"trigger_id": "01M4QNBZ1SPDJ4F3K9NA9H72SC",
"command_id": "01M4QNBZ21PD87BG3EHS08AN78",
"status": "accepted"
},
"error": null
}Resume a paused v2 trigger #
/v2/triggers/{trigger_id}/resume scope triggersResumes a paused v2 trigger: the engine evaluates it again from the next tick, in the state it was paused in.
More about this
202: read the trigger to see the result (the engine refuses a resume of a trigger that is not paused).
Parameters
- trigger_id path · string required
The trigger's id.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | trigger_not_found | No trigger with this id for the caller. |
| 409 | trigger_fired | The trigger already fired; its exit is being handled. |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
All fields (request and response schema)
Loading fields…
curl -X POST "https://cirrus.pocketful.in/v2/triggers/<trigger_id>/resume" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v2/triggers/<trigger_id>/resume",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/<trigger_id>/resume', {
method: 'POST',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"trigger_id": "01M4QNBZ1SH6BRRRMPJ7RRSVMY",
"command_id": "01M4QNBZ1Z8KTB78CXT3BMKEBB",
"status": "accepted"
},
"error": null
}Check a v2 trigger without creating it #
/v2/triggers/validate scope triggersA dry run of POST /v2/triggers with the same body: nothing is sent to the engine, stored or counted against the account's order budget.
More about this
200 with valid and every reason create would refuse it (errors, the same field and message as create's 422), what its conditions read (reads), where it stands against the limits (limits) and, for bracket / ladder, who would hold each part of its protection on this account and product (protection: the broker or the server).
**Expressions** (a strict CEL subset; numbers are doubles). Variables: leg 0's tick fields ltp, bid, ask, bid_qty, ask_qty, volume, oi, atp, open, high, low; prev.<field> (previous tick); legs[i].<field> and legs[i].prev.<field>; entry_px, sl_px, state; account.mtm, user.mtm, strategy.mtm, position.qty; now_ist (minutes since midnight IST, 09:15 = 555). Functions: crosses_above(a, b) / crosses_below(a, b) (against the previous tick; a gap counts), min, max, abs. Operators: || && ! == != < <= > >= + - * / and c ? a : b. Prices (init_sl, trail.step, ladder price) read only entry_px and literals. Limits: 2048 bytes and 32 levels of nesting per condition; the per-trigger limits in limits. Errors name the spec path and byte, e.g. when: unknown variable ltpp (at byte 0). Full reference with examples per kind: docs/api/trigger-expressions.md.
Body fields
- account string required
Account id (see
/v1/accounts). - kind string required
The machine that runs a trigger.
One of: alert, bracket, ladder, risk_limit
- spec object required
Kind-specific rules, conditions written in the CEL subset (
ltp <= 120 && bid_qty > ask_qty). Checked before anything is sent: an unknown field or a condition that does not compile is a 422. - strategy string | null
Strategy the trigger belongs to (risk limits at strategy level).
- legs object[]
- valid_until string | null
RFC 3339; the trigger expires then.
- watch object[]
Instruments the conditions read but the trigger never trades (at most 3), for indicator conditions:
[{"name": "nifty", "symbol": "<instrument token>"}]lets a condition saynifty.rsi(14, '5m')ornifty.ltp. Names are[a-z][a-z0-9_]{0,15}, not a variable or function name. Each counts toward the distinct-instrument limit. Fixed at create (a change keeps them). Refused (422) while indicator conditions are not available. - route string
Where a
bracketprotecting a held position lives.auto(default) puts a simple protection (one leg already held, a plain price stop-loss and / or target, nothing else) at the broker with the rules ofPOST /v1/triggers: a broker GTT for a positional product where the broker has GTTs (a one-leg-GTT broker holds the stop-loss, a server-side trigger the target), a broker stop-loss order for an intraday (MIS) position where the broker's stop orders are proven; anything else is a v2 trigger the server evaluates.triggeralways makes the v2 trigger (the earlier behaviour).gtt/stop_orderask for that route and are refused (422) whereautowould not take it;bracket/coverare refused.One of: auto, bracket, cover, gtt, trigger, stop_order
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | malformed_json | The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types). |
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | account_not_found | No linked broker account with this id for the caller. |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
All fields (request and response schema)
Loading fields…
curl -X POST "https://cirrus.pocketful.in/v2/triggers/validate" \
-H "Authorization: token $CIRRUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account": "P1",
"kind": "bracket",
"legs": [
{
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"quantity": 5,
"product": "MIS"
}
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 25"
}
}
}'import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v2/triggers/validate",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
json={
"account": "P1",
"kind": "bracket",
"legs": [
{
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"quantity": 5,
"product": "MIS",
},
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02",
},
"stop_loss": {
"init_sl": "entry_px - 25",
},
},
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/validate', {
method: 'POST',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"account": "P1",
"kind": "bracket",
"legs": [
{
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"quantity": 5,
"product": "MIS"
}
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 25"
}
}
}),
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"valid": true,
"errors": [],
"reads": {
"symbols": [
"CT:TEST:RELIANCE"
],
"fields": [
"ltp"
],
"variables": [
"entry_px",
"sl_px"
]
},
"limits": {
"ok": true,
"reason": null,
"legs": 1,
"max_legs": 8,
"expressions": 2,
"max_expressions": 32,
"bytes": 310,
"max_bytes": 16384
},
"protection": {
"parts": {
"stop_loss": {
"held_by": "server",
"level": 2475,
"adjusted_by_server": false,
"note": null
},
"target": {
"held_by": "server",
"level": 2550,
"adjusted_by_server": false,
"note": null
}
},
"reason": "broker placement of v2 protection is switched off (OMS_TRIGGERS_V2_BROKER_PROTECTION)"
}
},
"error": null
}What v2 triggers can do #
/v2/triggers/capabilities scope readThe kinds and their spec fields, the expression language (variables, functions, operators and its size limits), the limits this deployment runs with and, with ?account=, what that account's broker can hold (order types, and each protection fact verified or unverified) and whether broker protection is on.
More about this
See docs/api/trigger-expressions.md.
Parameters
- account query · string
One of the caller's accounts: adds what its broker can hold.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | account_not_found | No linked broker account with this id for the caller. |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
All fields (request and response schema)
Loading fields…
curl "https://cirrus.pocketful.in/v2/triggers/capabilities" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v2/triggers/capabilities",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/capabilities', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"kinds": [
{
"kind": "alert",
"description": "Notifies once `when` holds, then ends (`fired`).",
"legs": "one or more",
"spec_fields": [
{
"name": "when",
"type": "condition",
"required": true,
"description": "Tick fields of every leg, risk variables, `now_ist`."
}
]
},
{
"kind": "bracket",
"description": "Entry (a condition, or a known `entry_px`), target, stop-loss and trailing stop on one position.",
"legs": "one",
"spec_fields": [
{
"name": "entry.when",
"type": "condition",
"required": false,
"description": "Waits (`pending`) until it holds; `entry_px` becomes that tick's LTP. Exclusive with `entry_px`."
},
{
"name": "entry_px",
"type": "number",
"required": false,
"description": "The entry price of a position already held (starts `active`). Exclusive with `entry`."
}
]
}
],
"expressions": {
"variables": [
{
"name": "ltp",
"description": "Leg 0's tick field; `prev.<field>` the previous tick's, `legs[i].<field>` leg i's."
},
{
"name": "bid",
"description": "Leg 0's tick field; `prev.<field>` the previous tick's, `legs[i].<field>` leg i's."
}
],
"functions": [
{
"name": "crosses_above",
"description": "crosses_above(a, b): a was at or below b on the previous tick and is above it now."
},
{
"name": "crosses_below",
"description": "crosses_below(a, b): a was at or above b on the previous tick and is below it now."
}
],
"operators": [
"||",
"&&"
],
"max_source_bytes": 2048,
"max_depth": 32,
"max_nodes": 256
},
"limits": {
"max_triggers": 500,
"max_legs": 8,
"max_expressions": 32,
"max_symbols": 100,
"max_trigger_bytes": 16384,
"max_rungs": 20
},
"entries_enabled": false,
"account": null
},
"error": null
}A v2 trigger's journey #
/v2/triggers/{trigger_id}/journey scope readEverything that happened to one of the caller's v2 triggers, newest first: what was asked (api: created, modified with the spec diff, paused, resumed, cancelled, with who asked), what the engine did and why (engine: each transition with the condition that decided it and the values it read, e.g.
More about this
ltp=2479.5, sl_px=2480 in ltp <= sl_px; notes such as an input not known), commands it refused (result), exit orders (exit) and the broker protection (protection). Kept for 90 days by default, also after the trigger ended. Another user's trigger is not found.
Parameters
- trigger_id path · string required
The trigger's id.
- limit query · integer
Entries per page, 1 to 200 (default 50).
- cursor query · string
next_cursorof the previous page. - source query · string
Only entries from
api,engine,result,exitorprotection.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_query | The query string does not fit the route's parameters. |
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | trigger_not_found | No trigger with this id for the caller. |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
All fields (request and response schema)
Loading fields…
curl "https://cirrus.pocketful.in/v2/triggers/<trigger_id>/journey" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v2/triggers/<trigger_id>/journey",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/<trigger_id>/journey', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"entries": [
{
"entry_id": "evt:01M4QRFDBAQC442FN7H0ATGR3A:1",
"at": "2026-10-12T15:23:05.396+05:30",
"seq": 1,
"source": "engine",
"kind": "transition",
"summary": "active -> active",
"actor": null,
"details": {
"from": "active",
"to": "active",
"actions": [
{
"type": "move_sl",
"legs": [
0
],
"price": 2480
}
],
"tick": {
"symbol": "CT:TEST:RELIANCE",
"ltp": 2551
},
"reason": null,
"explain": null
}
},
{
"entry_id": "api:01M4QRFDBBMKYVDBYQ05TTGVX2",
"at": "2026-10-12T15:23:05.386+05:30",
"seq": 0,
"source": "api",
"kind": "created",
"summary": "created in the app (command 01M4QRFDBBMKYVDBYQ05TTGVX2)",
"actor": {
"user": "user-01M4QRFD9EACA9SER6K3EHY0BQ",
"credential": "app"
},
"details": {
"kind": "bracket",
"legs": [
{
"symbol": "CT:TEST:RELIANCE",
"side": "BUY",
"qty": 5,
"product": "MIS"
}
],
"spec": {
"entry_px": 2500,
"target": {
"when": "ltp >= entry_px * 1.02"
},
"stop_loss": {
"init_sl": "entry_px - 25"
}
},
"valid_until": null
}
}
],
"next_cursor": null
},
"error": null
}A v2 trigger's indicator values now #
/v2/triggers/{trigger_id}/indicators scope readEach indicator a trigger's conditions read, as the engine holds it now: the last closed candle's value, the forming candle's, its warm-up status, how far it is from the condition's level and a tick-mode hold in progress.
More about this
Read from the engine's memory (nothing is computed or stored for it); poll every 2 s while a page shows it. status unavailable when the engine did not answer within 500 ms. At most one request per second per trigger.
Parameters
- trigger_id path · string required
The trigger's id.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | trigger_not_found | No trigger with this id for the caller. |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | service_unavailable | A dependency is temporarily unavailable, or the feature is off on this server; retry later. |
All fields (request and response schema)
Loading fields…
curl "https://cirrus.pocketful.in/v2/triggers/<trigger_id>/indicators" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v2/triggers/<trigger_id>/indicators",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/triggers/<trigger_id>/indicators', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);