{"openapi":"3.1.0","info":{"title":"Edge S2 Briefing Service","description":"Serves OGT briefings and handles report ingestion.","version":"0.1.0"},"paths":{"/products/{sku}/":{"get":{"summary":"Product Page","operationId":"product_page_products__sku___get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/{sku}/flashcards.json":{"get":{"summary":"Flashcards Json","operationId":"flashcards_json_products__sku__flashcards_json_get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/{sku}/deck.pdf":{"get":{"summary":"Deck Pdf","operationId":"deck_pdf_products__sku__deck_pdf_get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/{sku}/asset/{filename}":{"get":{"summary":"Product Asset","operationId":"product_asset_products__sku__asset__filename__get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/store/{sku}":{"get":{"summary":"Sales Page","description":"Public marketing page for a video product. No token required.","operationId":"sales_page_store__sku__get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/courses":{"get":{"summary":"Courses Index","description":"Catalog page listing every course/clip available. Linked from the top nav.\nInline HTML for the drop-in — port to a Jinja template when ogt-engine has\nbandwidth, so it inherits the OGT site shell (topbar + footer).","operationId":"courses_index_courses_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/admin/courses":{"get":{"summary":"Admin Courses Rollup","description":"Engagement funnel rollup. Reads from ogt_course_events + ogt_waitlist_signups\ncollections. Pass ?days=30 for a wider window. Behind admin basic auth.","operationId":"admin_courses_rollup_admin_courses_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/courses/waitlist":{"get":{"summary":"Waitlist Form","description":"Tiny standalone form page. Replace the inline template with a Jinja\nrender using the OGT site shell so it inherits the nav + footer.","operationId":"waitlist_form_courses_waitlist_get","parameters":[{"name":"course","in":"query","required":false,"schema":{"type":"string","default":"sgc-active","title":"Course"}},{"name":"source_clip","in":"query","required":false,"schema":{"type":"string","default":"","title":"Source Clip"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/courses/{slug}":{"get":{"summary":"Course Player No Slash","description":"Redirect /courses/<slug> → /courses/<slug>/ (301) so browser relative-URL\nresolution finds the asset proxy at /courses/<slug>/<asset>. Without the\ntrailing slash, `<img src=\"ogt-owl.jpg\">` resolves to /courses/ogt-owl.jpg\n(parent dir) instead of /courses/<slug>/ogt-owl.jpg.","operationId":"course_player_no_slash_courses__slug__get","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"}}}}}}},"/courses/{slug}/":{"get":{"summary":"Course Player","description":"Serve the player shell for /courses/<slug>/. Injects the slug→clip mapping\nso the player knows which scenes.json to load via relative URLs (which then\nhit the asset-proxy route below).","operationId":"course_player_courses__slug___get","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"}}}}}}},"/courses/{slug}/{asset}":{"get":{"summary":"Course Asset","description":"Serve an asset under /courses/<slug>/... from the GCS bucket.\n\nMedia (mp4/m4a/…) is **302-redirected straight to the public GCS URL** rather\nthan proxied. The old code buffered the whole object into memory and returned\nit as one Response(content=…); Cloud Run caps a non-chunked response at 32 MiB,\nso a chapter video larger than that (tomorrows-trades mka-3.mp4 was 36 MiB)\ncame back as an empty HTTP 500 and the browser's <video> showed a black screen.\nA redirect sidesteps the proxy, the middleware, and the 32 MiB ceiling entirely,\nlets GCS serve range/seek natively at any size, and offloads media bandwidth\noff Cloud Run. Non-media assets (scenes.json, vtt, png — all tiny, same-origin\nso JS fetch() has no CORS issue) are still proxied through.","operationId":"course_asset_courses__slug___asset__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"asset","in":"path","required":true,"schema":{"type":"string","title":"Asset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/courses/track":{"post":{"summary":"Track Event","description":"Player engagement beacon. Tolerates malformed payloads silently so a\nmisbehaving client never spams the FastAPI error log.","operationId":"track_event_api_courses_track_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/courses/waitlist":{"post":{"summary":"Waitlist Signup","description":"Capture a waitlist signup. Idempotent on (course_slug, email) — a second\nsignup with the same email updates the timestamp instead of creating a dup.","operationId":"waitlist_signup_api_courses_waitlist_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/courses/feedback":{"post":{"summary":"Submit Feedback","description":"End-of-course feedback. Tolerates missing/empty fields silently — the form\nis opt-in field-by-field; the player allows submission with any combination.","operationId":"submit_feedback_api_courses_feedback_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/courses/feedback":{"get":{"summary":"Admin Courses Feedback","description":"Read every feedback submission in the window. Filterable by clip slug.\nBehind admin basic auth, same as /admin/courses.","operationId":"admin_courses_feedback_admin_courses_feedback_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"clip","in":"query","required":false,"schema":{"type":"string","default":"","title":"Clip"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Home Page","description":"Serve the OGT public home page.","operationId":"home_page__get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/about":{"get":{"summary":"About Page","description":"Serve the About Dr. Ken Long page.","operationId":"about_page_about_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/loop":{"get":{"summary":"Trading Loop Page","description":"Serve the Trading Loop pillar page (Plan/Prepare/Execute/Assess).","operationId":"trading_loop_page_loop_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/career-change":{"get":{"summary":"Career Change Page","description":"Serve the 'Trading as a Second Career' audience pillar page.","operationId":"career_change_page_career_change_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/code":{"get":{"summary":"Code Index","description":"Serve the indicator code library index.","operationId":"code_index_code_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/code/{indicator}":{"get":{"summary":"Code Hub","description":"Serve an indicator's code hub (all platforms).","operationId":"code_hub_code__indicator__get","parameters":[{"name":"indicator","in":"path","required":true,"schema":{"type":"string","title":"Indicator"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/code/{indicator}/{platform}":{"get":{"summary":"Code Spoke","description":"Serve an indicator's per-platform code page.","operationId":"code_spoke_code__indicator___platform__get","parameters":[{"name":"indicator","in":"path","required":true,"schema":{"type":"string","title":"Indicator"}},{"name":"platform","in":"path","required":true,"schema":{"type":"string","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learn/terminology":{"get":{"summary":"Terminology Page","description":"Serve the Trading Terminology Distinctions page.","operationId":"terminology_page_learn_terminology_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/learn":{"get":{"summary":"Learn Page","description":"Serve the Learn page with all guide concepts.","operationId":"learn_page_learn_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/learn/patterns":{"get":{"summary":"Pattern Library Page","description":"Serve the Pattern Library — actual patterns, organized by family.","operationId":"pattern_library_page_learn_patterns_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/learn/concepts/{slug}":{"get":{"summary":"Concept Detail Page","description":"Per-concept detail page. Turns each glossary entry into its own\nindexable, citable document with DefinedTerm + BreadcrumbList + Article\nJSON-LD. Before this route existed every concept lived as an anchor\nfragment on /learn — search engines and LLMs could only cite the master\npage. See GEO/LLM discoverability audit 2026-05-26 P1-4.","operationId":"concept_detail_page_learn_concepts__slug__get","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"}}}}}}},"/learn/patterns/{slug}":{"get":{"summary":"Pattern Detail Page","description":"Per-pattern detail page. Turns each of the 15 patterns\n(SSC, CD, K2, ED, Owl, Pocket, Z3Pinch, 5DD, WO, CH, OR, 551W, Godzilla,\nFireworks, Morning-Hook) into its own indexable document with HowTo +\nArticle + BreadcrumbList JSON-LD. See P1-5 of the GEO/LLM audit.","operationId":"pattern_detail_page_learn_patterns__slug__get","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"}}}}}}},"/survey":{"get":{"summary":"Survey Page","description":"Serve the five-question survey page.","operationId":"survey_page_survey_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/survey-response":{"post":{"summary":"Api Survey Response","description":"Capture one survey response. Body is the form payload from /survey.\n\nStores to gs://ogt-briefings/survey/responses/YYYY-MM-DD.jsonl.","operationId":"api_survey_response_api_survey_response_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-digest":{"post":{"summary":"Admin Send Digest","description":"One-shot trigger for The Sunday Owl digest.\n\nBody: {\"cohort\": \"test|production|all\", \"dry_run\": bool, \"issue_id\": int|null}\nAuth: X-Cron-Secret header OR admin Basic auth.\n\nAlways test cohort first:\n  curl -sX POST .../api/admin/send-digest -u admin:owl2026 \\\n    -H 'Content-Type: application/json' \\\n    -d '{\"cohort\":\"test\",\"dry_run\":false}'","operationId":"admin_send_digest_api_admin_send_digest_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/email/move-to-daily":{"get":{"summary":"Api Email Move To Daily","description":"Subscriber-facing one-click: move from digest to daily.\n\nCalled from the link in week-2+ digest issues. Adds the email to\nsubscribers.json (creating an entry if missing) and removes it from\ndigest_subscribers.json. Returns a small confirmation page.","operationId":"api_email_move_to_daily_api_email_move_to_daily_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-survey":{"post":{"summary":"Admin Send Survey","description":"One-shot trigger to send the five-question survey email.\n\nBody: {\"cohort\": \"test|production|all\", \"dry_run\": bool}\nAuth: X-Cron-Secret header OR admin Basic auth.\n\nStart with cohort=test (sends to 3 ops recipients). If happy, re-run with\ncohort=production to blast the live list.","operationId":"admin_send_survey_api_admin_send_survey_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-survey-followup":{"post":{"summary":"Admin Send Survey Followup","description":"Send the survey follow-up email (reminder + 3-question vote).\n\nBody: {\n  \"cohort\": \"test|production|all\",\n  \"dry_run\": bool,\n  \"exclude_emails\": [\"a@b.com\", ...]   # subs to skip on production send\n}\nAuth: X-Cron-Secret header OR admin Basic auth.","operationId":"admin_send_survey_followup_api_admin_send_survey_followup_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-test":{"post":{"summary":"Admin Send Test","description":"Generic test-send. Sends ONE test email with arbitrary content.\n\nBody:\n  to:       single email address (required)\n  subject:  subject line (required) - \"[TEST] \" is auto-prepended\n  html:     HTML body (required)\n  plain:    plaintext body (optional; falls back to a strip of the HTML)\n\nReturns: {sent: bool, to, subject}","operationId":"admin_send_test_api_admin_send_test_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-test-video":{"post":{"summary":"Admin Send Test Video","description":"Convenience: send a test of the video-card CTA for a given course.\n\nBody:\n  to:      single email address (required)\n  course:  slug from /courses (default lions-and-wolves). Available:\n           lions-and-wolves, monkey-behind-the-wheel, petulant-child,\n           teeter-totter, trading-as-combat\n  subject: optional override; default uses the course title\n\nAuto-detects whether the course's hero GIF is published. If yes,\nembeds it. If not, falls back to the SVG play-button card.","operationId":"admin_send_test_video_api_admin_send_test_video_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-phantom-outreach":{"post":{"summary":"Admin Send Phantom Outreach","description":"Send a one-off outreach to subscribers we believe were unsubscribed\nsilently by mail-gateway prefetchers (multi-hit-within-seconds signature\nin Cloud Logging).\n\nAsks them to REPLY (not click) to be re-added — a deliberate human\naction that prefetchers can't fake.\n\nBody: {\"emails\": [\"a@b.com\", ...], \"dry_run\": bool}","operationId":"admin_send_phantom_outreach_api_admin_send_phantom_outreach_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-correction":{"post":{"summary":"Admin Send Correction","description":"One-off Day-31 correction send to the wrapped cohort (got the\nerroneous Day-1 'We're back' email this morning). Body: {\"dry_run\": bool}.","operationId":"admin_send_correction_api_admin_send_correction_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/survey-responses":{"get":{"summary":"Admin Survey Responses","description":"Aggregate and return all survey responses to date.\n\nAuth: admin Basic OR X-Cron-Secret.","operationId":"admin_survey_responses_admin_survey_responses_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/essays":{"get":{"summary":"Essays Page","description":"Serve the essays listing page.","operationId":"essays_page_essays_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/essays/{slug}":{"get":{"summary":"Essay Detail","description":"Serve a single essay by slug.","operationId":"essay_detail_essays__slug__get","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"}}}}}}},"/store":{"get":{"summary":"Store Page","description":"Serve the Store page.","operationId":"store_page_store_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/why-trading":{"get":{"summary":"Why Trading Page","description":"Serve the Why Trading page.","operationId":"why_trading_page_why_trading_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/partners":{"get":{"summary":"Partners Page","description":"Serve the Partners page (randomized order).","operationId":"partners_page_partners_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/faq":{"get":{"summary":"Faq Page","description":"Serve the FAQ page with FAQPage structured data.","operationId":"faq_page_faq_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/contact":{"get":{"summary":"Contact Page","description":"Serve the Contact page with the public support email.","operationId":"contact_page_contact_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/mcp":{"get":{"summary":"Mcp Get","operationId":"mcp_get_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Mcp Post","operationId":"mcp_post_mcp_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"options":{"summary":"Mcp Preflight","operationId":"mcp_preflight_mcp_options","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/mcp-registry-auth":{"get":{"summary":"Mcp Registry Auth","description":"Domain-ownership proof for the official MCP registry (HTTP verification).\nPublishes the Ed25519 public key authorizing the com.owlgrouptrading\nnamespace. Private key held off-repo at <localstore>/.egyre/.","operationId":"mcp_registry_auth__well_known_mcp_registry_auth_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/.well-known/mcp.json":{"get":{"summary":"Mcp Server Card","description":"MCP Server Card — machine-discoverable description of the public MCP\nserver at /mcp, so agents can find and connect to it without prior knowledge.","operationId":"mcp_server_card__well_known_mcp_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/robots.txt":{"get":{"summary":"Robots Txt","description":"Serve robots.txt — explicitly allows all AI crawlers.","operationId":"robots_txt_robots_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/llms.txt":{"get":{"summary":"Llms Txt","description":"Serve llms.txt — curated site map for LLM consumption. Dynamic so new\nessays, concepts, and patterns appear here automatically.","operationId":"llms_txt_llms_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/llms-full.txt":{"get":{"summary":"Llms Full Txt","description":"Serve llms-full.txt — complete concept definitions for deep LLM context.","operationId":"llms_full_txt_llms_full_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/concepts.jsonl":{"get":{"summary":"Concepts Jsonl","description":"One line per concept, JSON-LD-shaped — the format LLM ingest pipelines\nprefer for atomic documents. Each line is a self-contained DefinedTerm\nrecord with name, definition, term type, canonical URL, and any\nassociated videos/references. Crawlers that don't want to walk 71\nHTML pages can ingest this file in a single request.","operationId":"concepts_jsonl_concepts_jsonl_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/patterns.jsonl":{"get":{"summary":"Patterns Jsonl","description":"One line per trading pattern — the 15 named setups (SSC, CD, K2, ED,\nOwl, Pocket, Z3Pinch, 5DD, WO, CH, OR, 551W, Godzilla, Fireworks,\nMorning-Hook). Each record carries the family, bias, timeframe, and\ncanonical URL so an LLM consumer can reason about which pattern fits\nwhich question without scraping HTML.","operationId":"patterns_jsonl_patterns_jsonl_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/sitemap.xml":{"get":{"summary":"Sitemap Xml","description":"Serve a basic sitemap.xml for search engines and AI crawlers.","operationId":"sitemap_xml_sitemap_xml_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/briefings":{"get":{"summary":"Briefings Page","description":"Serve the Edge S2 briefing listing (3-pane shell).","operationId":"briefings_page_briefings_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/view/daily":{"get":{"summary":"View Latest Daily","description":"Serve the latest daily briefing wrapped in the Edge S2 shell.","operationId":"view_latest_daily_view_daily_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/view/weekly":{"get":{"summary":"View Latest Weekly","description":"Serve the latest weekly briefing wrapped in the Edge S2 shell.","operationId":"view_latest_weekly_view_weekly_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/view/{date}":{"get":{"summary":"View Briefing","description":"Serve the full briefing HTML for a specific date, wrapped in the Edge S2 shell.","operationId":"view_briefing_view__date__get","parameters":[{"name":"date","in":"path","required":true,"schema":{"type":"string","title":"Date"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/briefings":{"get":{"summary":"List Briefings","description":"Return JSON list of available briefings with metadata.","operationId":"list_briefings_api_briefings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/subscribe-tier":{"post":{"summary":"Subscribe Tier","description":"Create a Stripe Checkout session for the selected tier.","operationId":"subscribe_tier_api_subscribe_tier_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/checkout/fix-email":{"post":{"summary":"Checkout Fix Email","description":"Buyer self-service: re-address a just-completed video-product\npurchase. The Stripe session id (only the buyer holds it — it is in\ntheir success URL) is the credential; Stripe must report the session\npaid and the tier must be a video product. Correction rules\n(same session_id, max re-addresses, fulfillment send) live in\n``buyer_email.correct_purchase_email``, shared with the admin resend.","operationId":"checkout_fix_email_api_checkout_fix_email_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/checkout/success":{"get":{"summary":"Checkout Success","description":"Thank-you page. For video-product purchases, surfaces the access URL\nimmediately so the buyer doesn't have to wait for the email.","operationId":"checkout_success_checkout_success_get","parameters":[{"name":"session_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/guide/{concept}":{"get":{"summary":"Get Guide Concept","description":"Return guide concept content as JSON.","operationId":"get_guide_concept_api_guide__concept__get","parameters":[{"name":"concept","in":"path","required":true,"schema":{"type":"string","title":"Concept"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"swing","title":"Segment"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/course-rating":{"get":{"summary":"Course Rating","description":"1-click rating from a feedback email. Validates the token-sku pair\nagainst the customer DB; if invalid, shows a generic 'thanks' page\nrather than leaking which token/sku combos are valid.","operationId":"course_rating_api_course_rating_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}},{"name":"sku","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sku"}},{"name":"stars","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Stars"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/course-feedback":{"post":{"summary":"Course Feedback","description":"Append a text-feedback comment to an existing rating record. Same\ntoken-sku validation as /api/course-rating. Comments are appended as\na fresh record (NOT a mutation of the original) so the JSONL stays\nappend-only and last-write-wins works for reading.","operationId":"course_feedback_api_course_feedback_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-feedback":{"post":{"summary":"Admin Send Feedback","description":"Fire the buyer-feedback rating email.\n\nBody: {\"to\": \"test|all\", \"dry_run\": bool}\n  to=\"test\"  → sends [TEST] copy to deepsys.9 only\n  to=\"all\"   → sends to every buyer in feedback_sender.BUYERS\n\nAuth: X-Cron-Secret header OR admin Basic auth. Always dry-run first\nwhen targeting \"all\".","operationId":"admin_send_feedback_api_admin_send_feedback_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/concepts":{"get":{"summary":"List All Concepts","description":"Return all trading concepts as structured JSON for AI agents and integrations.","operationId":"list_all_concepts_api_concepts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/ingest":{"post":{"summary":"Ingest Report","description":"Upload a PDF report, run the pipeline, return the generated briefing URL.","operationId":"ingest_report_api_ingest_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_ingest_report_api_ingest_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/essays/ingest":{"post":{"summary":"Ingest Essay","description":"Receive an essay via Koala Writer webhook or manual POST.\n\nAccepts two formats:\n1. Koala webhook JSON: {\"title\": \"...\", \"html\": \"...\", \"url\": \"...\"}\n2. Manual JSON: {\"title\": \"...\", \"content\": \"...\", \"date\": \"YYYY-MM-DD\"}\n\nThe HTML is saved to data/essays/{slug}.html and appears on /essays.","operationId":"ingest_essay_api_essays_ingest_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/essays":{"get":{"summary":"List Essays Api","description":"Return JSON list of published essays.","operationId":"list_essays_api_api_essays_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/subscribe":{"post":{"summary":"Subscribe Email","description":"Subscribe an email to the OGT daily commentary list.\n\nAdds the email to gs://ogt-briefings/email/subscribers.json (the same\nlist the daily-emails cron sends to) and returns a glossary access URL\nso the homepage form can show the user a one-click link to the\nTortoise Glossary on success.\n\nIdempotent: re-subscribing returns ok with added=False and still\nissues the glossary URL (deterministic per email).\n\nThe entire email pipeline runs from Cloud Run + Gmail SMTP — no\nthird-party newsletter service in the loop.\n\nBot-filtered via src/engage/signup_filter (honeypot + IANA TLD allowlist\n+ MX-record DNS check). Bots get the same generic 400 a malformed-email\nuser would see, so they can't iterate against our filter.","operationId":"subscribe_email_api_subscribe_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/glossary":{"get":{"summary":"Glossary Page","description":"Gated glossary page. Requires valid email + token query params.","operationId":"glossary_page_glossary_get","parameters":[{"name":"e","in":"query","required":false,"schema":{"type":"string","default":"","title":"E"}},{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/glossary-token":{"get":{"summary":"Get Glossary Token","description":"Generate a glossary access token for a given email (internal use).","operationId":"get_glossary_token_api_glossary_token_get","parameters":[{"name":"email","in":"query","required":false,"schema":{"type":"string","default":"","title":"Email"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/engage":{"post":{"summary":"Engage","description":"Receive a batch of engagement events from the client.\n\nFire-and-forget: validation + rate-limit run synchronously, but the\nGCS write (append_batch) is queued as a BackgroundTask so the response\nreturns immediately. Beacons don't need synchronous confirmation, and\npushing the network I/O off the request path was a key fix after the\n2026-06-10 saturation incident — synchronous GCS writes per beacon\nwere compounding with MCP crawler load on a single 1-vCPU instance.","operationId":"engage_api_engage_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/dashboard":{"get":{"summary":"Admin Dashboard Index","description":"Redirect to the default tab if authed; show the gate page otherwise.","operationId":"admin_dashboard_index_admin_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/admin/dashboard/{tab}":{"get":{"summary":"Admin Dashboard Tab","description":"Render one tab of the dashboard. Same auth rules as the index.","operationId":"admin_dashboard_tab_admin_dashboard__tab__get","parameters":[{"name":"tab","in":"path","required":true,"schema":{"type":"string","title":"Tab"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/resend-product-email":{"post":{"summary":"Admin Resend Product Email","description":"Manually re-send a product fulfillment email to a buyer. Used when\nthe original send failed (e.g., the SKU wasn't registered in\nPRODUCT_INFO at purchase time). Token + amount are pulled from\ncustomers.jsonl by session_id; pass the session_id in the body.\n\nOptional ``to_email``: the buyer mistyped their address at checkout.\nDelegates to ``buyer_email.correct_purchase_email`` — the same path the\nbuyer-facing ``/api/checkout/fix-email`` uses — which records a fresh\ntoken under the corrected address with the SAME session_id (no revenue\ndouble-count) and sends the fulfillment email there.","operationId":"admin_resend_product_email_admin_resend_product_email_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/aggregate":{"get":{"summary":"Admin Aggregate","description":"Trigger the daily aggregator. Defaults to yesterday (UTC) which is\nwhat the nightly cron passes. Pass `?date=YYYY-MM-DD` to backfill or\nre-run a specific date. Admin-token-gated like the dashboard.","operationId":"admin_aggregate_admin_aggregate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Admin Aggregate","description":"Trigger the daily aggregator. Defaults to yesterday (UTC) which is\nwhat the nightly cron passes. Pass `?date=YYYY-MM-DD` to backfill or\nre-run a specific date. Admin-token-gated like the dashboard.","operationId":"admin_aggregate_admin_aggregate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/compact":{"get":{"summary":"Admin Compact","description":"Compact per-batch engagement blobs for a date into a single file.\n\nFixes the aggregator 30s-timeout from 2026-06-12 — at ~4000 batches/day,\nenumerating + reading the per-batch dir blew past Cloud Run's request\nbudget. The compacted single file is one O(1) GCS read.\n\nDefaults to yesterday (UTC). Pass `?date=YYYY-MM-DD` to target a\nspecific date — useful for backfilling broken days.\n\nIdempotent — running again on an already-compacted day folds in any\nlate-arriving per-batch blobs and re-emits the single file. Safe to\nschedule nightly via Cloud Scheduler.\n\nAdmin-token-gated like /admin/aggregate.","operationId":"admin_compact_admin_compact_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Admin Compact","description":"Compact per-batch engagement blobs for a date into a single file.\n\nFixes the aggregator 30s-timeout from 2026-06-12 — at ~4000 batches/day,\nenumerating + reading the per-batch dir blew past Cloud Run's request\nbudget. The compacted single file is one O(1) GCS read.\n\nDefaults to yesterday (UTC). Pass `?date=YYYY-MM-DD` to target a\nspecific date — useful for backfilling broken days.\n\nIdempotent — running again on an already-compacted day folds in any\nlate-arriving per-batch blobs and re-emits the single file. Safe to\nschedule nightly via Cloud Scheduler.\n\nAdmin-token-gated like /admin/aggregate.","operationId":"admin_compact_admin_compact_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/funnel-diagnostic.json":{"get":{"summary":"Admin Funnel Diagnostic","description":"JSON funnel diagnostic for the 3 video-product SKUs.\n\nCombines:\n  - Engage event traffic per SKU (landed, scrolled, buy_now_click)\n  - Behavioral bot classification (no session_end + no interaction = bot)\n  - Stripe checkout sessions in window, bucketed by status (open/complete/expired)","operationId":"admin_funnel_diagnostic_admin_funnel_diagnostic_json_get","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"}}}}}}},"/admin/engagement":{"get":{"summary":"Admin Engagement","description":"Engagement dashboard — last N days.","operationId":"admin_engagement_admin_engagement_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/stripe-webhook":{"post":{"summary":"Stripe Webhook","description":"Receive Stripe webhooks to track checkout completion.","operationId":"stripe_webhook_api_stripe_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/_debug/emails-snapshot":{"get":{"summary":"Emails Snapshot","description":"Operational introspection — returns metadata about the emails-30day.json\nthe running container loaded. Lets us verify deploys actually picked up\nfresh content (a stale-image bug we've hit before).\n\nProtected by the same X-Cron-Secret header as the cron endpoints.","operationId":"emails_snapshot_api__debug_emails_snapshot_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/email-preview":{"get":{"summary":"Admin Email Preview","description":"Browsable review of an email sequence — renders every email exactly as\nit sends, stacked in one scrollable page. Admin basic-auth gated.\n\n?set=month1  -> the live 30-day sequence (emails-30day.json)\n?set=month2  -> the Days 31-60 draft (emails-month2.json)","operationId":"admin_email_preview_admin_email_preview_get","parameters":[{"name":"set","in":"query","required":false,"schema":{"type":"string","default":"month2","title":"Set"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/preview-cohort-send":{"post":{"summary":"Admin Preview Cohort Send","description":"Dry-run the cohort-aware send for a given date and cohort.\n\nBody: {\"date\": \"YYYY-MM-DD\", \"cohort\": \"test|production|all\"}\nReturns the same shape as run_cohort_send but with no actual SMTP sends.","operationId":"admin_preview_cohort_send_api_admin_preview_cohort_send_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/cron/daily-emails":{"post":{"summary":"Cron Daily Emails","description":"Cron-triggered endpoint to send daily concept emails.\n\nProtected by X-Cron-Secret header or Cloud Scheduler OIDC.","operationId":"cron_daily_emails_api_cron_daily_emails_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/send-test-email":{"post":{"summary":"Admin Send Test Email","description":"Send a single rotation email to a specific recipient for ad-hoc testing.\n\nBody: {\"email\": \"...\", \"day\": N} (day defaults to 1)\nAuth: X-Cron-Secret header OR admin Basic auth.","operationId":"admin_send_test_email_api_admin_send_test_email_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/bounce-unsubscribe":{"post":{"summary":"Admin Bounce Unsubscribe","description":"Batch-unsubscribe a list of bounced addresses with a single GCS write.\n\nBody: {\"emails\": [\"a@b.com\", ...]}\nAuth: X-Cron-Secret header OR admin Basic auth.","operationId":"admin_bounce_unsubscribe_api_admin_bounce_unsubscribe_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/cron/growth-report":{"post":{"summary":"Cron Growth Report","description":"Daily growth-stats email to ops. Run after the daily-emails cron so\nyesterday's data is closed and today's send is recorded.","operationId":"cron_growth_report_api_cron_growth_report_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/cron/process-bounces":{"post":{"summary":"Cron Process Bounces","description":"Automated bounce sweep — read hootdesk DSNs, suppress hard bounces of active\nsubscribers. Run before the daily send so a freshly-bounced address is gone by\nthe next send. Idempotent (processes only unseen bounce mail).","operationId":"cron_process_bounces_api_cron_process_bounces_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/cron/weekly-preview":{"post":{"summary":"Cron Weekly Preview","description":"Weekly preview digest (next 5 days) to test cohort only.","operationId":"cron_weekly_preview_api_cron_weekly_preview_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/cron/briefing-teaser":{"post":{"summary":"Cron Briefing Teaser","description":"Track B: send a briefing teaser email.\n\nBody options:\n    briefing_filename: specific briefing HTML file (e.g., daily-2026-04-21.html)\n    briefing_type:     'daily' or 'weekly' (default daily; used when filename unset)\n    cohort:            'test' (default) or 'production'\n    dry_run:           bool","operationId":"cron_briefing_teaser_api_cron_briefing_teaser_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/email/subscribe":{"post":{"summary":"Api Email Subscribe","description":"Add a subscriber to the daily concept email list.\n\nBot-filtered like /api/subscribe (honeypot + IANA TLD + MX check). This\nendpoint originally validated only `\"@\" in email`, and bots found it —\nthe 2026-06/07 garbage signups (info@grof-onam-.gdd etc.) all came in\nthrough here. Generic 400 so bots can't probe which layer tripped.","operationId":"api_email_subscribe_api_email_subscribe_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/email-pixel":{"get":{"summary":"Email Pixel","description":"Open-tracking pixel embedded in daily emails. Returns a 1x1 transparent\nGIF and logs (email_hash, day) to gs://ogt-briefings/email/open-log/. The\nHMAC-hashed `e` value can be reverse-looked-up server-side for any known\nsubscriber email — see email_tracking.email_hash().","operationId":"email_pixel_api_email_pixel_get","parameters":[{"name":"e","in":"query","required":false,"schema":{"type":"string","default":"","title":"E"}},{"name":"day","in":"query","required":false,"schema":{"type":"string","default":"","title":"Day"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/email-unsubscribe":{"get":{"summary":"Email Unsubscribe Get","description":"Unsubscribe — confirmation page. The actual unsub fires on POST.\n\nBackground: Outlook / Hotmail / corporate-firewall gateways prefetch\nevery URL in inbound email (including the unsubscribe link) for security\nscanning. A GET that immediately unsubscribed produced silent phantom\nopt-outs — confirmed via timing analysis (multiple sub-second hits\non the same email from the same IP).\n\nNow GET renders a confirmation page; the unsub only fires when a\nhuman clicks the button (POST). Mail-gateway prefetchers don't\nsubmit forms.","operationId":"email_unsubscribe_get_api_email_unsubscribe_get","parameters":[{"name":"email","in":"query","required":false,"schema":{"type":"string","default":"","title":"Email"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Email Unsubscribe Post","description":"Process the unsubscribe. Triggered by:\n  1. Human clicking Confirm on the GET page (form-submit with email field)\n  2. Gmail's List-Unsubscribe-Post header (`List-Unsubscribe=One-Click`)\nEither way it's a deliberate action, not a prefetcher.","operationId":"email_unsubscribe_post_api_email_unsubscribe_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health Check","description":"Return service health with briefing counts.","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"Body_ingest_report_api_ingest_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_ingest_report_api_ingest_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}