# API Reference ## Base and authentication All Futures routes are mounted on the existing Hermes FastAPI application and port. In production, use the Space base URL and an authenticated browser/session or HTTP Basic credentials. When `HERMES_ADMIN_PASSWORD` is set, Futures API routes require: ```http Authorization: Basic ``` Default username: `admin`, configurable with `HERMES_DASHBOARD_BASIC_AUTH_USERNAME`. All Futures responses set `Cache-Control: no-store`. `/futures` also sets no-cache headers and runtime SHA-256 headers. ## `GET /futures` Returns the packaged HTML dashboard. Important response headers: ```text X-Hermes-Template-SHA256 X-Hermes-Router-SHA256 Cache-Control: no-store, no-cache, must-revalidate, max-age=0 ``` ## `GET /api/futures/status` Returns application status, runtime-file evidence, market health, latest bounded plan state, source metadata, account summary, and diagnostics. Representative shape: ```json { "application": { "status": "online", "runtimeStatus": "verified | mismatch | unknown", "runtimeFiles": {} }, "marketData": {"status": "healthy | degraded | unavailable"}, "tradingReadiness": "ready | blocked", "mergeStatus": "complete | partial | unknown", "analysisState": "NOT_ANALYZED", "sourceMetadata": { "datasource4": {}, "binance": {}, "datasource2": {} }, "fieldSources": {}, "fieldMetadata": {}, "verifiedFutures": false, "missingRequiredFields": [], "staleRequiredFields": [], "latestTradePlan": null, "latestPlanId": null, "latestSignalScore": null, "riskApproved": false, "tradingMode": "paper", "equity": 10000.0, "realizedPnlToday": 0.0, "openPositionCount": 0, "serverTime": 0 } ``` Consumers should treat additional fields as additive and avoid strict whole-object equality. ## `GET /api/futures/symbols` Returns the merged catalog. ```json { "symbols": [ { "symbol": "BTCUSDT", "baseAsset": "BTC", "quoteAsset": "USDT", "futuresVerified": true, "marketOnly": false, "contractType": "PERPETUAL", "status": "TRADING", "source": "datasource4", "rank": 1, "updatedAt": "2026-07-21T00:00:00Z" } ], "source": "...", "updatedAt": "...", "counts": { "total": 0, "verifiedFutures": 0, "marketOnly": 0 } } ``` Catalog membership alone does not authorize execution. Only items with `futuresVerified=true` are eligible for Paper revalidation. ## `GET /api/futures/positions` Returns Paper mode and enriched open positions. ```json { "mode": "paper", "positions": [ { "symbol": "BTC/USDT:USDT", "side": "long", "size": 0.01, "entryPrice": 60000.0, "markPrice": 60500.0, "unrealizedPnl": 5.0 } ] } ``` Mark price enrichment is best-effort. Missing mark data produces `null`, not zero. ## `GET /api/futures/market` Query parameters: | Parameter | Type | Default | Constraints | |---|---|---:|---| | `symbol` | string | `BTCUSDT` | length 3–32; normalized server-side | | `interval` | enum | `5m` | `1m`, `5m`, `15m`, `1h` | | `limit` | integer | `120` | 20–500 | Example: ```http GET /api/futures/market?symbol=BTCUSDT&interval=5m&limit=120 ``` Successful/partial shape: ```json { "state": "available | partial | stale | unavailable", "analysisState": "NOT_ANALYZED | STALE | API_UNAVAILABLE", "dataUsability": "usable | degraded | unavailable", "reason": null, "symbol": "BTCUSDT", "interval": "5m", "limit": 120, "candles": [ {"timestamp": 0, "open": 0, "high": 0, "low": 0, "close": 0, "volume": 0} ], "currentPrice": null, "markPrice": null, "change24h": null, "volume24h": null, "fundingRate": null, "openInterest": null, "source": "datasource4 | binance_public | datasource2 | mixed | unavailable", "sourcesUsed": [], "fieldSources": {}, "fieldMetadata": {}, "freshness": "fresh | stale | invalid | unknown", "verifiedFutures": false, "futuresVerification": {}, "warnings": [], "missingFields": [], "analysisRequiredFieldsMissing": [], "staleRequiredFields": [], "mergeStatus": "complete | partial | unavailable", "tradingReadiness": "ready | blocked", "rejectionReasons": [], "sourceMetadata": {}, "technicalDiagnostics": {}, "fetchedAt": 0 } ``` If acquisition raises, the route returns HTTP `503` with the same high-level keys, empty candles, null values, `state=unavailable`, `analysisState=API_UNAVAILABLE`, and blocked readiness. No mock candles are permitted in production responses. ## `GET /api/futures/trade-plan` Query parameters: | Parameter | Type | Required | Constraints | |---|---|---:|---| | `symbol` | string | yes | length 3–32; normalized server-side | Returns the latest durable Paper plan for the authenticated owner and symbol. When no plan exists, the route still returns HTTP `200` with null plan fields so the SPA can distinguish an empty feed from a missing route. ```json { "symbol": "BTCUSDT", "plan": {}, "tradePlan": {}, "latestTradePlan": {} } ``` Plan objects match the bounded analyze response shape, including `planId`, `decision`, geometry, and risk metadata. ## `GET /api/futures/signals` Query parameters: | Parameter | Type | Required | Constraints | |---|---|---:|---| | `symbol` | string | yes | length 3–32 | Returns deterministic signal components from the latest durable plan for the authenticated owner. ```json { "symbol": "BTCUSDT", "components": {}, "signalComponents": {} } ``` ## `GET /api/futures/advisory` Query parameters: | Parameter | Type | Required | Constraints | |---|---|---:|---| | `symbol` | string | yes | length 3–32 | Returns the latest optional AI context record for the symbol when `EXTERNAL_AI_ENABLED` is on. Advisory output is informational only and never changes deterministic analysis. Records are sourced from the authenticated owner's latest durable plan (`external_advisory` field). When advisory is disabled or no plan exists, `advisory` and `external_advisory` are `null` and `enabled` reflects configuration. When present, advisory objects follow `external_ai/advisory_schema.py` plus runtime metadata: ```json { "symbol": "BTCUSDT", "enabled": true, "advisory": { "advisory_id": "hex", "status": "available", "provider": "hermes_auxiliary", "model": "futures_advisory", "market_bias": "bullish", "confidence": 0.62, "summary": "…", "supporting_points": ["…"], "risk_warnings": ["…"], "latency_ms": 420, "fallback_used": false, "error": null, "attempts": [ { "provider": "hermes_auxiliary", "model": "futures_advisory", "latency_ms": 420, "error": null, "status": "succeeded" } ], "provider_order": ["futures_advisory"] }, "external_advisory": { } } ``` `status` is `available` or `unavailable`. Unavailable records may include `error` (for example `disabled`, `timeout`, `invalid_advisory`). ## `GET /api/futures/activity` Query parameters: | Parameter | Type | Default | Constraints | |---|---|---:|---| | `limit` | integer | `50` | 1–200 | Returns sanitized recent audit events for the authenticated dashboard owner. ```json { "items": [ { "type": "plan_superseded", "event": "plan_superseded", "message": "Plan superseded by abc123", "timestamp": 0, "created_at": 0, "time": "2026-07-24T00:00:00Z" } ], "events": [], "activity": [], "limit": 50 } ``` ## `POST /api/futures/analyze` Request: ```json { "symbol": "BTCUSDT", "risk_profile": "moderate", "include_external_context": false } ``` Allowed risk profiles: ```text conservative moderate aggressive ``` Unknown request fields are rejected. Representative response: ```json { "planId": "server-generated-reference", "symbol": "BTCUSDT", "decision": "LONG | SHORT | NO_TRADE", "analysis_state": "LONG | SHORT | NO_TRADE", "score": null, "confidence": null, "components": {}, "core_reasons": [], "warnings": [], "entry": null, "stop_loss": null, "take_profit": null, "reward_to_risk": null, "risk_profile": "moderate", "risk_percent": null, "requested_leverage": 5, "effective_leverage": null, "quantity": null, "estimated_slippage_percent": null, "risk_approved": false, "rejection_reasons": [], "noTradeGuard": true, "plan_type": "directional_plan | non_executable_plan", "executable": false, "futuresVerified": false, "trading_readiness": "blocked", "created_at": "...", "expires_at": "...", "external_advisory": null } ``` A `NO_TRADE` response is a successful deterministic evaluation, not an HTTP failure. An internal analysis failure returns HTTP `503` with `detail="Futures analysis failed"` and clears the current plan state. ## `POST /api/futures/paper/execute` Request: ```json { "symbol": "BTCUSDT", "risk_profile": "moderate", "planId": "server-generated-reference" } ``` The endpoint may return: - `403` for unverified contract or non-Paper mode; - `409` for superseded/unknown plan, symbol/risk change, expiry, prior execution, blocked readiness, failed fresh revalidation, or non-executable plan; - `422` for invalid request shape/symbol; - `200` for the final Paper result. The endpoint is intentionally absent from the read-only audit tool. ## Telegram routes ### `POST /api/telegram/webhook` Public webhook ingress protected by: ```http X-Telegram-Bot-Api-Secret-Token: ``` Limits request body to 256 KiB, applies per-user rate limiting, requires owner/allowed-user authorization, and invokes analysis-only commands. ### `GET /api/telegram/status` Returns enabled/mode/webhook/proxy/relay/authorized-user/alert-scheduler status. It does not expose tokens or user IDs. ### `GET /api/telegram/bootstrap/status` Requires the same Telegram secret header and returns only: ```json {"ok": true, "ownerClaimed": true, "bootstrapConsumed": true} ```