{"openapi":"3.1.0","info":{"title":"Badger API","version":"1.0.0","description":"Send your test totals from CI and show a verified badge. v1 changes are additive only. Changelog at /changelog."},"servers":[{"url":"/v1"}],"tags":[{"name":"Projects","description":"Create a project and get its write key."},{"name":"Results","description":"Send the latest test result."},{"name":"Keys","description":"Rotate the write key."},{"name":"System","description":"Health."},{"name":"Webhooks","description":"Billing webhooks (not enabled)."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Your project's write key: Authorization: Bearer bdg_wk_..."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"invalid_request"},"message":{"type":"string","example":"total: passed + failed must not exceed total"}},"required":["code","message"]}},"required":["error"]},"CreateProject":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email"},"name":{"type":"string","minLength":1,"maxLength":100}},"required":["email"],"example":{"email":"you@example.com","name":"Checkout suite"}},"ResultInput":{"type":"object","properties":{"passed":{"type":"integer","minimum":0,"maximum":10000000},"failed":{"type":"integer","minimum":0,"maximum":10000000},"total":{"type":"integer","minimum":1,"maximum":10000000},"run_id":{"type":"string","minLength":1,"maxLength":128},"branch":{"type":"string","minLength":1,"maxLength":255},"commit":{"type":"string","minLength":1,"maxLength":64}},"required":["passed","failed","total"],"example":{"passed":142,"failed":3,"total":145,"run_id":"github-run-9876543210","branch":"main","commit":"3f2a9c1"}}},"parameters":{}},"paths":{"/healthz":{"get":{"summary":"Health check","description":"Checks that the service and its database are reachable.","tags":["System"],"responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]}},"required":["status"]}}}}}}},"/projects":{"post":{"summary":"Create a project","description":"Creates a project and returns its write key once. We cannot show the key again; rotate it if you lose it.","tags":["Projects"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProject"}}}},"responses":{"201":{"description":"Project created","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"badge_id":{"type":"string"},"plan":{"type":"string","enum":["free","pro","enterprise"]}},"required":["id","name","badge_id","plan"]},"write_key":{"type":"string"},"badge_url":{"type":"string"},"verify_url":{"type":"string"}},"required":["project","write_key","badge_url","verify_url"]}}}},"400":{"description":"The body is not valid. The message names the field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address, or this email already has 10 projects.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/results":{"post":{"summary":"Submit a test result","description":"Stores the latest result for the project. Send `run_id` to make retries safe: a repeated `run_id` returns 200 with `duplicate: true` and is not counted.","tags":["Results"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultInput"}}}},"responses":{"200":{"description":"Result stored, or already stored for this run_id","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["stored"]},"duplicate":{"type":"boolean"},"usage":{"type":"object","properties":{"month":{"type":"string"},"count":{"type":"number"},"limit":{"type":["number","null"]}},"required":["month","count","limit"]}},"required":["status","duplicate","usage"]}}}},"400":{"description":"The body is not valid. The message names the field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid write key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The body is larger than 1 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, daily cap or monthly limit reached. Check the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/keys/rotate":{"post":{"summary":"Rotate the write key","description":"Issues a new write key and revokes the one used for this request.","tags":["Keys"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"New key","content":{"application/json":{"schema":{"type":"object","properties":{"write_key":{"type":"string"}},"required":["write_key"]}}}},"401":{"description":"Missing or invalid write key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many rotations for this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/paddle":{"post":{"summary":"Paddle webhook (not enabled)","description":"Placeholder. Billing is not live yet.","tags":["Webhooks"],"responses":{"501":{"description":"Not implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{}}