{
  "openapi": "3.2.0",
  "info": {
    "title": "Kelly API",
    "version": "2026-10-05",
    "summary": "Kelly's fantasy and betting numbers, computed by its engine.",
    "description": "Kelly answers fantasy and betting questions from live engine data. Cite the as-of time with every number. Odds are information, not advice, so rank no picks, keep the cost line on wagering results and, when a result carries accept_url, show the person that link to finish the step on askkelly.ai.\n\nEvery number carries its source and as-of time. Off the site, results carry engine-computed values only; per-book prices and Kelly's own probability are not served here.\n\nVersions: send `Api-Version: YYYY-MM-DD`; omitted means 2026-10-05. Errors are RFC 9457 problems with a stable `code`: `invalid_request` (400), `credential_in_url` (400), `unsupported_api_version` (400), `unauthenticated` (401), `not_entitled` (403), `attestation_required` (403), `insufficient_scope` (403), `not_found` (404), `method_not_allowed` (405), `idempotency_in_progress` (409), `version_sunset` (410), `answer_expired` (410), `operation_expired` (410), `precondition_failed` (412), `payload_too_large` (413), `idempotency_key_reused` (422), `missing_execution_input` (422), `invalid_inputs` (422), `not_stake_eligible` (422), `ref_not_allowed_in_batch` (422), `formula_explain_only` (422), `calc_not_available` (422), `needs_jurisdiction` (422), `needs_operator` (422), `needs_league` (422), `rate_limited` (429), `quota_exhausted` (429), `internal_error` (500), `upstream_error` (502), `service_unavailable` (503), `upstream_timeout` (504), `too_many_topics` (403).",
    "contact": {
      "name": "Kelly",
      "url": "https://askkelly.ai",
      "email": "ops@askkelly.ai"
    }
  },
  "servers": [
    {
      "url": "https://api.askkelly.ai",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "status",
      "summary": "Status and usage",
      "description": "Check that the API answers and see who it thinks you are and what you have left."
    },
    {
      "name": "search",
      "summary": "Search",
      "description": "Find the ID of a player, team, game, market, league or reference topic from its name."
    },
    {
      "name": "games-and-markets",
      "summary": "Games and markets",
      "description": "Read a game's state, and a market's consensus prices and no-vig probabilities, each with its source and time."
    },
    {
      "name": "players",
      "summary": "Players",
      "description": "Read a player's projection, range and status, and compare players side by side."
    },
    {
      "name": "calculations",
      "summary": "Calculations",
      "description": "Run betting and fantasy maths on your own numbers: odds, no-vig, value, parlays, hedges, stakes and records."
    },
    {
      "name": "formulas-and-strategies",
      "summary": "Formulas and strategies",
      "description": "Look up the formulas, strategies and recipes behind the calculations."
    },
    {
      "name": "reference",
      "summary": "Rules, laws and help",
      "description": "Look up sourced, dated rules, laws, tax, operator policies, terms and help."
    },
    {
      "name": "answers",
      "summary": "Answers",
      "description": "Ask a question in words and get a sourced answer, and the method behind any number."
    },
    {
      "name": "leagues",
      "summary": "Leagues",
      "description": "Read the fantasy leagues a person has connected."
    },
    {
      "name": "simulations",
      "summary": "Simulations",
      "description": "Simulate seasons, playoffs, trades, draft slots and contests."
    },
    {
      "name": "wire",
      "summary": "Kelly Wire",
      "description": "Follow games, markets and answers as they change, over Kelly Wire."
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "get_health",
        "summary": "Check that the service answers",
        "description": "Always 200 while the process answers; the body says which dependencies are reachable. Served by both the API and the Wire service.",
        "tags": [
          "status"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "$ref": "#/components/parameters/ClientRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The service answers.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "Api-Version": {
                "$ref": "#/components/headers/Api-Version"
              },
              "Client-Request-Id": {
                "$ref": "#/components/headers/Client-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                },
                "examples": {
                  "ok": {
                    "summary": "The service answers",
                    "value": {
                      "object": "health",
                      "ok": true,
                      "service": "kelly-api",
                      "version": "unknown",
                      "db": true,
                      "redis": true,
                      "migrated": true
                    },
                    "x-captured-at": "2026-10-04T17:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        },
        "x-status": "live",
        "x-group": "status",
        "x-errors": [
          "invalid_request",
          "unsupported_api_version",
          "internal_error"
        ],
        "x-price": {
          "credits": 0,
          "per": "call",
          "formula": null,
          "unit": "one cached engine read",
          "note": "Nothing is charged during the beta: credits count against your plan's allowance only."
        },
        "x-public": true,
        "x-mcp-tool": null
      }
    },
    "/v1/status": {
      "get": {
        "operationId": "get_status",
        "summary": "See who is calling and what is left",
        "description": "Your credential, mode, plan and scopes, your rate-limit window and your credits for the period, with the same values in the response headers. It costs nothing and is never refused for quota, so it is the call to make first, with or without a key.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "$ref": "#/components/parameters/ClientRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The caller as the API sees it.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "Api-Version": {
                "$ref": "#/components/headers/Api-Version"
              },
              "Client-Request-Id": {
                "$ref": "#/components/headers/Client-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Resource": {
                "$ref": "#/components/headers/X-RateLimit-Resource"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "X-Credits-Used": {
                "$ref": "#/components/headers/X-Credits-Used"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/X-Credits-Remaining"
              },
              "X-Credits-Last": {
                "$ref": "#/components/headers/X-Credits-Last"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                },
                "examples": {
                  "anonymous": {
                    "summary": "No key: the keyless tier",
                    "value": {
                      "object": "status",
                      "ok": true,
                      "api_version": "2026-10-05",
                      "credential": "anonymous",
                      "mode": "live",
                      "plan": "anonymous",
                      "account_id": null,
                      "scopes": [],
                      "rate": {
                        "policy": "anon-tool",
                        "limit": 60,
                        "remaining": 59,
                        "window_seconds": 600,
                        "reset": 1791135600,
                        "resource": "get_status"
                      },
                      "credits": {
                        "used": 0,
                        "remaining": 200,
                        "last": 0,
                        "allowance": 200,
                        "period": "d20261004",
                        "reset": 1791158400
                      }
                    },
                    "x-captured-at": "2026-10-04T17:30:00Z"
                  },
                  "test_key": {
                    "summary": "A test key on the Free plan",
                    "value": {
                      "object": "status",
                      "ok": true,
                      "api_version": "2026-10-05",
                      "credential": "key",
                      "mode": "test",
                      "plan": "free",
                      "account_id": "acct_n0y9wqdrz2b9fyx6vmsy",
                      "scopes": [
                        "read:odds",
                        "read:players",
                        "read:reference",
                        "run:answers",
                        "wire:connect"
                      ],
                      "rate": {
                        "policy": "free",
                        "limit": 120,
                        "remaining": 119,
                        "window_seconds": 60,
                        "reset": 1791135060,
                        "resource": "all"
                      },
                      "credits": {
                        "used": 0,
                        "remaining": 1000,
                        "last": 0,
                        "allowance": 1000,
                        "period": "m20261001",
                        "reset": 1793491200
                      }
                    },
                    "x-captured-at": "2026-10-04T17:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "405": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        },
        "tags": [
          "status"
        ],
        "security": [
          {},
          {
            "ApiKey": []
          }
        ],
        "x-status": "live",
        "x-group": "status",
        "x-errors": [
          "invalid_request",
          "credential_in_url",
          "unsupported_api_version",
          "unauthenticated",
          "method_not_allowed",
          "rate_limited",
          "internal_error",
          "service_unavailable"
        ],
        "x-price": {
          "credits": 0,
          "per": "call",
          "formula": null,
          "unit": "one cached engine read",
          "note": "Nothing is charged during the beta: credits count against your plan's allowance only."
        },
        "x-public": true,
        "x-mcp-tool": null
      }
    },
    "/v1/reference": {
      "get": {
        "operationId": "list_reference",
        "summary": "Search published reference entries",
        "description": "Rules, laws, tax tables, operators' policies, terms, guides and help, each sourced and dated. Searches by words (full text and close spellings) and filters by kind, jurisdiction, operator and product. Results are in relevance order only. Only published entries are listed.",
        "tags": [
          "reference"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "$ref": "#/components/parameters/ClientRequestId"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Words to search for, such as 'is sports betting legal in New Jersey' or 'moneyline'. Matches titles, other phrasings, summaries, text and facts, and tolerates misspellings. Without q, the list is every published entry matching the filters, in topic order.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 500
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Only entries of this kind.",
            "schema": {
              "type": "string",
              "enum": [
                "term",
                "rule",
                "law",
                "tax",
                "policy",
                "guide",
                "help"
              ]
            }
          },
          {
            "name": "jurisdiction",
            "in": "query",
            "required": false,
            "description": "Only entries for this ISO 3166 code, such as US, US-NJ or CA-ON (any case).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}(-[A-Za-z0-9]{1,3})?$"
            }
          },
          {
            "name": "operator",
            "in": "query",
            "required": false,
            "description": "Only entries about this operator's own rules or policies, by its slug.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]{1,39}$"
            }
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "description": "Only entries covering this product.",
            "schema": {
              "type": "string",
              "enum": [
                "sportsbook",
                "dfs",
                "pickem",
                "prediction_market",
                "sweepstakes",
                "season_fantasy",
                "horse_racing",
                "online_casino",
                "lottery",
                "exchange"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Entries per page, 1 to 25; default 10.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The next_cursor of the previous page. Send the same filters with it; changed filters are refused with invalid_request.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of published reference entries.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "Api-Version": {
                "$ref": "#/components/headers/Api-Version"
              },
              "Client-Request-Id": {
                "$ref": "#/components/headers/Client-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Resource": {
                "$ref": "#/components/headers/X-RateLimit-Resource"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "X-Credits-Used": {
                "$ref": "#/components/headers/X-Credits-Used"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/X-Credits-Remaining"
              },
              "X-Credits-Last": {
                "$ref": "#/components/headers/X-Credits-Last"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReferenceList"
                },
                "examples": {
                  "search": {
                    "summary": "Search by words",
                    "value": {
                      "object": "list",
                      "data": [
                        {
                          "object": "reference",
                          "id": "ref_rz6yhwpy7apph7cz588p",
                          "topic": "term/sample-moneyline",
                          "kind": "term",
                          "title": "Moneyline (sample entry)",
                          "summary": "A sample moneyline is a bet on which side wins outright, with no point spread.",
                          "jurisdiction": null,
                          "products": [
                            "sportsbook"
                          ],
                          "operator": null,
                          "version": 1,
                          "reviewed_at": "2026-09-01",
                          "review_due": "2027-09-01",
                          "stale": false,
                          "url": "https://askkelly.ai/learn/term/sample-moneyline"
                        }
                      ],
                      "has_more": false,
                      "next_cursor": null
                    },
                    "x-captured-at": "2026-10-04T17:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "405": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        },
        "security": [
          {},
          {
            "ApiKey": []
          }
        ],
        "x-status": "live",
        "x-group": "reference",
        "x-errors": [
          "invalid_request",
          "credential_in_url",
          "unsupported_api_version",
          "unauthenticated",
          "method_not_allowed",
          "rate_limited",
          "internal_error",
          "service_unavailable",
          "quota_exhausted"
        ],
        "x-price": {
          "credits": 1,
          "per": "call",
          "formula": null,
          "unit": "one cached engine read",
          "note": "Nothing is charged during the beta: credits count against your plan's allowance only."
        },
        "x-public": true,
        "x-mcp-tool": null
      }
    },
    "/v1/reference/{topic_id}": {
      "get": {
        "operationId": "get_reference",
        "summary": "Get a published reference entry",
        "description": "One published entry: its answer, text, facts with their sources, review dates and, for law and tax, its notice. A topic that is unknown or still in review is not found.",
        "tags": [
          "reference"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "$ref": "#/components/parameters/ClientRequestId"
          },
          {
            "name": "topic_id",
            "in": "path",
            "required": true,
            "description": "The topic's Kelly ID (ref_…), from a list or search result.",
            "schema": {
              "type": "string",
              "pattern": "^ref_[0123456789abcdefghjkmnpqrstvwxyz]{20}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The published entry.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "Api-Version": {
                "$ref": "#/components/headers/Api-Version"
              },
              "Client-Request-Id": {
                "$ref": "#/components/headers/Client-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Resource": {
                "$ref": "#/components/headers/X-RateLimit-Resource"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "X-Credits-Used": {
                "$ref": "#/components/headers/X-Credits-Used"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/X-Credits-Remaining"
              },
              "X-Credits-Last": {
                "$ref": "#/components/headers/X-Credits-Last"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reference"
                },
                "examples": {
                  "entry": {
                    "summary": "One published entry",
                    "value": {
                      "object": "reference",
                      "id": "ref_rz6yhwpy7apph7cz588p",
                      "topic": "term/sample-moneyline",
                      "kind": "term",
                      "title": "Moneyline (sample entry)",
                      "summary": "A sample moneyline is a bet on which side wins outright, with no point spread.",
                      "jurisdiction": null,
                      "products": [
                        "sportsbook"
                      ],
                      "operator": null,
                      "version": 1,
                      "reviewed_at": "2026-09-01",
                      "review_due": "2027-09-01",
                      "stale": false,
                      "url": "https://askkelly.ai/learn/term/sample-moneyline",
                      "as_of": "2026-09-02T09:05:00Z",
                      "source": "Kelly",
                      "seq": 1,
                      "stale_after_ms": null,
                      "attribution": null,
                      "request_id": "req_c3d612d481910879015c2ea7",
                      "live_topics": [
                        "reference:ref_rz6yhwpy7apph7cz588p"
                      ],
                      "rg_line": null,
                      "body": "Sample entry for tests. The price shows the payout for each side; the favourite pays less than the underdog. A moneyline settles on the final result under the operator's rules.",
                      "facts": [
                        {
                          "id": "f1",
                          "claim": "A sample moneyline pays on the outright winner, with no spread.",
                          "key": null,
                          "value": null,
                          "unit": null,
                          "product": null,
                          "operator": null,
                          "jurisdiction": null,
                          "sources": [
                            "s1"
                          ]
                        },
                        {
                          "id": "f2",
                          "claim": "The favourite's price pays less than the underdog's in this sample.",
                          "key": "favourite_pays_less",
                          "value": true,
                          "unit": null,
                          "product": null,
                          "operator": null,
                          "jurisdiction": null,
                          "sources": [
                            "s1"
                          ]
                        }
                      ],
                      "sources": [
                        {
                          "id": "s1",
                          "url": "https://example.org/sample/glossary/moneyline",
                          "title": "Sample glossary: moneyline",
                          "publisher": "Sample Regulator",
                          "primary": true,
                          "retrieved_at": "2026-09-01"
                        }
                      ],
                      "notice": null,
                      "published_at": "2026-09-02T09:05:00Z"
                    },
                    "x-captured-at": "2026-10-04T17:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "405": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        },
        "security": [
          {},
          {
            "ApiKey": []
          }
        ],
        "x-status": "live",
        "x-group": "reference",
        "x-errors": [
          "invalid_request",
          "credential_in_url",
          "unsupported_api_version",
          "unauthenticated",
          "method_not_allowed",
          "rate_limited",
          "internal_error",
          "service_unavailable",
          "quota_exhausted",
          "not_found"
        ],
        "x-price": {
          "credits": 1,
          "per": "call",
          "formula": null,
          "unit": "one cached engine read",
          "note": "Nothing is charged during the beta: credits count against your plan's allowance only."
        },
        "x-public": true,
        "x-mcp-tool": "get_explanation"
      }
    },
    "/v1/wire/tickets": {
      "post": {
        "operationId": "get_wire_ticket",
        "summary": "Get a Kelly Wire ticket",
        "description": "Mints a single-use ticket that lives 60 seconds, for the socket (wss://wire.askkelly.ai/v1, sent in the first message) or for one stream (GET /v1/stream/{topic}?ticket=). Needs an API key with the wire:connect scope, or an OAuth token. Stream tickets name their topics; socket tickets may name some (bound to them) or none. Each topic is still checked against the plan when it is opened.",
        "tags": [
          "wire"
        ],
        "security": [
          {},
          {
            "ApiKey": [
              "wire:connect"
            ]
          },
          {
            "OAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "$ref": "#/components/parameters/ClientRequestId"
          }
        ],
        "requestBody": {
          "required": false,
          "description": "What the ticket is for; an empty object asks for a socket ticket.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WireTicketRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The ticket. It is shown once; responses are never cached.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "Api-Version": {
                "$ref": "#/components/headers/Api-Version"
              },
              "Client-Request-Id": {
                "$ref": "#/components/headers/Client-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Resource": {
                "$ref": "#/components/headers/X-RateLimit-Resource"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "X-Credits-Used": {
                "$ref": "#/components/headers/X-Credits-Used"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/X-Credits-Remaining"
              },
              "X-Credits-Last": {
                "$ref": "#/components/headers/X-Credits-Last"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WireTicket"
                },
                "examples": {
                  "sse": {
                    "summary": "A stream ticket for one game, on the recorded Sunday",
                    "value": {
                      "object": "wire_ticket",
                      "ticket": "wtkt_nj7ftwgg0hbac03jg4wb",
                      "expires_at": "2026-10-04T17:31:00Z",
                      "topics": [
                        "game:gm_hwxy9tewzag7fee8hb01"
                      ],
                      "transport": "sse",
                      "mode": "test",
                      "url": "https://api.askkelly.ai/v1/stream/game:gm_hwxy9tewzag7fee8hb01"
                    },
                    "x-captured-at": "2026-10-04T17:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "403": {
            "$ref": "#/components/responses/Problem"
          },
          "405": {
            "$ref": "#/components/responses/Problem"
          },
          "413": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        },
        "x-status": "live",
        "x-group": "wire",
        "x-errors": [
          "invalid_request",
          "credential_in_url",
          "unsupported_api_version",
          "unauthenticated",
          "method_not_allowed",
          "rate_limited",
          "internal_error",
          "service_unavailable",
          "quota_exhausted",
          "insufficient_scope",
          "not_entitled",
          "payload_too_large",
          "too_many_topics"
        ],
        "x-price": {
          "credits": 1,
          "per": "call",
          "formula": null,
          "unit": "one cached engine read",
          "note": "Nothing is charged during the beta: credits count against your plan's allowance only."
        },
        "x-public": true,
        "x-mcp-tool": null
      }
    },
    "/v1/stream/{topic}": {
      "get": {
        "operationId": "stream_topic",
        "summary": "Stream one topic as server-sent events",
        "description": "One topic as server-sent events (STR-21), for widgets, simple clients and cards whose host blocks sockets. Each event's `id` is the topic's seq: first `event: snapshot` (the topic's state, its last seq, expected latency and liveness), then one event per change, named by its type (game.state, market.update, market.suspended, ...), each seq the previous + 1. Send `Last-Event-ID` to resume: missed changes replay while they are within the 120-second window, else a fresh snapshot comes first. A gap is answered with a fresh snapshot. A comment line pings every 20 seconds; `event: drain` comes 30 seconds before the stream's 24-hour limit, and `event: error` (a catalogue code) when the topic is no longer available, after which the stream ends. Off askkelly.ai streams carry engine-computed values only. Each open stream costs wire_topic_hour credits when it opens and each hour it stays open.",
        "tags": [
          "wire"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "$ref": "#/components/parameters/ClientRequestId"
          },
          {
            "name": "topic",
            "in": "path",
            "required": true,
            "description": "The topic, such as game:gm_… or market:mkt_… (the Wire topics in the AsyncAPI document).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z]+:[A-Za-z0-9_\\-/:]+$",
              "maxLength": 200
            }
          },
          {
            "name": "ticket",
            "in": "query",
            "required": false,
            "description": "A ticket minted for sse that names this topic (POST /v1/wire/tickets): single use, 60 seconds or less (STR-24). The one credential a URL may carry; never an API key. Omit it on askkelly.ai, where the session is used.",
            "schema": {
              "type": "string",
              "pattern": "^wtkt_[0123456789abcdefghjkmnpqrstvwxyz]{20}$"
            }
          },
          {
            "name": "Last-Event-ID",
            "in": "header",
            "required": false,
            "description": "The last event id received (a seq), to resume after a reconnect.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{1,18}$"
            }
          },
          {
            "name": "last_event_id",
            "in": "query",
            "required": false,
            "description": "The same as Last-Event-ID, for a new EventSource, which can't set the header; the header wins when both are sent.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{1,18}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The stream. Events: snapshot, one per change (named by its type), drain, error and warning; comment lines are pings.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "Api-Version": {
                "$ref": "#/components/headers/Api-Version"
              },
              "Client-Request-Id": {
                "$ref": "#/components/headers/Client-Request-Id"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-sent events; each data line is one Kelly Wire envelope."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "403": {
            "$ref": "#/components/responses/Problem"
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "405": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        },
        "x-status": "live",
        "x-group": "wire",
        "x-errors": [
          "invalid_request",
          "credential_in_url",
          "unsupported_api_version",
          "unauthenticated",
          "method_not_allowed",
          "rate_limited",
          "internal_error",
          "service_unavailable",
          "quota_exhausted",
          "insufficient_scope",
          "not_entitled",
          "not_found",
          "too_many_topics"
        ],
        "x-price": {
          "credits": 2,
          "per": "hour",
          "formula": "Charged when the stream opens and for each hour, or part of one, it stays open.",
          "unit": "one cached engine read",
          "note": "Nothing is charged during the beta: credits count against your plan's allowance only."
        },
        "x-public": true,
        "x-mcp-tool": null
      }
    },
    "/v1/answers": {
      "post": {
        "operationId": "create_answer",
        "summary": "Ask a question",
        "description": "Kelly's answer to a question in words, with every figure's source and time. Send `Accept: text/event-stream` to stream it as server-sent events, one per answer event; otherwise the finished answer comes back as JSON with 201. Needs an API key with run:answers, never the keyless tier. A key answers from public data only: odds maths and payouts, no-vig and hold, lines, games, players, and reviewed terms, house rules, tax and the law; other questions get a polite not-yet. `end_user_id` (optional, opaque, up to 64 characters) names the caller's own user and grants nothing; it becomes required with league-aware answers, which will need the league owner's OAuth token with read:league and an explicit `league_id` (trifecta.md F2). Answer IDs are random handles, never minted from natural keys (ARCH-8). The question is never logged or stored.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "$ref": "#/components/parameters/ClientRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The question (1 to 1,000 characters; the body at most 16 KB). It is never logged or stored.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnswerRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The answer as server-sent events, when `Accept: text/event-stream` is sent: `answer.started`, then each sentence's new `answer.citation`s and its `answer.delta`, then `answer.completed` (the `answer:{id}` topic's events in the AsyncAPI document; each `id` counts from 1). A failure after the stream starts ends it with `event: error`, whose data is a problem object.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "Api-Version": {
                "$ref": "#/components/headers/Api-Version"
              },
              "Client-Request-Id": {
                "$ref": "#/components/headers/Client-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Resource": {
                "$ref": "#/components/headers/X-RateLimit-Resource"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "X-Credits-Used": {
                "$ref": "#/components/headers/X-Credits-Used"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/X-Credits-Remaining"
              },
              "X-Credits-Last": {
                "$ref": "#/components/headers/X-Credits-Last"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-sent events; each data line is one answer event's data."
                },
                "examples": {
                  "stream": {
                    "summary": "The same question streamed as server-sent events",
                    "value": "event: answer.started\nid: 1\ndata: {\"answer_id\":\"ans_z2dw1zfpzg0hg9cx1c4b\",\"status\":\"streaming\",\"category\":null,\"playbook\":null,\"tier\":\"standard\",\"supersedes\":null,\"live_topics\":[],\"created_at\":\"2026-10-04T17:30:00Z\"}\n\nevent: answer.citation\nid: 2\ndata: {\"answer_id\":\"ans_z2dw1zfpzg0hg9cx1c4b\",\"citation_id\":\"c1\",\"kind\":\"calculation\",\"label\":\"Kelly remove vig calculation\",\"source\":\"Kelly\",\"as_of\":\"2026-10-04T17:30:00Z\",\"ref\":null,\"url\":null,\"reviewed_at\":null,\"formulas\":[\"F1.7@1\",\"F2.4@1\"]}\n\nevent: answer.delta\nid: 3\ndata: {\"answer_id\":\"ans_z2dw1zfpzg0hg9cx1c4b\",\"index\":0,\"text\":\"Each side is 50.0% with the vig removed.\",\"citations\":[\"c1\"]}\n\nevent: answer.completed\nid: 4\ndata: {\"answer_id\":\"ans_z2dw1zfpzg0hg9cx1c4b\",\"status\":\"completed\",\"summary\":\"No-vig probability, Chiefs: 50.0% (Kelly, 17:30 UTC).\",\"rg_line\":\"21+ · Help: 1-800-MY-RESET\",\"assumptions\":[],\"links\":{},\"sentence_count\":1,\"live_topics\":[],\"as_of\":\"2026-10-04T17:30:00Z\",\"source\":\"Kelly\",\"attribution\":null,\"completed_at\":\"2026-10-04T17:30:00Z\"}\n\n",
                    "x-captured-at": "2026-10-04T17:30:00Z"
                  }
                }
              }
            }
          },
          "201": {
            "description": "The finished answer: completed, declined (a question Kelly redirects, such as one about a league) or failed. Never cached.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "Api-Version": {
                "$ref": "#/components/headers/Api-Version"
              },
              "Client-Request-Id": {
                "$ref": "#/components/headers/Client-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Resource": {
                "$ref": "#/components/headers/X-RateLimit-Resource"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "X-Credits-Used": {
                "$ref": "#/components/headers/X-Credits-Used"
              },
              "X-Credits-Remaining": {
                "$ref": "#/components/headers/X-Credits-Remaining"
              },
              "X-Credits-Last": {
                "$ref": "#/components/headers/X-Credits-Last"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Answer"
                },
                "examples": {
                  "completed": {
                    "summary": "A question about odds, answered from the engine's no-vig maths",
                    "value": {
                      "object": "answer",
                      "answer_id": "ans_gqpvxwaqdbdxwfndzq7k",
                      "status": "completed",
                      "category": 17,
                      "playbook": null,
                      "tier": "standard",
                      "supersedes": null,
                      "summary": "No-vig probability, Chiefs: 50.0% (Kelly, 17:30 UTC).",
                      "sentences": [
                        {
                          "index": 0,
                          "text": "Each side is 50.0% with the vig removed.",
                          "citations": [
                            "c1"
                          ]
                        }
                      ],
                      "citations": [
                        {
                          "citation_id": "c1",
                          "kind": "calculation",
                          "label": "Kelly remove vig calculation",
                          "source": "Kelly",
                          "as_of": "2026-10-04T17:30:00Z",
                          "ref": null,
                          "url": null,
                          "reviewed_at": null,
                          "formulas": [
                            "F1.7@1",
                            "F2.4@1"
                          ]
                        }
                      ],
                      "figures": [
                        {
                          "figure_id": "f1",
                          "field": "calculation_result.result.fair_probs",
                          "label": "No-vig probability, Chiefs",
                          "value": 0.5000000000000001,
                          "display": "50.0%",
                          "unit": "probability",
                          "as_of": "2026-10-04T17:30:00Z",
                          "source": "Kelly",
                          "citation_id": "c1",
                          "topic": null,
                          "selection_id": null,
                          "edge_status": null
                        },
                        {
                          "figure_id": "f2",
                          "field": "calculation_result.result.fair_probs",
                          "label": "No-vig probability, Bills",
                          "value": 0.5000000000000001,
                          "display": "50.0%",
                          "unit": "probability",
                          "as_of": "2026-10-04T17:30:00Z",
                          "source": "Kelly",
                          "citation_id": "c1",
                          "topic": null,
                          "selection_id": null,
                          "edge_status": null
                        }
                      ],
                      "live_topics": [],
                      "rg_line": "21+ · Help: 1-800-MY-RESET",
                      "assumptions": [],
                      "links": {},
                      "as_of": "2026-10-04T17:30:00Z",
                      "source": "Kelly",
                      "attribution": null,
                      "created_at": "2026-10-04T17:30:00Z",
                      "completed_at": "2026-10-04T17:30:00Z"
                    },
                    "x-captured-at": "2026-10-04T17:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "403": {
            "$ref": "#/components/responses/Problem"
          },
          "405": {
            "$ref": "#/components/responses/Problem"
          },
          "413": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        },
        "tags": [
          "answers"
        ],
        "security": [
          {
            "ApiKey": [
              "run:answers"
            ]
          }
        ],
        "x-status": "live",
        "x-group": "answers",
        "x-errors": [
          "invalid_request",
          "credential_in_url",
          "unsupported_api_version",
          "unauthenticated",
          "method_not_allowed",
          "rate_limited",
          "internal_error",
          "service_unavailable",
          "quota_exhausted",
          "insufficient_scope",
          "not_entitled",
          "payload_too_large"
        ],
        "x-price": {
          "credits": 100,
          "per": "call",
          "formula": "By tier: answer_fast, answer_standard or answer_deep in the cost table; the live slice meters every question as answer_standard.",
          "unit": "one cached engine read",
          "note": "Nothing is charged during the beta: credits count against your plan's allowance only."
        },
        "x-public": false,
        "x-mcp-tool": null
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "x-kelly-object": "problem",
        "description": "An RFC 9457 problem. `code` is stable and never changes; clients must tolerate codes they do not know. `detail` is prose and may be reworded.",
        "required": [
          "type",
          "title",
          "status",
          "code",
          "doc_url",
          "retryable"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "The error's documentation URI."
          },
          "title": {
            "type": "string",
            "description": "A short, fixed summary of the code."
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599,
            "description": "The HTTP status."
          },
          "code": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
            "description": "Stable snake_case code from the error catalogue."
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "What went wrong this time, for developers."
          },
          "param": {
            "type": [
              "string",
              "null"
            ],
            "description": "The parameter, field or header at fault."
          },
          "doc_url": {
            "type": "string",
            "format": "uri",
            "description": "Cause and fix for this code."
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The Request-Id of the failed request."
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether retrying the same request can succeed."
          },
          "display_message": {
            "type": "string",
            "description": "Plain wording an app may show its end user (API-ERR-3); it never carries developer detail."
          },
          "instance": {
            "type": "string",
            "format": "uri-reference",
            "description": "A URI for this occurrence, when there is one."
          }
        }
      },
      "Health": {
        "type": "object",
        "x-kelly-object": "health",
        "description": "Liveness and dependency status. `ok` is true whenever the process answers; the booleans say what is down, and null means not configured or not checked. No error text ever appears.",
        "required": [
          "object",
          "ok",
          "service",
          "version",
          "db",
          "redis",
          "migrated"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "health",
            "description": "Always `health`."
          },
          "ok": {
            "type": "boolean",
            "description": "True whenever the process can answer."
          },
          "service": {
            "type": "string",
            "description": "Which service answered."
          },
          "version": {
            "type": "string",
            "description": "The running release."
          },
          "db": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the database answered; null when not configured."
          },
          "redis": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether Redis answered; null when not configured."
          },
          "migrated": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether migrations are applied; null when not checked."
          }
        }
      },
      "Status": {
        "type": "object",
        "x-kelly-object": "status",
        "description": "Who the API thinks is calling, and what the caller has left. It names only the caller's own account, never a key or another subject.",
        "required": [
          "object",
          "ok",
          "api_version",
          "credential",
          "mode",
          "plan",
          "account_id",
          "scopes",
          "rate",
          "credits"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "status",
            "description": "Always `status`."
          },
          "ok": {
            "type": "boolean",
            "description": "Always true when the call is answered."
          },
          "api_version": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "description": "The API version this call was served at."
          },
          "credential": {
            "type": "string",
            "enum": [
              "anonymous",
              "session",
              "key",
              "oauth"
            ],
            "description": "How the caller was identified.",
            "x-open-enum": true
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "live data, or test: the sandbox's recorded Sunday.",
            "x-open-enum": true
          },
          "plan": {
            "type": "string",
            "enum": [
              "anonymous",
              "free",
              "pro",
              "developer",
              "scale"
            ],
            "description": "The plan the caller's limits and credits come from.",
            "x-open-enum": true
          },
          "account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The caller's own account; null when keyless."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A scope the credential carries."
            },
            "description": "The scopes the credential carries, sorted."
          },
          "rate": {
            "type": [
              "object",
              "null"
            ],
            "description": "The rate-limit window this call counted against; null when none applies.",
            "required": [
              "policy",
              "limit",
              "remaining",
              "window_seconds",
              "reset",
              "resource"
            ],
            "properties": {
              "policy": {
                "type": "string",
                "description": "The policy's name, as in the RateLimit-Policy header."
              },
              "limit": {
                "type": "integer",
                "description": "Requests allowed in the window."
              },
              "remaining": {
                "type": "integer",
                "description": "Requests left in the window."
              },
              "window_seconds": {
                "type": "integer",
                "description": "The window's length in seconds."
              },
              "reset": {
                "type": "integer",
                "description": "When the window resets, in Unix seconds."
              },
              "resource": {
                "type": "string",
                "description": "The bucket: the operation's name, or `all`."
              }
            }
          },
          "credits": {
            "type": [
              "object",
              "null"
            ],
            "description": "Credits for the period; null when the caller has no allowance to count.",
            "required": [
              "used",
              "remaining",
              "last",
              "allowance",
              "period",
              "reset"
            ],
            "properties": {
              "used": {
                "type": "integer",
                "description": "Credits used so far in the period, this call included."
              },
              "remaining": {
                "type": "integer",
                "description": "Credits left in the period."
              },
              "last": {
                "type": "integer",
                "description": "What this call cost: nothing, for status."
              },
              "allowance": {
                "type": "integer",
                "description": "The period's allowance."
              },
              "period": {
                "type": "string",
                "description": "The period: the UTC day for keyless callers (dYYYYMMDD), the billing period for accounts (mYYYYMMDD)."
              },
              "reset": {
                "type": "integer",
                "description": "When the period resets, in Unix seconds."
              }
            }
          }
        }
      },
      "Provenance": {
        "type": "object",
        "x-kelly-object": "envelope",
        "description": "The stamp every served value carries (L6, ARCH-3, MCP-RES-2).",
        "required": [
          "as_of",
          "source"
        ],
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "When the value was true; for a derived value, its newest input's time."
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "description": "Who produced the value, as shown to readers."
          },
          "seq": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "The entity stream's sequence number, when live."
          },
          "stale_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "On live data: milliseconds it stays current."
          },
          "attribution": {
            "type": [
              "string",
              "null"
            ],
            "description": "Attribution the source's terms require."
          }
        },
        "additionalProperties": false
      },
      "ApiEnvelope": {
        "type": "object",
        "x-kelly-object": "envelope",
        "description": "The provenance members of a Kelly API resource, plus its type, request and live topics.",
        "required": [
          "as_of",
          "source",
          "object"
        ],
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "When the value was true; for a derived value, its newest input's time."
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "description": "Who produced the value, as shown to readers."
          },
          "seq": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "The entity stream's sequence number, when live."
          },
          "stale_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "On live data: milliseconds it stays current."
          },
          "attribution": {
            "type": [
              "string",
              "null"
            ],
            "description": "Attribution the source's terms require."
          },
          "object": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]*$",
            "description": "The resource's type (API-RES-2)."
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The request that produced this response."
          },
          "live_topics": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Kelly Wire topics that update this resource (API and cards only)."
          },
          "rg_line": {
            "type": [
              "string",
              "null"
            ],
            "description": "The cost line (COMP-1, D19) on every resource and result that touches wagering: one engine-computed cost fact, or the short form, plus 21+ and the helpline. Show it with the value. Null on resources that don't touch wagering."
          }
        },
        "additionalProperties": false
      },
      "ListPage": {
        "type": "object",
        "x-kelly-object": "list",
        "description": "A page of a list: never a bare array (API-RES-4), paginated from version one (API-PAGE-1).",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list",
            "description": "Always `list`.",
            "default": "list"
          },
          "data": {
            "type": "array",
            "items": {},
            "description": "The page's items, in the list's documented order."
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether another page follows."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, encrypted cursor for the next page."
          }
        },
        "additionalProperties": false
      },
      "ReferenceList": {
        "type": "object",
        "x-kelly-object": "list",
        "description": "A page of reference entries, in relevance order when searching and topic order otherwise.",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list",
            "description": "Always `list`.",
            "default": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReferenceSummary"
            },
            "description": "The page's entries."
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether another page follows."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page; the filters are frozen into it."
          }
        },
        "additionalProperties": false
      },
      "ReferenceSummary": {
        "type": "object",
        "x-kelly-object": "reference",
        "description": "A published reference entry as a list shows it: what it covers, its answer in one sentence and its review dates.",
        "required": [
          "id",
          "topic",
          "kind",
          "title",
          "summary",
          "version",
          "reviewed_at",
          "review_due",
          "stale",
          "url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "reference",
            "description": "Always `reference`.",
            "default": "reference"
          },
          "id": {
            "type": "string",
            "pattern": "^ref_[0123456789abcdefghjkmnpqrstvwxyz]{20}$",
            "description": "The topic's Kelly ID (ref_…)."
          },
          "topic": {
            "type": "string",
            "description": "The topic key, such as law/us-nj or term/moneyline."
          },
          "kind": {
            "type": "string",
            "enum": [
              "term",
              "rule",
              "law",
              "tax",
              "policy",
              "guide",
              "help"
            ],
            "description": "term, rule, law, tax, policy, guide or help; clients must tolerate new kinds.",
            "x-open-enum": true
          },
          "title": {
            "type": "string",
            "description": "The entry's title."
          },
          "summary": {
            "type": "string",
            "description": "One sentence that answers the question."
          },
          "jurisdiction": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166 code: US, US-NJ, CA-ON, GB; null if none."
          },
          "products": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "sportsbook",
                "dfs",
                "pickem",
                "prediction_market",
                "sweepstakes",
                "season_fantasy",
                "horse_racing",
                "online_casino",
                "lottery",
                "exchange"
              ],
              "x-open-enum": true
            },
            "description": "The products the entry covers; clients must tolerate new products."
          },
          "operator": {
            "type": [
              "string",
              "null"
            ],
            "description": "The operator, for an operator's own rules or policies."
          },
          "version": {
            "type": "integer",
            "minimum": 1,
            "description": "The published version's number."
          },
          "reviewed_at": {
            "type": "string",
            "format": "date",
            "description": "The date the facts were last checked (YYYY-MM-DD)."
          },
          "review_due": {
            "type": "string",
            "format": "date",
            "description": "The date the facts must be checked again by (YYYY-MM-DD)."
          },
          "stale": {
            "type": "boolean",
            "description": "Whether review_due has passed: the entry may be out of date."
          },
          "url": {
            "type": "string",
            "description": "The entry's page on the site."
          }
        },
        "additionalProperties": false
      },
      "Reference": {
        "type": "object",
        "x-kelly-object": "reference",
        "description": "A published reference entry: the answer, the facts behind it with their sources, and its review dates. Only published versions are served; a topic still in review is not found.",
        "required": [
          "id",
          "topic",
          "kind",
          "title",
          "summary",
          "version",
          "reviewed_at",
          "review_due",
          "stale",
          "url",
          "as_of",
          "source",
          "body",
          "facts",
          "sources",
          "published_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "reference",
            "description": "Always `reference`.",
            "default": "reference"
          },
          "id": {
            "type": "string",
            "pattern": "^ref_[0123456789abcdefghjkmnpqrstvwxyz]{20}$",
            "description": "The topic's Kelly ID (ref_…)."
          },
          "topic": {
            "type": "string",
            "description": "The topic key, such as law/us-nj or term/moneyline."
          },
          "kind": {
            "type": "string",
            "enum": [
              "term",
              "rule",
              "law",
              "tax",
              "policy",
              "guide",
              "help"
            ],
            "description": "term, rule, law, tax, policy, guide or help; clients must tolerate new kinds.",
            "x-open-enum": true
          },
          "title": {
            "type": "string",
            "description": "The entry's title."
          },
          "summary": {
            "type": "string",
            "description": "One sentence that answers the question."
          },
          "jurisdiction": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166 code: US, US-NJ, CA-ON, GB; null if none."
          },
          "products": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "sportsbook",
                "dfs",
                "pickem",
                "prediction_market",
                "sweepstakes",
                "season_fantasy",
                "horse_racing",
                "online_casino",
                "lottery",
                "exchange"
              ],
              "x-open-enum": true
            },
            "description": "The products the entry covers; clients must tolerate new products."
          },
          "operator": {
            "type": [
              "string",
              "null"
            ],
            "description": "The operator, for an operator's own rules or policies."
          },
          "version": {
            "type": "integer",
            "minimum": 1,
            "description": "The published version's number."
          },
          "reviewed_at": {
            "type": "string",
            "format": "date",
            "description": "The date the facts were last checked (YYYY-MM-DD)."
          },
          "review_due": {
            "type": "string",
            "format": "date",
            "description": "The date the facts must be checked again by (YYYY-MM-DD)."
          },
          "stale": {
            "type": "boolean",
            "description": "Whether review_due has passed: the entry may be out of date."
          },
          "url": {
            "type": "string",
            "description": "The entry's page on the site."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "x-kelly-rights": "envelope.as_of",
            "description": "When the value was true; for a derived value, its newest input's time."
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "envelope.source",
            "description": "Who produced the value, as shown to readers."
          },
          "seq": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "x-kelly-rights": "envelope.seq",
            "description": "The entity stream's sequence number, when live."
          },
          "stale_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "x-kelly-rights": "envelope.stale_after_ms",
            "description": "On live data: milliseconds it stays current."
          },
          "attribution": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "envelope.attribution",
            "description": "Attribution the source's terms require."
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "envelope.request_id",
            "description": "The request that produced this response."
          },
          "live_topics": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "x-kelly-rights": "envelope.live_topics",
            "description": "Kelly Wire topics that update this resource (API and cards only)."
          },
          "rg_line": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "envelope.rg_line",
            "description": "The cost line (COMP-1, D19) on every resource and result that touches wagering: one engine-computed cost fact, or the short form, plus 21+ and the helpline. Show it with the value. Null on resources that don't touch wagering."
          },
          "body": {
            "type": "string",
            "description": "The entry's text, in Markdown; may be empty when the summary says it all."
          },
          "facts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReferenceFact"
            },
            "description": "The facts the entry rests on, each with its sources."
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReferenceSource"
            },
            "description": "The sources the facts cite."
          },
          "notice": {
            "type": [
              "string",
              "null"
            ],
            "description": "The notice a law or tax entry carries; null otherwise."
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this version was published."
          }
        },
        "additionalProperties": false
      },
      "ReferenceFact": {
        "type": "object",
        "x-kelly-object": "reference_fact",
        "description": "One checkable statement in the entry, with the sources that support it. `key`, `value` and `unit` make it machine-readable where that helps (a legal status, a minimum age, a tax threshold).",
        "required": [
          "id",
          "claim",
          "sources"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The fact's ID within this version, such as f1."
          },
          "claim": {
            "type": "string",
            "description": "The statement, in the entry's own words."
          },
          "key": {
            "type": [
              "string",
              "null"
            ],
            "description": "A machine key for the fact, such as legal_status or minimum_age; the set of keys grows over time."
          },
          "value": {
            "type": [
              "string",
              "integer",
              "number",
              "boolean",
              "null"
            ],
            "description": "The fact's typed value; dates are YYYY-MM-DD strings."
          },
          "unit": {
            "type": [
              "string",
              "null"
            ],
            "description": "The value's unit, such as years, USD or percent."
          },
          "product": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "sportsbook",
              "dfs",
              "pickem",
              "prediction_market",
              "sweepstakes",
              "season_fantasy",
              "horse_racing",
              "online_casino",
              "lottery",
              "exchange",
              null
            ],
            "description": "The product the fact applies to, when one.",
            "x-open-enum": true
          },
          "operator": {
            "type": [
              "string",
              "null"
            ],
            "description": "The operator the fact applies to, when one."
          },
          "jurisdiction": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166 code the fact applies to, when it differs from the entry's."
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "IDs of the sources in this version that support the fact."
          }
        },
        "additionalProperties": false
      },
      "ReferenceSource": {
        "type": "object",
        "x-kelly-object": "reference_source",
        "description": "Where a fact comes from: the page, its publisher and when it was read. Primary sources are regulators, statutes, tax authorities, courts and operators' own published terms.",
        "required": [
          "id",
          "url",
          "title",
          "publisher",
          "primary",
          "retrieved_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The source's ID within this version, such as s1; facts cite it."
          },
          "url": {
            "type": "string",
            "description": "The page the fact was checked against."
          },
          "title": {
            "type": "string",
            "description": "The page's title."
          },
          "publisher": {
            "type": "string",
            "description": "Who published the page."
          },
          "primary": {
            "type": "boolean",
            "description": "Whether this is a primary source (regulator, statute, tax authority, court, or the operator's own terms)."
          },
          "retrieved_at": {
            "type": "string",
            "format": "date",
            "description": "The date the page was read (YYYY-MM-DD)."
          }
        },
        "additionalProperties": false
      },
      "WireTicketRequest": {
        "type": "object",
        "x-kelly-object": "wire_ticket_request",
        "description": "What a ticket is for: the socket or a stream, the topics it is bound to, and live or test data. Every member is optional: an empty object asks for a socket ticket in the credential's own mode, bound to no topic.",
        "properties": {
          "transport": {
            "type": "string",
            "enum": [
              "ws",
              "sse"
            ],
            "description": "ws for the socket (wss://…/v1), sse for GET /v1/stream/{topic}. A ticket works on the transport it was minted for only.",
            "default": "ws",
            "x-open-enum": true
          },
          "topics": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 5000,
            "items": {
              "type": "string"
            },
            "description": "Topics the ticket is bound to, such as game:gm_… or market:mkt_…; required for sse. At most the plan's topic limit. Omit on a socket ticket to subscribe to any topic the plan allows."
          },
          "mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "live",
              "test",
              null
            ],
            "description": "test serves the recorded Sunday (sample data) to any credential; live is refused for a test-mode key. Omitted: the credential's own mode.",
            "x-open-enum": true
          }
        },
        "additionalProperties": false
      },
      "WireTicket": {
        "type": "object",
        "x-kelly-object": "wire_ticket",
        "description": "A single-use Kelly Wire ticket. Present it within its lifetime: in the socket's first message (auth), or as ?ticket= on the stream URL. It is a secret: don't log it.",
        "required": [
          "object",
          "ticket",
          "expires_at",
          "topics",
          "transport",
          "mode",
          "url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "wire_ticket",
            "description": "Always `wire_ticket`.",
            "default": "wire_ticket"
          },
          "ticket": {
            "type": "string",
            "pattern": "^wtkt_[0123456789abcdefghjkmnpqrstvwxyz]{20}$",
            "description": "The ticket (wtkt_…); single use, 60 seconds or less."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the ticket stops working, unused."
          },
          "topics": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "The topics it is bound to; null for any topic the plan allows."
          },
          "transport": {
            "type": "string",
            "enum": [
              "ws",
              "sse"
            ],
            "description": "The transport it works on.",
            "x-open-enum": true
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "live data, or test: the recorded Sunday.",
            "x-open-enum": true
          },
          "url": {
            "type": "string",
            "description": "Where to connect: the socket's URL, or the stream's URL (its {topic} filled in when the ticket names one topic). It never contains the ticket."
          }
        },
        "additionalProperties": false
      },
      "AnswerRequest": {
        "type": "object",
        "required": [
          "question"
        ],
        "properties": {
          "question": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000,
            "description": "The question, in words."
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Za-z]{2}$",
            "description": "The person's US state, two letters, for the helpline and the law."
          },
          "end_user_id": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 64,
            "description": "Your own opaque ID for the person asking (G1-F02). It grants nothing, and the slice reads no league with it; it becomes required when answers read a league."
          }
        },
        "additionalProperties": false
      },
      "Answer": {
        "type": "object",
        "x-kelly-object": "answer",
        "description": "One Kelly answer: what POST /v1/answers, get_explanation and the answer:{id} snapshot carry.",
        "required": [
          "answer_id",
          "status",
          "tier",
          "summary",
          "as_of",
          "source",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "answer",
            "x-kelly-rights": "answer.object",
            "description": "Always answer.",
            "default": "answer"
          },
          "answer_id": {
            "type": "string",
            "pattern": "^ans_[0123456789abcdefghjkmnpqrstvwxyz]{20}$",
            "x-kelly-rights": "answer.answer_id",
            "description": "The answer's handle (ans_)."
          },
          "status": {
            "type": "string",
            "enum": [
              "streaming",
              "completed",
              "declined",
              "failed"
            ],
            "x-kelly-rights": "answer.status",
            "description": "streaming, completed, declined (a handled question Kelly redirects) or failed.",
            "x-open-enum": true
          },
          "category": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 36,
            "x-kelly-rights": "answer.category",
            "description": "The playbook category, 1 to 36."
          },
          "playbook": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9]{1,2}@[0-9]+$",
            "x-kelly-rights": "answer.playbook",
            "description": "The playbook and version that shaped it."
          },
          "tier": {
            "type": "string",
            "enum": [
              "fast",
              "standard",
              "deep"
            ],
            "x-kelly-rights": "answer.tier",
            "description": "The model tier (answers/config.toml).",
            "x-open-enum": true
          },
          "supersedes": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^ans_[0123456789abcdefghjkmnpqrstvwxyz]{20}$",
            "x-kelly-rights": "answer.supersedes",
            "description": "The answer a live recompute replaces (D22)."
          },
          "summary": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "answer.summary",
            "description": "One or two sentences from a server template, never the model."
          },
          "sentences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnswerSentence"
            },
            "x-kelly-rights": "answer.sentences",
            "description": "The gated prose, in order."
          },
          "citations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnswerCitation"
            },
            "x-kelly-rights": "answer.citations",
            "description": "Where figures and claims come from."
          },
          "figures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnswerFigure"
            },
            "x-kelly-rights": "answer.figures",
            "description": "Every number shown, from the engine."
          },
          "live_topics": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "x-kelly-rights": "envelope.live_topics",
            "description": "Kelly Wire topics that move its figures."
          },
          "rg_line": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "answer.rg_line",
            "description": "The D19 cost line, on every answer touching wagering."
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "x-kelly-rights": "answer.assumptions",
            "description": "Defaults the server applied (D28)."
          },
          "links": {
            "type": "object",
            "x-kelly-rights": "answer.links",
            "description": "Pages on Kelly's own site for what it may not carry.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "x-kelly-rights": "envelope.as_of",
            "description": "The newest input's time."
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "envelope.source",
            "description": "Who produced it, as shown to readers."
          },
          "attribution": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "envelope.attribution",
            "description": "Attribution a source's terms require."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "x-kelly-rights": "answer.created_at",
            "description": "When the answer started."
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "x-kelly-rights": "answer.completed_at",
            "description": "When it completed, declined or failed."
          }
        },
        "additionalProperties": false
      },
      "AnswerSentence": {
        "type": "object",
        "x-kelly-object": "answer_sentence",
        "description": "One gated sentence of the model's prose.",
        "required": [
          "index",
          "text"
        ],
        "properties": {
          "index": {
            "type": "integer",
            "minimum": 0,
            "x-kelly-rights": "answer.sentence_index",
            "description": "Its position, from 0."
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "answer.sentence_text",
            "description": "The sentence, after the gate; never retracted."
          },
          "citations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "x-kelly-rights": "answer.citations",
            "description": "Citation IDs it rests on."
          }
        },
        "additionalProperties": false
      },
      "AnswerCitation": {
        "type": "object",
        "x-kelly-object": "answer_citation",
        "description": "Where a figure or claim comes from.",
        "required": [
          "citation_id",
          "kind",
          "label",
          "source",
          "as_of"
        ],
        "properties": {
          "citation_id": {
            "type": "string",
            "pattern": "^c[1-9][0-9]{0,2}$",
            "x-kelly-rights": "answer.citation_id",
            "description": "The citation's handle within its answer."
          },
          "kind": {
            "type": "string",
            "enum": [
              "calculation",
              "market",
              "game",
              "player",
              "league",
              "reference",
              "search"
            ],
            "x-kelly-rights": "answer.citation_kind",
            "description": "What it cites: an engine calculation, a market, a reference entry, ...",
            "x-open-enum": true
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "answer.label",
            "description": "What a reader sees, such as 'Kelly no-vig calculation'."
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "envelope.source",
            "description": "Who produced the cited value, as shown to readers."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "x-kelly-rights": "envelope.as_of",
            "description": "When the cited value was true."
          },
          "ref": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "answer.ref",
            "description": "The cited object's Kelly ID or reference topic ID."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "answer.url",
            "description": "A page on askkelly.ai for the cited object, when it has one."
          },
          "reviewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "x-kelly-rights": "answer.reviewed_at",
            "description": "A reference entry's review date (MCP-RISK-4)."
          },
          "formulas": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "x-kelly-rights": "answer.formulas",
            "description": "Formula cards a calculation used (F<part>.<n>@v)."
          }
        },
        "additionalProperties": false
      },
      "AnswerFigure": {
        "type": "object",
        "x-kelly-object": "answer_figure",
        "description": "One number the answer shows, filled by the server from the engine.",
        "required": [
          "figure_id",
          "field",
          "label",
          "value",
          "display",
          "unit",
          "as_of",
          "source"
        ],
        "properties": {
          "figure_id": {
            "type": "string",
            "pattern": "^f[1-9][0-9]{0,2}$",
            "x-kelly-rights": "answer.figure_id",
            "description": "The figure's handle within its answer."
          },
          "field": {
            "type": "string",
            "minLength": 3,
            "x-kelly-rights": "answer.figure_field",
            "description": "The Rights Register field the value is, such as selection.no_vig_prob."
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "answer.label",
            "description": "What the figure is, for a reader."
          },
          "value": {
            "x-kelly-rights": "answer.figure_value",
            "description": "The engine's value, unrounded where it is a number.",
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "integer"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "display": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "answer.figure_display",
            "description": "The server's formatted string; prose quotes exactly this."
          },
          "unit": {
            "type": "string",
            "enum": [
              "probability",
              "percent",
              "american",
              "decimal",
              "points",
              "money_minor",
              "count",
              "text"
            ],
            "x-kelly-rights": "answer.figure_unit",
            "description": "How to read value.",
            "x-open-enum": true
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "x-kelly-rights": "envelope.as_of",
            "description": "When the value was true."
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "x-kelly-rights": "envelope.source",
            "description": "Who produced it, as shown to readers."
          },
          "citation_id": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "answer.citation_id",
            "description": "The citation it rests on."
          },
          "topic": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "answer.figure_topic",
            "description": "The Kelly Wire topic that moves it (D22), when live."
          },
          "selection_id": {
            "type": [
              "string",
              "null"
            ],
            "x-kelly-rights": "selection.id",
            "description": "The market side it belongs to (sel_), if any."
          },
          "edge_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "edge",
              "no_edge",
              "not_validated",
              "market_only",
              null
            ],
            "x-kelly-rights": "selection.edge_status",
            "description": "D18's edge status, on named selections only.",
            "x-open-enum": true
          }
        },
        "additionalProperties": false
      }
    },
    "responses": {
      "Problem": {
        "description": "An RFC 9457 problem (API-ERR-1). The code is stable; the detail is prose.",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "Api-Version": {
            "$ref": "#/components/headers/Api-Version"
          },
          "Client-Request-Id": {
            "$ref": "#/components/headers/Client-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "parameters": {
      "ApiVersion": {
        "name": "Api-Version",
        "in": "header",
        "required": false,
        "description": "The dated API version (API-VER-2). Omitted: the version your account was pinned to at its first call, or 2026-10-05 for a keyless caller. An unsupported value is refused with 400 unsupported_api_version.",
        "schema": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "enum": [
            "2026-10-05"
          ]
        }
      },
      "ClientRequestId": {
        "name": "Client-Request-Id",
        "in": "header",
        "required": false,
        "description": "Your own ID for this request, echoed back when well formed (API-OPS-1).",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Makes a non-idempotent request safe to retry; kept at least 24 hours (API-IDEM-1).",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      }
    },
    "headers": {
      "Request-Id": {
        "description": "This request's ID: minted by the edge, or the caller's own when well formed (API-OPS-1).",
        "schema": {
          "type": "string"
        }
      },
      "Api-Version": {
        "description": "The version this response was served at (API-VER-2).",
        "schema": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        }
      },
      "Client-Request-Id": {
        "description": "The caller's own request ID, echoed when sent and well formed (API-OPS-1).",
        "schema": {
          "type": "string"
        }
      },
      "X-RateLimit-Limit": {
        "description": "Requests allowed in the current window (API-OPS-2).",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current window (API-OPS-2).",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Reset": {
        "description": "When the window resets, in Unix seconds (API-OPS-2).",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Resource": {
        "description": "The bucket this request counted against (API-OPS-2).",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit": {
        "description": "Remaining quota per draft-ietf-httpapi-ratelimit-headers-11; a change to its syntax is a behavioural change (API-OPS-5).",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit-Policy": {
        "description": "Quota policies per draft-ietf-httpapi-ratelimit-headers-11 (API-OPS-5).",
        "schema": {
          "type": "string"
        }
      },
      "X-Credits-Used": {
        "description": "Engine-work credits used so far in the period, this request included (API-OPS-3; ADR-K012).",
        "schema": {
          "type": "integer"
        }
      },
      "X-Credits-Remaining": {
        "description": "Credits left in the period: the UTC day for anonymous callers, the billing period for accounts (API-OPS-3).",
        "schema": {
          "type": "integer"
        }
      },
      "X-Credits-Last": {
        "description": "Credits this request cost, from the published cost table (API-OPS-3).",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying (API-IDEM-4).",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key in the Authorization header: kelly_live_… for live data, kelly_test_… for test mode. Never in a URL (API-AUTH-2). A key carries the scopes its owner picked; an operation's requirement names the one it needs."
      },
      "OAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "An OAuth 2.1 access token for a person's own data (their leagues, simulations and tracker), with the scopes they consented to: read:league, run:simulation, read:tracker. Authorization servers and scopes are published at https://api.askkelly.ai/.well-known/oauth-protected-resource (RFC 9728)."
      }
    }
  },
  "x-kelly-surface": "api",
  "x-generated-by": "python -m registry.generate --out registry/generated"
}
