{"openapi":"3.1.0","info":{"title":"prep50 Explanations API","description":"\nThe prep50 explanations API.\n\n    uvicorn api.server:app --host 127.0.0.1 --port 8100\n\nOther prep50 products send question ids and get back textbook-referenced\nexplanations: the explanation text, the books and printed pages it cites,\nand the model's verdict on the recorded answer. Work is asynchronous because\na question takes ~6 s of model time: POST returns a job id at once; the\ncaller polls GET /v1/jobs/{id} or supplies a callback URL. A question\nexplained before is returned immediately from the table at no cost.\n\nEvery call carries X-API-Key. Keys are made with api/keys.py, one per\nproduct, each with a request rate and a daily model budget; every request\nand every job's spend is logged per client (GET /v1/usage).\n\nWhat this API does NOT do: write into prep50's question bank. It returns\ncontent; publishing an explanation where students see it stays a decision\nthe owning product makes (and, for prep50 itself, a human one through\nexplain_questions.py apply). A 'held' result carries a draft and\nreview_required = true and must not be shown to students as it is.\n\nDocs: GET /docs (OpenAPI, generated).\n","version":"1.0"},"paths":{"/v1/health":{"get":{"tags":["service"],"summary":"Health","description":"Database reachable, queue depth, and when the worker last reported.","operationId":"health_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/explanations":{"post":{"tags":["explanations"],"summary":"Create Explanations","description":"Ask for explanations. Returns a job at once; questions explained\nbefore come back in it already finished, the rest are queued.","operationId":"create_explanations_v1_explanations_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExplainRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}":{"get":{"tags":["explanations"],"summary":"Get Job","description":"A job and its results so far. Poll until status is done or failed.","operationId":"get_job_v1_jobs__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/explanations/{question_id}":{"get":{"tags":["explanations"],"summary":"Get Explanation","description":"The explanation of one question if it has been generated, by any job\nor run. 404 if none exists yet: POST /v1/explanations to make one.","operationId":"get_explanation_v1_explanations__question_id__get","parameters":[{"name":"question_id","in":"path","required":true,"schema":{"type":"integer","title":"Question Id"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExplanationResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/usage":{"get":{"tags":["service"],"summary":"Usage","description":"This key's requests, questions explained, tokens and model spend per UTC day.","operationId":"usage_v1_usage_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"ExplainRequest":{"properties":{"question_ids":{"items":{"type":"integer"},"type":"array","maxItems":500,"minItems":1,"title":"Question Ids","description":"prep50 question ids"},"callback_url":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"null"}],"title":"Callback Url","description":"POSTed the finished job"}},"type":"object","required":["question_ids"],"title":"ExplainRequest"},"ExplanationResult":{"properties":{"question_id":{"type":"integer","title":"Question Id"},"status":{"type":"string","title":"Status","description":"ready | held | skipped | failed"},"hold_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hold Reason"},"review_required":{"type":"boolean","title":"Review Required"},"explanation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Explanation","description":"Markdown with $...$ maths; references appended"},"references":{"items":{"$ref":"#/components/schemas/Reference"},"type":"array","title":"References"},"key_verdict":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Verdict","description":"The model's view of the recorded answer: AGREE | DISAGREE | UNCLEAR"},"cited_chunk_ids":{"items":{"type":"integer"},"type":"array","title":"Cited Chunk Ids"},"cached":{"type":"boolean","title":"Cached"},"cost_usd":{"type":"number","title":"Cost Usd"},"generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Generated At"}},"type":"object","required":["question_id","status","review_required","references","cited_chunk_ids","cached","cost_usd"],"title":"ExplanationResult"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Job":{"properties":{"job_id":{"type":"string","title":"Job Id"},"status":{"type":"string","title":"Status"},"total":{"type":"integer","title":"Total"},"finished":{"type":"integer","title":"Finished"},"created_at":{"type":"string","title":"Created At"},"started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Started At"},"finished_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Finished At"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"results":{"items":{"$ref":"#/components/schemas/ExplanationResult"},"type":"array","title":"Results","default":[]}},"type":"object","required":["job_id","status","total","finished","created_at"],"title":"Job"},"Reference":{"properties":{"book":{"type":"string","title":"Book"},"pages":{"type":"string","title":"Pages"}},"type":"object","required":["book","pages"],"title":"Reference"},"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"}}}}