{"openapi":"3.1.0","info":{"title":"LeagueSolver API","description":"Generate fair, conflict-free round-robin sports league schedules with a single API call. Infeasible requests return a concrete diagnosis instead of a bare error.","version":"0.2.0"},"paths":{"/health":{"get":{"tags":["ops"],"summary":"Health","description":"Liveness probe (monitored by UptimeRobot every 5 minutes).","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Health Health Get"}}}}}}},"/v1/keys":{"post":{"tags":["keys"],"summary":"Issue Api Key","description":"Issue a new Free-plan API key and email it. The raw key is returned only once.","operationId":"issue_api_key_v1_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyIssueRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyIssueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/schedules/league":{"post":{"tags":["schedules"],"summary":"Create League Schedule","description":"Generate a round-robin league schedule (authenticated and metered).\n\nEnforces the caller's plan: monthly solve quota (402 when exhausted) and team-count cap\n(403 when exceeded). Returns 200 with ``status`` optimal | feasible | infeasible;\nstructurally invalid input returns 400, schema violations 422, over rate limit 429.","operationId":"create_league_schedule_v1_schedules_league_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleRequestIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/checkout":{"post":{"tags":["billing"],"summary":"Create Checkout","description":"Start a Stripe Checkout session to subscribe the authenticated key to a paid plan.","operationId":"create_checkout_v1_billing_checkout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/stripe":{"post":{"tags":["billing"],"summary":"Stripe Webhook","description":"Receive Stripe subscription events and sync the affected key's plan.\n\nThe signature is verified against STRIPE_WEBHOOK_SECRET; unsigned or tampered payloads are\nrejected with 400. Unhandled event types are acknowledged with 200 so Stripe stops retrying.","operationId":"stripe_webhook_v1_webhooks_stripe_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Stripe Webhook V1 Webhooks Stripe Post"}}}}}}}},"components":{"schemas":{"CheckoutRequest":{"properties":{"plan":{"type":"string","enum":["starter","pro"],"title":"Plan"}},"additionalProperties":false,"type":"object","required":["plan"],"title":"CheckoutRequest"},"CheckoutResponse":{"properties":{"checkout_url":{"type":"string","title":"Checkout Url"}},"type":"object","required":["checkout_url"],"title":"CheckoutResponse"},"ConstraintsIn":{"properties":{"max_games_per_team_per_week":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Games Per Team Per Week"},"min_rest_days":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Min Rest Days"},"balance_home_away":{"type":"boolean","title":"Balance Home Away","default":false}},"type":"object","title":"ConstraintsIn"},"FairnessReportOut":{"properties":{"teams":{"items":{"$ref":"#/components/schemas/TeamFairnessOut"},"type":"array","title":"Teams"}},"type":"object","required":["teams"],"title":"FairnessReportOut"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"KeyIssueRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"additionalProperties":false,"type":"object","required":["email"],"title":"KeyIssueRequest"},"KeyIssueResponse":{"properties":{"api_key":{"type":"string","title":"Api Key"},"key_prefix":{"type":"string","title":"Key Prefix"},"plan":{"type":"string","title":"Plan"},"message":{"type":"string","title":"Message","default":"Store this key now; it will not be shown again."}},"type":"object","required":["api_key","key_prefix","plan"],"title":"KeyIssueResponse","description":"Returned once at key creation. ``api_key`` is never shown again."},"MatchOut":{"properties":{"home":{"type":"string","title":"Home"},"away":{"type":"string","title":"Away"},"venue":{"type":"string","title":"Venue"},"kickoff":{"type":"string","format":"date-time","title":"Kickoff"}},"type":"object","required":["home","away","venue","kickoff"],"title":"MatchOut"},"ScheduleRequestIn":{"properties":{"teams":{"items":{"$ref":"#/components/schemas/TeamIn"},"type":"array","minItems":2,"title":"Teams"},"venues":{"items":{"$ref":"#/components/schemas/VenueIn"},"type":"array","minItems":1,"title":"Venues"},"format":{"type":"string","const":"round_robin","title":"Format","default":"round_robin"},"rounds":{"type":"integer","enum":[1,2],"title":"Rounds","default":1},"constraints":{"$ref":"#/components/schemas/ConstraintsIn"},"objective":{"type":"string","const":"minimize_schedule_length","title":"Objective","default":"minimize_schedule_length"}},"additionalProperties":false,"type":"object","required":["teams","venues"],"title":"ScheduleRequestIn","description":"Request body for ``POST /v1/schedules/league``."},"ScheduleResponse":{"properties":{"status":{"type":"string","title":"Status"},"matches":{"items":{"$ref":"#/components/schemas/MatchOut"},"type":"array","title":"Matches"},"fairness_report":{"anyOf":[{"$ref":"#/components/schemas/FairnessReportOut"},{"type":"null"}]},"solve_time_ms":{"type":"integer","title":"Solve Time Ms"},"diagnosis":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Diagnosis"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings","default":[]}},"type":"object","required":["status","matches","fairness_report","solve_time_ms"],"title":"ScheduleResponse","description":"Response body. ``status`` is ``optimal`` | ``feasible`` | ``infeasible``.\n\nOn ``infeasible``, ``matches`` is empty, ``fairness_report`` is null, and ``diagnosis``\nexplains which constraints conflict."},"TeamFairnessOut":{"properties":{"team":{"type":"string","title":"Team"},"home_games":{"type":"integer","title":"Home Games"},"away_games":{"type":"integer","title":"Away Games"},"min_rest_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Rest Days"}},"type":"object","required":["team","home_games","away_games","min_rest_days"],"title":"TeamFairnessOut"},"TeamIn":{"properties":{"id":{"type":"string","minLength":1,"title":"Id"},"home_venue":{"type":"string","minLength":1,"title":"Home Venue"}},"type":"object","required":["id","home_venue"],"title":"TeamIn"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VenueIn":{"properties":{"id":{"type":"string","minLength":1,"title":"Id"},"available_slots":{"items":{"type":"string","format":"date-time"},"type":"array","minItems":1,"title":"Available Slots"}},"type":"object","required":["id","available_slots"],"title":"VenueIn"}}}}