{"openapi":"3.1.0","info":{"title":"BlackBook Quiz API","description":"\nStandalone REST API for the **BlackBook (Equifax Express) KBA credit-score quiz**.\n\n### Flow behind the main endpoint (`POST /api/v1/quiz`)\n1. `POST /api/leads-express` mints a lead under the shared FordDirect ID `1141004`\n   (a fresh random **DealerPACode** is rotated per attempt round).\n2. `PUT /api/leads-express/{leadId}` attaches the consumer identity (name, address,\n   phone, synthesized email) atomically with the mint.\n3. `POST /api/equifax-express/getQuestions` fetches the KBA questions.\n4. `POST /api/equifax-express/getScore` answers with a cross-worker **answer memory**\n   that converges Review loops; first score wins (default 2 workers).\n\nHard decisions (`Deny`, `Equifax rejection code not found`,\n`Insufficient Information to Score`) stop the run immediately.\n`429` anywhere burns the round lead and re-mints with a rotated PACode.\n\n### Auth model\n1. `POST /api/v1/auth/login` with an API key (`bbk_...`) -> **session token**.\n2. Sessions live **3 hours** (fixed, from login). Send every authenticated request as\n   `Authorization: Bearer <token>` (or `X-Session-Token`).\n3. Scopes: `quiz` can run quizzes and read its own runs; `admin` additionally\n   manages keys/sessions/runs in the panel (`/panel`) and `/api/v1/admin/*`.\n\nRequests are proxied through a rotating residential proxy (DataImpulse) by default.\nThe proxy is changeable three ways (precedence high → low): the request's `proxy`\nfield → the runtime admin setting (`PUT /api/v1/admin/settings`, no restart,\npersists in DB) → the `BBK_QUIZ_PROXY` env / built-in default. `\"\"` = direct.\nSupported schemes: socks5, socks5h, socks4, socks4a, http, https. The service\nnever silently bypasses the proxy — a dead proxy fails the run with\n`Proxy startup probe failed` in the description.\n\n### Result cache (permanent dedup)\nConclusive decisions (`Scored`, `Deny`, `Equifax rejection code not found`,\n`Insufficient Information to Score`) are cached per identity (name + address +\nzip; phone excluded) **permanently**: each person is checked once, and every\nre-check returns the stored decision instantly — `cached: true`,\n`cached_from_run_id` set, no new lead mints or runs. `force_refresh: true`\nre-runs and re-seeds; admins can reset a person or everyone via\n`DELETE /api/v1/admin/cache`. Inconclusive outcomes (`Retry exceeded`,\ntransient `Failed`) are never cached — those people are re-checked.\n\n### Async tasks (concurrent checks)\n`POST /api/v1/quiz` holds the connection for the whole run (use for single\nchecks). For **concurrency and batches** use the task service instead:\n`POST /api/v1/tasks` (one person) or `POST /api/v1/tasks/batch`\n(`{\"people\": [...]}`, up to 50) → 202 Accepted with task ids immediately;\n`GET /api/v1/tasks/{id}` to poll (`queued → running → completed/failed/\ncancelled`, `result` = the same payload as the sync endpoint when completed);\n`DELETE /api/v1/tasks/{id}` to cancel. Server-side concurrency cap\n(`BBK_MAX_CONCURRENT_TASKS`, default 4) with a FIFO queue (`queue_position`).\n\n> A quiz run takes seconds to a few minutes — use a client timeout of **300s**.\n","version":"1.0.0"},"paths":{"/api/v1":{"get":{"tags":["discovery"],"summary":"Machine-readable service index","operationId":"api_index_api_v1_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Api Index Api V1 Get"}}}}}}},"/api/v1/health":{"get":{"tags":["discovery"],"summary":"Health check","operationId":"health_api_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Api V1 Health Get"}}}}}}},"/api/v1/auth/login":{"post":{"tags":["auth"],"summary":"Exchange an API key for a session token (3h lifetime)","operationId":"login_api_v1_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/session":{"get":{"tags":["auth"],"summary":"Current session info","operationId":"whoami_api_v1_auth_session_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Whoami Api V1 Auth Session Get"}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Revoke the current session","operationId":"logout_api_v1_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Logout Api V1 Auth Logout Post"}}}}}}},"/api/v1/quiz":{"post":{"tags":["quiz"],"summary":"Run the full BlackBook quiz for one person","description":"Acquires everything the quiz needs — mints a BlackBook lead (FordDirect dealerid `1141004` + rotated DealerPACode), attaches the identity via PUT, fetches Equifax KBA questions and scores them with converging answer memory — then returns the final decision with a description.\n\nProvide either a one-line `address` (`\"<street>, <city>, <ST> <ZIP>\"`) or the structured `street`/`city`/`state`/`zip` fields (not both).\n\nReturns `decision` = `Scored` (with `score`), a hard Equifax decision (`Deny`, `Equifax rejection code not found`, `Insufficient Information to Score`), `Failed`, or `Retry exceeded`. Allow the client up to 300s.","operationId":"run_quiz_api_v1_quiz_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuizRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuizResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks":{"post":{"tags":["tasks"],"summary":"Submit an async quiz check (single person)","description":"Async task service for **concurrent checks with a single API**.\n\n- `POST /api/v1/tasks` — one person, returns immediately (202 Accepted) with a task id.\n- `POST /api/v1/tasks/batch` — up to 50 people per call.\n- `GET /api/v1/tasks/{id}` — poll until `status` is `completed` / `failed` / `cancelled`; when completed, `result` holds the **same payload as the synchronous POST /api/v1/quiz**. `completed` means a decision was produced — check `result.ok` and `result.decision`, it does NOT mean the person was approved.\n- `DELETE /api/v1/tasks/{id}` — cancel: queued tasks stop instantly; running ones stop at the next attempt boundary (a 409 means it already finished).\n\nThe server runs up to **8 checks simultaneously** (BBK_MAX_CONCURRENT_TASKS; each check itself uses 2 quiz workers); extra tasks queue FIFO with a `queue_position`. Per-key backlog is capped at **100 active tasks** (429 above that). Queued tasks survive server restarts; running ones are marked failed. A person already checked (conclusive result) is served from the permanent result cache instantly (`result.cached: true`) — send `force_refresh: true` to re-run them.","operationId":"submit_task_api_v1_tasks_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuizRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Submit Task Api V1 Tasks Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["tasks"],"summary":"List tasks","description":"Async task service for **concurrent checks with a single API**.\n\n- `POST /api/v1/tasks` — one person, returns immediately (202 Accepted) with a task id.\n- `POST /api/v1/tasks/batch` — up to 50 people per call.\n- `GET /api/v1/tasks/{id}` — poll until `status` is `completed` / `failed` / `cancelled`; when completed, `result` holds the **same payload as the synchronous POST /api/v1/quiz**. `completed` means a decision was produced — check `result.ok` and `result.decision`, it does NOT mean the person was approved.\n- `DELETE /api/v1/tasks/{id}` — cancel: queued tasks stop instantly; running ones stop at the next attempt boundary (a 409 means it already finished).\n\nThe server runs up to **8 checks simultaneously** (BBK_MAX_CONCURRENT_TASKS; each check itself uses 2 quiz workers); extra tasks queue FIFO with a `queue_position`. Per-key backlog is capped at **100 active tasks** (429 above that). Queued tasks survive server restarts; running ones are marked failed. A person already checked (conclusive result) is served from the permanent result cache instantly (`result.cached: true`) — send `force_refresh: true` to re-run them.\n\nFilters: `?status=`, `?ids=1,2,3`, `?mine=true` (admin). Admin sees all tasks; quiz keys see their own.","operationId":"list_tasks_api_v1_tasks_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(queued|running|completed|failed|cancelled)$"},{"type":"null"}],"title":"Status"}},{"name":"ids","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated task ids, e.g. 1,2,3","title":"Ids"},"description":"Comma-separated task ids, e.g. 1,2,3"},{"name":"mine","in":"query","required":false,"schema":{"type":"boolean","description":"Admin only: only this key's tasks","default":false,"title":"Mine"},"description":"Admin only: only this key's tasks"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Tasks Api V1 Tasks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/batch":{"post":{"tags":["tasks"],"summary":"Submit a batch of async quiz checks (up to 50)","description":"Async task service for **concurrent checks with a single API**.\n\n- `POST /api/v1/tasks` — one person, returns immediately (202 Accepted) with a task id.\n- `POST /api/v1/tasks/batch` — up to 50 people per call.\n- `GET /api/v1/tasks/{id}` — poll until `status` is `completed` / `failed` / `cancelled`; when completed, `result` holds the **same payload as the synchronous POST /api/v1/quiz**. `completed` means a decision was produced — check `result.ok` and `result.decision`, it does NOT mean the person was approved.\n- `DELETE /api/v1/tasks/{id}` — cancel: queued tasks stop instantly; running ones stop at the next attempt boundary (a 409 means it already finished).\n\nThe server runs up to **8 checks simultaneously** (BBK_MAX_CONCURRENT_TASKS; each check itself uses 2 quiz workers); extra tasks queue FIFO with a `queue_position`. Per-key backlog is capped at **100 active tasks** (429 above that). Queued tasks survive server restarts; running ones are marked failed. A person already checked (conclusive result) is served from the permanent result cache instantly (`result.cached: true`) — send `force_refresh: true` to re-run them.","operationId":"submit_task_batch_api_v1_tasks_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchTasksRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Submit Task Batch Api V1 Tasks Batch Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}":{"get":{"tags":["tasks"],"summary":"Task status + result (poll here)","description":"Async task service for **concurrent checks with a single API**.\n\n- `POST /api/v1/tasks` — one person, returns immediately (202 Accepted) with a task id.\n- `POST /api/v1/tasks/batch` — up to 50 people per call.\n- `GET /api/v1/tasks/{id}` — poll until `status` is `completed` / `failed` / `cancelled`; when completed, `result` holds the **same payload as the synchronous POST /api/v1/quiz**. `completed` means a decision was produced — check `result.ok` and `result.decision`, it does NOT mean the person was approved.\n- `DELETE /api/v1/tasks/{id}` — cancel: queued tasks stop instantly; running ones stop at the next attempt boundary (a 409 means it already finished).\n\nThe server runs up to **8 checks simultaneously** (BBK_MAX_CONCURRENT_TASKS; each check itself uses 2 quiz workers); extra tasks queue FIFO with a `queue_position`. Per-key backlog is capped at **100 active tasks** (429 above that). Queued tasks survive server restarts; running ones are marked failed. A person already checked (conclusive result) is served from the permanent result cache instantly (`result.cached: true`) — send `force_refresh: true` to re-run them.","operationId":"task_detail_api_v1_tasks__task_id__get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"integer","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Task Detail Api V1 Tasks  Task Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tasks"],"summary":"Cancel a task","description":"Async task service for **concurrent checks with a single API**.\n\n- `POST /api/v1/tasks` — one person, returns immediately (202 Accepted) with a task id.\n- `POST /api/v1/tasks/batch` — up to 50 people per call.\n- `GET /api/v1/tasks/{id}` — poll until `status` is `completed` / `failed` / `cancelled`; when completed, `result` holds the **same payload as the synchronous POST /api/v1/quiz**. `completed` means a decision was produced — check `result.ok` and `result.decision`, it does NOT mean the person was approved.\n- `DELETE /api/v1/tasks/{id}` — cancel: queued tasks stop instantly; running ones stop at the next attempt boundary (a 409 means it already finished).\n\nThe server runs up to **8 checks simultaneously** (BBK_MAX_CONCURRENT_TASKS; each check itself uses 2 quiz workers); extra tasks queue FIFO with a `queue_position`. Per-key backlog is capped at **100 active tasks** (429 above that). Queued tasks survive server restarts; running ones are marked failed. A person already checked (conclusive result) is served from the permanent result cache instantly (`result.cached: true`) — send `force_refresh: true` to re-run them.","operationId":"cancel_task_api_v1_tasks__task_id__delete","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"integer","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Cancel Task Api V1 Tasks  Task Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/runs":{"get":{"tags":["runs"],"summary":"List quiz runs","description":"Admin scope sees all runs; quiz scope sees only runs made with its key. `?mine=true` limits admins to their own key.","operationId":"list_runs_api_v1_runs_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"mine","in":"query","required":false,"schema":{"type":"boolean","description":"Admin only: only this key's runs","default":false,"title":"Mine"},"description":"Admin only: only this key's runs"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Runs Api V1 Runs Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/runs/{run_id}":{"get":{"tags":["runs"],"summary":"Quiz run detail (incl. flow log)","operationId":"run_detail_api_v1_runs__run_id__get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"integer","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Run Detail Api V1 Runs  Run Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/keys":{"get":{"tags":["admin"],"summary":"List API keys","operationId":"admin_list_keys_api_v1_admin_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Admin List Keys Api V1 Admin Keys Get"}}}}}},"post":{"tags":["admin"],"summary":"Create an API key (full key returned ONCE)","operationId":"admin_create_key_api_v1_admin_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Admin Create Key Api V1 Admin Keys Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/keys/{key_id}":{"patch":{"tags":["admin"],"summary":"Enable/disable an API key","operationId":"admin_patch_key_api_v1_admin_keys__key_id__patch","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer","title":"Key Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyPatchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Patch Key Api V1 Admin Keys  Key Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete an API key (revokes its sessions)","operationId":"admin_delete_key_api_v1_admin_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Delete Key Api V1 Admin Keys  Key Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/sessions":{"get":{"tags":["admin"],"summary":"List sessions","operationId":"admin_list_sessions_api_v1_admin_sessions_get","parameters":[{"name":"all","in":"query","required":false,"schema":{"type":"boolean","description":"Include expired/revoked sessions","default":false,"title":"All"},"description":"Include expired/revoked sessions"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin List Sessions Api V1 Admin Sessions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/sessions/{session_id}":{"delete":{"tags":["admin"],"summary":"Revoke a session","operationId":"admin_revoke_session_api_v1_admin_sessions__session_id__delete","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"integer","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Revoke Session Api V1 Admin Sessions  Session Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/stats":{"get":{"tags":["admin"],"summary":"Aggregate service stats","operationId":"admin_stats_api_v1_admin_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Admin Stats Api V1 Admin Stats Get"}}}}}}},"/api/v1/admin/settings":{"get":{"tags":["admin"],"summary":"Service settings (incl. effective quiz proxy + cache info)","operationId":"admin_get_settings_api_v1_admin_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Admin Get Settings Api V1 Admin Settings Get"}}}}}},"put":{"tags":["admin"],"summary":"Change quiz proxy / cache TTL at runtime (no restart)","operationId":"admin_update_settings_api_v1_admin_settings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Admin Update Settings Api V1 Admin Settings Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/cache":{"get":{"tags":["admin"],"summary":"List cached results (who has been checked)","description":"Every identity with a conclusive decision, newest first. Entries are permanent; DELETE this endpoint (optionally ?identity=<hash> from this list) to reset a person or everyone.","operationId":"admin_list_cache_api_v1_admin_cache_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin List Cache Api V1 Admin Cache Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Clear the result cache (or one person via ?identity=)","description":"The cache is permanent by design — this is the only reset besides per-request force_refresh. No params: clears everyone. ?identity=<hash>: resets that person only (hashes come from GET /api/v1/admin/cache).","operationId":"admin_clear_cache_api_v1_admin_cache_delete","parameters":[{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional single cache entry key to delete","title":"Identity"},"description":"Optional single cache entry key to delete"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Clear Cache Api V1 Admin Cache Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"BatchTasksRequest":{"properties":{"people":{"items":{"$ref":"#/components/schemas/QuizRequest"},"type":"array","maxItems":50,"minItems":1,"title":"People","description":"1-50 people; each entry has the same shape as POST /api/v1/quiz's body."}},"type":"object","required":["people"],"title":"BatchTasksRequest"},"CreateKeyRequest":{"properties":{"name":{"type":"string","maxLength":64,"minLength":1,"title":"Name","examples":["panel-worker"]},"scope":{"type":"string","pattern":"^(quiz|admin)$","title":"Scope","default":"quiz"}},"type":"object","required":["name"],"title":"CreateKeyRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"KeyPatchRequest":{"properties":{"disabled":{"type":"boolean","title":"Disabled"}},"type":"object","required":["disabled"],"title":"KeyPatchRequest"},"LoginRequest":{"properties":{"api_key":{"type":"string","title":"Api Key","description":"API key (bbk_...). May also be sent in the X-Api-Key header.","default":"","examples":["bbk_0123456789abcdef0123456789abcdef0123456789abcdef"]}},"type":"object","title":"LoginRequest"},"LoginResponse":{"properties":{"session_id":{"type":"integer","title":"Session Id"},"token":{"type":"string","title":"Token","description":"Session token; send as 'Authorization: Bearer <token>'"},"token_type":{"type":"string","title":"Token Type","default":"Bearer"},"expires_at":{"type":"string","title":"Expires At"},"expires_in":{"type":"integer","title":"Expires In","description":"Seconds until expiry (3h default)"},"key_name":{"type":"string","title":"Key Name"},"scope":{"type":"string","title":"Scope","description":"'admin' or 'quiz'"}},"type":"object","required":["session_id","token","expires_at","expires_in","key_name","scope"],"title":"LoginResponse"},"QuizPerson":{"properties":{"name":{"type":"string","title":"Name"},"firstname":{"type":"string","title":"Firstname"},"lastname":{"type":"string","title":"Lastname"},"street":{"type":"string","title":"Street"},"city":{"type":"string","title":"City"},"state":{"type":"string","title":"State"},"zip":{"type":"string","title":"Zip"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"}},"type":"object","required":["name","firstname","lastname","street","city","state","zip"],"title":"QuizPerson"},"QuizRequest":{"properties":{"name":{"type":"string","title":"Name","description":"Full name; first and last token are used.","examples":["William Tyers"]},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Optional; rides on the lead as phonenumber.","examples":["4849098909"]},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address","description":"One-line address: '<street>, <city>, <ST> <ZIP>'. Alternative to street/city/state/zip.","examples":["5727 Windjammer Ct, Madison, OH 44057"]},"street":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street","examples":["5727 Windjammer Ct"]},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","examples":["Madison"]},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State","description":"2-letter state code","examples":["OH"]},"zip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Zip","examples":["44057"]},"workers":{"anyOf":[{"type":"integer","maximum":4.0,"minimum":1.0},{"type":"null"}],"title":"Workers","description":"Override concurrent quiz workers (default 2)."},"retries_per_worker":{"anyOf":[{"type":"integer","maximum":30.0,"minimum":1.0},{"type":"null"}],"title":"Retries Per Worker","description":"Override retry budget per worker (default 15)."},"timeout_seconds":{"anyOf":[{"type":"number","maximum":60.0,"minimum":5.0},{"type":"null"}],"title":"Timeout Seconds","description":"Override per-request HTTP timeout (default 15s)."},"proxy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proxy","description":"Per-request proxy override, e.g. 'socks5://user:pass@gw.host:824' (supported schemes: socks5, socks5h, socks4, socks4a, http, https). Empty string = connect directly for this run. Omit/null = use the server's configured proxy (runtime setting > BBK_QUIZ_PROXY env).","examples":["socks5://user:pass@gw.example.com:824"]},"force_refresh":{"type":"boolean","title":"Force Refresh","description":"Bypass the result cache and run a fresh quiz (re-seeds the cache entry on conclusive outcomes).","default":false}},"type":"object","required":["name"],"title":"QuizRequest"},"QuizResponse":{"properties":{"run_id":{"type":"integer","title":"Run Id"},"ok":{"type":"boolean","title":"Ok","description":"True when a credit score was returned"},"status":{"type":"string","title":"Status","description":"'scored' | 'failed' | 'error' | 'cancelled'"},"decision":{"type":"string","title":"Decision","description":"'Scored' | 'Deny' | 'Equifax rejection code not found' | 'Insufficient Information to Score' | 'Failed' | 'Retry exceeded' | 'Error'"},"score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Score"},"description":{"type":"string","title":"Description"},"attempts":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Attempts"},"winner_worker_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Winner Worker Id"},"elapsed_sec":{"type":"number","title":"Elapsed Sec"},"proxy":{"type":"string","title":"Proxy","description":"Masked proxy used for this run (credentials stripped)"},"person":{"$ref":"#/components/schemas/QuizPerson"},"cached":{"type":"boolean","title":"Cached","description":"True when served from the result cache (no new quiz ran)","default":false},"cached_from_run_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cached From Run Id","description":"Original run that produced the cached result"},"lead_ids":{"items":{"type":"string"},"type":"array","title":"Lead Ids","description":"BlackBook leadIds minted during the run"},"flow_log":{"items":{"type":"string"},"type":"array","title":"Flow Log","description":"Round-by-round engine log (also in the panel's run detail)"},"engine_result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Engine Result"}},"type":"object","required":["run_id","ok","status","decision","description","elapsed_sec","proxy","person","lead_ids","flow_log"],"title":"QuizResponse"},"SettingsUpdate":{"properties":{"quiz_proxy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quiz Proxy","description":"Proxy URL (socks5://user:pass@gw.host:port; schemes: socks5, socks5h, socks4, socks4a, http, https), empty string = connect directly. null = clear the override (env default); omit the field to leave it unchanged."}},"type":"object","title":"SettingsUpdate"},"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"}}}}