{
    "openapi": "3.0.3",
    "info": {
        "title": "Winpot MX Public Content API",
        "version": "1.0.1",
        "description": "Read-only, unauthenticated endpoints covering public casino/sportsbook content: site widgets, jackpot feed, and sports insights. This spec intentionally excludes anything requiring login or touching accounts, balances, deposits, withdrawals, or KYC — those endpoints are not part of Winpot's public API surface. This API is tenant-routed: every request MUST include an `Origin: https://www.winpot.mx` header, or the API returns 404 `{\"message\":\"Domain not found\"}` regardless of path. All api.winpot.mx endpoints below were live-verified with this header present.\n\nThe `/api/agent/*` and `/api/oauth/token` paths are a second, separate surface served by the Winpot site origin (https://www.winpot.mx), not by api.winpot.mx — each carries its own `servers` entry. They implement anonymous agentic registration (auth.md): an agent obtains a `public:read` bearer credential and reads the public game catalogue and public info pages with it. That scope grants nothing a logged-out browser cannot already reach. See https://www.winpot.mx/auth.md."
    },
    "servers": [
        { "url": "https://api.winpot.mx/", "description": "Production" }
    ],
    "paths": {
        "/api/agent/auth": {
            "servers": [
                { "url": "https://www.winpot.mx/", "description": "Winpot MX site origin (agentic registration)" }
            ],
            "post": {
                "summary": "Register an anonymous agent credential",
                "description": "Agentic registration (auth.md). Unauthenticated. Issues a `public:read` credential covering the public game catalogue and public info pages — nothing player-scoped, no money movement, no KYC. Anonymous is the only supported identity type; there is no claim ceremony.",
                "security": [],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "type": { "type": "string", "enum": ["anonymous"], "default": "anonymous" }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Credential issued",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/AgentRegistration" }
                            }
                        }
                    },
                    "400": {
                        "description": "A non-anonymous identity type was requested",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/AgentError" }
                            }
                        }
                    },
                    "501": {
                        "description": "Agent auth is not configured on this deployment",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/AgentError" }
                            }
                        }
                    }
                }
            }
        },
        "/api/oauth/token": {
            "servers": [
                { "url": "https://www.winpot.mx/", "description": "Winpot MX site origin (agentic registration)" }
            ],
            "post": {
                "summary": "Exchange client credentials for an access token",
                "description": "RFC 6749 client_credentials. Mints a fresh one-hour `public:read` access token from the `client_id` / `client_secret` returned by /api/agent/auth. Client authentication via HTTP Basic or request body.",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": { "$ref": "#/components/schemas/ClientCredentialsGrant" }
                        },
                        "application/json": {
                            "schema": { "$ref": "#/components/schemas/ClientCredentialsGrant" }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Access token issued",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/AgentAccessToken" }
                            }
                        }
                    },
                    "400": {
                        "description": "Unsupported grant type or scope",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/AgentError" }
                            }
                        }
                    },
                    "401": {
                        "description": "client_id / client_secret missing, expired, or mismatched",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/AgentError" }
                            }
                        }
                    }
                }
            }
        },
        "/api/agent/v1/games": {
            "servers": [
                { "url": "https://www.winpot.mx/", "description": "Winpot MX site origin (agentic registration)" }
            ],
            "get": {
                "summary": "Search the public game catalogue",
                "description": "Public casino game catalogue search. Requires a `public:read` bearer token.",
                "security": [{ "AgentBearer": [] }],
                "parameters": [
                    {
                        "name": "q",
                        "in": "query",
                        "required": false,
                        "description": "Substring matched case-insensitively against game names. Omit to list the catalogue head.",
                        "schema": { "type": "string" }
                    },
                    {
                        "name": "limit",
                        "in": "query",
                        "required": false,
                        "description": "Maximum games to return. Clamped to 1..100.",
                        "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Matching games",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/AgentGameSearch" }
                            }
                        }
                    },
                    "401": { "$ref": "#/components/responses/AgentUnauthorized" }
                }
            }
        },
        "/api/agent/v1/info": {
            "servers": [
                { "url": "https://www.winpot.mx/", "description": "Winpot MX site origin (agentic registration)" }
            ],
            "get": {
                "summary": "Public info page links",
                "description": "Links to the public terms, payment-options, deposits and withdrawals pages. Requires a `public:read` bearer token.",
                "security": [{ "AgentBearer": [] }],
                "parameters": [
                    {
                        "name": "topic",
                        "in": "query",
                        "required": false,
                        "description": "Return a single page. Omit for all four.",
                        "schema": {
                            "type": "string",
                            "enum": ["terms", "payment-options", "deposits", "withdrawals"]
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Info page links",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "pages": {
                                            "type": "object",
                                            "additionalProperties": {
                                                "type": "object",
                                                "properties": {
                                                    "url": { "type": "string", "format": "uri" },
                                                    "description": { "type": "string" }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Unknown topic",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/AgentError" }
                            }
                        }
                    },
                    "401": { "$ref": "#/components/responses/AgentUnauthorized" }
                }
            }
        },
        "/site/config": {
            "get": {
                "summary": "Public site widgets",
                "description": "Returns public marketing widgets shown on the homepage: top/last winners, the Android app download link, and hot/cold game lists.",
                "parameters": [
                    { "$ref": "#/components/parameters/OriginHeader" },
                    {
                        "name": "type",
                        "in": "query",
                        "required": true,
                        "description": "Which widget(s) to fetch. Repeat the parameter to request more than one, e.g. type=hot_games&type=cold_games. Verified live with single and repeated values.",
                        "schema": {
                            "type": "array",
                            "items": {
                                "type": "string",
                                "enum": ["last_winners", "top_winners", "android", "hot_games", "cold_games"]
                            }
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Requested widget data",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "last_winners": {
                                            "type": "array",
                                            "items": { "$ref": "#/components/schemas/Winner" }
                                        },
                                        "top_winners": {
                                            "type": "array",
                                            "items": { "$ref": "#/components/schemas/Winner" }
                                        },
                                        "android": { "$ref": "#/components/schemas/AndroidAppLink" },
                                        "hot_games": { "$ref": "#/components/schemas/ColdHotConfig" },
                                        "cold_games": { "$ref": "#/components/schemas/ColdHotConfig" }
                                    }
                                }
                            }
                        }
                    },
                    "404": { "$ref": "#/components/responses/DomainNotFound" }
                }
            }
        },
        "/games/jackpot/feed": {
            "get": {
                "summary": "Live jackpot feed",
                "description": "Public jackpot totals shown on site widgets, grouped by provider. The `providers` parameter is required and must be sent as repeated bracket-suffixed keys (e.g. `providers[]=Pragmatic&providers[]=Zitro`) — a single plain `providers=X` is rejected by the server with a validation error.",
                "parameters": [
                    { "$ref": "#/components/parameters/OriginHeader" },
                    {
                        "name": "providers[]",
                        "in": "query",
                        "required": true,
                        "description": "One or more provider names to fetch jackpots for. Must use the `providers[]=` key for every value, including a single value.",
                        "schema": {
                            "type": "array",
                            "items": { "type": "string", "description": "ContentProvider enum value, e.g. Pragmatic, Zitro, Evolution" }
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Jackpot data keyed by provider name",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": { "$ref": "#/components/schemas/ProviderJackpots" },
                                    "description": "Object keyed by the requested provider name(s)"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Missing or malformed providers parameter",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "statusCode": { "type": "integer" },
                                        "message": { "type": "string" },
                                        "validation": { "type": "array", "items": { "type": "string" } }
                                    }
                                }
                            }
                        }
                    },
                    "404": { "$ref": "#/components/responses/DomainNotFound" }
                }
            }
        },
        "/sport/insights/tips/visitor": {
            "get": {
                "summary": "Public sportsbook tips",
                "description": "Sports betting insights/tips intended for anonymous visitors, shown on the sportsbook tipster widget.",
                "parameters": [
                    { "$ref": "#/components/parameters/OriginHeader" }
                ],
                "responses": {
                    "200": {
                        "description": "List of tipster insights",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": { "$ref": "#/components/schemas/TipsterItem" }
                                }
                            }
                        }
                    },
                    "404": { "$ref": "#/components/responses/DomainNotFound" }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "AgentBearer": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT",
                "description": "Anonymous agent credential from POST https://www.winpot.mx/api/agent/auth, scope `public:read`. See https://www.winpot.mx/auth.md. Applies only to the /api/agent/* paths on www.winpot.mx, not to the api.winpot.mx content endpoints, which take no credential."
            }
        },
        "parameters": {
            "OriginHeader": {
                "name": "Origin",
                "in": "header",
                "required": true,
                "description": "Required on every request. This API is tenant-routed by Origin; without it, every path returns 404 \"Domain not found\" regardless of whether the path itself is valid.",
                "schema": { "type": "string", "enum": ["https://www.winpot.mx"] }
            }
        },
        "responses": {
            "AgentUnauthorized": {
                "description": "Missing, expired or invalid bearer token. The WWW-Authenticate header names the Protected Resource Metadata document so the registration endpoint can be rediscovered.",
                "headers": {
                    "WWW-Authenticate": {
                        "description": "Bearer challenge carrying realm, error, scope and resource_metadata.",
                        "schema": { "type": "string" }
                    }
                },
                "content": {
                    "application/json": {
                        "schema": { "$ref": "#/components/schemas/AgentError" }
                    }
                }
            },
            "DomainNotFound": {
                "description": "Returned for any request missing the required Origin header (tenant could not be resolved)",
                "content": {
                    "application/json": {
                        "schema": {
                            "type": "object",
                            "properties": {
                                "statusCode": { "type": "integer", "enum": [404] },
                                "message": { "type": "string", "enum": ["Domain not found"] },
                                "data": { "type": "object", "properties": { "domain": { "nullable": true } } }
                            }
                        }
                    }
                }
            }
        },
        "schemas": {
            "AgentRegistration": {
                "type": "object",
                "properties": {
                    "identity_type": { "type": "string", "enum": ["anonymous"] },
                    "credential_type": { "type": "string", "enum": ["access_token"] },
                    "client_id": { "type": "string", "description": "Opaque credential id, `agt_` + 32 hex characters." },
                    "client_secret": { "type": "string", "description": "Long-lived (90 day) assertion used at the token endpoint. Returned once." },
                    "access_token": { "type": "string" },
                    "token_type": { "type": "string", "enum": ["Bearer"] },
                    "expires_in": { "type": "integer", "description": "Access token lifetime in seconds." },
                    "scope": { "type": "string", "enum": ["public:read"] },
                    "resource": { "type": "string", "format": "uri" },
                    "token_endpoint": { "type": "string", "format": "uri" },
                    "resource_documentation": { "type": "string", "format": "uri" }
                }
            },
            "ClientCredentialsGrant": {
                "type": "object",
                "required": ["grant_type"],
                "properties": {
                    "grant_type": { "type": "string", "enum": ["client_credentials"] },
                    "client_id": { "type": "string" },
                    "client_secret": { "type": "string" },
                    "scope": { "type": "string", "enum": ["public:read"] }
                }
            },
            "AgentAccessToken": {
                "type": "object",
                "properties": {
                    "access_token": { "type": "string" },
                    "token_type": { "type": "string", "enum": ["Bearer"] },
                    "expires_in": { "type": "integer" },
                    "scope": { "type": "string", "enum": ["public:read"] }
                }
            },
            "AgentGameSearch": {
                "type": "object",
                "properties": {
                    "query": { "type": "string", "nullable": true },
                    "total_matched": { "type": "integer" },
                    "count": { "type": "integer" },
                    "games": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "id": { "type": "string" },
                                "oid": { "type": "string" },
                                "name": { "type": "string" },
                                "vendor": { "type": "string", "nullable": true },
                                "type": { "type": "string", "nullable": true }
                            }
                        }
                    }
                }
            },
            "AgentError": {
                "type": "object",
                "properties": {
                    "error": { "type": "string" },
                    "error_description": { "type": "string" }
                }
            },
            "Winner": {
                "type": "object",
                "properties": {
                    "currency": { "type": "string" },
                    "game_id": { "type": "string" },
                    "game_name": { "type": "string" },
                    "nickname": { "type": "string" },
                    "total_win": { "type": "number" }
                }
            },
            "AndroidAppLink": {
                "type": "object",
                "properties": {
                    "url": {
                        "type": "object",
                        "properties": {
                            "casino": { "type": "string" },
                            "sport": { "type": "string" }
                        }
                    }
                }
            },
            "LiveRTPGamesItem": {
                "type": "object",
                "properties": {
                    "rtp": { "type": "string" },
                    "game_id": { "type": "string" }
                }
            },
            "ColdHotConfig": {
                "type": "object",
                "properties": {
                    "fullDay": {
                        "type": "array",
                        "items": { "$ref": "#/components/schemas/LiveRTPGamesItem" }
                    },
                    "halfDay": {
                        "type": "array",
                        "items": { "$ref": "#/components/schemas/LiveRTPGamesItem" }
                    }
                }
            },
            "ProviderJackpot": {
                "type": "object",
                "properties": {
                    "vendor": { "type": "string" },
                    "jackpotID": { "type": "string" },
                    "name": { "type": "string" },
                    "amount": {
                        "type": "object",
                        "additionalProperties": { "type": "number" },
                        "description": "Jackpot amount keyed by currency code, e.g. MXN"
                    },
                    "games": { "type": "array", "items": { "type": "string" } },
                    "status": { "type": "string", "enum": ["active", "pending", "inactive"] }
                }
            },
            "ProviderJackpots": {
                "type": "object",
                "properties": {
                    "jackpots": {
                        "type": "array",
                        "items": { "$ref": "#/components/schemas/ProviderJackpot" }
                    }
                }
            },
            "TipsterOdd": {
                "type": "object",
                "properties": {
                    "oddId": { "type": "number" },
                    "price": { "type": "number" }
                }
            },
            "TipsterMessage": {
                "type": "object",
                "properties": {
                    "odds": {
                        "type": "array",
                        "items": { "$ref": "#/components/schemas/TipsterOdd" }
                    },
                    "type": { "type": "string", "enum": ["regular", "data_driven", "vip"] },
                    "message": {
                        "type": "object",
                        "properties": {
                            "en": { "type": "string" },
                            "es": { "type": "string" }
                        }
                    }
                }
            },
            "TipsterItem": {
                "type": "object",
                "properties": {
                    "eventId": { "type": "number" },
                    "name": { "type": "string" },
                    "eventDate": { "type": "string", "format": "date-time" },
                    "tipsterMessage": { "$ref": "#/components/schemas/TipsterMessage" }
                }
            }
        }
    }
}
