{"openapi":"3.1.0","info":{"title":"MyRadAgent Cloud","version":"2.0"},"paths":{"/api/admin/pending-approvals":{"get":{"tags":["admin"],"summary":"List Pending Approvals","description":"Quick list of users awaiting approval. Drives the admin review UI.","operationId":"list_pending_approvals_api_admin_pending_approvals_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/users/{user_id}/approve":{"post":{"tags":["admin"],"summary":"Approve User","description":"One-click approve a pending user. Sends best-effort notification email.","operationId":"approve_user_api_admin_users__user_id__approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users":{"get":{"tags":["admin"],"summary":"List Users","operationId":"list_users_api_admin_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["admin"],"summary":"Create User","operationId":"create_user_api_admin_users_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUser"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/users/{user_id}":{"put":{"tags":["admin"],"summary":"Update User","operationId":"update_user_api_admin_users__user_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUser"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete User","description":"Delete a user permanently (for rejected registrations).","operationId":"delete_user_api_admin_users__user_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/revoke-tokens":{"post":{"tags":["admin"],"summary":"Admin Revoke User Tokens","description":"Admin force-logout: kill every JWT outstanding for the target user.\n\nUse cases: ex-employee account, suspected credential compromise, user\nreport of \"I think someone got into my account.\" Implementation is\none timestamp write — every is_token_revoked() check rejects tokens\nwith iat <= this column. Does NOT revoke our own (admin's) token,\nso the admin stays logged in to keep working.\n\nRe-login by the target user mints a new token with a fresh iat that\nis unaffected by the cutoff, so this is non-permanent — it only\ninvalidates the current outstanding tokens, not future ones.","operationId":"admin_revoke_user_tokens_api_admin_users__user_id__revoke_tokens_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/reset-password":{"post":{"tags":["admin"],"summary":"Reset User Password","description":"Generate a temporary password for a user.","operationId":"reset_user_password_api_admin_users__user_id__reset_password_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/unlock":{"post":{"tags":["admin"],"summary":"Unlock User","description":"Clear account lockout (locked_until + failed_login_count).","operationId":"unlock_user_api_admin_users__user_id__unlock_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/toggle-overage":{"post":{"tags":["admin"],"summary":"Toggle Overage","description":"Toggle overage_billing_enabled for a user.","operationId":"toggle_overage_api_admin_users__user_id__toggle_overage_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/prompts":{"get":{"tags":["admin"],"summary":"List Prompts","operationId":"list_prompts_api_admin_prompts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["admin"],"summary":"Upsert Prompt","operationId":"upsert_prompt_api_admin_prompts_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/analytics":{"get":{"tags":["admin"],"summary":"Admin Analytics","description":"System-wide analytics dashboard data.","operationId":"admin_analytics_api_admin_analytics_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/audit":{"get":{"tags":["admin"],"summary":"Audit Log","operationId":"audit_log_api_admin_audit_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/teaching":{"get":{"tags":["admin"],"summary":"List Teaching","operationId":"list_teaching_api_admin_teaching_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/export":{"get":{"tags":["admin"],"summary":"Export All Data","description":"Export ALL training data, settings, prompts, templates for analysis.\nAdmin only. Returns JSON with everything needed to analyze and improve the AI.","operationId":"export_all_data_api_admin_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/reports/{report_id}/teaching":{"put":{"tags":["admin"],"summary":"Toggle Teaching","description":"Mark/unmark a report as a teaching case (shared across all users' RAG).","operationId":"toggle_teaching_api_admin_reports__report_id__teaching_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"integer","title":"Report Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/export/sqlite":{"get":{"tags":["admin"],"summary":"Export Sqlite Db","description":"Export training data as SQLite (rad_feedback.db).\nDefault scope=self (admin's own reports only). scope=all is break-glass:\nrequires explicit confirm_all=true since it returns every user's PHI.","operationId":"export_sqlite_db_api_admin_export_sqlite_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"scope","in":"query","required":false,"schema":{"type":"string","default":"self","title":"Scope"}},{"name":"confirm_all","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Confirm All"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/teaching-images":{"get":{"tags":["admin"],"summary":"List Teaching Images","description":"List all teaching images (without base64 data for speed).","operationId":"list_teaching_images_api_admin_teaching_images_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["admin"],"summary":"Create Teaching Image","description":"Upload a new teaching reference image.","operationId":"create_teaching_image_api_admin_teaching_images_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeachingImageCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/teaching-images/{image_id}":{"get":{"tags":["admin"],"summary":"Get Teaching Image","description":"Get full teaching image with base64 data.","operationId":"get_teaching_image_api_admin_teaching_images__image_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["admin"],"summary":"Update Teaching Image","description":"Update a teaching image (description, criteria, tags).","operationId":"update_teaching_image_api_admin_teaching_images__image_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeachingImageCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete Teaching Image","description":"Delete a teaching image.","operationId":"delete_teaching_image_api_admin_teaching_images__image_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/teaching-images/{image_id}/toggle":{"put":{"tags":["admin"],"summary":"Toggle Teaching Image","description":"Activate/deactivate a teaching image.","operationId":"toggle_teaching_image_api_admin_teaching_images__image_id__toggle_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/corrected-images":{"get":{"tags":["admin"],"summary":"List Corrected Images","description":"List all corrected images in the learning library.","operationId":"list_corrected_images_api_admin_corrected_images_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/corrected-images/{image_id}":{"get":{"tags":["admin"],"summary":"Get Corrected Image","description":"Get full details of a corrected image.","operationId":"get_corrected_image_api_admin_corrected_images__image_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete Corrected Image","description":"Delete a corrected image from the learning library.","operationId":"delete_corrected_image_api_admin_corrected_images__image_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/learning-stats":{"get":{"tags":["admin"],"summary":"Learning Stats","description":"Analytics on image learning effectiveness.","operationId":"learning_stats_api_admin_learning_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/teaching-images/bulk":{"post":{"tags":["admin"],"summary":"Bulk Import Teaching","description":"Bulk import teaching rules (no images required).","operationId":"bulk_import_teaching_api_admin_teaching_images_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkRuleImport"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/models":{"get":{"tags":["admin"],"summary":"Get Model Status Endpoint","description":"Get current model versions and available updates.","operationId":"get_model_status_endpoint_api_admin_models_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/models/check":{"post":{"tags":["admin"],"summary":"Force Model Check","description":"Force an immediate model version check.","operationId":"force_model_check_api_admin_models_check_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/models/update":{"post":{"tags":["admin"],"summary":"Update Model Endpoint","description":"Update a specific provider's model. Body: {provider, model}","operationId":"update_model_endpoint_api_admin_models_update_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Req"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/models/revert":{"post":{"tags":["admin"],"summary":"Revert Model Endpoint","description":"Revert a provider to its previous model. Body: {provider}","operationId":"revert_model_endpoint_api_admin_models_revert_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Req"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/models/auto-update":{"post":{"tags":["admin"],"summary":"Toggle Auto Update","description":"Toggle auto-update. Body: {enabled: true/false}","operationId":"toggle_auto_update_api_admin_models_auto_update_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Req"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/models/history":{"get":{"tags":["admin"],"summary":"Get Model History Endpoint","description":"Get model version history for rollback.","operationId":"get_model_history_endpoint_api_admin_models_history_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/models/audit":{"post":{"tags":["admin"],"summary":"Audit Models Endpoint","description":"A/B compare old vs new chat-compat filter against live provider data.\n\nBody (optional): {live_probe: bool}\n    - false (default): just diff old_filter vs new_filter from one\n      listing snapshot per provider, with per-id drop reasons.\n    - true: ALSO ping each new_filter OpenAI model with\n      chat.completions.create (max_tokens=1) to confirm chat\n      compatibility end-to-end. Slower (~1s per model).\n\nReturns the structured trace from audit_model_filter() — see ai_engine.py.","operationId":"audit_models_endpoint_api_admin_models_audit_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Req"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/models/verify":{"post":{"tags":["admin"],"summary":"Verify Models Live","description":"Ping each AI provider with a 1-token test call to verify connectivity.\nReturns latency and actual model string for each provider.","operationId":"verify_models_live_api_admin_models_verify_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/enable-pgvector":{"post":{"tags":["admin"],"summary":"Enable Pgvector","description":"Check and enable pgvector extension on the PostgreSQL instance.","operationId":"enable_pgvector_api_admin_enable_pgvector_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/backfill":{"post":{"tags":["admin"],"summary":"Backfill Reports","description":"One-time backfill: auto-flag teaching candidates, compute confidence,\nset source_type, and fix missing fields on all existing reports.","operationId":"backfill_reports_api_admin_backfill_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/backfill-embeddings":{"post":{"tags":["admin"],"summary":"Backfill Embeddings","description":"Compute and store pgvector embeddings for all reports missing them.","operationId":"backfill_embeddings_api_admin_backfill_embeddings_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/backfill-image-embeddings":{"post":{"tags":["admin"],"summary":"Backfill Image Embeddings","description":"Backfill embeddings for corrected images that have image data but no embedding.\nUses AI to describe each image, then embeds the description for Layer 3 matching.","operationId":"backfill_image_embeddings_api_admin_backfill_image_embeddings_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/backfill-phi-redaction":{"post":{"tags":["admin"],"summary":"Backfill Phi Redaction","description":"HIPAA: Retroactively redact PHI from original and ai_output fields in all reports.\nNon-destructive: only processes fields that contain potential PHI patterns.","operationId":"backfill_phi_redaction_api_admin_backfill_phi_redaction_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/backfill-metadata":{"post":{"tags":["admin"],"summary":"Backfill Report Metadata","description":"Backfill structured metadata for all reports missing it.\nZero AI cost — pure regex extraction from finalized text.","operationId":"backfill_report_metadata_api_admin_backfill_metadata_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/backfill-impression-memory":{"post":{"tags":["admin"],"summary":"Backfill Impression Memory","description":"Retroactively generate impression memories from edited reports.\nExtracts impression section from original AI output and finalized text,\nsaves the correction pattern for future style matching.","operationId":"backfill_impression_memory_api_admin_backfill_impression_memory_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/backfill-vision-signatures":{"post":{"tags":["admin"],"summary":"Backfill Vision Signatures","description":"Backfill vision signatures from AI output text on existing reports and corrected images.","operationId":"backfill_vision_signatures_api_admin_backfill_vision_signatures_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/atlas":{"get":{"tags":["admin"],"summary":"Atlas Browse","description":"Browse the RadioPedia atlas. Filter by body_part, modality, or search.","operationId":"atlas_browse_api_admin_atlas_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"body_part","in":"query","required":false,"schema":{"type":"string","default":"","title":"Body Part"}},{"name":"modality","in":"query","required":false,"schema":{"type":"string","default":"","title":"Modality"}},{"name":"search","in":"query","required":false,"schema":{"type":"string","default":"","title":"Search"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/atlas/{record_id}":{"get":{"tags":["admin"],"summary":"Atlas Detail","description":"Get full atlas record including image.","operationId":"atlas_detail_api_admin_atlas__record_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"record_id","in":"path","required":true,"schema":{"type":"integer","title":"Record Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Atlas Delete","description":"Delete an atlas record.","operationId":"atlas_delete_api_admin_atlas__record_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"record_id","in":"path","required":true,"schema":{"type":"integer","title":"Record Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/atlas-stats":{"get":{"tags":["admin"],"summary":"Atlas Stats","description":"Get atlas statistics overview.","operationId":"atlas_stats_api_admin_atlas_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/blog/digest-draft":{"post":{"tags":["admin"],"summary":"Blog Digest Draft","description":"Generate an AI draft summary of the top-N aggregated blog entries for\nthe given category. Admin-only; output is a draft the admin must review\nbefore publishing. Does not persist anything.","operationId":"blog_digest_draft_api_admin_blog_digest_draft_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"top_n","in":"query","required":false,"schema":{"type":"integer","default":8,"title":"Top N"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"clinical","title":"Category"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog/cache":{"get":{"tags":["admin"],"summary":"List Blog Cache","operationId":"list_blog_cache_api_admin_blog_cache_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"include_hidden","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Hidden"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog/cache/{cache_id}/feature":{"post":{"tags":["admin"],"summary":"Toggle Feature Blog Entry","operationId":"toggle_feature_blog_entry_api_admin_blog_cache__cache_id__feature_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"cache_id","in":"path","required":true,"schema":{"type":"integer","title":"Cache Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog/cache/{cache_id}/hide":{"post":{"tags":["admin"],"summary":"Toggle Hide Blog Entry","description":"Hide a cached analysis from /blog (404s the article page without deleting).","operationId":"toggle_hide_blog_entry_api_admin_blog_cache__cache_id__hide_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"cache_id","in":"path","required":true,"schema":{"type":"integer","title":"Cache Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog/cache/{cache_id}":{"delete":{"tags":["admin"],"summary":"Delete Blog Cache Entry","description":"Permanently purge a cached analysis (next /api/blog/analyze will regenerate).","operationId":"delete_blog_cache_entry_api_admin_blog_cache__cache_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"cache_id","in":"path","required":true,"schema":{"type":"integer","title":"Cache Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog/sources":{"get":{"tags":["admin"],"summary":"List Blog Sources","description":"Per-feed source health: latency, last entry count, consecutive failures.\nPersisted across redeploys via BlogSourceHealth table.\nPass ?refresh=true to force re-fetch all feeds (bypass 6h cache).","operationId":"list_blog_sources_api_admin_blog_sources_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog/ab-results":{"get":{"tags":["admin"],"summary":"Blog Ab Results","description":"Compare engagement metrics between variant A and B over last N days.\nReturns: unique sessions, page views, click-through rate, scroll depth,\narticle-view rate per variant.","operationId":"blog_ab_results_api_admin_blog_ab_results_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/fallback-stats":{"get":{"tags":["admin"],"summary":"Fallback Stats","description":"Infer fallback rates from existing `usage_log.models_invoked` JSON —\nany call whose models_invoked list has length > 1 ran a fallback engine.\nNo schema change needed.\n\nReturns per-endpoint breakdown: total calls, calls with fallbacks,\nfallback rate, and which engines commonly run together.","operationId":"fallback_stats_api_admin_fallback_stats_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog/guideline-hits":{"get":{"tags":["admin"],"summary":"Blog Guideline Hits","description":"Surface blog entries whose title/summary mentions a radiology guideline\nkeyword (BI-RADS, Fleischner, TI-RADS, etc.). Useful for deciding when to\nrefresh guideline_enforcer rules against new official publications.","operationId":"blog_guideline_hits_api_admin_blog_guideline_hits_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/blog/stats":{"get":{"tags":["admin"],"summary":"Blog Stats","description":"Audit dashboard for blog AI usage: how many summaries, top-read, per model.","operationId":"blog_stats_api_admin_blog_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/career/guides":{"get":{"tags":["admin"],"summary":"List Career Guides","description":"List all editorial career guides. Admin-only.","operationId":"list_career_guides_api_admin_career_guides_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Archived"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Upsert Career Guide","description":"Create or update a guide keyed by slug. Invalidates body_html cache.","operationId":"upsert_career_guide_api_admin_career_guides_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CareerGuideUpsert"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/career/guides/{guide_id}":{"get":{"tags":["admin"],"summary":"Get Career Guide","description":"Fetch full guide including body_markdown for editing.","operationId":"get_career_guide_api_admin_career_guides__guide_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"guide_id","in":"path","required":true,"schema":{"type":"integer","title":"Guide Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete Career Guide","description":"Soft-delete by archiving (set status='archived').","operationId":"delete_career_guide_api_admin_career_guides__guide_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"guide_id","in":"path","required":true,"schema":{"type":"integer","title":"Guide Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/career/guide-stats":{"get":{"tags":["admin"],"summary":"Career Guide Stats","description":"Per-guide view + scroll-depth summary over the last N days.","operationId":"career_guide_stats_api_admin_career_guide_stats_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/login":{"post":{"summary":"Login","operationId":"login_api_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/register":{"post":{"summary":"Register","operationId":"register_api_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/reset-password":{"post":{"summary":"Reset Password","description":"Admin-only password reset.","operationId":"reset_password_api_auth_reset_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/auth/me":{"get":{"summary":"Get Me","operationId":"get_me_api_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/auth/logout":{"post":{"summary":"Logout","description":"Clear the JWT cookie + record an explicit revocation for the\npresented token's jti. Idempotent — safe to call anonymous.\n\nJS still has to clear localStorage on its side; this endpoint exists so\nthat the server-side cookie is cleared in the same round-trip, the\naudit log records the explicit logout, AND the token's jti goes into\nthe revocation list so a copy of the token from logs/clipboard cannot\nbe reused after the user logs out.","operationId":"logout_api_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/sessions/revoke-all":{"post":{"summary":"Revoke All Sessions","description":"User-self mass revoke: kill every JWT issued for me up to this\nsecond. Use case: \"I lost my laptop, sign me out everywhere.\"\n\nImplementation is one timestamp write — every is_token_revoked()\ncheck compares the token's iat against this column and rejects if\niat <= cutoff. Tokens minted AFTER this call (e.g. the new login\nthe user does to recover) are unaffected because their iat is later.\n\nAlso clears the current request's cookies so the calling tab is\nimmediately signed out without an extra login round trip.","operationId":"revoke_all_sessions_api_auth_sessions_revoke_all_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/models/current":{"get":{"summary":"Get Current Models Public","description":"Current model id per provider, for the user-facing AI Model selector.\n\nSurfaces the live CURRENT_MODELS dict so the \"Powered by:\" subtitle\nunder the AI Model toggle reflects whatever the admin most recently\nswapped to. Authenticated users only — model strings aren't secret\nbut no need to expose them to anonymous traffic.","operationId":"get_current_models_public_api_models_current_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/perfect":{"post":{"summary":"Perfect Report","operationId":"perfect_report_api_perfect_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerfectRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/confirm":{"post":{"summary":"Confirm Report","description":"Confirm report and train on this user's data.","operationId":"confirm_report_api_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/redo":{"post":{"summary":"Redo Report","operationId":"redo_report_api_redo_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedoRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/challenge":{"post":{"summary":"Challenge","operationId":"challenge_api_challenge_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChallengeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/challenge/save":{"post":{"summary":"Save Challenge Learning","description":"Save challenge learning from desktop without making AI calls.\nDesktop handles multi-turn conversation + image context; we save results to PostgreSQL.","operationId":"save_challenge_learning_api_challenge_save_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChallengeSaveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/reports/import":{"post":{"summary":"Import Gold Standard Reports","description":"Batch import finalized reports as gold-standard training data.\nThese are real signed reports — highest learning value (edit_weight=0.90).\nGenerates embeddings, detects study type, stores for RAG and style learning.","operationId":"import_gold_standard_reports_api_reports_import_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportReportsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/nightly-eval":{"post":{"summary":"Nightly Self Evaluation","description":"AI self-evaluation: reviews recent reports, flags potential errors, generates morning brief.\nRuns as admin-triggered endpoint (can be scheduled via Railway cron).","operationId":"nightly_self_evaluation_api_admin_nightly_eval_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NightlyEvalRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/morning-brief":{"get":{"summary":"Get Morning Brief","description":"Get the most recent nightly evaluation results (morning brief).","operationId":"get_morning_brief_api_morning_brief_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/visits/summary":{"get":{"summary":"Admin Visits Summary","description":"Site visit aggregations for the admin Site Analytics tab.\n\nArgs:\n    days:          Look-back window (1-90, default 7).\n    include_bots:  1 to include known crawlers, 0 to exclude.\n    flush:         1 to drain the in-memory buffer before aggregating\n                   so the dashboard reflects the very latest visits\n                   (use sparingly — adds a small write).","operationId":"admin_visits_summary_api_admin_visits_summary_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"include_bots","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Include Bots"}},{"name":"flush","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Flush"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/visits/recent":{"get":{"summary":"Admin Visits Recent","description":"Live-tail view: newest N visits, newest first.","operationId":"admin_visits_recent_api_admin_visits_recent_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"include_bots","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Include Bots"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/visits/sessions":{"get":{"summary":"Admin Visits Sessions","description":"Reconstructed sessions (30-min idle gap), newest first.\n\nEach session bundles consecutive visits from the same ip_hash\ninto a journey: landing page, exit page, page sequence, duration,\nUA family, country. Powers the expandable \"Sessions\" table in\nthe admin Site Analytics tab.","operationId":"admin_visits_sessions_api_admin_visits_sessions_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"include_bots","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Include Bots"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/usage":{"get":{"summary":"Admin Usage","description":"Admin-only cost + usage dashboard. Aggregates usage_log over a window.\n\nQuery params:\n  days — look-back window (default 30)\nReturns:\n  totals, daily trend, per-endpoint, per-model, per-user, top expensive calls","operationId":"admin_usage_api_admin_usage_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/my/costs-summary":{"get":{"summary":"My Costs Summary","description":"Admin-only cost summary for desktop status bar.\n\nCached per-user for 30s — desktop polls every 60s, but multi-session and\nduplicate clients otherwise hammer this with a 2x SUM over UsageLog.\nCache hit serves from memory in microseconds; cache miss runs the queries\nand refreshes.","operationId":"my_costs_summary_api_my_costs_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/shift-stats":{"get":{"summary":"Admin Shift Stats","description":"Admin-only operational telemetry — designed to mirror what we'd want to\nreview after a busy shift.\n\nQuery params:\n  hours — look-back window in hours (default 24, max 168 = 1 week)\n\nReturns:\n  window: {hours, since, until}\n  utilization: {total_calls, calls_per_hour, success_rate, hourly_buckets[]}\n  latency_by_endpoint: [{endpoint, calls, mean_ms, p50_ms, p95_ms}]\n  latency_by_ai_mode: [{ai_mode, calls, mean_ms, p50_ms, p95_ms}]\n  crashes: {total, by_endpoint[], by_status_code[], recent[]}\n  delta_distribution: {zero_edit, small (≤5%), medium (≤15%), large (>15%), buckets}","operationId":"admin_shift_stats_api_admin_shift_stats_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/usage/csv":{"get":{"summary":"Admin Usage Csv","description":"Export usage_log as CSV for accounting.","operationId":"admin_usage_csv_api_admin_usage_csv_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/config/keys":{"get":{"summary":"Get Config Keys","description":"Return API keys + model names to admin desktop clients for direct AI calls.\nAdmin-only — keys are sensitive. HIPAA: audit-logged.","operationId":"get_config_keys_api_config_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/context/cache":{"post":{"summary":"Get Context Cache","description":"Return all learning context for desktop direct-call mode.\nServer-side cache per user — invalidated on each confirm. ~50ms on cache hit.\nUses Redis when available (works across workers), falls back to in-memory.","operationId":"get_context_cache_api_context_cache_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextCacheRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/build-prompt":{"post":{"summary":"Build Prompt For Desktop","description":"Return the fully-assembled prompt + system instruction for desktop DirectAI.\n\nDesktop just sends this prompt to GPT/Claude — zero local prompt construction.\nCloud is the single brain for prompt building: RAG, impression memory,\ncorrection warnings, admin rules, style notes, templates, MIPS, guidelines\nare all baked in. This eliminates the desktop quality gap.","operationId":"build_prompt_for_desktop_api_build_prompt_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildPromptRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/version":{"get":{"summary":"Get Version","description":"Public — returns current desktop client version + build hash.\nDesktop uses build_hash to detect ANY code change (no manual version bump needed).","operationId":"get_version_api_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/health":{"get":{"summary":"Health","description":"Public health check — no auth required.","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/status":{"get":{"summary":"Status","operationId":"status_api_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/config/gemini-key":{"get":{"summary":"Get Gemini Key","description":"Return Gemini API key to admin desktop clients only.\nUsed by Volume Intelligence for local Gemini video screening.\nHIPAA: restricted to admin role — keys are sensitive credentials.","operationId":"get_gemini_key_api_config_gemini_key_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/my/analytics":{"get":{"summary":"My Analytics","operationId":"my_analytics_api_my_analytics_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/analytics/practice":{"get":{"summary":"Practice Analytics","description":"Practice-oriented analytics dashboard.\n\nLayers on top of /api/my/analytics with:\n- Accuracy & time-savings trend by month (last 12)\n- Per-study-type performance (now that study_type column is populated)\n- MIPS compliance rates, re-scanned on-demand for recent reports\n- Correction-type trend over recent weeks\n\nRead-only. No DB writes. Expensive queries are bounded by time window.","operationId":"practice_analytics_api_analytics_practice_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/my/reports":{"get":{"summary":"My Reports","operationId":"my_reports_api_my_reports_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":500,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/macros":{"get":{"summary":"Get Macros","description":"Get all macros (shared + user's own).","operationId":"get_macros_api_macros_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"summary":"Create Macro","description":"Create a new macro.","operationId":"create_macro_api_macros_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MacroCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/macros/{macro_id}":{"delete":{"summary":"Delete Macro","description":"Delete a macro (only own macros or admin).","operationId":"delete_macro_api_macros__macro_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"macro_id","in":"path","required":true,"schema":{"type":"integer","title":"Macro Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/templates":{"get":{"summary":"Get Templates","operationId":"get_templates_api_templates_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"summary":"Create Template","operationId":"create_template_api_templates_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/templates/{template_id}":{"delete":{"summary":"Delete Template","operationId":"delete_template_api_templates__template_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"integer","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Update Template","operationId":"update_template_api_templates__template_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"integer","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/templates/{template_id}/toggle":{"put":{"summary":"Toggle Template","operationId":"toggle_template_api_templates__template_id__toggle_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"integer","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/merge":{"post":{"summary":"Merge Worksheets","description":"Merge worksheet data into report — same as F11 with snips on desktop.","operationId":"merge_worksheets_api_merge_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/multiview":{"post":{"summary":"Multiview Analysis","description":"Multi-view study analysis — same consensus logic as X-Ray Vision.","operationId":"multiview_analysis_api_multiview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MultiViewRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/xray":{"post":{"summary":"Xray Analysis","description":"X-Ray Vision — AI analysis with multi-model consensus + teaching library.","operationId":"xray_analysis_api_xray_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/XRayRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/impression":{"post":{"summary":"Impression Only","description":"Rewrite only the IMPRESSION section of a report.","operationId":"impression_only_api_impression_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImpressionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/batch":{"post":{"summary":"Batch Process","description":"Process multiple reports at once.","operationId":"batch_process_api_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/my/settings":{"get":{"summary":"Get Settings","description":"Get user preferences.","operationId":"get_settings_api_my_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"put":{"summary":"Update Settings","description":"Update user preferences, style notes, and billing preferences.","operationId":"update_settings_api_my_settings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/my/billing-status":{"get":{"summary":"My Billing Status","description":"User-facing billing status: tier, quota, period, overage state.","operationId":"my_billing_status_api_my_billing_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/my/billing-portal":{"post":{"summary":"My Billing Portal","description":"Create a Stripe Customer Portal session for self-service billing.","operationId":"my_billing_portal_api_my_billing_portal_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/my/style-rules":{"get":{"summary":"Get Style Rules","operationId":"get_style_rules_api_my_style_rules_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"summary":"Create Style Rule","operationId":"create_style_rule_api_my_style_rules_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StyleRuleCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/my/style-rules/{rule_id}":{"put":{"summary":"Update Style Rule","operationId":"update_style_rule_api_my_style_rules__rule_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"integer","title":"Rule Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StyleRuleCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Style Rule","operationId":"delete_style_rule_api_my_style_rules__rule_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"integer","title":"Rule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/my/style-rules/{rule_id}/toggle":{"put":{"summary":"Toggle Style Rule","operationId":"toggle_style_rule_api_my_style_rules__rule_id__toggle_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"integer","title":"Rule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/fix-weights":{"post":{"summary":"Fix Historical Weights","description":"Fix historical report weights that were incorrectly set to 0.25.\nRecalculates delta from original/ai_output vs finalized, and applies\ncorrection_type boost. Admin only.","operationId":"fix_historical_weights_api_admin_fix_weights_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/clear-migrated":{"post":{"summary":"Clear Migrated","description":"Clear all migrated reports for a clean re-import. Admin only.\nUses require_admin dependency (defense-in-depth) instead of inline check.","operationId":"clear_migrated_api_admin_clear_migrated_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/migrate":{"post":{"summary":"Bulk Migrate","description":"Bulk import training cases from desktop app. Admin only.","operationId":"bulk_migrate_api_admin_migrate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkImportRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/agent/paste":{"post":{"summary":"Agent Paste","description":"Tell the desktop agent to paste to PowerScribe.","operationId":"agent_paste_api_agent_paste_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/agent/status":{"post":{"summary":"Agent Status","description":"Receive status from desktop agent and broadcast to webapp via WebSocket.","operationId":"agent_status_api_agent_status_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentStatusUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/ai/generate":{"post":{"summary":"Ai Generate Sync","description":"Synchronous AI generation — used by desktop thin client.","operationId":"ai_generate_sync_api_ai_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/ai/rag":{"post":{"summary":"Ai Rag","description":"Find similar past reports — used by desktop thin client for context injection.","operationId":"ai_rag_api_ai_rag_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RAGRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/ai/ocr":{"post":{"summary":"Ai Ocr","description":"OCR an image via AI — used by desktop thin client for worksheet extraction.","operationId":"ai_ocr_api_ai_ocr_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OCRRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/ai/extract-teaching":{"post":{"summary":"Extract Teaching Data","description":"Smart extraction: AI analyzes an uploaded teaching image and extracts\nstructured teaching data (name, modality, body_part, category, tags, \ndescription, findings_criteria). Auto-populates the teaching image form.","operationId":"extract_teaching_data_api_ai_extract_teaching_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeachingExtractRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/ai/smart-analyze":{"post":{"summary":"Smart Analyze Image","description":"Smart image classification + analysis.\nDetermines if an image is a worksheet/table or a radiological study,\nthen routes to the appropriate analysis pipeline.","operationId":"smart_analyze_image_api_ai_smart_analyze_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmartAnalyzeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/teach/chat":{"post":{"summary":"Teach Chat","description":"Interactive teaching chat — radiologist teaches the AI through conversation.\nAI asks clarifying questions, learns from explanations, and can save teachings.","operationId":"teach_chat_api_teach_chat_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeachChatMessage"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/teach/save":{"post":{"summary":"Save Teaching","description":"Extract key teachings from conversation and save permanently.\nAI analyzes the conversation and creates appropriate teaching entries.","operationId":"save_teaching_api_teach_save_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveTeachingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/agent/download":{"get":{"summary":"Download Agent","description":"Serve the desktop agent for download.","operationId":"download_agent_api_agent_download_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/robots.txt":{"get":{"summary":"Robots","operationId":"robots_robots_txt_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/favicon.ico":{"get":{"summary":"Favicon","operationId":"favicon_favicon_ico_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Index","description":"Public marketing landing page (SEO-indexable). The webapp lives at /app.","operationId":"index__get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/app/{path}":{"get":{"summary":"Webapp","description":"Authenticated webapp shell. SPA handles client-side routing.\nServes minified HTML (comments stripped, blank-line runs collapsed)\nto reduce wire size and slow down casual source-scraping.","operationId":"webapp_app__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/app":{"get":{"summary":"Webapp","description":"Authenticated webapp shell. SPA handles client-side routing.\nServes minified HTML (comments stripped, blank-line runs collapsed)\nto reduce wire size and slow down casual source-scraping.","operationId":"webapp_app_get","parameters":[{"name":"path","in":"query","required":false,"schema":{"type":"string","default":"","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/google{token}.html":{"get":{"summary":"Google Site Verification","operationId":"google_site_verification_google_token__html_get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sitemap.xml":{"get":{"summary":"Sitemap Xml","operationId":"sitemap_xml_sitemap_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/blog":{"get":{"summary":"Blog Page","description":"Public, SEO-indexable blog index. 5-min edge cache; 6h data cache.\nCookie-assigned A/B variant for layout experimentation.\n\nPersonalization: when a logged-in user has reaction-derived topic\naffinities, entries are re-ranked with a 60/40 chronological/affinity\nblend per category. Anonymous users always see strict chronological.\n`?explore=1` opts a logged-in user out of personalization for the\ncurrent request (purely server-side; no cookie state).\n`?force=1` bypasses the 6h feed cache (admin only).","operationId":"blog_page_blog_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"cat","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Cat"}},{"name":"v","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"V"}},{"name":"force","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Force"}},{"name":"explore","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Explore"}},{"name":"_r","in":"query","required":false,"schema":{"type":"integer","default":0,"title":" R"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blog/subscribe":{"post":{"summary":"Api Blog Subscribe","description":"Email subscription endpoint for the /blog footer form.\n\nAnonymous-friendly (no auth required) — anyone reading the blog can\nsubscribe to the weekly digest. Stores the email regardless of SMTP\nconfig so we can collect the list now and start sending later.\n\nAnti-abuse: 5 subscribe attempts per IP per hour. Email is normalized\n(lowercase + strip) and the endpoint is idempotent — already-subscribed\nreturns 200, not 400, so the form doesn't leak existence info.","operationId":"api_blog_subscribe_api_blog_subscribe_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogSubscribeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blog/unsubscribe":{"get":{"summary":"Api Blog Unsubscribe","description":"One-click unsubscribe — link embedded in every digest email.\nToken is the unsubscribe_token column from the subscriber row.","operationId":"api_blog_unsubscribe_api_blog_unsubscribe_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog-digest-send":{"post":{"summary":"Api Admin Blog Digest Send","description":"Manually trigger a weekly digest send. Admin-only. Idempotent —\nwon't re-send to a subscriber who got one in the last 5 days.\nWire this to a Railway scheduled job or external cron once SMTP is\nconfigured.","operationId":"api_admin_blog_digest_send_api_admin_blog_digest_send_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/article/share-sms-status":{"get":{"summary":"Api Share Sms Status","description":"UI uses this on page load to decide whether to render the SMS\nShare button. Hides the button entirely when the feature is\nunconfigured, so users don't click a button that just 503s.","operationId":"api_share_sms_status_api_article_share_sms_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/article/share-sms":{"post":{"summary":"Api Article Share Sms","description":"Share a blog article via SMS. Authenticated, rate-limited 10/day\nper user. Logs every attempt to sms_shares for audit + analytics.\nGated on Twilio env vars — returns 503 if not configured so the\nUI can degrade gracefully.","operationId":"api_article_share_sms_api_article_share_sms_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareSMSRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/blog/track":{"post":{"summary":"Api Blog Track","description":"Client-side event logger for A/B analytics. Members-only — matches\nthe SSR /blog* gate so the analytics surface and the content surface\nhave the same visibility. user_id is always populated (no anonymous\nrows) and the dependency rejects unauthenticated POSTs with 401.","operationId":"api_blog_track_api_blog_track_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogTrackEvent"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/blog/react":{"post":{"summary":"Api Blog React","description":"Set, flip, or clear a logged-in user's reaction to an article.\nUpdates topic affinity by `delta = new_value - old_value` so flipping\nfrom like → dislike is a -2 swing on that topic, matching intuition.","operationId":"api_blog_react_api_blog_react_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogReactRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/blog/reactions":{"get":{"summary":"Api Blog Reactions","description":"Batched fetch of reaction counts (+ user's own value if logged in)\nfor up to 100 slugs. Used by the /blog index to hydrate buttons after\npage load without bloating the SSR HTML.","operationId":"api_blog_reactions_api_blog_reactions_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"slugs","in":"query","required":false,"schema":{"type":"string","default":"","title":"Slugs"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blog/comment":{"post":{"summary":"Api Blog Comment","description":"Submit a comment. First _BLOG_PENDING_THRESHOLD per user start\n'pending'; thereafter auto-approved. Spam vector mitigations: max\nlength, basic rate limit (1 comment per user per 30s), no HTML.","operationId":"api_blog_comment_api_blog_comment_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogCommentRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/blog/comments/{slug}":{"get":{"summary":"Api Blog Comments","description":"List approved comments on an article, threaded by parent_id.\nIncludes the requester's own pending comments so they see them\nimmediately after posting (with status='pending' badge).","operationId":"api_blog_comments_api_blog_comments__slug__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog-comments-pending":{"get":{"summary":"Api Admin Blog Comments Pending","description":"Moderation queue. Returns oldest-first so admin works through a\nchronological backlog without missing entries.","operationId":"api_admin_blog_comments_pending_api_admin_blog_comments_pending_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/blog-comment/{comment_id}/moderate":{"post":{"summary":"Api Admin Blog Moderate","operationId":"api_admin_blog_moderate_api_admin_blog_comment__comment_id__moderate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogModerateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/blog-feed-health":{"get":{"summary":"Api Admin Blog Feed Health","description":"Per-source RSS feed health. Pass ?force=1 to bypass the 6h cache and\nre-fetch every source synchronously — useful when articles look stuck\nat the top of /blog. Returns latency, entry counts, and consecutive\nfailure runs so silently-broken feeds surface.","operationId":"api_admin_blog_feed_health_api_admin_blog_feed_health_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"force","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Force"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/blog/article/{slug}":{"get":{"summary":"Blog Article","description":"Individual article page with AI in-depth analysis. SEO-indexable, shareable.","operationId":"blog_article_blog_article__slug__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"_r","in":"query","required":false,"schema":{"type":"integer","default":0,"title":" R"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/blog/career":{"get":{"summary":"Blog Career Hub","description":"Career & Practice hub page. A/B variant A groups by stage; B is flat.","operationId":"blog_career_hub_blog_career_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"v","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"V"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/blog/career/{slug}":{"get":{"summary":"Blog Career Guide","description":"Individual career guide page. Bumps view_count, logs guide_view event.","operationId":"blog_career_guide_blog_career__slug__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/blog/guide":{"get":{"summary":"Blog Guide Hub","description":"First-party SEO guides hub — long-form radiology AI content.","operationId":"blog_guide_hub_blog_guide_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}},"security":[{"HTTPBearer":[]}]}},"/blog/guide/{slug}":{"get":{"summary":"Blog Guide Post","description":"Individual SEO guide post — bumps view_count, no A/B (single layout).","operationId":"blog_guide_post_blog_guide__slug__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/blog/rss.xml":{"get":{"summary":"Blog Rss","description":"RSS 2.0 feed of aggregated entries for subscribers / readers.\nMembers-only — feed readers must send `Authorization: Bearer <jwt>`\nor the rad_jwt cookie.","operationId":"blog_rss_blog_rss_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/blog/feed":{"get":{"summary":"Api Blog Feed","description":"JSON feed of aggregated entries, grouped by category. Public (no auth).","operationId":"api_blog_feed_api_blog_feed_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/blog/analyze":{"get":{"summary":"Api Blog Analyze","description":"Return (generating if necessary) the AI in-depth analysis for one article.\nRate-limited per IP. Cached forever in blog_summary_cache.","operationId":"api_blog_analyze_api_blog_analyze_get","parameters":[{"name":"slug","in":"query","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/terms":{"get":{"summary":"Legal Terms","operationId":"legal_terms_terms_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/privacy":{"get":{"summary":"Legal Privacy","operationId":"legal_privacy_privacy_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/disclaimer":{"get":{"summary":"Legal Disclaimer","operationId":"legal_disclaimer_disclaimer_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/pricing":{"get":{"summary":"Pricing Page","operationId":"pricing_page_pricing_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/billing/checkout":{"post":{"summary":"Billing Checkout","description":"Create a Stripe Checkout session OR upgrade an existing subscription.\n\nBranch logic:\n  - User has NO active Stripe subscription → create Checkout Session,\n    return {url} for redirect to Stripe-hosted checkout.\n  - User HAS an active subscription → modify in place with proration,\n    return {upgraded: true, prorated: true, tier, period}. Caller (the\n    frontend) should show a success message instead of redirecting.\n\nThis prevents the double-subscription bug where a user already on\nStarter clicking \"Subscribe Professional\" would get billed for BOTH\ntiers until they manually canceled the old subscription in Stripe\nCustomer Portal.","operationId":"billing_checkout_api_billing_checkout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/billing/portal":{"post":{"summary":"Billing Portal","description":"Create a Stripe Customer Portal session for self-service management.","operationId":"billing_portal_api_billing_portal_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/stripe/webhook":{"post":{"summary":"Stripe Webhook","description":"Receive Stripe webhook events. Idempotent via ProcessedStripeEvent table.\nSignature verification rejects forged requests. Always returns 200 on\nverification success so Stripe doesn't retry — handler swallows internal\nerrors to avoid Stripe's retry storm masking real issues.","operationId":"stripe_webhook_api_stripe_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/bill-overages":{"post":{"summary":"Admin Bill Overages","description":"Push unbilled overage charges to Stripe as invoice items.\n\nDefaults to dry_run=True so an accidental click doesn't charge anyone.\nAdmin explicitly sets ?dry_run=false to actually bill.","operationId":"admin_bill_overages_api_admin_bill_overages_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Dry Run"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/user-calls":{"get":{"summary":"Admin User Calls","description":"Per-user call detail — last N calls with timestamp, endpoint, cost, latency.","operationId":"admin_user_calls_api_admin_user_calls_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"query","required":true,"schema":{"type":"integer","title":"User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/slow-calls":{"get":{"summary":"Admin Slow Calls","description":"Calls slower than threshold_ms in the last N hours.","operationId":"admin_slow_calls_api_admin_slow_calls_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"threshold_ms","in":"query","required":false,"schema":{"type":"integer","default":60000,"title":"Threshold Ms"}},{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/error-rates":{"get":{"summary":"Admin Error Rates","description":"Error rates grouped by endpoint, user, or day.","operationId":"admin_error_rates_api_admin_error_rates_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"group_by","in":"query","required":false,"schema":{"type":"string","default":"endpoint","title":"Group By"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/overage-summary":{"get":{"summary":"Admin Overage Summary","description":"Current overage billing state — who's in overage, how much accrued.","operationId":"admin_overage_summary_api_admin_overage_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/logout":{"get":{"summary":"Logout Page","description":"Clear localStorage professional + consumer session and return to login.\n\nBookmarkable shortcut for users stuck in a stale session — e.g., when\na professional account previously authenticated as a consumer and the\nindex page keeps redirecting to /patient on every visit.\n\nClears BOTH the professional (radagent_token/radagent_user) and the\nconsumer (pt_token) localStorage keys so the user lands back at /login.","operationId":"logout_page_logout_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/download/desktop":{"get":{"summary":"Download Desktop","description":"Serve the desktop client as a downloadable zip with embedded auth token.","operationId":"download_desktop_download_desktop_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/portable-bundle-debug":{"get":{"summary":"Admin Portable Bundle Debug","description":"One-shot diagnostic: confirms whether the cloud process can resolve\nthe portable bundle's signed CDN URL via the GH API. Admin-only.\nRemove or leave in place — cheap, no PII.","operationId":"admin_portable_bundle_debug_api_admin_portable_bundle_debug_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/download/desktop/portable":{"get":{"summary":"Download Desktop Portable","description":"Serve the self-contained Windows bundle (Python embedded, ~70MB).\nBest for non-technical users — no Python install, no pip install,\njust unzip and double-click RadAgent.bat. The bundle does NOT embed\na per-user auth token, so the desktop will prompt for login on first\nlaunch (one-time).\n\nResolution order:\n  1) GH API with GITHUB_TOKEN → signed CDN URL → 302 (preferred)\n  2) Fallback to the source-file zip (works without GH auth, but\n     requires the user to have Python pre-installed)","operationId":"download_desktop_portable_download_desktop_portable_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/download/chrome-extension":{"get":{"summary":"Download Chrome Extension","description":"Serve the Chrome extension as a downloadable zip with embedded auth token.","operationId":"download_chrome_extension_download_chrome_extension_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/smart":{"post":{"summary":"Smart Reader","description":"Unified Smart AI Reader. Accepts any combination of text + images.\nAuto-classifies images (local, zero API cost) and routes to the\ncorrect existing pipeline. Returns uniform response format.","operationId":"smart_reader_api_smart_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmartReaderRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/smart-video":{"post":{"summary":"Smart Video","description":"Upload a PACS scroll-through video, extract frames, route to\nvolume_intelligence or multiview. Admin-only in ship 1.\n\nEnforces: ENABLE_VIDEO_UPLOAD flag, per-user daily cap, content-type\nwhitelist, size/duration/codec bounds, mandatory PHI crop calibration.","operationId":"smart_video_api_smart_video_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_smart_video_api_smart_video_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/video/preview-frame":{"post":{"summary":"Video Preview Frame","description":"Extract the first frame of an uploaded video (no validation bounds)\nfor calibration UI. Returns base64 JPEG. Admin-only in ship 1.\nDoes NOT apply PHI crop — calibration needs the full frame so the user\ncan draw the crop rectangle.","operationId":"video_preview_frame_api_video_preview_frame_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_video_preview_frame_api_video_preview_frame_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/settings/video-crop":{"get":{"summary":"Get Video Crop Region","description":"Return the user's current crop region, or null if uncalibrated.","operationId":"get_video_crop_region_api_settings_video_crop_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"summary":"Save Video Crop Region","description":"Persist the user's video PHI crop region. Admin-only in ship 1.\nValidates via core.video_processing.parse_crop_region semantics.","operationId":"save_video_crop_region_api_settings_video_crop_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoCropRegionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]},"delete":{"summary":"Clear Video Crop Region","description":"Clear the user's video PHI crop region. After this, /api/smart-video\nreturns 422 calibration_required until the region is re-saved.","operationId":"clear_video_crop_region_api_settings_video_crop_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/patient/{path}":{"get":{"summary":"Patient Page","description":"Consumer patient page — separate UI from professional. Page itself\nis publicly accessible (it shows a sign-in/request-access form), but\nall functional UI elements are gated client-side until sign-in.","operationId":"patient_page_patient__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/patient":{"get":{"summary":"Patient Page","description":"Consumer patient page — separate UI from professional. Page itself\nis publicly accessible (it shows a sign-in/request-access form), but\nall functional UI elements are gated client-side until sign-in.","operationId":"patient_page_patient_get","parameters":[{"name":"path","in":"query","required":false,"schema":{"type":"string","default":"","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/patient/register":{"post":{"summary":"Consumer Register","description":"Register a consumer account — requires admin approval before access is granted.","operationId":"consumer_register_api_patient_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumerRegisterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/patient/login":{"post":{"summary":"Consumer Login","description":"Consumer login. Requires admin approval before access is granted.","operationId":"consumer_login_api_patient_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/explain":{"post":{"summary":"Explain For Consumer","description":"Consumer explanation endpoint. Two-pass: clinical analysis then consumer wrap.","operationId":"explain_for_consumer_api_explain_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumerExplainRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/explain/chat":{"post":{"summary":"Consumer Chat","description":"Follow-up chat about consumer's results.","operationId":"consumer_chat_api_explain_chat_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumerChatRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/patient/history":{"get":{"summary":"Consumer History","description":"Get the signed-in consumer's saved analysis history.\nTenant-filtered on user_id — never returns other users' rows.","operationId":"consumer_history_api_patient_history_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/patient/history/{analysis_id}":{"get":{"summary":"Consumer History Detail","description":"Get full detail of one saved analysis. Tenant-filtered on user_id —\nreturning 404 (not 403) for mismatched owners avoids leaking existence.","operationId":"consumer_history_detail_api_patient_history__analysis_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"analysis_id","in":"path","required":true,"schema":{"type":"integer","title":"Analysis Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AgentStatusUpdate":{"properties":{"ai_mode":{"type":"string","title":"Ai Mode","default":""},"action":{"type":"string","title":"Action","default":""},"details":{"type":"string","title":"Details","default":""}},"type":"object","title":"AgentStatusUpdate"},"BatchRequest":{"properties":{"reports":{"items":{"type":"string"},"type":"array","title":"Reports"}},"type":"object","required":["reports"],"title":"BatchRequest"},"BlogCommentRequest":{"properties":{"slug":{"type":"string","title":"Slug"},"body":{"type":"string","title":"Body"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Id"}},"type":"object","required":["slug","body"],"title":"BlogCommentRequest"},"BlogModerateRequest":{"properties":{"action":{"type":"string","title":"Action"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note","default":""}},"type":"object","required":["action"],"title":"BlogModerateRequest"},"BlogReactRequest":{"properties":{"slug":{"type":"string","title":"Slug"},"value":{"type":"integer","title":"Value"}},"type":"object","required":["slug","value"],"title":"BlogReactRequest"},"BlogSubscribeRequest":{"properties":{"email":{"type":"string","title":"Email"}},"type":"object","required":["email"],"title":"BlogSubscribeRequest"},"BlogTrackEvent":{"properties":{"event_type":{"type":"string","title":"Event Type"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug","default":""},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","default":""},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"}},"type":"object","required":["event_type"],"title":"BlogTrackEvent"},"Body_smart_video_api_smart_video_post":{"properties":{"video":{"type":"string","contentMediaType":"application/octet-stream","title":"Video"},"indication":{"type":"string","title":"Indication","default":""},"body_part":{"type":"string","title":"Body Part","default":""},"modality":{"type":"string","title":"Modality","default":""},"crop_region":{"type":"string","title":"Crop Region","default":""},"ai_mode":{"type":"string","title":"Ai Mode","default":""}},"type":"object","required":["video"],"title":"Body_smart_video_api_smart_video_post"},"Body_video_preview_frame_api_video_preview_frame_post":{"properties":{"video":{"type":"string","contentMediaType":"application/octet-stream","title":"Video"}},"type":"object","required":["video"],"title":"Body_video_preview_frame_api_video_preview_frame_post"},"BuildPromptRequest":{"properties":{"text":{"type":"string","title":"Text"},"prestudy":{"type":"string","title":"Prestudy","default":""},"n_worksheets":{"type":"integer","title":"N Worksheets","default":0}},"type":"object","required":["text"],"title":"BuildPromptRequest","description":"Request body for /api/build-prompt — desktop sends report text,\ncloud returns fully-assembled prompt with all context baked in."},"BulkCase":{"properties":{"original":{"type":"string","title":"Original","default":""},"finalized":{"type":"string","title":"Finalized","default":""},"procedure_type":{"type":"string","title":"Procedure Type","default":"general"},"body_part":{"type":"string","title":"Body Part","default":"general"},"style_delta":{"type":"number","title":"Style Delta","default":0.0},"correction_type":{"type":"string","title":"Correction Type","default":"migrated"},"edit_weight":{"type":"number","title":"Edit Weight","default":0.25},"was_edited":{"type":"boolean","title":"Was Edited","default":false},"model_used":{"type":"string","title":"Model Used","default":"migrated"},"timestamp":{"type":"string","title":"Timestamp","default":""}},"type":"object","title":"BulkCase"},"BulkImportRequest":{"properties":{"cases":{"items":{"$ref":"#/components/schemas/BulkCase"},"type":"array","title":"Cases"}},"type":"object","required":["cases"],"title":"BulkImportRequest"},"BulkRuleImport":{"properties":{"rules":{"items":{},"type":"array","title":"Rules"}},"type":"object","required":["rules"],"title":"BulkRuleImport"},"CareerGuideUpsert":{"properties":{"slug":{"type":"string","title":"Slug"},"title":{"type":"string","title":"Title"},"stage":{"type":"string","title":"Stage","default":""},"subtitle":{"type":"string","title":"Subtitle","default":""},"body_markdown":{"type":"string","title":"Body Markdown","default":""},"status":{"type":"string","title":"Status","default":"published"},"byline":{"type":"string","title":"Byline","default":"Curated by MyRadAgent editorial team"},"reviewer_credit":{"type":"string","title":"Reviewer Credit","default":""},"mark_reviewed":{"type":"boolean","title":"Mark Reviewed","default":false}},"type":"object","required":["slug","title"],"title":"CareerGuideUpsert"},"ChallengeRequest":{"properties":{"message":{"type":"string","title":"Message"},"report_text":{"type":"string","title":"Report Text","default":""},"image_b64":{"type":"string","title":"Image B64","default":""}},"type":"object","required":["message"],"title":"ChallengeRequest"},"ChallengeSaveRequest":{"properties":{"message":{"type":"string","title":"Message"},"report_text":{"type":"string","title":"Report Text","default":""},"revised":{"type":"string","title":"Revised","default":""}},"type":"object","required":["message"],"title":"ChallengeSaveRequest","description":"Desktop sends this after handling the AI call locally — we just save learning."},"CheckoutRequest":{"properties":{"tier":{"type":"string","title":"Tier"},"billing_period":{"type":"string","title":"Billing Period","default":"monthly"}},"type":"object","required":["tier"],"title":"CheckoutRequest"},"ConfirmRequest":{"properties":{"final_text":{"type":"string","title":"Final Text"},"original_text":{"type":"string","title":"Original Text","default":""},"ai_text":{"type":"string","title":"Ai Text","default":""},"correction_type":{"type":"string","title":"Correction Type","default":""},"duration_seconds":{"type":"integer","title":"Duration Seconds","default":0},"confidence_score":{"type":"number","title":"Confidence Score","default":0.0},"confidence_level":{"type":"string","title":"Confidence Level","default":""},"redo_count":{"type":"integer","title":"Redo Count","default":0},"image_b64":{"type":"string","title":"Image B64","default":""},"source_type":{"type":"string","title":"Source Type","default":""},"study_type":{"type":"string","title":"Study Type","default":""},"body_part":{"type":"string","title":"Body Part","default":""},"model_used":{"type":"string","title":"Model Used","default":""}},"type":"object","required":["final_text"],"title":"ConfirmRequest"},"ConsumerChatRequest":{"properties":{"message":{"type":"string","title":"Message"},"context":{"type":"string","title":"Context","default":""},"history":{"items":{},"type":"array","title":"History","default":[]}},"type":"object","required":["message"],"title":"ConsumerChatRequest"},"ConsumerExplainRequest":{"properties":{"text":{"type":"string","title":"Text","default":""},"report_pdf_b64":{"type":"string","title":"Report Pdf B64","default":""},"report_photo_b64":{"type":"string","title":"Report Photo B64","default":""},"images_b64":{"items":{"type":"string"},"type":"array","title":"Images B64","default":[]},"video_b64":{"type":"string","title":"Video B64","default":""},"compare":{"type":"boolean","title":"Compare","default":false},"prior_text":{"type":"string","title":"Prior Text","default":""},"prior_photo_b64":{"type":"string","title":"Prior Photo B64","default":""},"prior_images_b64":{"items":{"type":"string"},"type":"array","title":"Prior Images B64","default":[]}},"type":"object","title":"ConsumerExplainRequest"},"ConsumerRegisterRequest":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"},"disclaimer_accepted":{"type":"boolean","title":"Disclaimer Accepted","default":false},"physician_confirmed":{"type":"boolean","title":"Physician Confirmed","default":false}},"type":"object","required":["email","password"],"title":"ConsumerRegisterRequest"},"ContextCacheRequest":{"properties":{"report_text":{"type":"string","title":"Report Text","default":""},"prestudy":{"type":"string","title":"Prestudy","default":""}},"type":"object","title":"ContextCacheRequest"},"CreateUser":{"properties":{"email":{"type":"string","title":"Email"},"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"full_name":{"type":"string","title":"Full Name","default":""},"role":{"type":"string","title":"Role","default":"radiologist"},"institution":{"type":"string","title":"Institution","default":""}},"type":"object","required":["email","username","password"],"title":"CreateUser"},"GenerateRequest":{"properties":{"prompt":{"type":"string","title":"Prompt"},"context":{"type":"string","title":"Context","default":""},"prestudy":{"type":"string","title":"Prestudy","default":""}},"type":"object","required":["prompt"],"title":"GenerateRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ImportReportsRequest":{"properties":{"reports":{"items":{"type":"string"},"type":"array","title":"Reports"}},"type":"object","required":["reports"],"title":"ImportReportsRequest","description":"Batch import finalized reports as gold-standard training data."},"ImpressionRequest":{"properties":{"text":{"type":"string","title":"Text"},"prestudy":{"type":"string","title":"Prestudy","default":""}},"type":"object","required":["text"],"title":"ImpressionRequest"},"LoginRequest":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginRequest"},"MacroCreate":{"properties":{"name":{"type":"string","title":"Name"},"category":{"type":"string","title":"Category","default":"General"},"body_part":{"type":"string","title":"Body Part","default":"general"},"content":{"type":"string","title":"Content"},"is_shared":{"type":"boolean","title":"Is Shared","default":true}},"type":"object","required":["name","content"],"title":"MacroCreate"},"MergeRequest":{"properties":{"report_text":{"type":"string","title":"Report Text"},"worksheet_texts":{"items":{"type":"string"},"type":"array","title":"Worksheet Texts"},"prestudy":{"type":"string","title":"Prestudy","default":""}},"type":"object","required":["report_text","worksheet_texts"],"title":"MergeRequest"},"MultiViewRequest":{"properties":{"images_b64":{"items":{"type":"string"},"type":"array","title":"Images B64"},"clinical_history":{"type":"string","title":"Clinical History","default":""},"use_consensus":{"type":"boolean","title":"Use Consensus","default":true},"is_volume":{"type":"boolean","title":"Is Volume","default":false},"ai_mode":{"type":"string","title":"Ai Mode","default":""}},"type":"object","required":["images_b64"],"title":"MultiViewRequest"},"NightlyEvalRequest":{"properties":{"days_back":{"type":"integer","title":"Days Back","default":1},"max_reports":{"type":"integer","title":"Max Reports","default":50}},"type":"object","title":"NightlyEvalRequest","description":"Trigger nightly self-evaluation (admin only)."},"OCRRequest":{"properties":{"image_b64":{"type":"string","title":"Image B64"}},"type":"object","required":["image_b64"],"title":"OCRRequest"},"PasswordResetRequest":{"properties":{"email":{"type":"string","title":"Email"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["email","new_password"],"title":"PasswordResetRequest"},"PerfectRequest":{"properties":{"text":{"type":"string","title":"Text"},"prestudy":{"type":"string","title":"Prestudy","default":""},"images_b64":{"items":{"type":"string"},"type":"array","title":"Images B64","default":[]},"use_consensus":{"type":"boolean","title":"Use Consensus","default":true},"ai_mode":{"type":"string","title":"Ai Mode","default":""}},"type":"object","required":["text"],"title":"PerfectRequest"},"PriorReportInput":{"properties":{"report_text":{"type":"string","title":"Report Text","default":""},"study_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Study Date"},"modality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modality"},"body_part":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Part"},"accession":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accession"},"source":{"type":"string","title":"Source","default":"unknown"}},"type":"object","title":"PriorReportInput","description":"A single prior report passed in for summarization against current study."},"PromptUpdate":{"properties":{"name":{"type":"string","title":"Name"},"content":{"type":"string","title":"Content"},"is_active":{"type":"boolean","title":"Is Active","default":true}},"type":"object","required":["name","content"],"title":"PromptUpdate"},"RAGRequest":{"properties":{"query":{"type":"string","title":"Query"},"n":{"type":"integer","title":"N","default":5},"body_part":{"type":"string","title":"Body Part","default":""}},"type":"object","required":["query"],"title":"RAGRequest"},"RedoRequest":{"properties":{"original_text":{"type":"string","title":"Original Text","default":""},"instructions":{"type":"string","title":"Instructions","default":""},"ai_text":{"type":"string","title":"Ai Text","default":""},"image_b64":{"type":"string","title":"Image B64","default":""}},"type":"object","title":"RedoRequest"},"RegisterRequest":{"properties":{"email":{"type":"string","title":"Email"},"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"full_name":{"type":"string","title":"Full Name","default":""},"institution":{"type":"string","title":"Institution","default":""},"accept_terms":{"type":"boolean","title":"Accept Terms","default":false}},"type":"object","required":["email","username","password"],"title":"RegisterRequest"},"SaveTeachingRequest":{"properties":{"history":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"History"},"save_as":{"type":"string","title":"Save As","default":"auto"},"name":{"type":"string","title":"Name","default":""},"image_b64":{"type":"string","title":"Image B64","default":""}},"type":"object","required":["history"],"title":"SaveTeachingRequest"},"SettingsUpdate":{"properties":{"style_notes":{"type":"string","title":"Style Notes","default":""},"full_name":{"type":"string","title":"Full Name","default":""},"overage_billing_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Overage Billing Enabled"}},"type":"object","title":"SettingsUpdate"},"ShareSMSRequest":{"properties":{"slug":{"type":"string","title":"Slug"},"recipient_phone":{"type":"string","title":"Recipient Phone"}},"type":"object","required":["slug","recipient_phone"],"title":"ShareSMSRequest"},"SmartAnalyzeRequest":{"properties":{"image_b64":{"type":"string","title":"Image B64"},"clinical_context":{"type":"string","title":"Clinical Context","default":""},"ai_mode":{"type":"string","title":"Ai Mode","default":""}},"type":"object","required":["image_b64"],"title":"SmartAnalyzeRequest"},"SmartReaderRequest":{"properties":{"text":{"type":"string","title":"Text","default":""},"images_b64":{"items":{"type":"string"},"type":"array","title":"Images B64","default":[]},"prestudy":{"type":"string","title":"Prestudy","default":""},"ai_mode":{"type":"string","title":"Ai Mode","default":""},"use_consensus":{"type":"boolean","title":"Use Consensus","default":true},"prior_reports":{"items":{"$ref":"#/components/schemas/PriorReportInput"},"type":"array","title":"Prior Reports","default":[]},"summarize_priors":{"type":"boolean","title":"Summarize Priors","default":false},"video_b64":{"type":"string","title":"Video B64","default":""}},"type":"object","title":"SmartReaderRequest"},"StyleRuleCreate":{"properties":{"name":{"type":"string","title":"Name"},"content":{"type":"string","title":"Content"},"is_active":{"type":"boolean","title":"Is Active","default":true}},"type":"object","required":["name","content"],"title":"StyleRuleCreate"},"TeachChatMessage":{"properties":{"message":{"type":"string","title":"Message"},"image_b64":{"type":"string","title":"Image B64","default":""},"history":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"History","default":[]}},"type":"object","required":["message"],"title":"TeachChatMessage"},"TeachingExtractRequest":{"properties":{"image_b64":{"type":"string","title":"Image B64"}},"type":"object","required":["image_b64"],"title":"TeachingExtractRequest"},"TeachingImageCreate":{"properties":{"name":{"type":"string","title":"Name"},"modality":{"type":"string","title":"Modality","default":"XR"},"body_part":{"type":"string","title":"Body Part","default":"chest"},"category":{"type":"string","title":"Category","default":"general"},"tags":{"type":"string","title":"Tags","default":""},"description":{"type":"string","title":"Description","default":""},"findings_criteria":{"type":"string","title":"Findings Criteria","default":""},"image_b64":{"type":"string","title":"Image B64","default":""}},"type":"object","required":["name"],"title":"TeachingImageCreate"},"TemplateCreate":{"properties":{"name":{"type":"string","title":"Name"},"procedure_type":{"type":"string","title":"Procedure Type","default":"General"},"body_part":{"type":"string","title":"Body Part","default":"general"},"template_text":{"type":"string","title":"Template Text"},"is_shared":{"type":"boolean","title":"Is Shared","default":true}},"type":"object","required":["name","template_text"],"title":"TemplateCreate"},"UpdateUser":{"properties":{"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"is_approved":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Approved"},"institution":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Institution"}},"type":"object","title":"UpdateUser"},"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"},"VideoCropRegionRequest":{"properties":{"x":{"type":"number","title":"X"},"y":{"type":"number","title":"Y"},"w":{"type":"number","title":"W"},"h":{"type":"number","title":"H"}},"type":"object","required":["x","y","w","h"],"title":"VideoCropRegionRequest"},"XRayRequest":{"properties":{"image_b64":{"type":"string","title":"Image B64"},"clinical_history":{"type":"string","title":"Clinical History","default":""},"view_type":{"type":"string","title":"View Type","default":"single"},"use_consensus":{"type":"boolean","title":"Use Consensus","default":true},"ai_mode":{"type":"string","title":"Ai Mode","default":""}},"type":"object","required":["image_b64"],"title":"XRayRequest"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}