{"openapi":"3.1.0","info":{"title":"NegativeEV bet checker","version":"1.4.5","summary":"Grade MLB, WNBA, PGA, and ATP bets against play-by-play game simulations.","description":"NegativeEV runs the actual game thousands of times, play by play, and compares how often a bet would have won with the probability the sportsbook's price implies. Bets are graded through the MCP or A2A endpoint below; the two GET endpoints report whether there is a slate to grade against and how many checks the caller has left. Anonymous callers are metered per device; a Supabase access token sent as `Authorization: Bearer` raises the allowance.","termsOfService":"https://negativeev.com/terms","contact":{"name":"NegativeEV","url":"https://negativeev.com/contact"}},"externalDocs":{"description":"Guide for agents","url":"https://negativeev.com/AGENTS.md"},"servers":[{"url":"https://negativeev.com"}],"components":{"securitySchemes":{"supabaseBearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Supabase access token. See https://negativeev.com/auth.md."}},"schemas":{"Error":{"type":"object","title":"Endpoint error","description":"What the checking endpoints answer a refused call with. `error` is the stable code to branch on; `message` is written to be shown to a person as-is.","required":["error","message"],"properties":{"error":{"type":"string","description":"Stable machine code for the refusal, e.g. `rate_limited`, `invalid_token`, `out_of_checks`. Branch on this, never on the message.","examples":["rate_limited"]},"message":{"type":"string","description":"One sentence explaining the refusal, safe to relay verbatim.","examples":["Too many checks too quickly — give it a moment and try again."]},"reset_in":{"type":"integer","description":"Seconds until the caller's allowance frees up. Present only on a quota or pace refusal; wait this long rather than retrying immediately.","examples":[42]}}},"Problem":{"type":"object","title":"Problem details (RFC 9457)","description":"What a wrong path or an unhandled failure answers with, in the shape RFC 9457 defines, served as application/problem+json.","required":["type","title","status","detail"],"properties":{"type":{"type":"string","format":"uri","description":"Problem type URI. `about:blank` means the status code is the whole story.","examples":["about:blank"]},"title":{"type":"string","description":"Short human-readable summary of the problem type.","examples":["Not Found"]},"status":{"type":"integer","description":"The HTTP status code, repeated in the body.","examples":[404]},"detail":{"type":"string","description":"What went wrong on this specific request."},"instance":{"type":"string","description":"The path the problem occurred on.","examples":["/a2a/nope"]}}},"Slate":{"type":"object","title":"Slate state","description":"Whether there are simulations to grade against right now, and which day and model snapshot they are from.","required":["available","game_date","model_date"],"properties":{"available":{"type":"boolean","description":"Whether bets can be graded right now."},"game_date":{"type":["string","null"],"format":"date","description":"The slate's game day, US/Pacific."},"model_date":{"type":["string","null"],"format":"date","description":"The model snapshot the simulations were run with."},"slate_when":{"type":["string","null"],"description":"When the next slate is expected, when there is none."}}},"Usage":{"type":"object","title":"Caller allowance","description":"How many checks this caller has left in the current window. Anonymous callers are metered per device per day; a signed-in caller per hour.","required":["authed","limit","used","remaining","reset_in"],"properties":{"authed":{"type":"boolean","description":"Whether the call carried a valid bearer token."},"limit":{"type":"integer","description":"Checks allowed in the window."},"used":{"type":"integer","description":"Checks spent in the window so far."},"remaining":{"type":"integer","description":"Checks still available."},"reset_in":{"type":"integer","description":"Seconds until the window resets."}}},"JsonRpcRequest":{"type":"object","title":"JSON-RPC 2.0 request","description":"The envelope both the MCP and the A2A endpoint accept.","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","const":"2.0","description":"Always \"2.0\"."},"id":{"type":["string","integer","null"],"description":"Echoed back on the response. Omit only for a notification."},"method":{"type":"string","description":"The method being called."},"params":{"type":"object","description":"Method arguments."}}},"JsonRpcResponse":{"type":"object","title":"JSON-RPC 2.0 response","description":"Carries `result` on success and `error` on failure — never both. Both arrive with HTTP 200: a JSON-RPC error is a transport success.","required":["jsonrpc","id"],"properties":{"jsonrpc":{"type":"string","const":"2.0"},"id":{"type":["string","integer","null"]},"result":{"type":"object","description":"Present when the call succeeded."},"error":{"type":"object","description":"Present when it did not.","required":["code","message"],"properties":{"code":{"type":"integer","description":"JSON-RPC error code."},"message":{"type":"string","description":"What went wrong."}}}}}}},"security":[{},{"supabaseBearer":[]}],"paths":{"/slate":{"get":{"operationId":"getSlate","summary":"Is there a simulated slate to grade against","description":"Answers before a bet is sent, so a caller never spends a check against a day that has no simulations.","responses":{"200":{"description":"The slate's state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Slate"},"example":{"available":true,"game_date":"2026-08-27","model_date":"2026-08-26","slate_when":null}}}},"500":{"description":"The request failed before a response could be produced. Safe to retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/usage":{"get":{"operationId":"getUsage","summary":"Checks remaining for this caller","responses":{"200":{"description":"The caller's allowance and what is left of it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"},"example":{"authed":false,"limit":10,"used":0,"remaining":10,"reset_in":0}}}},"500":{"description":"The request failed before a response could be produced. Safe to retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/mcp":{"post":{"operationId":"callMcp","summary":"MCP (Streamable HTTP, stateless)","description":"JSON-RPC 2.0. Tools: check_bet, get_slate, get_usage. The server card at /.well-known/mcp/server-card.json carries the protocol versions served.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"},"example":{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"check_bet","arguments":{"bet":"Shohei Ohtani over 1.5 total bases -120"}}}}}},"responses":{"200":{"description":"An MCP JSON-RPC response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"401":{"description":"The bearer token was invalid or expired. Re-authenticate; do not retry anonymously.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"The caller is out of checks, or sending them too fast. Wait `reset_in` seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying."}}},"500":{"description":"The request failed before a response could be produced. Safe to retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/a2a":{"post":{"operationId":"sendA2aMessage","summary":"A2A SendMessage","description":"JSON-RPC 2.0. Send the bet as the text part of a user message; the reply is an agent message whose text part summarises the verdict and whose data part carries it in full. Agent card at /.well-known/agent-card.json.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"},"example":{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"messageId":"9d2e0b4a-1f6f-4a0e-9a1e-0c2b1f5d7e13","role":"ROLE_USER","parts":[{"text":"Shohei Ohtani over 1.5 total bases -120"}]}}}}}},"responses":{"200":{"description":"An A2A JSON-RPC response carrying a Message — or a JSON-RPC error object, which A2A also returns with HTTP 200.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"405":{"description":"The A2A endpoint accepts POST only.","headers":{"Allow":{"schema":{"type":"string"},"description":"Always `POST`."}}},"500":{"description":"The request failed before a response could be produced. Safe to retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}}}