API reference

One HTTP API, keyed and versioned

JSON over HTTPS at trade-logx.com/v1: personal API keys, cursor pagination, stable error codes and signed webhooks. It covers strategies, decisions, positions and backtests; the dashboard also uses internal endpoints that are not part of it.

Authentication

A bearer token in the header. Create keys in the dashboard under Settings → Security → API keys; each is scoped (read, or read and control), revocable, and shown only once.

bash
curl https://trade-logx.com/v1/positions \
  -H "Authorization: Bearer $NEXUS_API_KEY" \
  -H "Nexus-Version: 2026-09-24"

Versioning

The Nexus-Version header pins the response shape to a date. Omit it and you get the version your key was created against — never the newest, so a deploy on our side cannot change your parsing. The current version is 2026-09-24.

Rate limits

600 requests per minute per key, 20 per second burst. The remaining allowance is returned on every response in X-RateLimit-Remaining; a 429 always carries Retry-After.

Strategies

  • Returns the strategies the platform can run, each with the version that produced its record, its lifecycle (production or research-only), the timeframes it supports and its execution mode. Every strategy is in paper mode: live order routing is locked.

    example
    {
      "data": [
        {
          "id": "brain",
          "name": "Decision Brain",
          "version": "1.0.0",
          "lifecycle": "production",
          "mode": "paper",
          "timeframes": ["15m", "1h", "4h"],
          "markets": ["binance_usdm:perpetual"]
        }
      ],
      "live_routing": "locked"
    }
  • Needs the control scope. Paper is the only mode available, so a request for paper succeeds without changing anything, and a request for live is refused while live order routing is locked.

    example
    { "mode": "live" }
    
    → 409 { "error": { "code": "live_routing_locked",
                       "message": "Live order routing is locked on this platform; every strategy runs in paper mode." } }
    → 200 { "id": "brain", "mode": "paper", "changed": false }   // for { "mode": "paper" }

Decisions

  • Newest first. Filter by verdict (accepted or rejected), symbol and since (an ISO date). The rejections are the point: they are the only place you can see what the system nearly did, and why it did not. Pages of up to 200; pass next_cursor back as cursor for the next page.

    example
    GET /v1/decisions?verdict=rejected&since=2026-09-01&limit=50
    
    {
      "data": [
        {
          "id": "dec_41",
          "ts": "2026-09-24T09:15:00+00:00",
          "symbol": "XRPUSDT",
          "strategy": "Decision Brain",
          "verdict": "rejected",
          "quality_score": 48,
          "blocked_by": "quality",
          "reason": "Quality score 48 is below the minimum of 60.",
          "rules_failed": ["min_quality_score"],
          "executed": false
        }
      ],
      "next_cursor": "dec_41"
    }
  • Decisions are stored with their scores, rules and reason, but not the full market inputs they were made from, so they cannot be re-run. The endpoint answers 501 until those inputs are kept.

    example
    → 501 { "error": { "code": "not_available", "message": "Decisions are stored with their scores and rules, not the full market inputs they were made from, so they cannot be re-run yet." } }

Positions

  • Every open paper position, with the latest observed mark, unrealised P&L and R multiple. Stops and targets are managed by the engine on every candle — they are not orders held at an exchange, and the response says so.

    example
    {
      "data": [
        {
          "id": "7c1e…", "instance_id": "eb1a2ce8", "symbol": "XRPUSDT",
          "side": "long", "size": 1250.0, "entry": 0.5412, "mark": 0.5498,
          "r_multiple": 0.86, "unrealized_pnl": 10.75, "mode": "paper",
          "protective": { "stop": 0.5312, "target": 0.5712, "managed_by": "engine" }
        }
      ]
    }
  • Needs the control scope. Closes at a fresh observed price and never at a guessed one — if there is no recent mark the request is refused with no_mark. The close is written to the audit log under the key's name.

    example
    { "reason": "manual flatten before travel" }
    
    → 200 { "id": "7c1e…", "status": "closed", "close": { "exit": 0.5498, "pnl": 10.75 } }
    → 409 { "error": { "code": "no_mark", ... } }

Backtests

  • Needs the control scope. Runs a built-in strategy over the Binance candles cached on the server (300 to 1,500 bars). If the server has no data for that symbol and timeframe, the backtest fails with that reason rather than reporting an empty result. Parameter sweeps are not available through the API yet.

    example
    { "strategy": "brain", "symbol": "BTCUSDT", "timeframe": "15m", "bars": 1000 }
    
    → 202 { "id": "bt_2f77a1c9e0b4", "status": "queued" }
  • Gross performance and performance after modelled fees, spread and slippage are reported side by side, with the difference in R. A strategy whose edge disappears once costs are applied shows as exactly that. Results are kept while the server runs; only the key that queued a backtest can read it.

    example
    {
      "id": "bt_2f77a1c9e0b4",
      "status": "complete",
      "result": {
        "gross": { "trades": 38, "win_rate": 42.1, "expectancy_r": 0.12, "net_r": 4.56 },
        "net":   { "trades": 38, "win_rate": 39.5, "expectancy_r": -0.05, "net_r": -1.9 },
        "costs": { "cost_pct_per_side": 0.0006, "net_r_drag": 6.46 }
      }
    }

Webhooks

Every decision the engine records, pushed to your endpoint as it happens. Add endpoints in the dashboard under Settings → Security → Webhooks; the signing secret is shown once.

envelope
{
  "id": "evt_dec_41",
  "type": "decision.rejected",
  "occurred_at": "2026-09-24T09:18:00Z",
  "sequence": 41,
  "idempotency_key": "dec_41:rejected",
  "data": { "...": "the same object GET /v1/decisions/dec_41 returns" }
}

Events

decision.accepted, decision.rejected, and webhook.test when you press Send test. A new endpoint starts from the next decision; history is not replayed to it.

Signatures

Nexus-Signature: t=…,v1=… is HMAC-SHA256 of t.body with your secret. Verify the raw body before parsing and reject stale timestamps; every SDK has a helper for it.

Delivery is at-least-once, so handlers must be idempotent — the idempotency_key is stable across retries. Any answer other than 2xx is retried after 30 seconds, doubling up to an hour, for 24 hours. Every attempt, with the status code your endpoint returned, is listed under the endpoint in the dashboard.

Errors

Every error has the same shape: {"error": {"code": "…", "message": "…"}}. Branch on the code; the message is for people.

StatusCodeMeans
400invalid_requestMalformed body, an unknown Nexus-Version, or a parameter outside its allowed range.
401unauthenticatedMissing, malformed or revoked API key.
403insufficient_scopeThe key is valid but read-only; this operation needs the control scope.
404not_foundNo such strategy, decision, position or backtest (for this key).
409live_routing_lockedA request to trade live. Live order routing is locked for every caller.
409no_markA close without a fresh price to close at. Nothing is closed at a guessed price.
409ambiguous_closeThe position's instance holds several positions; close them from the dashboard.
429rate_limitedRetry after the seconds given in Retry-After.
501not_availableThe endpoint exists but the capability does not yet (decision replay).

409 live_routing_locked is by design

Live order routing is locked on this platform, so promoting a strategy to live is refused for every key and every strategy runs in paper mode. Whether the API itself is up is on the status page.