{
  "openapi": "3.0.3",
  "info": {
    "title": "Odds Aggregation API",
    "version": "1.0.0",
    "description": "A canonical odds-aggregation API providing real-time and historical sports\nbetting odds across books, markets, and sports.\n\n## Authentication\nAll endpoints require a Bearer token in the `Authorization` header:\n```\nAuthorization: Bearer your_api_key_here\n```\nAPI keys are issued per plan (free / starter / pro / enterprise).\n\n## Odds formats\nPass `?odds_format=decimal` (default), `?odds_format=american`, or\n`?odds_format=fractional` on any endpoint that returns odds.\n\n## Rate limits\nRate limits are enforced per API key per minute:\n| Plan       | Req / min |\n|------------|-----------|\n| free       | 30        |\n| starter    | 120       |\n| pro        | 600       |\n| enterprise | 6000      |\n\nExceeded limits return `429` with a `Retry-After` header.\n\n## Response envelope\nAll successful responses are wrapped in:\n```json\n{ \"data\": ..., \"meta\": { \"updated_at\": \"...\" } }\n```\nPaginated responses additionally include `links` and pagination fields in `meta`.\n",
    "contact": {
      "name": "API Support",
      "email": "api@oddsexchange.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.oddsexchange.com/api",
      "description": "Production"
    },
    {
      "url": "http://localhost:8088/api",
      "description": "Local development"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Sports & Leagues",
      "description": "Reference data — sports, leagues, and books"
    },
    {
      "name": "Events",
      "description": "Upcoming, live, and completed events"
    },
    {
      "name": "Odds",
      "description": "Current odds, best lines, and arb detection"
    },
    {
      "name": "Line Movement",
      "description": "Historical price movement for events"
    },
    {
      "name": "Alerts",
      "description": "Webhook-based price and movement alerts"
    },
    {
      "name": "Tools",
      "description": "Calculator utilities — arb, hedge, parlay, hold, odds conversion"
    }
  ],
  "paths": {
    "/v1/sports": {
      "get": {
        "operationId": "listSports",
        "summary": "List all sports",
        "tags": [
          "Sports & Leagues"
        ],
        "responses": {
          "200": {
            "description": "List of active sports",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Sport"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sports/{sport}/leagues": {
      "get": {
        "operationId": "listLeagues",
        "summary": "List leagues for a sport",
        "tags": [
          "Sports & Leagues"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SportSlug"
          }
        ],
        "responses": {
          "200": {
            "description": "List of leagues for the sport",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/League"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sports/{sport}/events": {
      "get": {
        "operationId": "listEvents",
        "summary": "List events for a sport",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SportSlug"
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by event status",
            "schema": {
              "type": "string",
              "enum": [
                "scheduled",
                "live",
                "completed",
                "cancelled"
              ]
            }
          },
          {
            "name": "date_from",
            "in": "query",
            "description": "ISO 8601 date — include events on or after this date",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "date_to",
            "in": "query",
            "description": "ISO 8601 date — include events on or before this date",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "$ref": "#/components/parameters/Page"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of events",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Event"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/events/{event}": {
      "get": {
        "operationId": "getEvent",
        "summary": "Get a single event",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EventId"
          }
        ],
        "responses": {
          "200": {
            "description": "Single event",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Event"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/events/{event}/odds": {
      "get": {
        "operationId": "getEventOdds",
        "summary": "Get odds for an event",
        "description": "Returns all markets for the event with full book comparison per outcome.\nUse `markets` to filter which markets are returned.\nUse `books` to filter which sportsbooks are included.\nUse `odds_format` to control the output format.\n",
        "tags": [
          "Odds"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EventId"
          },
          {
            "$ref": "#/components/parameters/OddsFormat"
          },
          {
            "name": "markets",
            "in": "query",
            "description": "Comma-separated market keys, e.g. `h2h,spreads,totals`",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "books",
            "in": "query",
            "description": "Comma-separated book keys to include",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_movement",
            "in": "query",
            "description": "Include last 5 price history rows per outcome inline",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Event odds with market and book comparison",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/EventOdds"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/odds/best": {
      "get": {
        "operationId": "getBestLines",
        "summary": "Cross-event best-line shopping",
        "description": "Returns the best available price for each outcome across all books,\nfor a given sport and market.\n",
        "tags": [
          "Odds"
        ],
        "parameters": [
          {
            "name": "sport",
            "in": "query",
            "required": true,
            "description": "Sport slug",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "market",
            "in": "query",
            "required": true,
            "description": "Market key",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "outcome",
            "in": "query",
            "description": "Filter to a specific outcome name",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "books",
            "in": "query",
            "description": "Comma-separated book keys to limit comparison",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/OddsFormat"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Best prices per outcome across all events",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/odds/arb": {
      "get": {
        "operationId": "getArbFeed",
        "summary": "Arbitrage opportunity feed",
        "description": "Scans scheduled and live events for arbitrage opportunities on moneyline\nmarkets. Returns opportunities where the implied probability sum < 1.0.\n",
        "tags": [
          "Odds"
        ],
        "parameters": [
          {
            "name": "sport",
            "in": "query",
            "description": "Filter to a single sport slug",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of current arb opportunities",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ArbOpportunity"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/events/{event}/odds/movement": {
      "get": {
        "operationId": "getLineMovement",
        "summary": "Line movement history for an event",
        "description": "Returns all recorded price changes for the event's markets, grouped by\nmarket → book → outcome.\n",
        "tags": [
          "Line Movement"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EventId"
          },
          {
            "$ref": "#/components/parameters/OddsFormat"
          },
          {
            "name": "market",
            "in": "query",
            "description": "Filter to a specific market key",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "book",
            "in": "query",
            "description": "Filter to a specific book key",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "outcome",
            "in": "query",
            "description": "Filter to a specific outcome name",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hours",
            "in": "query",
            "description": "How many hours back to look (1–168, default 48)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 168,
              "default": 48
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Line movement grouped by market → book → outcome",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MarketMovement"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/books": {
      "get": {
        "operationId": "listBooks",
        "summary": "List supported sportsbooks",
        "tags": [
          "Sports & Leagues"
        ],
        "responses": {
          "200": {
            "description": "All active books",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Book"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/alerts": {
      "get": {
        "operationId": "listAlerts",
        "summary": "List your active alerts",
        "tags": [
          "Alerts"
        ],
        "responses": {
          "200": {
            "description": "Paginated list of your alerts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createAlert",
        "summary": "Create a price or movement alert",
        "tags": [
          "Alerts"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AlertRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Alert created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/alerts/{alert}": {
      "delete": {
        "operationId": "deleteAlert",
        "summary": "Delete an alert",
        "tags": [
          "Alerts"
        ],
        "parameters": [
          {
            "name": "alert",
            "in": "path",
            "required": true,
            "description": "Alert ULID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Alert deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/tools/convert": {
      "post": {
        "operationId": "convertOdds",
        "summary": "Convert odds between formats",
        "tags": [
          "Tools"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "value",
                  "from",
                  "to"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "description": "Odds value in the input format. E.g. '+150', '2.50', '3/2'"
                  },
                  "from": {
                    "type": "string",
                    "enum": [
                      "american",
                      "decimal",
                      "fractional"
                    ]
                  },
                  "to": {
                    "type": "string",
                    "enum": [
                      "american",
                      "decimal",
                      "fractional"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Converted odds in all three formats",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/tools/hold": {
      "post": {
        "operationId": "calculateHold",
        "summary": "Calculate market hold / vig",
        "tags": [
          "Tools"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "legs"
                ],
                "properties": {
                  "legs": {
                    "type": "array",
                    "minItems": 2,
                    "items": {
                      "type": "object",
                      "required": [
                        "outcome",
                        "decimal"
                      ],
                      "properties": {
                        "outcome": {
                          "type": "string"
                        },
                        "decimal": {
                          "type": "number",
                          "minimum": 1.001
                        },
                        "book": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Hold percentage and no-vig fair values",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/HoldResult"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/tools/arb": {
      "post": {
        "operationId": "calculateArb",
        "summary": "Calculate arbitrage opportunity",
        "tags": [
          "Tools"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "legs"
                ],
                "properties": {
                  "legs": {
                    "type": "array",
                    "minItems": 2,
                    "items": {
                      "type": "object",
                      "required": [
                        "outcome",
                        "decimal"
                      ],
                      "properties": {
                        "outcome": {
                          "type": "string"
                        },
                        "decimal": {
                          "type": "number",
                          "minimum": 1.001
                        },
                        "book": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    }
                  },
                  "total_stake": {
                    "type": "number",
                    "minimum": 1,
                    "default": 100
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Arb calculation result",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/ArbCalcResult"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/tools/hedge": {
      "post": {
        "operationId": "calculateHedge",
        "summary": "Calculate hedge bet sizing",
        "tags": [
          "Tools"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "original_stake",
                  "leg1_decimal",
                  "leg2_decimal"
                ],
                "properties": {
                  "original_stake": {
                    "type": "number",
                    "minimum": 0.01
                  },
                  "leg1_decimal": {
                    "type": "number",
                    "minimum": 1.001
                  },
                  "leg2_decimal": {
                    "type": "number",
                    "minimum": 1.001
                  },
                  "leg1_is_freebet": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Hedge stake and profit scenarios",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/HedgeCalcResult"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/tools/parlay": {
      "post": {
        "operationId": "calculateParlay",
        "summary": "Calculate parlay payout",
        "tags": [
          "Tools"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "legs",
                  "stake"
                ],
                "properties": {
                  "legs": {
                    "type": "array",
                    "minItems": 2,
                    "items": {
                      "type": "object",
                      "required": [
                        "decimal"
                      ],
                      "properties": {
                        "label": {
                          "type": "string"
                        },
                        "decimal": {
                          "type": "number",
                          "minimum": 1.001
                        }
                      }
                    }
                  },
                  "stake": {
                    "type": "number",
                    "minimum": 0.01
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Parlay payout breakdown",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/ParlayCalcResult"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/tools/line-shopping": {
      "post": {
        "operationId": "calculateLineShopping",
        "summary": "Compare lines across books for one outcome",
        "tags": [
          "Tools"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "books"
                ],
                "properties": {
                  "outcome": {
                    "type": "string",
                    "nullable": true
                  },
                  "books": {
                    "type": "array",
                    "minItems": 2,
                    "items": {
                      "type": "object",
                      "required": [
                        "book",
                        "decimal"
                      ],
                      "properties": {
                        "book": {
                          "type": "string"
                        },
                        "decimal": {
                          "type": "number",
                          "minimum": 1.001
                        },
                        "point": {
                          "type": "number",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sorted book comparison for the outcome",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/LineShoppingResult"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/ping": {
      "get": {
        "tags": [
          "Sports & Leagues"
        ],
        "operationId": "ping",
        "summary": "Smoke endpoint",
        "description": "Confirms auth + throttle + usage logging end-to-end. Echoes the resolved plan.",
        "responses": {
          "200": {
            "description": "Pong",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "pong": {
                          "type": "boolean"
                        },
                        "plan": {
                          "type": "string"
                        },
                        "user_id": {
                          "type": "string"
                        },
                        "timestamp": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "feed_as_of": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Most recent write across the whole feed (cheap liveness check)."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/health": {
      "get": {
        "tags": [
          "Sports & Leagues"
        ],
        "operationId": "feedHealth",
        "summary": "Feed freshness + per-league coverage (PUBLIC, no ApiKey)",
        "security": [],
        "description": "UNAUTHENTICATED (throttle-only). Lets a consumer/uptime monitor tell a\nFROZEN pipeline apart from an off-season/quiet slate — the two otherwise\nlook identical (an empty payload). HTTP is always 200; read data.status.\nAlert on a stale data.as_of, not on an empty props array.\n",
        "responses": {
          "200": {
            "description": "Feed freshness report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "ok",
                            "no_data"
                          ]
                        },
                        "as_of": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Most recent write across the whole feed."
                        },
                        "server_time": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "leagues": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "league": {
                                "type": "string"
                              },
                              "in_season": {
                                "type": "boolean"
                              },
                              "upcoming_events": {
                                "type": "integer"
                              },
                              "next_event_at": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true
                              },
                              "odds": {
                                "type": "object",
                                "properties": {
                                  "last_sync": {
                                    "type": "string",
                                    "format": "date-time",
                                    "nullable": true
                                  },
                                  "age_seconds": {
                                    "type": "integer",
                                    "nullable": true
                                  },
                                  "stale": {
                                    "type": "boolean",
                                    "nullable": true
                                  },
                                  "events_priced": {
                                    "type": "integer"
                                  }
                                }
                              },
                              "props": {
                                "type": "object",
                                "properties": {
                                  "last_sync": {
                                    "type": "string",
                                    "format": "date-time",
                                    "nullable": true
                                  },
                                  "age_seconds": {
                                    "type": "integer",
                                    "nullable": true
                                  },
                                  "stale": {
                                    "type": "boolean",
                                    "nullable": true
                                  },
                                  "covered": {
                                    "type": "boolean"
                                  },
                                  "coverage_reason": {
                                    "type": "string",
                                    "nullable": true,
                                    "enum": [
                                      "off_season",
                                      "no_props_ingested",
                                      "not_prop_eligible"
                                    ]
                                  },
                                  "events_with_props": {
                                    "type": "integer"
                                  },
                                  "upcoming_events_with_props": {
                                    "type": "integer"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{event}/props": {
      "get": {
        "tags": [
          "Odds"
        ],
        "operationId": "getEventProps",
        "summary": "Player props for one event (pro+)",
        "description": "Gated to the player_props feature (pro/enterprise) — a lower tier gets\n403 plan_required, NOT a silent empty array. Returns per-(player, market)\nrows: each side (over/under or yes/no) carries a `books` map of present\nbooks at the active point plus derived best/avg/deviation. Every book\ncell has american + decimal odds, point, stale flag, age_min, and a\nstable prop_line_id. `meta.freshness` makes an empty/all-stale response\nself-describing (covered + coverage_reason).\n",
        "parameters": [
          {
            "name": "event",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Event ULID"
          },
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 64
            },
            "description": "Narrow to a single prop market_key (e.g. batter_hits)."
          }
        ],
        "responses": {
          "200": {
            "description": "Props envelope",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "event": {
                          "type": "object"
                        },
                        "books": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "props": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "player": {
                                "type": "string"
                              },
                              "position": {
                                "type": "string",
                                "nullable": true
                              },
                              "market_key": {
                                "type": "string"
                              },
                              "market": {
                                "type": "string"
                              },
                              "type": {
                                "type": "string",
                                "enum": [
                                  "ou",
                                  "yesno"
                                ]
                              },
                              "consensus": {
                                "type": "number",
                                "nullable": true
                              },
                              "activePoint": {
                                "type": "number",
                                "nullable": true
                              },
                              "points": {
                                "type": "array",
                                "items": {
                                  "type": "number",
                                  "nullable": true
                                }
                              },
                              "sides": {
                                "type": "object",
                                "description": "Keys over/under (ou) or yes/no (yesno).",
                                "additionalProperties": {
                                  "type": "object",
                                  "properties": {
                                    "label": {
                                      "type": "string"
                                    },
                                    "books": {
                                      "type": "object",
                                      "description": "book_key => cell",
                                      "additionalProperties": {
                                        "type": "object",
                                        "properties": {
                                          "book_key": {
                                            "type": "string"
                                          },
                                          "american": {
                                            "type": "integer"
                                          },
                                          "decimal": {
                                            "type": "number"
                                          },
                                          "point": {
                                            "type": "number",
                                            "nullable": true
                                          },
                                          "is_best": {
                                            "type": "boolean"
                                          },
                                          "stale": {
                                            "type": "boolean"
                                          },
                                          "age_min": {
                                            "type": "number",
                                            "nullable": true
                                          },
                                          "prop_line_id": {
                                            "type": "string"
                                          }
                                        }
                                      }
                                    },
                                    "best": {
                                      "type": "object",
                                      "nullable": true,
                                      "properties": {
                                        "book_key": {
                                          "type": "string"
                                        },
                                        "american": {
                                          "type": "integer"
                                        },
                                        "decimal": {
                                          "type": "number"
                                        },
                                        "prop_line_id": {
                                          "type": "string"
                                        }
                                      }
                                    },
                                    "avg_decimal": {
                                      "type": "number",
                                      "nullable": true
                                    },
                                    "avg_american": {
                                      "type": "integer",
                                      "nullable": true
                                    },
                                    "deviation_pct": {
                                      "type": "number",
                                      "nullable": true
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "plan": {
                          "type": "string"
                        },
                        "market": {
                          "type": "string",
                          "nullable": true
                        },
                        "player_count": {
                          "type": "integer"
                        },
                        "books_available": {
                          "type": "integer"
                        },
                        "freshness": {
                          "type": "object",
                          "properties": {
                            "covered": {
                              "type": "boolean"
                            },
                            "coverage_reason": {
                              "type": "string",
                              "nullable": true,
                              "enum": [
                                "props_stale",
                                "no_props_ingested",
                                "not_prop_eligible"
                              ]
                            },
                            "has_fresh_prices": {
                              "type": "boolean"
                            },
                            "book_cells_total": {
                              "type": "integer"
                            },
                            "book_cells_fresh": {
                              "type": "integer"
                            },
                            "freshest_age_min": {
                              "type": "integer",
                              "nullable": true
                            },
                            "aging_window_min": {
                              "type": "integer"
                            },
                            "as_of": {
                              "type": "string",
                              "format": "date-time",
                              "nullable": true
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Requires the pro plan or higher"
          },
          "404": {
            "description": "Event not found"
          }
        }
      }
    },
    "/v1/events/{event}/analytics": {
      "get": {
        "tags": [
          "Odds"
        ],
        "operationId": "getEventAnalytics",
        "summary": "Average + best odds per selection (WO-E2 §1)",
        "description": "Pro+ only. Returns OddsCalculationService::forMarket output per\nmarket: book_count, average (decimal/american/implied), best\n(decimal/american/book_id). Main lines only.\n",
        "parameters": [
          {
            "name": "event",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Event ULID"
          },
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "h2h",
                "spreads",
                "totals"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Analytics envelope",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "event": {
                          "type": "object"
                        },
                        "markets": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "market_key": {
                                "type": "string"
                              },
                              "market_label": {
                                "type": "string"
                              },
                              "selections": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "outcome_name": {
                                      "type": "string"
                                    },
                                    "point": {
                                      "type": "number",
                                      "nullable": true
                                    },
                                    "book_count": {
                                      "type": "integer"
                                    },
                                    "average": {
                                      "type": "object",
                                      "properties": {
                                        "decimal": {
                                          "type": "number"
                                        },
                                        "american": {
                                          "type": "integer"
                                        },
                                        "implied": {
                                          "type": "number"
                                        }
                                      }
                                    },
                                    "best": {
                                      "type": "object",
                                      "properties": {
                                        "decimal": {
                                          "type": "number"
                                        },
                                        "american": {
                                          "type": "integer"
                                        },
                                        "book_id": {
                                          "type": "string"
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Plan required — free/starter cannot access analytics",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "example": "plan_required"
                        },
                        "message": {
                          "type": "string"
                        },
                        "required_plan": {
                          "type": "string",
                          "example": "pro"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Event not found"
          }
        }
      }
    },
    "/v1/line-movement/{marketId}": {
      "get": {
        "tags": [
          "Line Movement"
        ],
        "operationId": "getLineMovementShipped",
        "summary": "Historical price series for one market (WO-E2 §2)",
        "description": "Starter+ only. Returns one series per (outcome, point, book) found in\nthe requested window. Free tier → 403. Window clamped by\nPlanGate::historyDays — starter 7d, pro 30d, enterprise unbounded.\nRequested `?from` further back than the tier ceiling is silently\nclamped; meta.window_from reports the effective window.\n",
        "parameters": [
          {
            "name": "marketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "selection",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Outcome|point key — e.g. 'Lakers|-3.5' or bare 'Lakers'"
          },
          {
            "name": "book",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Book key (stable identifier)"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Series envelope",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "market": {
                          "type": "object"
                        },
                        "series": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "outcome_name": {
                                "type": "string"
                              },
                              "point": {
                                "type": "number",
                                "nullable": true
                              },
                              "book_key": {
                                "type": "string"
                              },
                              "point_count": {
                                "type": "integer"
                              },
                              "points": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "t": {
                                      "type": "string",
                                      "format": "date-time"
                                    },
                                    "decimal": {
                                      "type": "number"
                                    },
                                    "american": {
                                      "type": "integer"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "plan": {
                          "type": "string"
                        },
                        "window_from": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "tier_max_days": {
                          "type": "integer",
                          "nullable": true
                        },
                        "series_count": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Plan required — free cannot access line movement"
          },
          "404": {
            "description": "Market not found"
          }
        }
      }
    },
    "/v1/arbs": {
      "get": {
        "tags": [
          "Odds"
        ],
        "operationId": "getArbs",
        "summary": "Arbitrage opportunities — h2h, next 48h (WO-E2 §3)",
        "description": "Starter+ only. Cross-book arbitrage on h2h for upcoming events.\nAlgorithm: best decimal per side; arb when sum(1/best) < 1. Margin\n= 1 - sum(1/best). Sorted by margin desc.\n",
        "parameters": [
          {
            "name": "league",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "League slug (basketball_nba) OR shortcode (NBA, EPL)"
          },
          {
            "name": "min_margin",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "default": 0.005
            },
            "description": "Minimum margin in [0, 1]. Default 0.5%."
          }
        ],
        "responses": {
          "200": {
            "description": "Arb feed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "event_id": {
                            "type": "string"
                          },
                          "league": {
                            "type": "object"
                          },
                          "home_team": {
                            "type": "string"
                          },
                          "away_team": {
                            "type": "string"
                          },
                          "commence_time": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "market_key": {
                            "type": "string",
                            "example": "h2h"
                          },
                          "margin": {
                            "type": "number"
                          },
                          "sides": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "outcome_name": {
                                  "type": "string"
                                },
                                "best_decimal": {
                                  "type": "number"
                                },
                                "best_american": {
                                  "type": "integer"
                                },
                                "book_key": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "plan": {
                          "type": "string"
                        },
                        "count": {
                          "type": "integer"
                        },
                        "min_margin": {
                          "type": "number"
                        },
                        "market": {
                          "type": "string",
                          "example": "h2h"
                        },
                        "window": {
                          "type": "string",
                          "example": "48h"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Plan required — free cannot access the arb feed"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key issued on your dashboard. Pass as: `Authorization: Bearer {key}`"
      }
    },
    "parameters": {
      "SportSlug": {
        "name": "sport",
        "in": "path",
        "required": true,
        "description": "Sport slug, e.g. `americanfootball_nfl`",
        "schema": {
          "type": "string"
        }
      },
      "EventId": {
        "name": "event",
        "in": "path",
        "required": true,
        "description": "Event ULID",
        "schema": {
          "type": "string"
        }
      },
      "OddsFormat": {
        "name": "odds_format",
        "in": "query",
        "description": "Output format for odds values. Default: `decimal`.",
        "schema": {
          "type": "string",
          "enum": [
            "decimal",
            "american",
            "fractional"
          ],
          "default": "decimal"
        }
      },
      "PerPage": {
        "name": "per_page",
        "in": "query",
        "description": "Results per page (1–200, default 50)",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "Page": {
        "name": "page",
        "in": "query",
        "description": "Page number (1-indexed)",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        }
      }
    },
    "schemas": {
      "SuccessEnvelope": {
        "type": "object",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {},
          "meta": {
            "type": "object",
            "required": [
              "updated_at"
            ],
            "properties": {
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "PaginatedEnvelope": {
        "type": "object",
        "required": [
          "data",
          "meta",
          "links"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {}
          },
          "meta": {
            "type": "object",
            "required": [
              "total",
              "page",
              "per_page",
              "last_page",
              "updated_at"
            ],
            "properties": {
              "total": {
                "type": "integer"
              },
              "page": {
                "type": "integer"
              },
              "per_page": {
                "type": "integer"
              },
              "last_page": {
                "type": "integer"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "links": {
            "type": "object",
            "properties": {
              "first": {
                "type": "string",
                "format": "uri",
                "nullable": true
              },
              "prev": {
                "type": "string",
                "format": "uri",
                "nullable": true
              },
              "next": {
                "type": "string",
                "format": "uri",
                "nullable": true
              },
              "last": {
                "type": "string",
                "format": "uri",
                "nullable": true
              }
            }
          }
        }
      },
      "Sport": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "name",
          "active"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "ULID"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "League": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "name",
          "active"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "region": {
            "type": "string",
            "nullable": true
          },
          "sport": {
            "$ref": "#/components/schemas/Sport"
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "Book": {
        "type": "object",
        "required": [
          "key",
          "name",
          "active"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "region": {
            "type": "string",
            "nullable": true
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "Event": {
        "type": "object",
        "required": [
          "id",
          "home_team",
          "away_team",
          "commence_time",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "ULID"
          },
          "provider_event_id": {
            "type": "string"
          },
          "home_team": {
            "type": "string"
          },
          "away_team": {
            "type": "string"
          },
          "commence_time": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "live",
              "completed",
              "cancelled"
            ]
          },
          "league": {
            "$ref": "#/components/schemas/League"
          }
        }
      },
      "Price": {
        "type": "object",
        "required": [
          "book_key",
          "book_name",
          "price",
          "price_decimal",
          "is_main",
          "is_opening",
          "last_update"
        ],
        "properties": {
          "book_key": {
            "type": "string"
          },
          "book_name": {
            "type": "string"
          },
          "price": {
            "description": "Odds in requested format",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ]
          },
          "price_decimal": {
            "type": "number"
          },
          "point": {
            "type": "number",
            "nullable": true
          },
          "is_main": {
            "type": "boolean"
          },
          "is_opening": {
            "type": "boolean"
          },
          "last_update": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Outcome": {
        "type": "object",
        "required": [
          "name",
          "best_price",
          "best_price_decimal",
          "best_book",
          "books"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "best_price": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ]
          },
          "best_price_decimal": {
            "type": "number"
          },
          "best_book": {
            "type": "string"
          },
          "opening_price": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ]
          },
          "books": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Price"
            }
          }
        }
      },
      "Market": {
        "type": "object",
        "required": [
          "key",
          "label",
          "outcomes"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "outcomes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Outcome"
            }
          }
        }
      },
      "EventOdds": {
        "type": "object",
        "required": [
          "event_id",
          "home_team",
          "away_team",
          "commence_time",
          "status",
          "markets"
        ],
        "properties": {
          "event_id": {
            "type": "string"
          },
          "home_team": {
            "type": "string"
          },
          "away_team": {
            "type": "string"
          },
          "commence_time": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string"
          },
          "markets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Market"
            }
          }
        }
      },
      "ArbOpportunity": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string"
          },
          "home_team": {
            "type": "string"
          },
          "away_team": {
            "type": "string"
          },
          "market_key": {
            "type": "string"
          },
          "profit_percent": {
            "type": "number"
          },
          "implied_prob_sum": {
            "type": "number"
          },
          "stakes": {
            "type": "array",
            "items": {
              "type": "number"
            }
          },
          "legs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "outcome": {
                  "type": "string"
                },
                "book": {
                  "type": "string"
                },
                "price": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "PriceHistoryPoint": {
        "type": "object",
        "properties": {
          "price": {},
          "price_decimal": {
            "type": "number"
          },
          "point": {
            "type": "number",
            "nullable": true
          },
          "recorded_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OutcomeMovement": {
        "type": "object",
        "properties": {
          "outcome_name": {
            "type": "string"
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PriceHistoryPoint"
            }
          }
        }
      },
      "BookMovement": {
        "type": "object",
        "properties": {
          "book_key": {
            "type": "string"
          },
          "outcomes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutcomeMovement"
            }
          }
        }
      },
      "MarketMovement": {
        "type": "object",
        "properties": {
          "market_key": {
            "type": "string"
          },
          "books": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookMovement"
            }
          }
        }
      },
      "AlertRequest": {
        "type": "object",
        "required": [
          "event_id",
          "market_key",
          "outcome_name",
          "threshold_type",
          "threshold_value",
          "webhook_url"
        ],
        "properties": {
          "event_id": {
            "type": "string"
          },
          "market_key": {
            "type": "string"
          },
          "outcome_name": {
            "type": "string"
          },
          "threshold_type": {
            "type": "string",
            "enum": [
              "above",
              "below",
              "movement"
            ]
          },
          "threshold_value": {
            "type": "number"
          },
          "webhook_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "HoldResult": {
        "type": "object",
        "properties": {
          "hold_percent": {
            "type": "number"
          },
          "implied_prob_sum": {
            "type": "string"
          },
          "outcomes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "outcome": {
                  "type": "string"
                },
                "book": {
                  "type": "string",
                  "nullable": true
                },
                "decimal": {
                  "type": "number"
                },
                "implied_prob": {
                  "type": "number"
                },
                "fair_value_prob": {
                  "type": "number"
                },
                "fair_decimal": {
                  "type": "number",
                  "nullable": true
                },
                "fair_american": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "ArbCalcResult": {
        "type": "object",
        "properties": {
          "arb_exists": {
            "type": "boolean"
          },
          "profit_percent": {
            "type": "number",
            "nullable": true
          },
          "guaranteed_profit": {
            "type": "number",
            "nullable": true
          },
          "total_stake": {
            "type": "number"
          },
          "implied_prob_sum": {
            "type": "number"
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "legs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "outcome": {
                  "type": "string"
                },
                "decimal": {
                  "type": "number"
                },
                "book": {
                  "type": "string",
                  "nullable": true
                },
                "stake": {
                  "type": "number",
                  "nullable": true
                },
                "gross_return": {
                  "type": "number",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "HedgeCalcResult": {
        "type": "object",
        "properties": {
          "hedge_stake": {
            "type": "number"
          },
          "profit_if_leg1_wins": {
            "type": "number"
          },
          "profit_if_leg2_wins": {
            "type": "number"
          },
          "guaranteed_profit": {
            "type": "number",
            "nullable": true
          },
          "is_free_money_hedge": {
            "type": "boolean"
          },
          "inputs": {
            "type": "object",
            "properties": {
              "original_stake": {
                "type": "number"
              },
              "leg1_decimal": {
                "type": "number"
              },
              "leg1_american": {
                "type": "string"
              },
              "leg2_decimal": {
                "type": "number"
              },
              "leg2_american": {
                "type": "string"
              },
              "is_freebet": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "ParlayCalcResult": {
        "type": "object",
        "properties": {
          "combined_decimal": {
            "type": "number"
          },
          "combined_american": {
            "type": "string"
          },
          "stake": {
            "type": "number"
          },
          "payout": {
            "type": "number"
          },
          "profit": {
            "type": "number"
          },
          "implied_probability": {
            "type": "string"
          },
          "leg_count": {
            "type": "integer"
          },
          "legs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "decimal": {
                  "type": "number"
                },
                "american": {
                  "type": "string"
                },
                "implied_prob": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "LineShoppingResult": {
        "type": "object",
        "properties": {
          "outcome": {
            "type": "string"
          },
          "best_book": {
            "type": "string"
          },
          "best_decimal": {
            "type": "number"
          },
          "best_american": {
            "type": "string"
          },
          "worst_decimal": {
            "type": "number"
          },
          "difference_units": {
            "type": "number"
          },
          "comparison": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "book": {
                  "type": "string"
                },
                "decimal": {
                  "type": "number"
                },
                "american": {
                  "type": "string"
                },
                "point": {
                  "type": "number",
                  "nullable": true
                },
                "implied_prob": {
                  "type": "number"
                }
              }
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Request validation failed",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string"
                },
                "errors": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                },
                "retry_after": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    }
  }
}