Trade Ideas API
Stream live and historical trade ideas.
A typed JSON REST API for consuming published trade ideas programmatically. Use /v1/trade-ideas/active for every LIVE signal (combine with entry_hit_at where you distinguish pre-entry vs in-trade), optional long poll for freshness, plus /closed, /history, /reports, and /v1/trade-ideas/{id}.
https://project-tia.appVersion · v1Auth · Bearer + per-client IPToken requiredMigrate base URL · 30 June 2026Rate limit · 120 req / minhttps://project-tia.app as your API base. Migrate integrator base URLs to https://project-tia.app by 30 June 2026. Until then, https://project-tia-production.up.railway.app remains supported — same API paths, tokens, and IP rules. API paths, bearer tokens, and response shapes are unchanged — update the host only.Overview
The Trade Ideas API is a JSON REST surface with five read endpoints:
GET /v1/trade-ideas/active— every trade idea with statusLIVE, newestpublished_atfirst.GET /v1/trade-ideas/closed— recently terminal ideas (TP2_HIT,SL_HIT,EXPIRED,WITHDRAWN) within the lookback window.GET /v1/trade-ideas/history— full published archive (LIVEplus terminal rows), paginated.GET /v1/trade-ideas/reports— aggregate performance metrics (win rate, ROI, hit counts) for a day / week / month / year or custom IST date window.GET /v1/trade-ideas/{id}— one idea byid; a successful response is the trade idea JSON object at the root, not an envelope withdata.
LIVE rows may still track without an entry stamp: use entry_hit_at, status, and hit timestamps in the JSON—not informal labels—to decide how you surface them in your own systems (Status values).
All evaluation against the exchange uses Bybit USDⓈ perpetual ticker last traded price (lastPrice, abbreviated LTP), not index or mark prices. Hits on entry, TP, and SL are derived from those prices on the tracker.
All responses are JSON; timestamps are ISO 8601 UTC instants on the wire (trailing Z). Numeric prices and percentages are strings so clients preserve precision without float parsing.
Access
Access is layered: a per-partner bearer token (provided when your integration is onboarded), optional per-client IP allowlists, and always-on rate limits. Bearer tokens are required in production.
Bearer token (required)
Send your assigned token on every request:
Authorization: Bearer tia_live_…Store the token as a secret environment variable (for example TIA_API_TOKEN on Railway) and attach it to your HTTP client — no URL or response-format changes are required.
export TIA_API_TOKEN="tia_live_…"
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/active"Per-client IP allowlists
When IP allowlisting is required for your integration, requests that include your bearer token must originate from an IP or CIDR registered for your account. Multiple partners may share the same egress IP (for example Railway) and remain distinguishable because each has a unique token.
203.0.113.42 → stored as /32) or CIDR ranges (203.0.113.0/24).Deprecated (do not use)
- Global IP-only allowlists — superseded by per-client IP lists used together with bearer tokens.
TIA_PUBLIC_API_BEARER_TOKENenvironment variable — ignored; use the per-partner token provided at onboarding instead./v1/picks/*paths — redirect to/v1/trade-ideas/*with HTTP308; migrate callers to the new paths.- API keys, scopes, and self-service token registration — not supported on this API.
Rate limits
Default quota: 120 requests per minute with a burst of 60. When you send a valid bearer token, limits apply per integration (not only per IP).
Prefer long polling on /v1/trade-ideas/active (see below)—one hanging request per update cycle uses fewer hits than spraying sub-second polls. Short interval polling remains fine under the quota.
When you exceed the limit you will receive an HTTP 429 with a Retry-After header (in seconds):
{
"error": "rate_limited"
}Status values
Every visible trade idea has exactly one wire status:
LIVE alone does not guarantee the tracker has stamped entry yet. Use entry_hit_at: null means no entry stamp under publish-time rules yet; once set, milestones like tp1_hit_at proceed per the tracker.For some ideas classified at publish time, take-profit / stop-loss / milestones are tracked by the tracker only after entry_hit_at becomes non-null. Until then,LIVE persists and later hit timestamps stay null even though price moves in the broader market.
expiry_at is the published validity cut-off. Operators may shorten it on a LIVE idea (dashboard early expire); integrators should accept an extra trade_idea.updated with a smaller expiry_at while the idea remains LIVE. If TP2 or SL triggers before then, status becomes terminal; otherwise EXPIRED. A touch of tp1_price records tp1_hit_at without changing status; the trade idea can remain LIVE.
Trade idea enrichment
For in-app widgets and partner overlays, map potential profit to est_potential_pct (leverage-adjusted move to TP2 at publish time). Two additive fields support deeplink and coin imagery:
mudrex_deeplink— opens Mudrex futures with published entry, SL, and TP1. Null without a Mudrex asset id.logo_url— verifiedimages.mudrex.comURL or a manual override. Null until probed OK or overridden.
Optional fields for in-app deeplinks and coin imagery. Both may be null until catalogue data is available — safe to ignore if your integration does not use them.
Response shape
Every trade idea object exposes the fields below with this key order on the wire. Numeric prices and percentages are strings so callers can parse with decimal libraries without floats.
{
"id": "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f",
"symbol": "BTCUSDT",
"direction": "LONG",
"margin_type": "ISOLATED",
"leverage": 10,
"entry_price": "62500.00000000",
"tp1_price": "63500.00000000",
"tp2_price": "64500.00000000",
"stop_loss": "62000.00000000",
"est_potential_pct": "1.60",
"est_loss_pct": "0.80",
"risk_reward_ratio": "1:2",
"confidence": "HIGH",
"status": "LIVE",
"published_at": "2026-05-12T13:42:01.000Z",
"expiry_at": "2026-05-13T13:42:01.000Z",
"entry_hit_at": null,
"tp1_hit_at": null,
"tp2_hit_at": null,
"sl_hit_at": null,
"expired_at": null,
"withdrawn_at": null,
"resolved_at": null,
"mudrex_deeplink": "mudrex://futures-buy-sell/abc123/long/BTCUSDT/10/62500.00000000/62000.00000000/63500.00000000",
"logo_url": "https://images.mudrex.com/BTC.png"
}Withdrawn trades use the same field order; terminal example WITHDRAWN:
{
"id": "9d3bacc1-aaaa-bbbb-cccc-dddddddddddd",
"symbol": "ETHUSDT",
"direction": "SHORT",
"margin_type": "ISOLATED",
"leverage": 5,
"entry_price": "3000.00000000",
"tp1_price": "2900.00000000",
"tp2_price": "2850.00000000",
"stop_loss": "3050.00000000",
"est_potential_pct": "2.40",
"est_loss_pct": "0.80",
"risk_reward_ratio": "1:3",
"confidence": "HIGH",
"status": "WITHDRAWN",
"published_at": "2026-05-14T09:00:00.000Z",
"expiry_at": "2026-05-15T09:00:00.000Z",
"entry_hit_at": null,
"tp1_hit_at": null,
"tp2_hit_at": null,
"sl_hit_at": null,
"expired_at": null,
"withdrawn_at": "2026-05-14T09:42:03.000Z",
"resolved_at": "2026-05-14T09:42:03.000Z",
"mudrex_deeplink": "mudrex://futures-buy-sell/def456/short/ETHUSDT/5/3000.00000000/3050.00000000/2850.00000000",
"logo_url": "https://images.mudrex.com/ETH.png"
}| Field | Type | Notes |
|---|---|---|
| id | string (uuid) | Stable identifier for this trade idea. |
| symbol | string | Trading pair, e.g. BTCUSDT. |
| direction | "LONG" | "SHORT" | Side of the trade. |
| margin_type | "ISOLATED" | Always ISOLATED in this version. |
| leverage | integer | Leverage multiplier carried on the idea (product rules cap per symbol; nominally up to 100× where supported). |
| entry_price | string (decimal) | Suggested entry price. |
| tp1_price | string (decimal) | First take-profit level. |
| tp2_price | string (decimal) | null | Second take-profit level. Optional. |
| stop_loss | string (decimal) | Stop-loss level. |
| est_potential_pct | string (decimal) | Leverage-adjusted potential profit at TP2. |
| est_loss_pct | string (decimal) | Leverage-adjusted stop-loss scenario (negative). Capped at −100.0 here to match isolated-margin loss floor in outbound JSON. |
| risk_reward_ratio | string ("1:R") | null | Risk : reward ratio in price units (e.g. 1:2.5 — risk 1, reward 2.5). |
| confidence | "HIGH" | "MEDIUM" | "LOW" | Confidence tier attached at publish time. |
| status | "LIVE" | "TP2_HIT" | "SL_HIT" | "EXPIRED" | "WITHDRAWN" | LIVE rows are evaluated on the tracker until they become terminal—including WITHDRAWN (operational removal from the live feed) or TP2 / SL / EXPIRED outcomes. |
| published_at | ISO 8601 UTC | null | Instant the idea entered the public feed. Rows returned by these endpoints include a timestamp when publishing has occurred. |
| expiry_at | ISO 8601 UTC | Validity cut-off from the published trade idea. |
| entry_hit_at | ISO 8601 UTC | null | When linear LTP first satisfied entry rules versus publish-time classification; null means no entry stamp yet. TP/SL handling follows tracker rules afterward. |
| tp1_hit_at | ISO 8601 UTC | null | When LTP touched tp1_price. May be set while status is still LIVE. |
| tp2_hit_at | ISO 8601 UTC | null | Set when TP2 resolved and status becomes TP2_HIT. |
| sl_hit_at | ISO 8601 UTC | null | Set when stop-loss resolves and status becomes SL_HIT. |
| expired_at | ISO 8601 UTC | null | Set when expiry resolves and status becomes EXPIRED. |
| withdrawn_at | ISO 8601 UTC | null | Set when status is WITHDRAWN via operational withdrawal while LIVE—null otherwise. |
| resolved_at | ISO 8601 UTC | null | Convenience: null while LIVE; for terminal rows mirrors tp2_hit_at, sl_hit_at, expired_at, or withdrawn_at as applicable. |
| mudrex_deeplink | string | null | Mudrex app deeplink with published entry, SL, and TP1. Null when Mudrex asset id is unknown. |
| logo_url | string | null | Verified coin image URL (Mudrex CDN when available). Null when no image is available. |
/v1/trade-ideas/activeReturns JSON { "data": [ … ] }: trade ideas currently LIVE (still tracked). Newest published_at first. When long polling (wait_ms with since_published_at), responses also wake when the LIVE-set membership changes—for example a row moving to WITHDRAWN—so removals from /active propagate without waiting full timeout.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| since_published_at | ISO 8601 UTC | optional | Watermark from your last snapshot: the max published_at among rows you processed. Ignored unless wait_ms > 0. When waiting, the connection is released when some LIVE trade idea has published_at STRICTLY AFTER this timestamp, or when wait_ms elapses. |
| wait_ms | int 0–28500 | optional | If > 0, enables long polling (requires since_published_at). Typical value 28500 (~28.5 s). Backend polls periodically and returns when a newer LIVE published_at qualifies, when the LIVE id set gains or loses membership (withdraw or overlapping publish), or at deadline. |
Request · curl
export TIA_API_TOKEN="tia_live_…"
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/active"
# Long poll (~28.5 s cap) until a fresher publish than your watermark:
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/active?wait_ms=28500&since_published_at=2026-05-15T12:00:00.000Z"Request · JavaScript
const headers = { Authorization: `Bearer ${process.env.TIA_API_TOKEN}` };
// One-shot snapshot
let res = await fetch("https://project-tia.app/v1/trade-ideas/active", { headers });
let { data } = await res.json();
// Long poll loop: keep the latest published watermark
function maxPublished(rows) {
return rows.reduce(
(m, r) =>
!m || Date.parse(r.published_at) > Date.parse(m) ? r.published_at : m,
null
);
}
let watermark = maxPublished(data) ?? "1970-01-01T00:00:00.000Z";
while (true) {
res = await fetch(
"https://project-tia.app/v1/trade-ideas/active?wait_ms=28500&since_published_at=" +
encodeURIComponent(watermark),
{ headers }
);
({ data } = await res.json());
if (data.length) watermark = maxPublished(data) ?? watermark;
// Diff data vs your local state — then reconnect with updated watermark.
}200 OK response
{
"data": [
{
"id": "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f",
"symbol": "BTCUSDT",
"direction": "LONG",
"margin_type": "ISOLATED",
"leverage": 10,
"entry_price": "62500.00000000",
"tp1_price": "63500.00000000",
"tp2_price": "64500.00000000",
"stop_loss": "62000.00000000",
"est_potential_pct": "1.60",
"est_loss_pct": "0.80",
"risk_reward_ratio": "1:2",
"confidence": "HIGH",
"status": "LIVE",
"published_at": "2026-05-12T13:42:01.000Z",
"expiry_at": "2026-05-13T13:42:01.000Z",
"entry_hit_at": null,
"tp1_hit_at": null,
"tp2_hit_at": null,
"sl_hit_at": null,
"expired_at": null,
"withdrawn_at": null,
"resolved_at": null,
"mudrex_deeplink": "mudrex://futures-buy-sell/abc123/long/BTCUSDT/10/62500.00000000/62000.00000000/63500.00000000",
"logo_url": "https://images.mudrex.com/BTC.png"
}
]
}/v1/trade-ideas/closedReturns JSON { data, since, limit }: terminal rows (TP2_HIT, SL_HIT, EXPIRED, WITHDRAWN) matching the lookback cursor on tp2_hit_at, sl_hit_at, expired_at, or withdrawn_at, newest closure first (see implementation sorting). Full trade idea objects in data.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| since | ISO 8601 UTC | optional | Lookback start. Defaults to 24 hours before the server clock at request time. |
| limit | int 1–100 | optional | Maximum rows. Defaults to 100. |
Request · curl
export TIA_API_TOKEN="tia_live_…"
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/closed?since=2026-05-12T00:00:00Z&limit=50"Request · JavaScript
const headers = { Authorization: `Bearer ${process.env.TIA_API_TOKEN}` };
const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
const res = await fetch(`https://project-tia.app/v1/trade-ideas/closed?since=${since}`, { headers });
const { data, since: windowStart, limit } = await res.json();200 OK response
{
"since": "2026-05-12T00:00:00.000Z",
"limit": 50,
"data": [
{
"id": "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f",
"symbol": "BTCUSDT",
"direction": "LONG",
"margin_type": "ISOLATED",
"leverage": 10,
"entry_price": "62500.00000000",
"tp1_price": "63500.00000000",
"tp2_price": "64500.00000000",
"stop_loss": "62000.00000000",
"est_potential_pct": "1.60",
"est_loss_pct": "0.80",
"risk_reward_ratio": "1:2",
"confidence": "HIGH",
"status": "TP2_HIT",
"published_at": "2026-05-12T13:42:01.000Z",
"expiry_at": "2026-05-13T13:42:01.000Z",
"entry_hit_at": "2026-05-12T13:58:02.000Z",
"tp1_hit_at": "2026-05-12T14:02:11.000Z",
"tp2_hit_at": "2026-05-12T14:11:09.000Z",
"sl_hit_at": null,
"expired_at": null,
"withdrawn_at": null,
"resolved_at": "2026-05-12T14:11:09.000Z",
"mudrex_deeplink": "mudrex://futures-buy-sell/abc123/long/BTCUSDT/10/62500.00000000/62000.00000000/63500.00000000",
"logo_url": "https://images.mudrex.com/BTC.png"
}
]
}/v1/trade-ideas/historyReturns JSON { data, paging }: every published idea with status LIVE, TP2_HIT, SL_HIT, EXPIRED, or WITHDRAWN (rejected / pending never appear), newest published_at first. paging.has_more and paging.next_cursor continue cursor-based pagination.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| from | ISO 8601 UTC | optional | Filter: only ideas published at or after this timestamp. |
| to | ISO 8601 UTC | optional | Filter: only ideas published at or before this timestamp. |
| limit | int 1–200 | optional | Page size. Defaults to 50. |
| cursor | id (uuid) | optional | Opaque page cursor — pass paging.next_cursor from the previous response to fetch the next page. |
Request · curl
export TIA_API_TOKEN="tia_live_…"
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/history?limit=50"Request · JavaScript
const headers = { Authorization: `Bearer ${process.env.TIA_API_TOKEN}` };
let cursor = null;
do {
const url = new URL("https://project-tia.app/v1/trade-ideas/history");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const { data, paging } = await (await fetch(url, { headers })).json();
for (const idea of data) console.log(idea.id, idea.status);
cursor = paging.has_more ? paging.next_cursor : null;
} while (cursor);200 OK response
{
"data": [
{
"id": "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f",
"symbol": "BTCUSDT",
"direction": "LONG",
"margin_type": "ISOLATED",
"leverage": 10,
"entry_price": "62500.00000000",
"tp1_price": "63500.00000000",
"tp2_price": "64500.00000000",
"stop_loss": "62000.00000000",
"est_potential_pct": "1.60",
"est_loss_pct": "0.80",
"risk_reward_ratio": "1:2",
"confidence": "HIGH",
"status": "LIVE",
"published_at": "2026-05-12T13:42:01.000Z",
"expiry_at": "2026-05-13T13:42:01.000Z",
"entry_hit_at": null,
"tp1_hit_at": null,
"tp2_hit_at": null,
"sl_hit_at": null,
"expired_at": null,
"withdrawn_at": null,
"resolved_at": null,
"mudrex_deeplink": "mudrex://futures-buy-sell/abc123/long/BTCUSDT/10/62500.00000000/62000.00000000/63500.00000000",
"logo_url": "https://images.mudrex.com/BTC.png"
}
],
"paging": {
"limit": 50,
"has_more": true,
"next_cursor": "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f"
}
}/v1/trade-ideas/reportsReturns JSON { "data": { … } }: aggregate performance metrics for trade ideas whose published_at falls in the selected window. Win rate uses the tp_any rule (TP1 or TP2 counts as a win; denominator is TP/SL hits only). ROI uses the same realized est_*_pct formulas as the internal desk. active_trades is a global snapshot of every LIVE idea at request time (not period-filtered).
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| period | day | week | month | year | custom | optional | Reporting window. Defaults to month (last 30 days, rolling). day = last 24h; week = last 7d; year = last 365d. |
| from | YYYY-MM-DD | optional | Required when period=custom. Start of IST calendar day (inclusive). |
| to | YYYY-MM-DD | optional | Optional when period=custom. End of IST calendar day (inclusive). Defaults to now when omitted. |
Request · curl
export TIA_API_TOKEN="tia_live_…"
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/reports"
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/reports?period=week"
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/reports?period=custom&from=2026-05-01&to=2026-05-31"Request · JavaScript
const headers = { Authorization: `Bearer ${process.env.TIA_API_TOKEN}` };
const res = await fetch("https://project-tia.app/v1/trade-ideas/reports?period=month", { headers });
const { data } = await res.json();
console.log(data.published_count, data.win_rate_pct, data.cumulative_roi_pct);
console.log(data.active_trades.total, "LIVE now");200 OK response
{
"data": {
"period": {
"kind": "month",
"from": "2026-05-06T18:30:00.000Z",
"to": "2026-06-05T09:00:00.000Z",
"label": "Last 30 days",
"timezone": "Asia/Kolkata"
},
"win_definition": "tp_any",
"published_count": 42,
"win_rate_pct": "65.0",
"win_rate_eligible": 30,
"wins": 20,
"losses": 10,
"cumulative_roi_pct": "12.5",
"average_roi_pct": "0.42",
"average_rr_ratio": "1:2",
"tp1_hits": 18,
"tp2_hits": 12,
"sl_hits": 10,
"expired_count": 5,
"withdrawn_count": 2,
"open_in_period": 3,
"active_trades": {
"total": 4,
"awaiting_entry": 1,
"in_progress": 3
}
}
}/v1/trade-ideas/{id}Returns HTTP 200 with the trade idea JSON object at the response root (same keys as elsewhere). Pending-review and rejected picks return HTTP 404 with { "error": "not_found" }.
Request · curl
export TIA_API_TOKEN="tia_live_…"
curl -sS -H "Authorization: Bearer $TIA_API_TOKEN" "https://project-tia.app/v1/trade-ideas/8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f"Request · JavaScript
const headers = { Authorization: `Bearer ${process.env.TIA_API_TOKEN}` };
const id = "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f";
const res = await fetch(`https://project-tia.app/v1/trade-ideas/${id}`, { headers });
if (res.status === 404) {
console.log("not found");
} else {
const idea = await res.json();
console.log(idea);
}200 OK response
Successful responses are not wrapped in data — the body is the serialized trade idea.
{
"id": "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f",
"symbol": "BTCUSDT",
"direction": "LONG",
"margin_type": "ISOLATED",
"leverage": 10,
"entry_price": "62500.00000000",
"tp1_price": "63500.00000000",
"tp2_price": "64500.00000000",
"stop_loss": "62000.00000000",
"est_potential_pct": "1.60",
"est_loss_pct": "0.80",
"risk_reward_ratio": "1:2",
"confidence": "HIGH",
"status": "LIVE",
"published_at": "2026-05-12T13:42:01.000Z",
"expiry_at": "2026-05-13T13:42:01.000Z",
"entry_hit_at": null,
"tp1_hit_at": null,
"tp2_hit_at": null,
"sl_hit_at": null,
"expired_at": null,
"withdrawn_at": null,
"resolved_at": null,
"mudrex_deeplink": "mudrex://futures-buy-sell/abc123/long/BTCUSDT/10/62500.00000000/62000.00000000/63500.00000000",
"logo_url": "https://images.mudrex.com/BTC.png"
}Outbound webhooks (optional integration)
Integrations may receive signed HTTPS POST payloads on URLs provisioned for your environment—an alternative to polling /v1/trade-ideas/active. Delivery starts when a trade idea is published (LIVE), continues on materially relevant live updates (entry / TP1 progress, terminals), and uses the same snake_case trade_idea object as REST. Limit orders waiting for a fill show LIVE with entry_hit_at: null exactly like REST.
- Headers include
Content-Type: application/json,X-TIA-Timestamp(Unix milliseconds), andX-TIA-Signature: sha256=<hex digest>where digest is HMAC-SHA256 over{timestamp}.{rawBody}(ASCII dot between the two). OptionalX-TIA-Webhook-Event-Group-Idmatches envelopeevent_id;X-TIA-Webhook-Event-Typemirrors envelopeevent_type. - Lifecycle events:
trade_idea.published(first publish),trade_idea.updated(entry or TP1 progress, or validity shortened whileLIVE),trade_idea.closed(TP2, SL, expiry, or withdrawal). Connectivity checks may sendintegration.ping.
{
"schema_version": 1,
"event_id": "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f",
"event_type": "trade_idea.published",
"occurred_at": "2026-05-12T13:42:01.000Z",
"trade_idea": {
"id": "8c2a9b7e-4f12-4d8a-9e3b-7a6e5b1c2d3f",
"symbol": "BTCUSDT",
"direction": "LONG",
"margin_type": "ISOLATED",
"leverage": 10,
"entry_price": "62500.00000000",
"tp1_price": "63500.00000000",
"tp2_price": "64500.00000000",
"stop_loss": "62000.00000000",
"est_potential_pct": "1.60",
"est_loss_pct": "0.80",
"risk_reward_ratio": "1:2",
"confidence": "HIGH",
"status": "LIVE",
"published_at": "2026-05-12T13:42:01.000Z",
"expiry_at": "2026-05-13T13:42:01.000Z",
"entry_hit_at": null,
"tp1_hit_at": null,
"tp2_hit_at": null,
"sl_hit_at": null,
"expired_at": null,
"withdrawn_at": null,
"resolved_at": null,
"mudrex_deeplink": "mudrex://futures-buy-sell/abc123/long/BTCUSDT/10/62500.00000000/62000.00000000/63500.00000000",
"logo_url": "https://images.mudrex.com/BTC.png"
}
}Verify timestamps within a reasonable clock skew window; tolerate at-least-once deliveries by deduplicating on event_id.
{
"schema_version": 1,
"event_id": "9f2c…",
"event_type": "integration.ping",
"occurred_at": "2026-05-12T13:43:01.000Z",
"message": "Handshake test from Project TIA."
}Error responses
Errors return JSON with at least an error code. Validation responses from route handlers usually include message with a plain-language hint; clients should not depend on the exact copy of message.
| HTTP | Error code | Meaning |
|---|---|---|
| 400 | invalid_since | `since` is not a valid ISO 8601 timestamp. |
| 400 | invalid_period | `period` is not day, week, month, year, or custom (GET /reports). |
| 400 | invalid_from | Invalid `from` — ISO 8601 on GET /history; YYYY-MM-DD (IST) on GET /reports when period=custom. |
| 400 | invalid_to | Invalid `to` — ISO 8601 on GET /history; YYYY-MM-DD (IST) on GET /reports when period=custom. |
| 400 | invalid_since_published_at | `since_published_at` is not a valid ISO timestamp (only used with active long polling). |
| 400 | missing_since_for_wait | `wait_ms` is set but `since_published_at` is missing on GET /active. |
| 403 | forbidden | Bearer token required and missing, invalid, or disabled client. |
| 403 | ip_not_allowed | Client IP lists are enforced; your caller IP is not on your client's whitelist, or the server could not determine a client IP (requires a valid bearer token when lists are on). |
| 404 | not_found | { id } lookups: idea missing or not public (pending / rejected); body is `{ "error": "not_found" }`. |
| 429 | rate_limited | Too many requests for your client or IP bucket. Respect the Retry-After header. |
Changelog
Breaking changes are called out explicitly. Integration requirements are in Access.
2026-06-10
- Canonical base URL — https://project-tia.app. Migrate integrator base URLs to https://project-tia.app by 30 June 2026. Until then, https://project-tia-production.up.railway.app remains supported — same API paths, tokens, and IP rules. Update
TIA_API_BASE_URL(or your client's equivalent) before 30 June 2026. - Bearer tokens mandatory in production. Bearer tokens are required on every /v1/trade-ideas/* request in production. Send Authorization: Bearer with your assigned tia_live_… token; requests without a valid token receive HTTP 403.The optional-token grace period has ended.
2026-06-09
- GET /v1/trade-ideas/reports — aggregate performance metrics for partners: published count, win rate (
tp_any— TP1 or TP2 is a win), cumulative and average ROI, average risk-reward ratio, TP1/TP2/SL/expired/withdrawn hit counts, and a globalactive_tradessnapshot (allLIVEideas at request time). Queryperiod(day,week,month,year,custom) with optional ISTfrom/todates for custom windows. Non-breaking addition; existing endpoints unchanged.
2026-05-31
- Validity may be shortened on
LIVEideas.expiry_atcan be moved earlier before the original cut-off. The trade idea stays onGET /v1/trade-ideas/activeuntil the newexpiry_at; integrators should handle an extratrade_idea.updatedwith the smaller timestamp. Terminal closure at validity is unchanged —EXPIREDorTP1_HITper existing rules at the new cut-off (distinct fromWITHDRAWN).
2026-05-28
- Per-partner bearer tokens. Each integration receives a unique
tia_live_…token at onboarding. SendAuthorization: Bearer …on every/v1/trade-ideas/*request. Store the token in a secret environment variable (for exampleTIA_API_TOKEN). When a valid token is present, rate limits apply per integration, not only per IP — so multiple partners on a shared Railway egress IP each get their own quota. - Per-client IP allowlists replace the former global IP-only model. When IP allowlisting is required, your token must be sent together with a caller IP registered for your account. Partners sharing one egress IP remain distinguishable by bearer token.
- Historical — bearer grace period (ended June 2026). Tokens were optional during onboarding; production now requires a valid per-partner bearer on every request.
- New / updated error codes:
403 forbidden(missing, invalid, or disabled bearer when tokens are required);403 ip_not_allowed(caller IP not on your client's whitelist when IP lists are enforced). See Error responses. - Deprecated: global IP-only allowlists; server env
TIA_PUBLIC_API_BEARER_TOKEN(use your assigned per-partner token instead);/v1/picks/*(308 redirect — use/v1/trade-ideas/*); API keys and self-service token registration.
2026-05-19
- Non-breaking additions on every
trade_ideaobject (REST and webhooks):mudrex_deeplink(string | null) — Mudrex app deeplink built from publishedentry_price,stop_loss, andtp1_pricewhen a catalogue asset id exists;logo_url(string | null) — verified coin image URL from the Mudrex CDN or a manual override. Existing integrations may ignore both keys; no changes todataenvelopes or existing field names.
2026-05-18
- Outbound webhooks (optional): signed
POSTto your HTTPS listener with schema version1envelopes. Eventstrade_idea.published,trade_idea.updated, andtrade_idea.closedcarry the sametrade_ideafields as REST (includingentry_hit_at: nullwhile a limit order awaits entry). VerifyX-TIA-Signatureover{timestamp}.{rawBody}and deduplicate onevent_id(at-least-once delivery). See Webhooks. est_loss_pctis floored at-100.0when leverage-adjusted maths would imply a deeper loss—reflects isolated-margin cap in serialized responses.
2026-05-17
WITHDRAWNstatus andwithdrawn_at: a LIVE trade idea may be ended without a TP/SL/expiry resolution. Rows leave/v1/trade-ideas/activebut remain on/historyandGET /v1/trade-ideas/{id}as terminal payloads. Long polling on/activewakes on LIVE-set membership changes (including withdrawal) ahead ofwait_mstimeouts.
2026-05-16
- Reference clarifications (no breaking change): interpreting
LIVEwithentry_hit_at, observingtp1_hit_atwhilestatusstaysLIVE, and using linear perpetual LTP for hit logic.
2026-05-13
v1: public URLs under/v1/trade-ideas/*. Legacy/v1/picks/*redirects with308— migrate to/v1/trade-ideas/*. Initial release used IP-based access only; per-partner bearer tokens and per-client IP lists were added 2026-05-28 (see that date above).GET /activesupports optionalwait_ms+since_published_atlong polling for near-real-time delivery without tight polling loops.