Protection (classic triggers)
The first trigger API: protect one position with a stop-loss, target and trail.
List triggers #
/v1/triggers scope readEvery trigger of the caller, with its live levels and status: server-side triggers and the mirrors of broker-held protection (bracket / cover legs, GTTs) armed from placed orders.
More about this
Ended protections (fired, exited, cancelled or deleted in the last 7 days, newest 500) follow the live ones, with status and ended_reason set.
Parameters
- status query · string
active: live triggers only;ended: ended ones only; absent: both.
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). |
| 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/v1/triggers" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v1/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/v1/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": "430cbd1c-3335-4d62-9938-411acd28860d",
"account": "P1",
"name": "SL + TGT + TSL",
"side": "BUY",
"product": "MIS",
"quantity": 5,
"price": 2500,
"order_id": null,
"broker_order_id": null,
"kind": "platform",
"paired_trigger_id": null,
"rules": {
"target": {
"enabled": true,
"type": "percentage",
"value": 2
},
"stop_loss": {
"enabled": true,
"type": "points",
"value": 25
},
"trail": {
"enabled": true,
"type": "points",
"value": 5
}
},
"stop_loss_price": 2475,
"initial_stop_loss_price": 2475,
"target_price": 2550,
"live": true,
"status": null,
"ended_reason": null,
"created_at": "2026-10-01 10:21:38",
"updated_at": "2026-10-01 10:21:38",
"instrument_token": "CT:TEST:RELIANCE",
"exits": true
},
{
"trigger_id": "31e804b4-68fb-40a2-8504-2e38f8f42476",
"account": "PF1",
"name": "SL + TGT",
"side": "BUY",
"product": "DELIVERY",
"quantity": 5,
"price": 2500,
"order_id": "250929000002",
"broker_order_id": "gtt-held",
"kind": "gtt_oco",
"paired_trigger_id": null,
"rules": {
"target": {
"enabled": true,
"type": "points",
"value": 100
},
"stop_loss": {
"enabled": true,
"type": "points",
"value": 25
},
"trail": {
"enabled": false,
"type": "percentage",
"value": 0
}
},
"stop_loss_price": 2475,
"initial_stop_loss_price": 2475,
"target_price": 2600,
"live": true,
"status": null,
"ended_reason": null,
"created_at": "2026-10-01 10:21:38",
"updated_at": "2026-10-01 10:21:38",
"instrument_token": "CT:TEST:RELIANCE",
"exits": false
}
],
"error": null
}Protect a position #
/v1/triggers scope triggersProtects an open position with a stop-loss / target (and trailing stop-loss), broker-native first.
More about this
Levels are measured from price (LTP when absent); a level already crossed at LTP is refused.
Where it lives (rules.route): auto (default) places a broker GTT when the account's broker has GTTs, the product is positional (CARRYFORWARD, DELIVERY, MTF) and nothing trails; for an intraday (MIS) position with a stop-loss and no trailing it places a broker stop-loss order (SL) where the broker's stop-loss orders are proven, sized to the position the broker holds (kind stop_order); otherwise a server-side trigger watches every tick and exits at market. gtt / stop_order ask for that route (refused with 422 before the broker where auto would not take it); trigger asks for the server-side trigger; bracket and cover are refused (they come only with an entry order). A broker whose GTT takes one leg, or a stop order, holds the stop-loss at the broker and a server-side trigger holds the target; the two name each other (paired_trigger_id) and whichever exits first ends the other. Any exit the server places for a position held by a stop order cancels the stop first (one that already went off is left to finish, and the exit waits for it). A stop order the broker refuses is replaced by a server-side trigger at once.
A broker-held GTT is answered as its display trigger (kind gtt_oco / gtt_single, exits: false, the GTT id in broker_order_id). If the broker refuses the GTT, rules.fallback decides: trigger (default) creates the server-side trigger instead, reject creates nothing (422 gtt_refused). A server-side trigger needs a running trigger exit worker: without one nothing is created (503 trigger_worker_down).
Body fields
- account string required
Account id (see
/v1/accounts). - instrument_token string required
Instrument id (see
/v1/instruments). - side string required
Side of the position's entry (BUY = long, protected by a SELL exit).
One of: BUY, SELL
- product string required
MIS(intraday),CARRYFORWARD(overnight derivatives, NRML),DELIVERY(equity delivery, CNC) orMTF(margin trading facility).One of: MIS, CARRYFORWARD, DELIVERY, MTF
- quantity integer (int64) required
Units to exit when a level is hit.
- rules object required
Stop-loss, target and trailing stop-loss. Enable a stop-loss, a target or both; a trail needs a stop-loss and is
percentageorpoints. - price number (double) | null
Entry / average price the levels are measured from; LTP if 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 | account_not_found | No linked broker account with this id for the caller. |
| 422 | invalid_request | The request is well-formed but not valid; |
| 422 | gtt_refused | The broker refused the GTT protecting a position and |
| 429 | rate_limited | Too many requests for this credential (or too many failed attempts); retry after the |
| 503 | trigger_worker_down | No trigger exit worker is running; nothing was created. |
| 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/v1/triggers" \
-H "Authorization: token $CIRRUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account": "P1",
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"product": "MIS",
"quantity": 5,
"price": 2500,
"rules": {
"stop_loss": {
"enabled": true,
"type": "points",
"value": 25
},
"target": {
"enabled": true,
"type": "percentage",
"value": 2
},
"trail": {
"enabled": true,
"type": "points",
"value": 5
}
}
}'import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v1/triggers",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
json={
"account": "P1",
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"product": "MIS",
"quantity": 5,
"price": 2500,
"rules": {
"stop_loss": {
"enabled": True,
"type": "points",
"value": 25,
},
"target": {
"enabled": True,
"type": "percentage",
"value": 2,
},
"trail": {
"enabled": True,
"type": "points",
"value": 5,
},
},
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v1/triggers', {
method: 'POST',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"account": "P1",
"instrument_token": "CT:TEST:RELIANCE",
"side": "BUY",
"product": "MIS",
"quantity": 5,
"price": 2500,
"rules": {
"stop_loss": {
"enabled": true,
"type": "points",
"value": 25
},
"target": {
"enabled": true,
"type": "percentage",
"value": 2
},
"trail": {
"enabled": true,
"type": "points",
"value": 5
}
}
}),
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"trigger_id": "b6ad3114-7fd9-414b-af5c-bcd933758e2c",
"account": "P1",
"name": "SL + TGT + TSL",
"side": "BUY",
"product": "MIS",
"quantity": 5,
"price": 2500,
"order_id": null,
"broker_order_id": null,
"kind": "platform",
"paired_trigger_id": null,
"rules": {
"target": {
"enabled": true,
"type": "percentage",
"value": 2
},
"stop_loss": {
"enabled": true,
"type": "points",
"value": 25
},
"trail": {
"enabled": true,
"type": "points",
"value": 5
}
},
"stop_loss_price": 2475,
"initial_stop_loss_price": 2475,
"target_price": 2550,
"live": true,
"status": null,
"ended_reason": null,
"created_at": "2026-10-01 10:21:38",
"updated_at": "2026-10-01 10:21:38",
"instrument_token": "CT:TEST:RELIANCE",
"exits": true
},
"error": null
}One trigger #
/v1/triggers/{trigger_id} scope readOne trigger of the caller, with its live levels and status (the same shape as a GET /v1/triggers item).
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/v1/triggers/<trigger_id>" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v1/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/v1/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": "428f23a8-7607-4a7d-9257-5bbdb49eb9b3",
"account": "P1",
"name": "SL + TGT + TSL",
"side": "BUY",
"product": "MIS",
"quantity": 5,
"price": 2500,
"order_id": null,
"broker_order_id": null,
"kind": "platform",
"paired_trigger_id": null,
"rules": {
"target": {
"enabled": true,
"type": "percentage",
"value": 2
},
"stop_loss": {
"enabled": true,
"type": "points",
"value": 25
},
"trail": {
"enabled": true,
"type": "points",
"value": 5
}
},
"stop_loss_price": 2475,
"initial_stop_loss_price": 2475,
"target_price": 2550,
"live": true,
"status": null,
"ended_reason": null,
"created_at": "2026-10-01 10:21:38",
"updated_at": "2026-10-01 10:21:38",
"instrument_token": "CT:TEST:RELIANCE",
"exits": true
},
"error": null
}Change a trigger #
/v1/triggers/{trigger_id} scope triggersChanges the rules, quantity or entry price of a trigger.
More about this
Levels are recomputed only for the legs whose inputs changed, so trailing progress on an untouched stop-loss is kept. Broker-held protection (exits: false: a bracket / cover order's legs, a GTT or a stop order) is changed at the broker first and answered with its new levels once the broker agreed; its trailing stop-loss and entry price stay as placed, a bracket keeps both legs, a cover or a stop order keeps a stop-loss only (a stop order's target is its paired trigger), and only a GTT's quantity can change (a stop order follows the position's quantity on its own).
Parameters
- trigger_id path · string required
The trigger's id.
Body fields
- rules object | null
New rules (all three legs).
- quantity integer (int64) | null
New quantity, in units.
- price number (double) | null
New entry price the levels are measured from.
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. |
| 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. |
| 409 | trigger_busy | The trigger changed while it was being updated; retry. |
| 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/v1/triggers/<trigger_id>" \
-H "Authorization: token $CIRRUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"price": 2510,
"quantity": 3
}'import os
import requests
response = requests.patch(
"https://cirrus.pocketful.in/v1/triggers/<trigger_id>",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
json={
"price": 2510,
"quantity": 3,
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v1/triggers/<trigger_id>', {
method: 'PATCH',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"price": 2510,
"quantity": 3
}),
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"trigger_id": "6b992f81-de42-4803-946d-cda864e0baaf",
"account": "P1",
"name": "SL + TGT + TSL",
"side": "BUY",
"product": "MIS",
"quantity": 3,
"price": 2510,
"order_id": null,
"broker_order_id": null,
"kind": "platform",
"paired_trigger_id": null,
"rules": {
"target": {
"enabled": true,
"type": "percentage",
"value": 2
},
"stop_loss": {
"enabled": true,
"type": "points",
"value": 25
},
"trail": {
"enabled": true,
"type": "points",
"value": 5
}
},
"stop_loss_price": 2485,
"initial_stop_loss_price": 2485,
"target_price": 2560.2,
"live": true,
"status": null,
"ended_reason": null,
"created_at": "2026-10-01 10:21:38",
"updated_at": "2026-10-01 10:21:38",
"instrument_token": "CT:TEST:RELIANCE",
"exits": true
},
"error": null
}Delete a trigger #
/v1/triggers/{trigger_id} scope triggersRemoves a trigger.
More about this
Broker-held protection is cancelled at the broker first (the bracket / cover order, the GTT or the stop order; a stop order that already went off is exiting at the broker and is not cancelled: 502, retry), and its state always copies the broker's. If the broker refuses the cancel, the protection is read back: shown cancelled or ended at the broker, the trigger is removed (broker_cancel cancelled, the broker's refusal in broker_message); shown fired, it ends as fired_at_broker (409 trigger_fired); otherwise it stays live with status cancel_pending (202, deleted false, broker_cancel pending, the broker's reason in broker_message) and is re-checked until the broker shows a final state, which it then copies. If the account is no longer linked the trigger is removed and broker_cancel is failed. If the broker does not answer or the session expired, the trigger is kept (502) so the delete can be retried.
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/v1/triggers/<trigger_id>" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.delete(
"https://cirrus.pocketful.in/v1/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/v1/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": "365acf45-6339-46bc-b364-a834de660866",
"deleted": true
},
"error": null
}