Signal URLs
Secret URLs that turn TradingView, Chartink or Kuberhunt alerts into orders.
List signal modes #
/v2/signal-modes app sessionEach of the caller's signal URLs with its strategy's mode: direct (alerts place orders from the Signals strategy's settings, as before) or algo (alerts become signals to a bound automated-strategy deployment, which decides with its own rules and safety checks).
More about this
App session only; works whatever the flags (a URL left in algo mode while algo mode is off shows algo: its alerts are ignored until it is switched back).
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/signal-modes" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v2/signal-modes",
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/signal-modes', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": [
{
"endpoint_id": "we_5aD4VrIDQtbAm8xA",
"kind": "kuberhunt",
"strategy_id": "kh-01M59MFHD1WNSQAQCAHHX3DEXZ",
"mode": "direct",
"deployment_id": null,
"switched_at": null,
"can_use_algo": false
},
{
"endpoint_id": "we_q02Q5Ag9f6C6q0o5",
"kind": "tradingview",
"strategy_id": "nifty-alerts-01M59MFHCN0NJ023A39J4CQN2X",
"mode": "algo",
"deployment_id": "01M59MFHD6BJ6PM60WFMFAV16X",
"switched_at": "2026-10-19T08:29:35.023+00:00",
"can_use_algo": true
}
],
"error": null
}Switch a signal URL's mode #
/v2/signal-modes/{endpoint_id} app sessionSwitches the URL's Signals strategy to algo (bound to deployment_id) or back to direct.
More about this
The URL, its secrets and what the provider sends do not change. In algo mode an alert is not placed: it becomes a signal to the deployment (TradingView entry asks to enter, exit to exit; a Chartink alert asks to enter), and the deployment decides with every safety check (entry window, risk, combined stop, daily limits, kill switch, at most one entry per cycle); an exit closes only what the deployment opened.
Allowed only when flat: no position held by the Signals strategy and no open trade on the deployments involved (409 not_flat). Kuberhunt URLs stay direct. The deployment must be the caller's, not stopped, and its strategy must take alerts on a signal URL (webhook@1). Switching to algo needs automated strategies and algo mode for signals enabled for the user; switching back to direct always works. While algo mode is off, alerts on a URL left in algo are ignored (never placed directly) and the user is told. Takes an optional Idempotency-Key. App session only.
Parameters
- endpoint_id path · string required
The signal URL's
endpoint_id.
Body fields
- mode string required
directoralgo.One of: direct, algo, unknown
- deployment_id string | null
algo: one of the caller's running deployments whose strategy takes alerts on a signal URL (webhook@1). Absent fordirect.
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). |
| 403 | own_session_required | Credentials can only be created from the user's own signed-in session. |
| 404 | endpoint_not_found | No signal URL with this id (or this secret) for the caller. |
| 404 | deployment_not_found | No deployment with this id for the caller. |
| 409 | not_flat | A signal strategy's mode can change only when flat: a position held by the signal strategy, or an open trade on the deployment, must close first. Nothing was changed. |
| 409 | deployment_finished | The deployment is stopped; deploy the strategy again to run it. |
| 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. |
All fields (request and response schema)
Loading fields…
curl -X PUT "https://cirrus.pocketful.in/v2/signal-modes/<endpoint_id>" \
-H "Authorization: token $CIRRUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "algo",
"deployment_id": "01M59MFHD6BJ6PM60WFMFAV16X"
}'import os
import requests
response = requests.put(
"https://cirrus.pocketful.in/v2/signal-modes/<endpoint_id>",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
json={
"mode": "algo",
"deployment_id": "01M59MFHD6BJ6PM60WFMFAV16X",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v2/signal-modes/<endpoint_id>', {
method: 'PUT',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"mode": "algo",
"deployment_id": "01M59MFHD6BJ6PM60WFMFAV16X"
}),
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"endpoint_id": "we_q02Q5Ag9f6C6q0o5",
"kind": "tradingview",
"strategy_id": "nifty-alerts-01M59MFHCN0NJ023A39J4CQN2X",
"mode": "algo",
"deployment_id": "01M59MFHD6BJ6PM60WFMFAV16X",
"switched_at": "2026-10-19T08:29:35.023+00:00",
"can_use_algo": true
},
"error": null
}List signal URLs #
/v1/hooks/endpoints app sessionThe signed-in user's live signal URLs (never their secrets).
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/v1/hooks/endpoints" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v1/hooks/endpoints",
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/hooks/endpoints', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": [
{
"endpoint_id": "we_6USUzj3Fc2VUfgXa",
"kind": "tradingview",
"strategy_id": "nifty-breakout-01M3PGPS2RED832PWZ7N5VS16P",
"created_at": "2026-09-29T12:02:21.916811+00:00",
"has_body_secret": true
}
],
"error": null
}Issue a signal URL #
/v1/hooks/endpoints app sessionIssues a secret URL for one strategy and provider.
More about this
Give the provider the API origin followed by path; for TradingView also put "secret": "<body_secret>" in the alert message. The URL (and body secret) are shown only in this response. Issuing a new URL for the same strategy and provider revokes the previous one at once.
What each provider sends to the URL, and every reply, is described on POST /v1/hooks/{kind}/{secret}. Only the account holder, signed in themselves, can issue URLs.
Body fields
- kind string required
The provider that will call the URL.
One of: kuberhunt, tradingview, chartink
- strategy_id string required
The strategy's id: 1-64 letters, digits,
-or_.
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 | own_session_required | Credentials can only be created from the user's own signed-in session. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 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. |
All fields (request and response schema)
Loading fields…
curl -X POST "https://cirrus.pocketful.in/v1/hooks/endpoints" \
-H "Authorization: token $CIRRUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"kind": "tradingview",
"strategy_id": "nifty-breakout-01M3PGPS2RED832PWZ7N5VS16P"
}'import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v1/hooks/endpoints",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
json={
"kind": "tradingview",
"strategy_id": "nifty-breakout-01M3PGPS2RED832PWZ7N5VS16P",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v1/hooks/endpoints', {
method: 'POST',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"kind": "tradingview",
"strategy_id": "nifty-breakout-01M3PGPS2RED832PWZ7N5VS16P"
}),
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"endpoint_id": "we_6USUzj3Fc2VUfgXa",
"kind": "tradingview",
"strategy_id": "nifty-breakout-01M3PGPS2RED832PWZ7N5VS16P",
"created_at": "2026-09-29T12:02:21.916811+00:00",
"has_body_secret": true,
"path": "/v1/hooks/tradingview/wh_wDMkI4XGVWLhusCX9Bb0UatmpoJdHTLECRdRR9ggged",
"body_secret": "tvs_bD1ZOvrvN4dKZrKmCkfTL7eg7i0LVOW0",
"note": "Keep this URL private: anyone with it can send signals. Issuing a new one revokes this."
},
"error": null
}Revoke a signal URL #
/v1/hooks/endpoints/{endpoint_id} app sessionRevokes the URL at once: the provider's next request gets 404.
Parameters
- endpoint_id path · string required
The signal URL's
endpoint_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 | endpoint_not_found | No signal URL with this id (or this secret) 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 DELETE "https://cirrus.pocketful.in/v1/hooks/endpoints/<endpoint_id>" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.delete(
"https://cirrus.pocketful.in/v1/hooks/endpoints/<endpoint_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/hooks/endpoints/<endpoint_id>', {
method: 'DELETE',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"endpoint_id": "we_6USUzj3Fc2VUfgXa",
"revoked": true
},
"error": null
}Rotate a signal URL #
/v1/hooks/endpoints/{endpoint_id}/rotate app sessionIssues a new secret URL (and, for TradingView, a new body secret) for the same endpoint_id, shown only in this response.
More about this
The old URL stops working at once. Only the account holder, signed in themselves, can rotate URLs.
Parameters
- endpoint_id path · string required
The signal URL's
endpoint_id.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No credential, or the credential is invalid, expired or revoked. |
| 403 | own_session_required | Credentials can only be created from the user's own signed-in session. |
| 403 | forbidden | The credential may not use this route (missing scope, app-session-only route, IP not allowed). |
| 404 | endpoint_not_found | No signal URL with this id (or this secret) 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/v1/hooks/endpoints/<endpoint_id>/rotate" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v1/hooks/endpoints/<endpoint_id>/rotate",
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/hooks/endpoints/<endpoint_id>/rotate', {
method: 'POST',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"endpoint_id": "we_6USUzj3Fc2VUfgXa",
"kind": "tradingview",
"strategy_id": "nifty-breakout-01M3PGPS2RED832PWZ7N5VS16P",
"created_at": "2026-09-29T12:02:21.926086+00:00",
"has_body_secret": true,
"path": "/v1/hooks/tradingview/wh_afgJybcPb09zIeVruYYtBlwm1paQkKo1VtryahqbOTB",
"body_secret": "tvs_GKEskzlA7wA0oZ2TWs3ph7g0mHUeiPba",
"note": "Give the provider this new URL now: the old one no longer works. It is shown only once."
},
"error": null
}List what a signal URL received #
/v1/hooks/endpoints/{endpoint_id}/deliveries app sessionEvery request the URL received in the last 7 days, newest first: when, the outcome in one word, why in plain words, the orders it placed and the status the provider was answered with (never the payload).
More about this
Page with limit and cursor.
Parameters
- endpoint_id path · string required
The signal URL's
endpoint_id. - limit query · integer
Page size, at least 1 (default 50; above 200 counts as 200).
- cursor query · string
next_cursorof the previous page.
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 | endpoint_not_found | No signal URL with this id (or this secret) 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. |
All fields (request and response schema)
Loading fields…
curl "https://cirrus.pocketful.in/v1/hooks/endpoints/<endpoint_id>/deliveries" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v1/hooks/endpoints/<endpoint_id>/deliveries",
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/hooks/endpoints/<endpoint_id>/deliveries', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"items": [
{
"delivery_id": "01M3PGPS5220JGR75R52C6M2XY",
"at": "2026-09-29T12:02:21.986Z",
"provider": "chartink",
"outcome": "rate_limited",
"reason": "too many alerts in a short time; extra ones were refused",
"orders_placed": 0,
"order_ids": [],
"http_status": 429,
"request_id": "01M3PGPS4YGSNDQW6RQGZXNDP9"
},
{
"delivery_id": "01M3PGPS4YZ5CPAXJSB94WSXSQ",
"at": "2026-09-29T12:02:21.982Z",
"provider": "chartink",
"outcome": "duplicate",
"reason": "the same alert arrived twice; the copy was skipped",
"orders_placed": 0,
"order_ids": [],
"http_status": 200,
"request_id": "01M3PGPS4WS2X3TGFQEG16DYKD"
},
{
"delivery_id": "01M3PGPS4WTGQ0EJZRCMW5RA52",
"at": "2026-09-29T12:02:21.980Z",
"provider": "chartink",
"outcome": "duplicate",
"reason": "the same alert arrived twice; the copy was skipped",
"orders_placed": 0,
"order_ids": [],
"http_status": 200,
"request_id": "01M3PGPS4V5PYNQGZ8ZC2A4FV2"
},
{
"delivery_id": "01M3PGPS4VERYPXBJRKY6DFR6Y",
"at": "2026-09-29T12:02:21.979Z",
"provider": "chartink",
"outcome": "placed",
"reason": null,
"orders_placed": 1,
"order_ids": [
"PAPER-1"
],
"http_status": 200,
"request_id": "01M3PGPS4KQG8PH1F4PJ1YC283"
}
],
"next_cursor": null
},
"error": null
}Dry-run a signal #
/v1/hooks/endpoints/{endpoint_id}/test app sessionRuns a payload through the same checks, parsing and strategy rules as a real delivery to this URL and says which orders it would place, without placing anything or touching any state (duplicate / sequence checks, daily entry counts, the URL's rate limit, the activity and delivery logs).
More about this
Send the provider's payload as the body (for Kuberhunt, with its signature headers to check the signature too); an empty body checks a sample alert. Problems with the payload are reported in errors with 200, not as HTTP errors.
Parameters
- endpoint_id path · string required
The signal URL's
endpoint_id. - X-Kuberhunt-Signature header · string
Kuberhunt only:
v1=<hex HMAC-SHA256>; without it the signature is not checked (a warning says so). - X-Kuberhunt-Timestamp header · string
Kuberhunt only: Unix seconds the signature was made at.
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 | endpoint_not_found | No signal URL with this id (or this secret) 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/v1/hooks/endpoints/<endpoint_id>/test" \
-H "Authorization: token $CIRRUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"stocks": "TESTSU",
"trigger_prices": "100.03",
"triggered_at": "9:30 am",
"scan_name": "Breakouts",
"alert_name": "Breakout alert"
}'import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v1/hooks/endpoints/<endpoint_id>/test",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
json={
"stocks": "TESTSU",
"trigger_prices": "100.03",
"triggered_at": "9:30 am",
"scan_name": "Breakouts",
"alert_name": "Breakout alert",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v1/hooks/endpoints/<endpoint_id>/test', {
method: 'POST',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"stocks": "TESTSU",
"trigger_prices": "100.03",
"triggered_at": "9:30 am",
"scan_name": "Breakouts",
"alert_name": "Breakout alert"
}),
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"valid": true,
"errors": [],
"warnings": [],
"would_place": [
{
"account": "P1",
"symbol": "TESTSU-EQ",
"exchange": "NSE",
"side": "BUY",
"qty": 3,
"order_type": "LIMIT",
"price": 100.55,
"product": "MIS"
}
]
},
"error": null
}Receive a provider signal #
/v1/hooks/{kind}/{secret} no authThe URL a provider (TradingView, Chartink, Kuberhunt) calls; no Authorization: the secret in the URL is the credential (plus the body secret for TradingView and the signature for Kuberhunt).
More about this
The body depends on kind (see TradingViewAlert, ChartinkAlert, KuberhuntEvent). Orders are placed as the URL's owner, following the strategy's settings.
Replies are not the REST envelope: they are made for the providers, which only look at the status. 200 means received (whatever it came to: see the URL's deliveries and the activity log); only 503 asks for the same delivery again. Each URL has its own rate limit, shown in X-RateLimit-Limit / X-RateLimit-Remaining on every reply once the URL is known.
Parameters
- kind path · string required
The provider the URL was issued for.
- secret path · string required
The URL secret (
wh_...), frompathwhen the URL was issued. - X-Kuberhunt-Signature header · string
Kuberhunt:
v1=<hex HMAC-SHA256>of{timestamp}.{raw body}, keyed with the strategy's Kuberhunt webhook secret. - X-Kuberhunt-Timestamp header · string
Kuberhunt: Unix seconds, within 300 s of now.
Errors
| HTTP | Code | When |
|---|---|---|
| 403 | — | A TradingView alert with the wrong body secret |
| 403 | oms_not_enabled | Order management is not enabled for this user. |
| 404 | — | An unknown or revoked URL |
| 429 | — | Over the URL's rate limit |
| 503 | — | Temporarily unavailable; send the same delivery again later. |
All fields (request and response schema)
Loading fields…
curl -X POST "https://cirrus.pocketful.in/v1/hooks/<kind>/<secret>" \
-H "Content-Type: application/json" \
-d '{
"stocks": "TESTSU",
"trigger_prices": "100.03",
"triggered_at": "9:20 am",
"scan_name": "Breakouts",
"alert_name": "Breakout alert"
}'import os
import requests
response = requests.post(
"https://cirrus.pocketful.in/v1/hooks/<kind>/<secret>",
json={
"stocks": "TESTSU",
"trigger_prices": "100.03",
"triggered_at": "9:20 am",
"scan_name": "Breakouts",
"alert_name": "Breakout alert",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v1/hooks/<kind>/<secret>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
"stocks": "TESTSU",
"trigger_prices": "100.03",
"triggered_at": "9:20 am",
"scan_name": "Breakouts",
"alert_name": "Breakout alert"
}),
});
const body = await response.json();
console.log(response.status, body);{
"message": "Webhook received"
}