{"openapi":"3.1.0","info":{"title":"Eclipse Signals API","version":"1.0.0","summary":"Proprietary trading-indicator outputs for autonomous agents. Self-serve, $129/month.","description":"Proprietary indicator intelligence from Eclipse’s 7 indicators (Trend Oracle, Bayesian Trend, Reversals, Ceres, Enzo, LLNR, PerpKit) across 251 tracked assets (176 crypto, 75 equities and metals) and 3 timeframes: current state (/state), state changes (/signals), recent changes grouped by asset (/flips), and cross-indicator agreement (/confluence).\n\nGETTING ACCESS IS FULLY SELF-SERVE AND NEEDS NO HUMAN. POST /api/v1/access/checkout returns a Stripe checkout URL plus a one-time claim_token; after payment, POST /api/v1/access/claim exchanges that token for your API key. $129/month or $1199/year, 10,000 requests/day. There is no approval step and no free tier.\n\nBefore buying you can call four endpoints with no key at all: /api/v1 (manifest), /api/v1/catalog (authoritative coverage), /api/v1/signals/sample (exact response shape) and /api/v1/stats (how many signals actually fire).\n\nOutputs are informational indicator readings, not investment advice.","contact":{"name":"Eclipse support & access recovery","url":"https://www.eclipse.trade/api/docs/errors#access-recovery"},"termsOfService":"https://www.eclipse.trade/terms"},"servers":[{"url":"https://www.eclipse.trade"}],"security":[{"apiKey":[]}],"x-access":{"model":"self-serve, no human approval","price_usd":{"monthly":129,"yearly":1199},"included_with":"Eclipse Premium subscription","premium_members":"Already an Eclipse Premium member? API access is included in your plan: create your key at https://www.eclipse.trade/dashboard (Settings, API access). No second purchase.","daily_request_quota":10000,"quota_resets":"00:00 UTC","purchase_flow":["POST /api/v1/access/checkout","pay at the returned checkout_url","POST /api/v1/access/claim"],"free_tier":false,"no_auth_endpoints":["/api/v1","/api/v1/catalog","/api/v1/openapi.json","/api/v1/signals/sample","/api/v1/stats"]},"x-mcp":{"endpoint":"https://www.eclipse.trade/api/v1/mcp","transport":"JSON-RPC 2.0 over HTTP POST","note":"tools/list requires no API key, so tools can be discovered before purchase."},"x-polling":{"recommended_interval_seconds":900,"cursor":"since (exclusive — strictly after the given timestamp)","rationale":"New signals only appear shortly after a candle close; faster polling returns identical data and burns quota."},"tags":[{"name":"Discovery","description":"No API key required — evaluate the product before paying."},{"name":"Signals","description":"The paid product: current state, changes, and agreement."},{"name":"Access","description":"Self-serve purchase and key issuance."}],"paths":{"/api/v1":{"get":{"operationId":"getManifest","tags":["Discovery"],"summary":"Product manifest (no auth)","description":"The canonical entry point: what the API serves, the auth scheme, every endpoint, and how to buy access.","security":[],"responses":{"200":{"description":"Machine-readable description of the API, data, and access process."}}}},"/api/v1/catalog":{"get":{"operationId":"getCatalog","tags":["Discovery"],"summary":"Authoritative live coverage (no auth)","description":"Every tracked asset, timeframe and indicator, generated from the compute jobs’ own constants. Verify coverage claims here before purchasing. Includes each indicator’s status and the endpoints that carry it, legacy keys that can appear in state, and a per-asset coverage table.","security":[],"responses":{"200":{"description":"The exact live universe the API serves.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogResponse"}}}}}}},"/api/v1/stats":{"get":{"operationId":"getStats","tags":["Discovery"],"summary":"Descriptive feed statistics, last 30 days (no auth)","description":"How many signals actually fired in the last 30 days, by indicator, timeframe and direction. Volume facts only — Eclipse publishes no win rates, accuracy or return figures. Use this to size ingestion and estimate trigger frequency before buying.","security":[],"responses":{"200":{"description":"Aggregate signal counts for the window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsResponse"}}}},"503":{"description":"Statistics temporarily unavailable."}}}},"/api/v1/signals/sample":{"get":{"operationId":"getSignalsSample","tags":["Discovery"],"summary":"Static documentation sample (no auth, not live data)","description":"Fixed sample rows in the exact live-feed shape, so you can validate your parser before paying.","security":[],"responses":{"200":{"description":"Fixed sample rows in the exact live-feed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignalsResponse"}}}}}}},"/api/v1/state":{"get":{"operationId":"getState","tags":["Signals"],"summary":"Current indicator state across the whole universe (API key required)","description":"One call returns the current directional state of every indicator on every tracked asset and timeframe, as of the last hourly compute. This is the last computed state, not a fresh on-demand calculation — use getSignals to see when a state last changed.","parameters":[{"name":"ticker","in":"query","schema":{"type":"string"},"description":"Comma-separated tickers to narrow to. GET /api/v1/catalog for valid values."},{"name":"asset_class","in":"query","schema":{"type":"string","enum":["crypto","stock","metal"]}},{"name":"timeframe","in":"query","schema":{"type":"string","enum":["1h","4h","1d"]}},{"name":"indicator","in":"query","schema":{"type":"string","enum":["trend_oracle","bayesian_trend","reversals","ceres","enzo","llnr","perpkit"]}},{"name":"direction","in":"query","schema":{"type":"string","enum":["bullish","bearish","neutral"]},"description":"Return only readings currently in this state."}],"responses":{"200":{"description":"Per-asset indicator state.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StateResponse"}}}},"400":{"description":"Invalid parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"401":{"description":"Missing or invalid API key. The body documents how to buy access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"403":{"description":"Revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"429":{"description":"Daily quota reached (resets 00:00 UTC). Retry-After is set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"503":{"description":"State not computed yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}}}}},"/api/v1/signals":{"get":{"operationId":"getSignals","tags":["Signals"],"summary":"Signal feed — state changes, newest first (API key required)","description":"Results are newest first. Keep since and filters unchanged while paging with offset; drain the backlog before advancing since to the first page's next_cursor. next_cursor is the newest timestamp, not a continuation token. Live offset pages can shift and late events can share a timestamp: use overlapping windows, deduplicate by tier and id, and reconcile with /api/v1/state. Lossless replay is not guaranteed.\n\ncreated_at is when Eclipse wrote the row, not when the signal fired. Universe-tier rows are written by the hourly job a few minutes after the candle that produced them closes; if an asset’s candles had not updated in time, its change is written at that timeframe’s next run instead. Alerts-tier rows are written when the alert job publishes them, and alerts published in the same run carry created_at values milliseconds apart even when they fired on different candles.\n\nAlerts tier only: the same action for the same ticker, indicator and timeframe is published at most once per 20 candles of that timeframe (80 hours on 4h, 20 days on 1d). An alert held back by this rule is published later, when its window ends, or never if another alert for that ticker, indicator and timeframe is published first. The universe tier has no such rule and logs every change.\n\nCLOSE ends the strategy’s open position but does not say whether that position was long or short. Because of the alerts-tier cooldown, the entry it closes may be missing from the feed or published late. BUY and SELL are legacy values; the current alert engines publish LONG, SHORT and CLOSE.","parameters":[{"name":"tier","in":"query","schema":{"type":"string","enum":["universe","alerts","all"],"default":"universe"},"description":"universe = all tracked assets; changes are logged for 6 indicators (trend_oracle, bayesian_trend, reversals, ceres, enzo, llnr). alerts = the subscriber alert engines incl. PerpKit, with explicit actions. all = merged."},{"name":"ticker","in":"query","schema":{"type":"string"},"description":"Asset symbol (case-insensitive exact match). GET /api/v1/catalog for valid values."},{"name":"indicator","in":"query","schema":{"type":"string","enum":["trend_oracle","bayesian_trend","reversals","ceres","enzo","llnr","perpkit"]},"description":"Stable id, screener id, or display name — all accepted."},{"name":"timeframe","in":"query","schema":{"type":"string","enum":["1h","4h","1d"]}},{"name":"direction","in":"query","schema":{"type":"string","enum":["bullish","bearish","neutral","exit"]}},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"},"description":"Results are newest first. Keep since and filters unchanged while paging with offset; drain the backlog before advancing since to the first page's next_cursor. next_cursor is the newest timestamp, not a continuation token. Live offset pages can shift and late events can share a timestamp: use overlapping windows, deduplicate by tier and id, and reconcile with /api/v1/state. Lossless replay is not guaranteed."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Signals, newest first.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignalsResponse"}}}},"400":{"description":"Invalid parameter, unknown ticker, or unknown indicator.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"401":{"description":"Missing or invalid API key. The body documents how to buy access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"403":{"description":"Revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"429":{"description":"Daily quota reached (resets 00:00 UTC). Retry-After is set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}}}}},"/api/v1/confluence":{"get":{"operationId":"getConfluence","tags":["Signals"],"summary":"Cross-indicator agreement, ranked (API key required)","description":"Assets where multiple different indicators currently agree, ranked. Every underlying vote is returned so the score can be audited or recomputed.","parameters":[{"name":"timeframe","in":"query","schema":{"type":"string","enum":["1h","4h","1d"],"default":"4h"}},{"name":"direction","in":"query","schema":{"type":"string","enum":["bullish","bearish"],"default":"bullish"}},{"name":"min_agree","in":"query","schema":{"type":"integer","minimum":1,"maximum":6,"default":3}},{"name":"asset_class","in":"query","schema":{"type":"string","enum":["crypto","stock","metal"]}}],"responses":{"200":{"description":"Ranked assets with every underlying indicator vote included.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfluenceResponse"}}}},"400":{"description":"Invalid parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"429":{"description":"Daily quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}}}}},"/api/v1/flips":{"get":{"operationId":"getFlips","tags":["Signals"],"summary":"Recent changes grouped by asset and ranked (API key required)","description":"The website’s Flips panel as data. Reads the universe-tier changes inside a window (the same rows getSignals returns with tier=universe), keeps only the newest change per asset, indicator and timeframe, groups them by asset and ranks the groups by how many indicators turned the same way. Every rule is repeated in the response under meta.rules.","parameters":[{"name":"timeframe","in":"query","schema":{"type":"string","enum":["1h","4h","1d"],"default":"1d"}},{"name":"hours","in":"query","schema":{"type":"integer","minimum":1,"maximum":72},"description":"Window in hours, per timeframe: 1h: default 3, max 6; 4h: default 12, max 24; 1d: default 48, max 72."},{"name":"direction","in":"query","schema":{"type":"string","enum":["all","bullish","bearish"],"default":"all"},"description":"bullish or bearish keeps only the cells whose newest change turned that way."}],"responses":{"200":{"description":"Ranked groups, one per asset.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlipsResponse"}}}},"400":{"description":"Invalid parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"401":{"description":"Missing or invalid API key. The body documents how to buy access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"403":{"description":"Revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}},"429":{"description":"Daily quota reached (resets 00:00 UTC). Retry-After is set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}}}}},"/api/v1/usage":{"get":{"operationId":"getUsage","tags":["Signals"],"summary":"Remaining daily quota for this key","responses":{"200":{"description":"When a key quota snapshot is available, responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Public endpoints and missing or invalid keys have no key quota snapshot; absent headers do not mean zero remaining requests.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily request quota for this key."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left today."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the quota resets (00:00 UTC)."}}}}}},"/api/v1/access/checkout":{"post":{"operationId":"createAccessCheckout","tags":["Access"],"summary":"Buy API access (no auth) — step 1 of 2","description":"Starts self-serve onboarding. Returns a Stripe Checkout URL and a one-time claim_token. PERSIST THE TOKEN BEFORE OPENING THE URL — it is shown exactly once and cannot be recovered. No human approval is involved at any point.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact_email"],"properties":{"contact_email":{"type":"string","format":"email","description":"Where the receipt and billing notices go."},"billing":{"type":"string","enum":["monthly","yearly"],"default":"monthly","description":"$129/month or $1199/year."},"agent_name":{"type":"string","maxLength":120,"description":"Optional label for your agent."}}}}}},"responses":{"201":{"description":"Checkout session created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"400":{"description":"Invalid body or email."},"429":{"description":"Too many checkout attempts from this address."},"503":{"description":"Billing temporarily unavailable — retryable, honour Retry-After."}}}},"/api/v1/access/claim":{"post":{"operationId":"claimApiKey","tags":["Access"],"summary":"Exchange a claim_token for your API key (no auth) — step 2 of 2","description":"Call after completing payment. Entitlement is verified against Stripe, not against the token alone. Returns the API key EXACTLY ONCE — store it immediately. If the response status is \"pending\", payment has not settled yet: retry every 10 seconds for up to 5 minutes.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["claim_token"],"properties":{"claim_token":{"type":"string","description":"The one-time token from createAccessCheckout."}}}}}},"responses":{"200":{"description":"Key issued, or payment still pending.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimResponse"}}}},"400":{"description":"Missing or malformed claim_token."},"404":{"description":"Unknown claim_token."},"409":{"description":"This token has already been redeemed; the key is not recoverable."}}}},"/api/v1/access-request":{"post":{"operationId":"createAccessRequest","tags":["Access"],"summary":"OPTIONAL human contact path — bulk pricing, redistribution or custom terms","description":"NOT required for normal access. Use createAccessCheckout to buy a key yourself. This endpoint exists only for commercial arrangements a self-serve subscription does not cover, such as redistribution rights or volume pricing; a human replies by email.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact_email"],"properties":{"contact_email":{"type":"string","format":"email"},"agent_name":{"type":"string","maxLength":120},"operator":{"type":"string","maxLength":120},"use_case":{"type":"string","maxLength":1000},"expected_volume":{"type":"string","maxLength":120}}}}}},"responses":{"200":{"description":"A request for this email already exists; it was updated."},"201":{"description":"Request received; a human follows up by email."},"400":{"description":"Invalid body or email."},"429":{"description":"Too many submissions from this address; retry later."}}}},"/api/v1/mcp":{"post":{"operationId":"mcpJsonRpc","tags":["Discovery"],"summary":"Model Context Protocol server (JSON-RPC 2.0)","description":"MCP endpoint exposing the same product as tools. `initialize`, `ping`, `tools/list` and `tools/call` are supported. tools/list needs NO API key, so an agent can inspect every tool and argument schema before purchasing; tools/call needs `Authorization: Bearer ek_...` and shares the REST quota.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","const":"2.0"},"id":{"type":["string","integer","null"]},"method":{"type":"string","enum":["initialize","ping","tools/list","tools/call"]},"params":{"type":"object"}}}}}},"responses":{"200":{"description":"JSON-RPC 2.0 result or error object."},"202":{"description":"Notification accepted (no body)."}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"ek_...","description":"API key from the self-serve flow: POST /api/v1/access/checkout, pay, then POST /api/v1/access/claim. Send as `Authorization: Bearer ek_...` (the header `X-Api-Key: ek_...` is also accepted). No human approval is required to obtain one."}},"schemas":{"Error":{"type":"object","description":"Every failure body is written to be read by a model: it says what went wrong and what to do next, including how to buy access on a 401.","properties":{"error":{"type":"string","enum":["missing_key","invalid_key","revoked_key","rate_limited","not_provisioned","invalid_parameter","unknown_ticker","unknown_indicator","unavailable"]},"message":{"type":"string"},"how_to_get_access":{"type":"object","description":"Present on auth failures — the full self-serve purchase flow and price."},"docs":{"type":"string","format":"uri"}},"required":["error","message"]},"Signal":{"type":"object","properties":{"id":{"type":"string","description":"Stable identifier — safe to dedupe on."},"tier":{"type":"string","enum":["universe","alerts"]},"ticker":{"type":"string","examples":["BTC"]},"asset_class":{"type":"string","enum":["crypto","stock","metal"]},"indicator":{"type":"string","enum":["trend_oracle","bayesian_trend","reversals","ceres","enzo","llnr","perpkit"]},"indicator_name":{"type":"string","examples":["Trend Oracle"]},"timeframe":{"type":"string","enum":["1h","4h","1d"]},"direction":{"type":"string","enum":["bullish","bearish","neutral","exit"]},"transition":{"type":["object","null"],"description":"The state change (universe tier). Null on the alerts tier.","properties":{"from":{"type":"string"},"to":{"type":"string"}}},"change_type":{"type":["string","null"],"enum":["reversal","activation","deactivation",null],"description":"Universe tier: what kind of change this is, from transition. reversal: bullish → bearish or bearish → bullish: the reading turned to the opposite direction. activation: neutral → bullish or bearish: the reading took a direction. deactivation: bullish or bearish → neutral: the reading lost its direction. Null on the alerts tier."},"action":{"type":["string","null"],"enum":["LONG","SHORT","BUY","SELL","CLOSE",null],"description":"Alerts tier only; null on the universe tier. CLOSE ends the strategy’s open position but does not say whether that position was long or short. Because of the alerts-tier cooldown, the entry it closes may be missing from the feed or published late. BUY and SELL are legacy values; the current alert engines publish LONG, SHORT and CLOSE."},"price":{"type":["number","null"],"description":"Asset price when the signal fired."},"created_at":{"type":"string","format":"date-time","description":"created_at is when Eclipse wrote the row, not when the signal fired. Universe-tier rows are written by the hourly job a few minutes after the candle that produced them closes; if an asset’s candles had not updated in time, its change is written at that timeframe’s next run instead. Alerts-tier rows are written when the alert job publishes them, and alerts published in the same run carry created_at values milliseconds apart even when they fired on different candles."}},"required":["id","tier","ticker","asset_class","indicator","timeframe","direction","created_at"]},"Pagination":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"returned":{"type":"integer","description":"Rows in this page."},"has_more":{"type":"boolean","description":"True when another matching row exists. Continue with offset before advancing your polling watermark."},"next_cursor":{"type":["string","null"],"format":"date-time","description":"Newest created_at in this page; not a continuation token. Drain all backlog pages before advancing since to the first page timestamp."}}},"SignalsResponse":{"type":"object","properties":{"signals":{"type":"array","items":{"$ref":"#/components/schemas/Signal"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"AssetState":{"type":"object","properties":{"ticker":{"type":"string"},"name":{"type":"string"},"asset_class":{"type":"string","enum":["crypto","stock","metal"]},"signals":{"type":"object","description":"indicator id → { timeframe → \"bullish\"|\"bearish\"|\"neutral\" }. Keys are the catalog’s state_indicators, plus these legacy keys on some assets. dexter: Dexter was Enzo’s name until 13 Apr 2026. This key is left over in the stored state of some assets that were tracked before the rename, and it has not been updated since, so its readings are out of date. Use enzo. /api/v1/confluence counts it as a vote on those assets, so their total_reporting can be one higher.","propertyNames":{"enum":["trend_oracle","bayesian_trend","reversals","ceres","enzo","llnr","perpkit","dexter"]},"additionalProperties":{"type":"object","additionalProperties":{"type":"string","enum":["bullish","bearish","neutral"]}}}}},"StateResponse":{"type":"object","properties":{"as_of":{"type":"string","format":"date-time","description":"When the underlying compute last ran."},"assets":{"type":"array","items":{"$ref":"#/components/schemas/AssetState"}},"counts":{"type":"object","properties":{"assets":{"type":"integer"},"readings":{"type":"integer"}}}}},"ConfluenceResult":{"type":"object","properties":{"ticker":{"type":"string"},"name":{"type":"string"},"asset_class":{"type":"string","enum":["crypto","stock","metal"]},"agree":{"type":"integer","description":"Indicators currently in the requested direction."},"total_reporting":{"type":"integer","description":"Indicators with a reading (no reading is excluded, not counted as disagreement)."},"score":{"type":"number","description":"agree / total_reporting."},"votes":{"type":"object","description":"Every underlying vote: indicator id → direction. Recompute the score yourself from this. Votes come from every key in the stored state: the catalog’s state_indicators, plus these legacy keys on some assets. dexter: Dexter was Enzo’s name until 13 Apr 2026. This key is left over in the stored state of some assets that were tracked before the rename, and it has not been updated since, so its readings are out of date. Use enzo. /api/v1/confluence counts it as a vote on those assets, so their total_reporting can be one higher.","propertyNames":{"enum":["trend_oracle","bayesian_trend","reversals","ceres","enzo","llnr","perpkit","dexter"]},"additionalProperties":{"type":"string","enum":["bullish","bearish","neutral"]}}}},"ConfluenceResponse":{"type":"object","properties":{"as_of":{"type":"string","format":"date-time"},"query":{"type":"object"},"results":{"type":"array","items":{"$ref":"#/components/schemas/ConfluenceResult"}}}},"StatsResponse":{"type":"object","description":"Descriptive volume only — never win rates, accuracy or returns.","properties":{"window":{"type":"object","properties":{"days":{"type":"integer"},"since":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"}}},"universe_tier":{"type":"object","properties":{"total_signals":{"type":["integer","null"]},"signals_per_day":{"type":["number","null"]},"by_indicator":{"type":"array","items":{"type":"object"}},"by_timeframe":{"type":"array","items":{"type":"object"}},"by_direction":{"type":"array","items":{"type":"object"}}}},"alerts_tier":{"type":"object"}}},"CheckoutResponse":{"type":"object","properties":{"status":{"type":"string","const":"checkout_created"},"mode":{"type":["string","null"],"enum":["test","live",null],"description":"\"live\" means a real payment. \"test\" means Stripe test mode, where no real charge is made. Every key reads the same production data either way, but a key bought in test mode stops working once the server bills in live mode."},"checkout_url":{"type":"string","format":"uri","description":"Complete payment here."},"claim_token":{"type":"string","description":"Shown ONCE. Persist before opening checkout_url."},"plan":{"type":"object","properties":{"tier":{"type":"string","const":"premium"},"billing":{"type":"string","enum":["monthly","yearly"]},"amount_usd":{"type":"number"}}},"next_step":{"type":"object","description":"Literal instructions for completing the flow."}}},"ClaimResponse":{"oneOf":[{"type":"object","required":["status","mode","api_key","usage"],"properties":{"status":{"type":"string","const":"active"},"mode":{"type":["string","null"],"enum":["test","live",null],"description":"\"live\" means a real payment. \"test\" means Stripe test mode, where no real charge is made. Every key reads the same production data either way, but a key bought in test mode stops working once the server bills in live mode."},"api_key":{"type":"string","description":"Returned exactly once. Store it immediately."},"store_this":{"type":"string"},"usage":{"type":"object","required":["header","daily_limit","quota_resets","start_here"],"properties":{"header":{"type":"string"},"daily_limit":{"type":"integer"},"quota_resets":{"type":"string"},"start_here":{"type":"string"}}},"docs":{"type":"string","format":"uri"},"catalog":{"type":"string","format":"uri"}}},{"type":"object","required":["status","mode","message","retry_after_seconds"],"properties":{"status":{"type":"string","const":"pending"},"mode":{"type":["string","null"],"enum":["test","live",null],"description":"\"live\" means a real payment. \"test\" means Stripe test mode, where no real charge is made. Every key reads the same production data either way, but a key bought in test mode stops working once the server bills in live mode."},"message":{"type":"string"},"retry_after_seconds":{"type":"integer","description":"Delay before checking payment again."}}}]},"UsageResponse":{"type":"object","properties":{"key":{"type":"object","properties":{"label":{"type":["string","null"]}}},"mode":{"type":["string","null"],"enum":["test","live",null],"description":"The Stripe mode this server bills in. A self-serve key only works in the mode it was bought in, so for one this is also the key’s own mode. \"live\" means a real payment. \"test\" means Stripe test mode, where no real charge is made. Every key reads the same production data either way, but a key bought in test mode stops working once the server bills in live mode."},"quota":{"type":["object","null"],"properties":{"daily_limit":{"type":"integer"},"used_today":{"type":"integer"},"remaining_today":{"type":"integer"},"resets_at":{"type":"string","format":"date-time"}}},"meta":{"type":"object"}}},"Flip":{"type":"object","required":["indicator","indicator_name","from","to","change_type","price","created_at"],"properties":{"indicator":{"type":"string","enum":["trend_oracle","bayesian_trend","reversals","ceres","enzo","llnr"]},"indicator_name":{"type":"string"},"from":{"type":"string","enum":["bullish","bearish","neutral"]},"to":{"type":"string","enum":["bullish","bearish","neutral"]},"change_type":{"type":["string","null"],"enum":["reversal","activation","deactivation",null]},"price":{"type":["number","null"],"description":"Asset price when this change was logged."},"created_at":{"type":"string","format":"date-time","description":"When Eclipse wrote the change, not when it fired."}}},"FlipGroup":{"type":"object","required":["ticker","name","asset_class","dominant","bullish","bearish","score","latest_at","flips"],"properties":{"ticker":{"type":"string"},"name":{"type":"string"},"asset_class":{"type":"string","enum":["crypto","stock","metal"]},"dominant":{"type":"string","enum":["bullish","bearish","mixed"],"description":"The larger side, or mixed on a tie."},"bullish":{"type":"integer","description":"Indicators whose newest change in the window turned bullish."},"bearish":{"type":"integer","description":"Indicators whose newest change in the window turned bearish."},"score":{"type":"integer","description":"The larger of bullish and bearish. Groups are ranked by it."},"latest_at":{"type":"string","format":"date-time","description":"The group’s most recent change."},"flips":{"type":"array","items":{"$ref":"#/components/schemas/Flip"},"description":"Newest first; one per indicator."}}},"FlipsResponse":{"type":"object","required":["as_of","query","groups","counts","capped","meta"],"properties":{"as_of":{"type":"string","format":"date-time"},"query":{"type":"object","properties":{"timeframe":{"type":"string","enum":["1h","4h","1d"]},"hours":{"type":"integer"},"direction":{"type":"string","enum":["all","bullish","bearish"]}}},"groups":{"type":"array","items":{"$ref":"#/components/schemas/FlipGroup"},"description":"Ranked best first."},"counts":{"type":"object","properties":{"groups":{"type":"integer"},"flips":{"type":"integer"}}},"capped":{"type":"boolean","description":"True when the row limit was reached, so older changes inside the window may be missing."},"meta":{"type":"object","description":"meta.rules states the windows, row limits, grouping and ranking rules."}}},"CatalogIndicator":{"type":"object","required":["id","name","available_in","status","appears_in"],"properties":{"id":{"type":"string","enum":["trend_oracle","bayesian_trend","reversals","ceres","enzo","llnr","perpkit"]},"name":{"type":"string"},"available_in":{"type":"array","items":{"type":"string","enum":["universe","alerts"]},"description":"The signal feeds (tiers of /api/v1/signals) that log this indicator’s events."},"status":{"type":"string","enum":["stable","experimental","legacy"]},"appears_in":{"type":"array","items":{"type":"string"},"description":"Every endpoint whose responses include this indicator."},"note":{"type":"string"}}},"LegacyIndicatorKey":{"type":"object","required":["id","status","alias_of","appears_in","note"],"properties":{"id":{"type":"string","enum":["dexter"]},"name":{"type":"string"},"status":{"type":"string","const":"legacy"},"alias_of":{"type":"string","enum":["trend_oracle","bayesian_trend","reversals","ceres","enzo","llnr","perpkit"],"description":"The current indicator to use instead."},"renamed_on":{"type":"string","format":"date"},"appears_in":{"type":"array","items":{"type":"string"}},"note":{"type":"string"}}},"CatalogResponse":{"type":"object","properties":{"generated_at":{"type":"string","format":"date-time"},"note":{"type":"string"},"assets":{"type":"object","properties":{"total":{"type":"integer"},"crypto":{"type":"integer"},"equities_and_metals":{"type":"integer"},"list":{"type":"array","items":{"type":"object","properties":{"ticker":{"type":"string"},"name":{"type":"string"},"asset_class":{"type":"string","enum":["crypto","stock","metal"]}}}}}},"timeframes":{"type":"array","items":{"type":"string","enum":["1h","4h","1d"]}},"indicators":{"type":"array","items":{"$ref":"#/components/schemas/CatalogIndicator"}},"indicator_fields":{"type":"object","additionalProperties":{"type":"string"}},"indicator_statuses":{"type":"object","additionalProperties":{"type":"string"},"description":"What each status promises."},"legacy_indicator_keys":{"type":"array","items":{"$ref":"#/components/schemas/LegacyIndicatorKey"}},"tiers":{"type":"object","description":"Per-tier coverage: universe (state_indicators, state_readings, indicators logged as changes) and alerts."},"coverage":{"type":"object","description":"Configured coverage for every asset × tier × timeframe.","properties":{"note":{"type":"string"},"indicators_by_tier":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"assets":{"type":"array","items":{"type":"object","properties":{"ticker":{"type":"string"},"asset_class":{"type":"string","enum":["crypto","stock","metal"]},"universe":{"type":"array","items":{"type":"string","enum":["1h","4h","1d"]},"description":"Timeframes the universe tier computes for this asset."},"alerts":{"type":"array","items":{"type":"string","enum":["1h","4h","1d"]},"description":"Timeframes the alerts tier computes for this asset; empty means universe only."}}}}}},"cadence":{"type":"object"}}}}}}