{"openapi":"3.0.0","paths":{"/api/health":{"get":{"operationId":"HealthController_health","parameters":[],"responses":{"200":{"description":""}},"tags":["Health"]}},"/api/health/ready":{"get":{"operationId":"HealthController_ready","parameters":[],"responses":{"200":{"description":""}},"tags":["Health"]}},"/api/access-request":{"post":{"operationId":"AccessRequestController_register","parameters":[],"responses":{"201":{"description":""}},"tags":["access-request"]}},"/api/metrics/catalog":{"get":{"operationId":"MetricsController_getCatalog","parameters":[],"responses":{"200":{"description":"The catalog of all available metrics, each marked with the minimum plan (minPlan) that unlocks it so the UI can show gated metrics locked."}},"tags":["metrics"]}},"/api/data/series":{"get":{"operationId":"DataController_series","parameters":[{"name":"exchange","required":true,"in":"query","schema":{"type":"string"}},{"name":"symbol","required":true,"in":"query","schema":{"type":"string"}},{"name":"metric","required":true,"in":"query","schema":{"type":"string"}},{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}},{"name":"interval","required":true,"in":"query","schema":{"type":"string","enum":["1m","5m","1h","1d"]}}],"responses":{"200":{"description":"Time-bucketed series of one metric for an exchange/symbol pair, gated by the caller plan (venues, granularity, history, columns, rows). Each element of `points` also carries `quality` (the row-quality bitmask ORed across the native windows in that bucket) and `qualityAll` (the same ANDed) as integers — per point, not a parallel array."}},"tags":["data"]}},"/api/data/latest":{"get":{"operationId":"DataController_latest","parameters":[{"name":"exchange","required":true,"in":"query","schema":{"type":"string"}},{"name":"symbol","required":true,"in":"query","schema":{"type":"string"}},{"name":"columns","required":false,"in":"query","description":"Comma-separated metric keys to return. Columns outside your plan are omitted and named in the X-BlackForge-Columns-Omitted response header.","schema":{"type":"string"}}],"responses":{"200":{"description":"Most recent completed bucket at the caller plan granularity, with only the plan-entitled columns. When `qualityFlags` is in the projection the response also carries a `quality` object as a SIBLING of `values` (never inside it): { raw, all, flags, contaminates, observedAt }."}},"tags":["data"]}},"/api/data/top":{"get":{"operationId":"DataController_top","parameters":[{"name":"metric","required":true,"in":"query","schema":{"type":"string"}},{"name":"exchange","required":false,"in":"query","schema":{"type":"string"}},{"name":"window","required":true,"in":"query","schema":{"type":"string","enum":["1h","24h","7d"]}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"type":"number"}}],"responses":{"200":{"description":"Top symbols ranked by a metric within a time window, gated by the caller plan (columns, venues, rows)."}},"tags":["data"]}},"/api/usage":{"get":{"operationId":"UsageController_recent","parameters":[],"responses":{"200":{"description":""}},"tags":["usage"]}},"/api/reference/exchanges":{"get":{"operationId":"ReferenceController_exchanges","parameters":[],"responses":{"200":{"description":"Distinct exchanges present in snapshots."}},"tags":["reference"]}},"/api/reference/symbols":{"get":{"operationId":"ReferenceController_symbols","parameters":[{"name":"exchange","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Distinct symbols for a given exchange, as a flat array of strings. On kraken the list also carries the BTC/DOGE alias spellings of the XBT/XDG pairs, which are accepted on input everywhere a symbol is; responses always echo the spelling that was sent."}},"tags":["reference"]}},"/api/dashboard":{"get":{"operationId":"DashboardController_get","parameters":[],"responses":{"200":{"description":""}},"tags":["dashboard"]},"put":{"operationId":"DashboardController_save","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveDashboardDto"}}}},"responses":{"200":{"description":""}},"tags":["dashboard"]}},"/api/keys":{"get":{"operationId":"ApikeysController_list","parameters":[],"responses":{"200":{"description":""}},"tags":["keys"]},"post":{"operationId":"ApikeysController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyDto"}}}},"responses":{"201":{"description":""}},"tags":["keys"]}},"/api/keys/{id}":{"delete":{"operationId":"ApikeysController_revoke","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["keys"]}},"/api/me":{"get":{"operationId":"MeController_get","parameters":[],"responses":{"200":{"description":""}},"tags":["me"]},"patch":{"operationId":"MeController_patch","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeDto"}}}},"responses":{"200":{"description":""}},"tags":["me"]},"delete":{"operationId":"MeController_deleteAccount","parameters":[],"responses":{"409":{"description":"The account still has an active paid subscription; cancel it in Billing first."}},"summary":"Delete the current user account","tags":["me"]}},"/api/me/referral":{"patch":{"description":"Idempotent: a second call overwrites the previous answer and re-stamps `referralAnsweredAt`. `GET /me` exposes only that timestamp, which is what the dashboard gates its modal on.","operationId":"MeController_setReferral","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateReferralDto"}}}},"responses":{"200":{"description":""}},"summary":"Record where the customer heard about BlackForge","tags":["me"]}},"/api/me/session":{"post":{"operationId":"MeController_recordSession","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionPointerDto"}}}},"responses":{"204":{"description":"Pointer stored."}},"summary":"Record a session-replay pointer for this user","tags":["me"]}},"/api/billing/checkout":{"post":{"operationId":"BillingController_checkout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutDto"}}}},"responses":{"201":{"description":""}},"tags":["billing"]}},"/api/billing/portal":{"post":{"operationId":"BillingController_portal","parameters":[],"responses":{"201":{"description":""}},"tags":["billing"]}},"/api/billing/summary":{"get":{"operationId":"BillingController_summary","parameters":[],"responses":{"200":{"description":""}},"tags":["billing"]}},"/api/me/attribution":{"post":{"operationId":"AttributionController_record","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordAttributionDto"}}}},"responses":{"204":{"description":"Accepted (set once)."}},"summary":"Record the campaign that brought this account","tags":["me"]}},"/api/v1/catalog":{"get":{"operationId":"V1Controller_catalog","parameters":[],"responses":{"200":{"description":"Full venue and metric catalog served to agents, each entry marked with the minimum plan that unlocks it. Returns an object, { venues, metrics, availableFrom }. Every metric lists the series `intervals` it is served at (1m, 5m, 1h, 1d; the plan decides which a key may use), and the metrics whose coarser values are rollups of minute values carry a one-sentence `rollupNote`. `availableFrom[\"1m\"]` is when the first one-minute row was written (ISO 8601), or null before any. `availableFrom.bookOnBoundary` maps every venue to the first window end (ISO 8601) whose order book was read as it stood at that instant, which such a row marks with `bookObservedAt` equal to `ts`, or null while none has been; the order book columns carry a one-sentence `bookTimingNote`. THE QUALITY BIT TABLE LIVES HERE: the `qualityFlags` entry in `metrics` carries the whole 16-bit taxonomy inline as `bits` — bit index, mask, name, description, the metric families each bit calls into question, and whether anything currently sets it. This API is the SERVER of that taxonomy; fetch it, never re-declare it. There is deliberately no second route serving the same table."}},"security":[{"apiKey":[]}],"tags":["v1"]}},"/api/v1/credits":{"post":{"operationId":"V1Controller_credits","parameters":[],"responses":{"200":{"description":"Buy prepaid calls with one USDC payment. Send a settled x402 payment in the PAYMENT-SIGNATURE header; the response reports the wallet balance in calls."}},"security":[{"apiKey":[]}],"tags":["v1"]}},"/api/v1/series":{"get":{"operationId":"V1Controller_series","parameters":[{"name":"exchange","required":true,"in":"query","schema":{"type":"string"}},{"name":"symbol","required":true,"in":"query","schema":{"type":"string"}},{"name":"metric","required":true,"in":"query","schema":{"type":"string"}},{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}},{"name":"interval","required":true,"in":"query","schema":{"type":"string","enum":["1m","5m","1h","1d"]}}],"responses":{"200":{"description":"Time-bucketed series of one metric for an exchange/symbol pair, gated by the key plan (venues, granularity, history, columns, rows). ROW QUALITY: every element of `points` carries two extra integers beside `ts` and `value` — `quality`, the row-quality bitmask ORed across the native 5-minute windows in that bucket (dirty if ANY window was), and `qualityAll`, the same bitmask ANDed (set only where EVERY window was). They are per point, not a parallel array. Decode them with the `bits` table on the `qualityFlags` entry of GET /v1/catalog. Nothing is ever hidden, filtered or nulled on account of quality: values are exactly as measured and these two integers tell you which to trust. A column a row never measured is null: a row rebuilt from the venue trade archive (TRADES_BACKFILLED) serves its book, enrichment and context columns as null, and coarser buckets average only the rows that measured them."}},"security":[{"apiKey":[]}],"tags":["v1"]}},"/api/v1/latest":{"get":{"operationId":"V1Controller_latest","parameters":[{"name":"exchange","required":true,"in":"query","schema":{"type":"string"}},{"name":"symbol","required":true,"in":"query","schema":{"type":"string"}},{"name":"columns","required":false,"in":"query","description":"Comma-separated metric keys to return. Columns outside your plan are omitted and named in the X-BlackForge-Columns-Omitted response header.","schema":{"type":"string"}}],"responses":{"200":{"description":"Most recent completed bucket at the key plan granularity, with only the plan-entitled columns. ROW QUALITY: whenever `qualityFlags` is in the projection the response carries a `quality` object as a SIBLING of `values` (never inside it) — { raw, all, flags, contaminates, observedAt }. `raw` is the bitmask ORed across the bucket and `all` the same ANDed; `flags` names the bits set in `raw`; `contaminates` lists the metric families they call into question, so a broken order book can leave the trade columns of the same row sound; `observedAt` is epoch ms for when the book was actually read. It is present on a clean row too, as raw 0 with empty lists. The raw integer also stays in `values.qualityFlags` as a plain number. Decode with the `bits` table on the `qualityFlags` entry of GET /v1/catalog."}},"security":[{"apiKey":[]}],"tags":["v1"]}},"/api/v1/symbols":{"get":{"operationId":"V1Controller_symbols","parameters":[{"name":"exchange","required":true,"in":"query","description":"Venue whose traded symbols to list, e.g. \"binance\".","schema":{"example":"binance","type":"string"}}],"responses":{"200":{"description":"Distinct symbols traded on one venue, ordered alphabetically, as a flat array of strings. Gated by the key plan (an unentitled venue returns 403); metadata only, not metered. KRAKEN ALIASES: kraken spells bitcoin XBT and dogecoin XDG, so this list also contains the BTC/DOGE spellings of those pairs (BTCUSD alongside XBTUSD, ETHBTC alongside ETHXBT) and both spellings are accepted as the `symbol` of any data request. LIMITS: the responses always echo the spelling YOU sent, never a rewritten one, and the `quoteAsset` COLUMN still reads \"XBT\" on the kraken pairs quoted in bitcoin. This is a single venue renaming two assets and nothing more: it does NOT make one symbol string work across venues, because the nine venues use three different separator conventions (dash on coinbase/kucoin/okx, underscore on gate, none elsewhere)."}},"security":[{"apiKey":[]}],"tags":["v1"]}},"/api/v1/usage":{"get":{"operationId":"V1Controller_usage","parameters":[],"responses":{"200":{"description":"The calling key's own row usage over the last 30 days ({ days: [{ date, count, lastAt }] }), plus rowsRemaining on this month's meter for metered plans."}},"security":[{"apiKey":[]}],"tags":["v1"]}},"/api/contact":{"post":{"operationId":"SubmissionsController_contact","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDto"}}}},"responses":{"201":{"description":""}},"tags":["submissions"]}},"/api/request-access":{"post":{"operationId":"SubmissionsController_requestAccess","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestAccessDto"}}}},"responses":{"201":{"description":""}},"tags":["submissions"]}},"/api/access/status":{"get":{"operationId":"AccessController_status","parameters":[],"responses":{"200":{"description":""}},"tags":["access"]}},"/api/admin/users":{"get":{"operationId":"AdminController_users","parameters":[],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/users/{sub}/plan":{"put":{"operationId":"AdminController_setUserPlan","parameters":[{"name":"sub","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetUserPlanDto"}}}},"responses":{"200":{"description":""}},"tags":["admin"]},"delete":{"operationId":"AdminController_clearUserPlan","parameters":[{"name":"sub","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/users/{sub}/sessions":{"get":{"operationId":"AdminController_userSessions","parameters":[{"name":"sub","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/users/{sub}/usage":{"get":{"operationId":"AdminController_userUsage","parameters":[{"name":"sub","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/users/{sub}/activity":{"get":{"operationId":"AdminController_userActivity","parameters":[{"name":"sub","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/activity":{"get":{"operationId":"AdminController_activityFeed","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"days","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/access-requests":{"get":{"operationId":"AdminController_accessRequestList","parameters":[],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/access":{"post":{"operationId":"AdminController_grantAccess","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantAccessDto"}}}},"responses":{"201":{"description":""}},"tags":["admin"]}},"/api/admin/access-requests/{email}":{"patch":{"operationId":"AdminController_setAccessRequestStatus","parameters":[{"name":"email","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetAccessRequestStatusDto"}}}},"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/admins":{"get":{"operationId":"AdminController_admins","parameters":[],"responses":{"200":{"description":""}},"tags":["admin"]},"post":{"operationId":"AdminController_grantAdmin","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantAdminDto"}}}},"responses":{"201":{"description":""}},"tags":["admin"]}},"/api/admin/admins/{email}":{"delete":{"operationId":"AdminController_revokeAdmin","parameters":[{"name":"email","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/admin/stats":{"get":{"operationId":"AdminController_getStats","parameters":[],"responses":{"200":{"description":""}},"tags":["admin"]}},"/api/agent/entitlements":{"get":{"operationId":"AgentController_entitlements","parameters":[],"responses":{"200":{"description":"The caller's plan entitlements and both meters, plus the agent tool surface this plan may be offered. Each tool carries how it is charged against the monthly row quota."}},"tags":["agent"]}},"/api/agent/meter/tool":{"post":{"operationId":"AgentController_meterTool","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterToolDto"}}}},"responses":{"200":{"description":"Row-equivalents charged for one agent tool call, plus the rows left on this month’s meter. 403 when the plan does not include the tool, 402 when the row quota is exhausted."}},"tags":["agent"]}},"/api/agent/meter/tokens":{"post":{"operationId":"AgentController_meterTokens","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterTokensDto"}}}},"responses":{"200":{"description":"Tokens recorded for one agent turn, priced at the model’s published rate, with the tokens left in this month’s allowance. 402 when the allowance is exhausted."}},"tags":["agent"]}},"/api/v1/formulas/validate":{"post":{"description":"Returns the normalised source, the pairs, the primitives, the horizon and the cost in rows (1 row per referenced pair per evaluation). A refusal is one located error object a model can correct. Trade and book-change terms cover the w seconds ending one second before the value's timestamp; book terms are read at the timestamp. Ultra only; any other plan gets 403 {\"error\":{\"code\":\"plan_required\",\"plan\":\"ultra\",\"upgrade\":\"https://blackforge.so/pricing\"}}.","operationId":"FormulasController_validate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateFormulaDto"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Check a formula without saving it. Free, not metered.","tags":["formulas"]}},"/api/v1/formulas":{"post":{"operationId":"FormulasController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFormulaDto"}}}},"responses":{"201":{"description":"Saves a formula. Values are computed server-side every interval while it is streamed or behind an active notification, charged 1 row per referenced pair per evaluation. Trade and book-change terms cover the w seconds ending one second before the value's timestamp; book terms are read at the timestamp. Ultra only; any other plan gets 403 {\"error\":{\"code\":\"plan_required\",\"plan\":\"ultra\",\"upgrade\":\"https://blackforge.so/pricing\"}}."}},"security":[{"apiKey":[]}],"tags":["formulas"]},"get":{"operationId":"FormulasController_list","parameters":[],"responses":{"200":{"description":"Your formulas, your plan's realtime limits and what is in use. Ultra only; any other plan gets 403 {\"error\":{\"code\":\"plan_required\",\"plan\":\"ultra\",\"upgrade\":\"https://blackforge.so/pricing\"}}."}},"security":[{"apiKey":[]}],"tags":["formulas"]}},"/api/v1/formulas/{id}":{"get":{"operationId":"FormulasController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"One formula. Trade and book-change terms cover the w seconds ending one second before the value's timestamp; book terms are read at the timestamp. Ultra only; any other plan gets 403 {\"error\":{\"code\":\"plan_required\",\"plan\":\"ultra\",\"upgrade\":\"https://blackforge.so/pricing\"}}."}},"security":[{"apiKey":[]}],"tags":["formulas"]},"delete":{"operationId":"FormulasController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Deletes a formula and every notification on it; the count is in the x-blackforge-deleted-alerts header."}},"security":[{"apiKey":[]}],"tags":["formulas"]}},"/api/v1/alerts":{"post":{"operationId":"FormulasController_createAlert","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAlertDto"}}}},"responses":{"201":{"description":"Tells you when your formula crosses the value you set, by email to the account address and/or a signed https webhook. The webhook secret is returned once. Ultra only; any other plan gets 403 {\"error\":{\"code\":\"plan_required\",\"plan\":\"ultra\",\"upgrade\":\"https://blackforge.so/pricing\"}}."}},"security":[{"apiKey":[]}],"tags":["formulas"]},"get":{"operationId":"FormulasController_listAlerts","parameters":[],"responses":{"200":{"description":"Your formula notifications and the limit."}},"security":[{"apiKey":[]}],"tags":["formulas"]}},"/api/v1/alerts/{id}":{"patch":{"operationId":"FormulasController_patchAlert","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchAlertDto"}}}},"responses":{"200":{"description":"Pause or resume a notification."}},"security":[{"apiKey":[]}],"tags":["formulas"]},"delete":{"operationId":"FormulasController_removeAlert","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Deletes a notification."}},"security":[{"apiKey":[]}],"tags":["formulas"]}},"/api/v1/alerts/{id}/events":{"get":{"operationId":"FormulasController_events","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The last 50 deliveries of one notification."}},"security":[{"apiKey":[]}],"tags":["formulas"]}},"/api/agent/formulas/validate":{"post":{"operationId":"AgentFormulasController_validate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateFormulaDto"}}}},"responses":{"200":{"description":"Check a formula without saving it. Trade and book-change terms cover the w seconds ending one second before the value's timestamp; book terms are read at the timestamp."}},"security":[{"bearer":[]}],"tags":["agent"]}},"/api/agent/formulas":{"post":{"operationId":"AgentFormulasController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFormulaDto"}}}},"responses":{"200":{"description":"Save a formula. Trade and book-change terms cover the w seconds ending one second before the value's timestamp; book terms are read at the timestamp."}},"security":[{"bearer":[]}],"tags":["agent"]},"get":{"operationId":"AgentFormulasController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]}},"/api/agent/formulas/{id}":{"get":{"operationId":"AgentFormulasController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]},"delete":{"operationId":"AgentFormulasController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]}},"/api/agent/realtime-alerts":{"post":{"operationId":"AgentFormulasController_createAlert","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentAlertDto"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]},"get":{"operationId":"AgentFormulasController_listAlerts","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]}},"/api/agent/realtime-alerts/{id}":{"patch":{"operationId":"AgentFormulasController_patchAlert","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchAlertDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]},"delete":{"operationId":"AgentFormulasController_removeAlert","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]}},"/api/agent/realtime-alerts/{id}/events":{"get":{"operationId":"AgentFormulasController_events","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]}},"/api/agent/realtime/ticket":{"post":{"operationId":"AgentFormulasController_ticket","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["agent"]}}},"info":{"title":"BlackForge Data API","description":"Market-metrics API for the BlackForge dashboard and autonomous agents. Authenticated dashboard routes use a bearer JWT; metered agent routes under /api/v1 use an x-api-key header.","version":"1.0","contact":{}},"tags":[],"servers":[],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key"},"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"SeriesDto":{"type":"object","properties":{"metricKey":{"type":"string"},"exchange":{"type":"string"},"symbol":{"type":"string"},"label":{"type":"string"},"color":{"type":"string","example":"#4c9dff","pattern":"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"}},"required":["metricKey","exchange","symbol"]},"ChartDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"series":{"type":"array","items":{"$ref":"#/components/schemas/SeriesDto"}},"timeframe":{"type":"string","enum":["1h","24h","7d","30d","90d","1y"]},"granularity":{"type":"string","enum":["1m","5m","1h","1d"]},"chartType":{"type":"string","enum":["line","area","bar","candlestick"]},"stacked":{"type":"boolean"},"fullWidth":{"type":"boolean"},"group":{"type":"string"}},"required":["id","title","series","timeframe","chartType"]},"ChartGroupDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"collapsed":{"type":"boolean"}},"required":["id","title"]},"SaveDashboardDto":{"type":"object","properties":{"charts":{"type":"array","items":{"$ref":"#/components/schemas/ChartDto"}},"groups":{"type":"array","items":{"$ref":"#/components/schemas/ChartGroupDto"}}},"required":["charts"]},"CreateApiKeyDto":{"type":"object","properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}},"required":["name"]},"SettingsDto":{"type":"object","properties":{"timezone":{"type":"string"},"defaultExchange":{"type":"string"},"defaultView":{"type":"string","enum":["overview","board"]},"watchlist":{"maxItems":100,"description":"Tracked pairs as EXCHANGE:SYMBOL. At most 100 entries.","type":"array","items":{"type":"string"}},"analyticsOptOut":{"type":"boolean","description":"True when the customer has objected to product analytics and session replay in the console. Absent means they have not objected; it is not written until they act."}}},"UpdateMeDto":{"type":"object","properties":{"displayName":{"type":"string"},"settings":{"$ref":"#/components/schemas/SettingsDto"}}},"UpdateReferralDto":{"type":"object","properties":{"sources":{"type":"array","maxItems":11,"description":"Where the customer heard about BlackForge. Non-empty, no duplicates, at most 11 entries (i.e. every option once).","items":{"type":"string","enum":["x","google","ai-assistant","reddit","youtube","hacker-news","product-hunt","github","discord-telegram","word-of-mouth","other"]}},"other":{"type":"string","maxLength":280,"description":"Free text, meaningful only when `sources` includes 'other'. Stored verbatim and shown only in the admin panel."}},"required":["sources"]},"CreateSessionPointerDto":{"type":"object","properties":{"sessionId":{"type":"string","maxLength":128,"description":"The PostHog session id. Charset [A-Za-z0-9_-] only — it is echoed into a replay URL in our own admin panel."},"startedAt":{"type":"string","description":"When the recording started, ISO 8601.","example":"2026-08-11T09:15:00.000Z"},"path":{"type":"string","maxLength":512,"description":"The console route the session started on. Display only."}},"required":["sessionId","startedAt"]},"CreateCheckoutDto":{"type":"object","properties":{"plan":{"type":"string","enum":["pro","max","ultra"]},"interval":{"type":"string","enum":["month","year"]}},"required":["plan","interval"]},"RecordAttributionDto":{"type":"object","properties":{"source":{"type":"string","maxLength":100,"description":"utm_source of the visit that led to the signup."},"medium":{"type":"string","maxLength":100,"description":"utm_medium."},"campaign":{"type":"string","maxLength":100,"description":"utm_campaign."},"content":{"type":"string","maxLength":100,"description":"utm_content."},"term":{"type":"string","maxLength":100,"description":"utm_term."},"gclid":{"type":"string","maxLength":256,"description":"Google Ads click id. Stored only with marketingConsent."},"gbraid":{"type":"string","maxLength":256,"description":"Google Ads iOS click id (app-to-web)."},"wbraid":{"type":"string","maxLength":256,"description":"Google Ads iOS click id (web-to-app)."},"twclid":{"type":"string","maxLength":256,"description":"X Ads click id."},"marketingConsent":{"type":"boolean","description":"True only when the visitor allowed the Marketing category on blackforge.so. Without it every click id is discarded and only the campaign tags are kept."}}},"ContactDto":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"message":{"type":"string"},"_hp":{"type":"string"}},"required":["email","message"]},"RequestAccessDto":{"type":"object","properties":{"email":{"type":"string"},"_hp":{"type":"string"}},"required":["email"]},"SetUserPlanDto":{"type":"object","properties":{"plan":{"type":"string","enum":["free","pro","max","ultra"]},"note":{"type":"string"},"expiresInDays":{"type":"number","minimum":1,"maximum":31}},"required":["plan"]},"GrantAccessDto":{"type":"object","properties":{}},"SetAccessRequestStatusDto":{"type":"object","properties":{}},"GrantAdminDto":{"type":"object","properties":{}},"MeterToolDto":{"type":"object","properties":{"tool":{"type":"string","enum":["explain_metric","resolve_symbol","show_chart","create_alert","get_entitlements","query","get_latest","get_series_digest","get_book","get_book_multi","simulate_fill","watch_book","watch_trades","get_spread_map","stats","correlate","convert","aggregate","calc","web_search","validate_formula","create_formula","list_formulas","watch_formula","create_formula_alert","delete_formula","delete_formula_alert"]},"pairs":{"type":"number","description":"`get_latest`: pairs resolved."},"rows":{"type":"number","description":"`query`: rows returned."},"rowsScanned":{"type":"number","description":"`get_series_digest`: rows scanned, not rows returned."},"levels":{"type":"number","description":"Ladder levels read, per venue."},"exchanges":{"type":"number","description":"Venues the call touched."},"prints":{"type":"number","description":"`watch_trades`: prints delivered."},"deltas":{"type":"number","description":"`watch_book`: level deltas delivered."}},"required":["tool"]},"MeterTokensDto":{"type":"object","properties":{"inputTokens":{"type":"number","description":"Prompt tokens this turn consumed."},"outputTokens":{"type":"number","description":"Completion tokens this turn produced."},"model":{"type":"string","description":"Bedrock model id. An id with no published rate is priced at the most expensive rate we know, never free."}}},"ValidateFormulaDto":{"type":"object","properties":{"expr":{"type":"string","description":"The formula source, e.g. `mid(binance:BTCUSDT) - mid(okx:BTC-USDT)`. Trade and book-change terms cover the w seconds ending one second before the value's timestamp; book terms are read at the timestamp."},"interval":{"type":"number","description":"Update interval in whole seconds, 1 to 60. Default 1."}},"required":["expr"]},"CreateFormulaDto":{"type":"object","properties":{"expr":{"type":"string","description":"The formula source, e.g. `mid(binance:BTCUSDT) - mid(okx:BTC-USDT)`. Trade and book-change terms cover the w seconds ending one second before the value's timestamp; book terms are read at the timestamp."},"interval":{"type":"number","description":"Update interval in whole seconds, 1 to 60. Default 1."},"name":{"type":"string","description":"A name you choose, 1 to 80 characters."},"ephemeral":{"type":"boolean","description":"Keep it for one hour only, outside your saved-formula count. For \"watch this for a minute\"."}},"required":["expr","name"]},"CreateAlertDto":{"type":"object","properties":{"formulaId":{"type":"string","description":"The formula to watch (`fml_…`)."},"direction":{"type":"string","enum":["above","below","cross"]},"threshold":{"type":"number","description":"The value you set."},"hysteresis":{"type":"number","description":"How far back across the value the formula must move before it can notify again. Absolute, default 0."},"cooldownSeconds":{"type":"number","description":"Seconds before it can notify again, 60 to 86400. Default 900."},"email":{"type":"boolean","description":"Email the account's verified address. Default true. The address is never an argument."},"webhookUrl":{"type":"string","description":"An https URL we POST each notification to, signed with `x-blackforge-signature`. API keys only."}},"required":["formulaId","direction","threshold"]},"PatchAlertDto":{"type":"object","properties":{"status":{"type":"string","enum":["active","paused"]}},"required":["status"]},"CreateAgentAlertDto":{"type":"object","properties":{"formulaId":{"type":"string","description":"The formula to watch (`fml_…`)."},"direction":{"type":"string","enum":["above","below","cross"]},"threshold":{"type":"number","description":"The value you set."},"hysteresis":{"type":"number","description":"How far back across the value the formula must move before it can notify again. Absolute, default 0."},"cooldownSeconds":{"type":"number","description":"Seconds before it can notify again, 60 to 86400. Default 900."},"email":{"type":"boolean","description":"Email the account's verified address. Default true. The address is never an argument."}},"required":["formulaId","direction","threshold"]},"TicketDto":{"type":"object","properties":{"src":{"type":"string","enum":["agent","console"]}}}}}}