{"openapi":"3.1.0","info":{"title":"BusX Chatbot","version":"2.0.0"},"paths":{"/web/{path}":{"get":{"summary":" Spa Catchall","operationId":"_spa_catchall_web__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/busx/intent/validate":{"post":{"tags":["intent"],"summary":"Validate Intent","description":"Validate an LLM-produced intent envelope against JSON Schemas.","operationId":"validate_intent_busx_intent_validate_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/busx/intent/parse":{"post":{"tags":["intent"],"summary":"Parse Intent","description":"Draft intent parsing from unstructured text.\n\nIMPORTANT:\n- This output is allowed to be incomplete.\n- Use missing_fields to drive follow-up questions.\n- When complete, you can call /validate (or validate internally).","operationId":"parse_intent_busx_intent_parse_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/login":{"post":{"tags":["dashboard"],"summary":"Login","operationId":"login_api_cs_1219019593ce_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/cs-1219019593ce/me":{"get":{"tags":["dashboard"],"summary":"Whoami","description":"Decode the caller's token and echo {username, role}. Used by the UI\nto gate feature visibility without a separate round-trip to login.","operationId":"whoami_api_cs_1219019593ce_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions":{"get":{"tags":["dashboard"],"summary":"Get Sessions","description":"Live sessions for the Agent Console.\n\n`limit`/`offset` are OPT-IN. With limit=0 (the default, and what the console\nsends today) the response is byte-identical to before — this is groundwork,\nnot a behaviour change.\n\nWhy it exists: CS staff measured 5-10s to take over a case. The server\nassembles this in 0.10s and then ships 1.52 MB to the browser on every poll\nand after every action. Gzip took that to 290 KB; not sending 1,175 rows is\nthe actual fix, and at ~1.3 KB/row a page of 50 is ~65 KB before compression.\n\nNOT yet safe for the console to use. The lanes and their counts are computed\nin the BROWSER from the full array (laneOf / isSessionDone, plus the\norg-level auto-archive window). Paginate before that logic moves here and\nthe sidebar counts silently become \"whatever landed in the first page\" —\nwrong, and indistinguishable from right. `total` below is the honest count\nand is unaffected by paging, but per-lane counts need the policy server-side\nfirst. That is the next change, and it is the one with the correctness risk.","operationId":"get_sessions_api_cs_1219019593ce_sessions_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/stats":{"get":{"tags":["dashboard"],"summary":"Get Stats","description":"Operational stats — ADMIN+ (above supervisor; operator decision 2026-07-11).\nDeliberately money-free: funnel counts, conversion %, channels, languages,\nroutes. Ticket-sales revenue + commissions live behind the superadmin-only\n/settlement + the money fields of /analytics.","operationId":"get_stats_api_cs_1219019593ce_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/analytics/stream":{"get":{"tags":["dashboard"],"summary":"Analytics Stream","description":"Server-sent events: one line whenever a booking is created or changes step.\n\nThe Sales tab refetched every 60s. That is fine for a chart and wrong for\nthe question the tab is actually asked -- \"did that one just go through?\" --\nwhere a minute of stale \"no\" sends people reloading to check whether the\npage is broken. This turns the poll into a push: the server says \"something\nmoved\", the client refetches through the ordinary authorised endpoint.\n\nThe event carries NO booking data on purpose. Keeping the numbers on one\nauthorised route means the superadmin money-stripping in get_analytics()\ncannot be bypassed by reading this instead.\n\nSuperadmin only, matching the sales list it exists to refresh. A stream\nthat told an agent-level account the instant money arrived would leak the\ntiming of every sale to a role that cannot see the sales.\n\nHeartbeats every 20s: an idle SSE connection is indistinguishable from a\ndead one to every proxy in the path, and they close it.","operationId":"analytics_stream_api_cs_1219019593ce_analytics_stream_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/analytics":{"get":{"tags":["dashboard"],"summary":"Get Analytics","description":"Persistent-store analytics: bookings/day, revenue/day, conversion\nfunnel, top routes, carrier scorecards. ADMIN+ get the operational view;\nthe MONEY fields (revenue/day, carrier avg-ticket) are SUPERADMIN-ONLY and\nstripped for everyone below — ticket-sales value belongs on the Sales tab.","operationId":"get_analytics_api_cs_1219019593ce_analytics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/cold":{"get":{"tags":["dashboard"],"summary":"Search Cold Sessions","description":"Follow-ups on conversations idle 7+ days (Eric, 1 Oct 2026: 7 days, \"as\nlong as a follow up is available\"). Those live in busx:cold:* and are not in\n/sessions; this finds them by name, phone, email, booking code, route or id.\nOpening one goes through /detail, whose store.get() brings it back live.","operationId":"search_cold_sessions_api_cs_1219019593ce_sessions_cold_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/detail":{"get":{"tags":["dashboard"],"summary":"Get Session Detail","description":"Full state + chat history for one session.","operationId":"get_session_detail_api_cs_1219019593ce_sessions__user_id__detail_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/opened":{"post":{"tags":["dashboard"],"summary":"Mark Session Opened","description":"Read receipt: stamp that the logged-in BusX agent opened this chat.\n\nCalled once when the console's Session Detail opens (NOT on the 3s detail\npoll). Identity is the authenticated console user — not a client-supplied\nname — so \"opened by\" can't be spoofed. Presence metadata only: no\ntranscript entry, no audit row (opens are too frequent for the audit log).","operationId":"mark_session_opened_api_cs_1219019593ce_sessions__user_id__opened_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/takeover":{"post":{"tags":["dashboard"],"summary":"Takeover Session","description":"Start or stop admin takeover of a user session.\n\nWhile admin_takeover=True the AI will pause on incoming user messages and\nrespond only with a short 'an agent is with you' notice — real responses\nmust come via POST /sessions/{user_id}/message.","operationId":"takeover_session_api_cs_1219019593ce_sessions__user_id__takeover_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TakeoverRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/stickers":{"get":{"tags":["dashboard"],"summary":"List Stickers","description":"The stickers an agent may send. One list, shared with the validator.","operationId":"list_stickers_api_cs_1219019593ce_stickers_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/upload":{"post":{"tags":["dashboard"],"summary":"Upload Attachment","description":"CS agent uploads an image/file to send to a customer. Same store/limits\nas the customer upload; returns the attachment reference to echo back on\nPOST /sessions/{id}/message.","operationId":"upload_attachment_api_cs_1219019593ce_upload_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_attachment_api_cs_1219019593ce_upload_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/booking-link":{"post":{"tags":["dashboard"],"summary":"Mint Booking Link","description":"Mint a signed booking-page link for a WhatsApp or Messenger customer\n(\"let me send you the booking screen\") — app/channels/wa_link.py /\nms_link.py. Links carry an HMAC token, never a bare id (WhatsApp ids are\nguessable phone numbers; Messenger gets the same posture for parity).\nLINE sessions use the static LIFF URL instead (frontend-side).","operationId":"mint_booking_link_api_cs_1219019593ce_sessions__user_id__booking_link_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/message":{"post":{"tags":["dashboard"],"summary":"Send Admin Message","description":"Admin types a message that the user sees as a bot-side reply,\nclearly attributed to the agent. May carry text, an attachment, or both.","operationId":"send_admin_message_api_cs_1219019593ce_sessions__user_id__message_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminMessageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/settlement":{"get":{"tags":["dashboard"],"summary":"Settlement Summary","description":"BusX per-ticket settlement (฿/ticket, ChatBot + GTM): tickets sold through the\nsystem this month, per channel/day, amount due, and the CS workload delivered\n(agent-serviced conversations from the audit log). `month` = YYYY-MM (default:\ncurrent month, UTC).","operationId":"settlement_summary_api_cs_1219019593ce_settlement_get","parameters":[{"name":"month","in":"query","required":false,"schema":{"type":"string","default":"","title":"Month"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/settlement.csv":{"get":{"tags":["dashboard"],"summary":"Settlement Csv","description":"The month's booking ledger as CSV — the auditable invoice attachment.","operationId":"settlement_csv_api_cs_1219019593ce_settlement_csv_get","parameters":[{"name":"month","in":"query","required":false,"schema":{"type":"string","default":"","title":"Month"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/react":{"post":{"tags":["dashboard"],"summary":"React To Message","description":"Agent reacts to a customer message (WhatsApp-style, one emoji per\nmessage per side). Web customers receive it via /buyer/poll.","operationId":"react_to_message_api_cs_1219019593ce_sessions__user_id__react_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/hint":{"post":{"tags":["dashboard"],"summary":"Set Hint","description":"Silent context the AI will use on the next turn (user never sees).","operationId":"set_hint_api_cs_1219019593ce_sessions__user_id__hint_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HintRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/languages":{"get":{"tags":["dashboard"],"summary":"Agent Language Choices","description":"The customer-language menu, straight from what the translator supports.\n\nServed rather than hard-coded in the console so the two can never disagree:\na language in the menu but not in this list answers 400 when an agent uses\nit, and it fails QUIETLY — the picker reverts optimistically, so it reads\nas a click that did not register. The console keeps a built-in copy as a\nfallback for a failed fetch, and a test asserts that copy matches this.","operationId":"agent_language_choices_api_cs_1219019593ce_languages_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/language":{"post":{"tags":["dashboard"],"summary":"Set Customer Language","description":"Agent pins the language of INCOMING messages, overriding detection.\n\nDetection is a script heuristic: it reads the alphabet, not the language.\nA Thai customer who types \"ok\" or \"BKK 2 pax\" is plain ASCII, so it answers\n\"en\" and the console then reverse-translates nothing and forward-translates\nthe agent's reply into English — to a customer who cannot read it. The\nagent can see what detection cannot, so they get to say.\n\nStored under its own key. `manual_language` already exists but belongs to\nthe CUSTOMER — their own web language picker writes it on every request, so\nan agent writing there would be silently overwritten by the next inbound\nmessage, which is the kind of write-with-no-durable-reader this codebase\nkeeps paying for. Sending \"\" clears the override and restores detection.","operationId":"set_customer_language_api_cs_1219019593ce_sessions__user_id__language_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LanguageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/request-location":{"post":{"tags":["dashboard"],"summary":"Request Location","description":"Agent asks the customer to share their GPS location (to help a lost\ntraveller find their stop). Sets a flag the customer's /buyer/poll surfaces\nso their client can prompt for consent. The customer's own /buyer/location\nendpoint records the grant; this only requests. Audited.","operationId":"request_location_api_cs_1219019593ce_sessions__user_id__request_location_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/typing":{"post":{"tags":["dashboard"],"summary":"Show Typing","description":"Show the customer a live 'agent is typing' animation while a CS agent\ncomposes a reply. LINE only (loading-animation API); a no-op elsewhere.\n\nCalled repeatedly (client-throttled) while the agent types, so it is\ndeliberately lightweight: NO state mutation, NO lock, NO audit row, and no\n404 on a missing session — it's a best-effort UX ping that must never slow\nthe console or fail the compose box. Returns whether the animation fired.","operationId":"show_typing_api_cs_1219019593ce_sessions__user_id__typing_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/audit":{"get":{"tags":["dashboard"],"summary":"Get Audit","operationId":"get_audit_api_cs_1219019593ce_audit_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/flag":{"post":{"tags":["dashboard"],"summary":"Flag Session","description":"Flag (or unflag) a conversation as urgent for follow-up.","operationId":"flag_session_api_cs_1219019593ce_sessions__user_id__flag_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlagRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/flag/resolve":{"post":{"tags":["dashboard"],"summary":"Resolve Flag","description":"Mark an urgent flag's follow-up as done (or reopen it).","operationId":"resolve_flag_api_cs_1219019593ce_sessions__user_id__flag_resolve_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlagResolveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/flags":{"get":{"tags":["dashboard"],"summary":"Get Flags","description":"All urgent flags (active by default), each enriched with a snapshot of\nthe live session if it still exists.","operationId":"get_flags_api_cs_1219019593ce_flags_get","parameters":[{"name":"include_resolved","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Resolved"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/assignable-users":{"get":{"tags":["dashboard"],"summary":"Get Assignable Users","description":"Dropdown source for assigning a conversation — agent-role-and-above staff.","operationId":"get_assignable_users_api_cs_1219019593ce_assignable_users_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/assign":{"post":{"tags":["dashboard"],"summary":"Assign Session","description":"Assign (or unassign) a conversation to a staff member.\n\nThis is an ownership LABEL only — it does NOT take over the chat or pause the\nAI, and it never restricts who can view or help. The AI keeps answering the\ncustomer normally; a human mutes it only by explicitly clicking Take over\n(`/takeover`). Re-assigning to a different person is a hand-off. Stored\nindependently of the live session (survives expiry), like urgent flags.","operationId":"assign_session_api_cs_1219019593ce_sessions__user_id__assign_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/keep":{"post":{"tags":["dashboard"],"summary":"Mark Keep Open","description":"Mark a conversation as still live, so idle auto-archive leaves it alone.\n\nAuto-archive exists because Active stopped meaning anything: with the sweep\noff, every conversation since launch sat in it. But an idle window is a\nguess about a person, and some cases are genuinely open while quiet —\nwaiting on a refund, a supplier, or a customer who answers once a day.\n\nThis exempts the conversation from the IDLE SWEEP only. Done still closes\nit, and a terminal booking step still closes it; otherwise \"keep open\"\nwould quietly become \"can never be finished\".","operationId":"mark_keep_open_api_cs_1219019593ce_sessions__user_id__keep_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeepRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/done":{"post":{"tags":["dashboard"],"summary":"Mark Done","description":"Manually mark a conversation Done (archive it out of the live lanes) or\nreopen it.\n\nClosing also HANDS THE CHAT BACK TO THE AI if the agent still held it.\nThis used to be a pure label that never touched the AI, and that was the\nsingle largest source of leaked takeovers: `Done` is the most prominent\nbutton on a row, an agent finishing a conversation clicks it, and\n`admin_takeover` stayed True forever. Measured on 2026-08-11: 51 of the 96\nlive takeovers were on conversations already marked Done.\n\nThe consequence was not cosmetic. A done+takeover conversation pops straight\nback into Active the moment the customer writes again — with the bot muted\nand the agent long gone, so nobody answers at all. Marking a conversation\nfinished and leaving it muted are contradictory; releasing here makes the\nclose mean what the agent intended. Reopening (done=False) deliberately does\nNOT re-take it: that is the agent's explicit call, not a side effect.","operationId":"mark_done_api_cs_1219019593ce_sessions__user_id__done_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoneRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/archive-bulk":{"post":{"tags":["dashboard"],"summary":"Archive Bulk","description":"Admin+: archive a batch of conversations in one action — the date-range\nbulk archive in Settings > General. Each id is marked Done and, like the\nsingle close above, handed back to the AI if an agent still held it —\notherwise a bulk archive would silently leave a batch of conversations muted\nwith nobody watching. Any of them pops back to Active if the customer writes\nagain, and now the bot can answer when it does. De-duped and capped;\naudit-logs the span and the count.","operationId":"archive_bulk_api_cs_1219019593ce_sessions_archive_bulk_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArchiveBulkRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/sessions/{user_id}/case":{"post":{"tags":["dashboard"],"summary":"Set Case","description":"Save a LIVE draft complaint-type tag while the conversation is still open.\nDoes NOT archive the chat, pause the AI, or feed the report — it only pre-fills\nthe close-tag, folding into the report when the chat is later marked Done.","operationId":"set_case_api_cs_1219019593ce_sessions__user_id__case_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/cs/taxonomy":{"get":{"tags":["dashboard"],"summary":"Cs Taxonomy","description":"Case categories in EN and TH. Served rather than duplicated in the\ndashboard bundle, so the form and the report can never disagree about what\nthe categories are.\n\nDeliberately NOT role-gated: every agent needs it to render the tag strip,\nand it is a list of category names — not data about anybody. The REPORT is\nwhere the access decision belongs, because that is what aggregates who\nresolved what.","operationId":"cs_taxonomy_api_cs_1219019593ce_cs_taxonomy_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/cs/report/export":{"get":{"tags":["dashboard"],"summary":"Cs Report Export","operationId":"cs_report_export_api_cs_1219019593ce_cs_report_export_get","parameters":[{"name":"start","in":"query","required":false,"schema":{"type":"string","default":"","title":"Start"}},{"name":"end","in":"query","required":false,"schema":{"type":"string","default":"","title":"End"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"en","title":"Lang"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/cs/report":{"get":{"tags":["dashboard"],"summary":"Cs Report","description":"The weekly B2C report, computed from closed conversations.\n\nReplaces a hand tally, which is why \"issues found this week\" historically\nmeant whatever the person writing it remembered on Friday.\n\nEvery section reports its own denominator. A breakdown of 12 categorised\ncases reads as the whole week's work unless it says 12 of 400 closed.","operationId":"cs_report_api_cs_1219019593ce_cs_report_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"en","title":"Lang"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/alerts":{"get":{"tags":["dashboard"],"summary":"Get Alerts","operationId":"get_alerts_api_cs_1219019593ce_alerts_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/alerts/{alert_id}/dismiss":{"post":{"tags":["dashboard"],"summary":"Dismiss Alert","operationId":"dismiss_alert_api_cs_1219019593ce_alerts__alert_id__dismiss_post","parameters":[{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","title":"Alert Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/knowledge":{"get":{"tags":["dashboard"],"summary":"Kb List","description":"List KB entries. `status` filters on the entry workflow state:\n`approved` (default — what staff normally see), `pending` (auto-captured\nawaiting review), `rejected` (kept for audit), or `all` (everything,\nused by the admin KB view to show counts and the Pending pill). Non-admins\nare limited to `approved` — see `_kb_visible_status`.","operationId":"kb_list_api_cs_1219019593ce_knowledge_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"approved","title":"Status"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dashboard"],"summary":"Kb Create","operationId":"kb_create_api_cs_1219019593ce_knowledge_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbCreateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/knowledge/{entry_id}/approve":{"post":{"tags":["dashboard"],"summary":"Kb Approve","operationId":"kb_approve_api_cs_1219019593ce_knowledge__entry_id__approve_post","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbApproveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/knowledge/{entry_id}/reject":{"post":{"tags":["dashboard"],"summary":"Kb Reject","operationId":"kb_reject_api_cs_1219019593ce_knowledge__entry_id__reject_post","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/knowledge/{entry_id}":{"patch":{"tags":["dashboard"],"summary":"Kb Update","operationId":"kb_update_api_cs_1219019593ce_knowledge__entry_id__patch","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["dashboard"],"summary":"Kb Delete","operationId":"kb_delete_api_cs_1219019593ce_knowledge__entry_id__delete","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/agent_command":{"post":{"tags":["dashboard"],"summary":"Agent Command","description":"Interpret a plain-language admin query, then (if safe) execute it.\n\nTwo-phase behaviour:\n  1. Read-only tools always execute (any authenticated user).\n  2. Mutating tools require agent+ role AND return {confirm_required: true}\n     unless confirm_override=True, letting the UI show a confirm dialog.\n\nThe role gate matters: the equivalent REST endpoints (takeover /\nsend_message / set_hint) require_role('agent'), but this NL path was\npreviously gated only by require_auth — letting a viewer run mutating\ntools through it. confirm_override is a UX guard, not an authz guard.","operationId":"agent_command_api_cs_1219019593ce_agent_command_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentCommandRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/export/audit.csv":{"get":{"tags":["dashboard"],"summary":"Export Audit Csv","operationId":"export_audit_csv_api_cs_1219019593ce_export_audit_csv_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/kb/translate":{"post":{"tags":["dashboard"],"summary":"Kb Translate","operationId":"kb_translate_api_cs_1219019593ce_kb_translate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbTranslateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/ui-strings":{"post":{"tags":["dashboard"],"summary":"Ui Strings","operationId":"ui_strings_api_cs_1219019593ce_ui_strings_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UiStringsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/translate":{"post":{"tags":["dashboard"],"summary":"Translate For Agent","description":"Translate text either direction for the Agent Console.\n\nTwo use-cases:\n  1. Customer→Staff: agent sees a Burmese message, calls this with\n     target_lang='en' (or 'th') to read it.\n  2. Staff→Customer: agent types in English, calls this with\n     target_lang=<customer language> before sending the reply.\n\nAuth: any agent role. Audit-logged. Honours the Anthropic kill switch\n(returns the original text if Anthropic is suspended) so a suspended\nstate degrades gracefully instead of erroring out the UI.","operationId":"translate_for_agent_api_cs_1219019593ce_translate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/anthropic/status":{"get":{"tags":["dashboard"],"summary":"Anthropic Status","description":"Current kill-switch state for the dashboard health badge.","operationId":"anthropic_status_api_cs_1219019593ce_anthropic_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/anthropic/model":{"get":{"tags":["dashboard"],"summary":"Anthropic Model Status","description":"Current active model + the menu of options for the picker.","operationId":"anthropic_model_status_api_cs_1219019593ce_anthropic_model_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dashboard"],"summary":"Anthropic Model Set","description":"Switch the active Claude model. Persists across restarts via\ndata/anthropic_model.flag. Pricing on the cost badge updates\nimmediately. Audit-logged.","operationId":"anthropic_model_set_api_cs_1219019593ce_anthropic_model_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicModelRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/anthropic/health":{"get":{"tags":["dashboard"],"summary":"Anthropic Health","description":"H19: authed source for the dashboard health badge (health + usage +\nmodel). The badge previously read the UNauthenticated /debug/cache_stats,\nexposing spend / model / kill-switch state to anyone. Same payload shape.","operationId":"anthropic_health_api_cs_1219019593ce_anthropic_health_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/org-settings":{"get":{"tags":["dashboard"],"summary":"Org Settings Get","description":"Current org-wide console settings. Any logged-in role may read them —\nthey drive how everyone's Live Sessions lanes are computed.","operationId":"org_settings_get_api_cs_1219019593ce_org_settings_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dashboard"],"summary":"Org Settings Set","description":"Admin+ only: change org-wide console settings. Audit-logged. Applies to\nevery operator on their next Live Sessions refresh.","operationId":"org_settings_set_api_cs_1219019593ce_org_settings_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgSettingsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/anthropic/toggle":{"post":{"tags":["dashboard"],"summary":"Anthropic Toggle","description":"SuperAdmin: manually suspend/resume the AI feed (all Anthropic API\ncalls). Persisted across restarts via data/anthropic_disabled.flag — a\nsuperadmin has to explicitly re-enable, a restart won't quietly turn\nClaude back on.","operationId":"anthropic_toggle_api_cs_1219019593ce_anthropic_toggle_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnthropicToggleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/accounting/ai":{"get":{"tags":["dashboard"],"summary":"Ai Accounting View","description":"Per-day AI spend + by-source / by-model breakdowns over the last N days.\nAlso echoes the live since-restart snapshot for context.","operationId":"ai_accounting_view_api_cs_1219019593ce_accounting_ai_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/export/ai_accounting.csv":{"get":{"tags":["dashboard"],"summary":"Export Ai Accounting Csv","description":"Per-day AI spend ledger as CSV for finance/accounting.","operationId":"export_ai_accounting_csv_api_cs_1219019593ce_export_ai_accounting_csv_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":365,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/channels/config":{"get":{"tags":["dashboard"],"summary":"Channels Config Get","description":"Masked snapshot of channel credentials for the Settings view.\n\nOpen to any logged-in role: a non-admin can SEE configuration status but\nonly an admin can change it (see the POST below). Secret fields return\nonly {configured, masked, source} — never the cleartext value.","operationId":"channels_config_get_api_cs_1219019593ce_channels_config_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dashboard"],"summary":"Channels Config Set","description":"Admin-only: update channel credentials. Audit-logs which keys changed\n(never the values). Returns the refreshed masked snapshot.","operationId":"channels_config_set_api_cs_1219019593ce_channels_config_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelConfigRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/channels/{channel}/mode":{"post":{"tags":["dashboard"],"summary":"Channels Mode","description":"Admin-only: set how a channel is handled.\n\n  • bot    — the AI auto-replies.\n  • agent  — AI stays silent; messages still appear in Live Sessions for a\n             human agent to answer.\n  • paused — inbound is ack'd and dropped (not even shown in Live Sessions).\n\nTakes effect on the next inbound message — no restart.","operationId":"channels_mode_api_cs_1219019593ce_channels__channel__mode_post","parameters":[{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelModeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/channels/{channel}/agent-reply":{"post":{"tags":["dashboard"],"summary":"Channels Agent Reply","description":"Admin-only: set/clear the Agent-mode auto-reply for a channel. Sent once\nper session as an acknowledgement while the channel is in 'agent' mode.\nBlank = none (the human agent replies with no automated message).","operationId":"channels_agent_reply_api_cs_1219019593ce_channels__channel__agent_reply_post","parameters":[{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelAgentReplyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/channels/{channel}/welcome":{"post":{"tags":["dashboard"],"summary":"Channels Welcome","description":"Admin-only: set the Bot-mode first-message welcome for a channel — a\ntoggle plus custom text. Enabled + blank = built-in BusX welcome; enabled +\ntext = that text; disabled = no greeting (bot goes straight to the date).","operationId":"channels_welcome_api_cs_1219019593ce_channels__channel__welcome_post","parameters":[{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelWelcomeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/channels/{channel}/verify":{"post":{"tags":["dashboard"],"summary":"Channels Config Verify","description":"Admin-only, READ-ONLY connection test for a channel's saved credentials.\n\nHits the provider's API with a cheap GET (LINE /v2/bot/info, WhatsApp phone\nlookup, Messenger /me) and reports {ok, detail}. Sends NOTHING to any\ncustomer — safe to run against a live account before flipping its webhook.","operationId":"channels_config_verify_api_cs_1219019593ce_channels__channel__verify_post","parameters":[{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/users":{"get":{"tags":["dashboard"],"summary":"Users List","description":"List all dashboard accounts (env + stored) with their role and source.\nNever includes passwords. Supervisor+ may manage users, but only at or below\ntheir own rank — the assignable-role list and per-row `editable` flag are\ncapped to the caller (the write endpoints enforce the same, so this isn't\njust cosmetic).","operationId":"users_list_api_cs_1219019593ce_users_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dashboard"],"summary":"Users Upsert","operationId":"users_upsert_api_cs_1219019593ce_users_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpsertRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/users/{username}":{"delete":{"tags":["dashboard"],"summary":"Users Delete","operationId":"users_delete_api_cs_1219019593ce_users__username__delete","parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","title":"Username"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/export/sessions.csv":{"get":{"tags":["dashboard"],"summary":"Export Sessions Csv","operationId":"export_sessions_csv_api_cs_1219019593ce_export_sessions_csv_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/backups":{"get":{"tags":["dashboard"],"summary":"List Backups","description":"Existing snapshots + whether the nightly automatic job is still alive.","operationId":"list_backups_api_cs_1219019593ce_backups_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Backups Api Cs 1219019593Ce Backups Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dashboard"],"summary":"Create Backup","description":"Snapshot data/ now — SQLite via the online backup API, so the bot keeps serving.","operationId":"create_backup_api_cs_1219019593ce_backups_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/BackupCreateRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Backup Api Cs 1219019593Ce Backups Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/host":{"get":{"tags":["seo-geo"],"summary":"Host","operationId":"host_api_cs_1219019593ce_seo_host_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/panels/{name}":{"get":{"tags":["seo-geo"],"summary":"Panel","operationId":"panel_api_cs_1219019593ce_seo_panels__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"sample","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sample"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/checks":{"get":{"tags":["seo-geo"],"summary":"Checks","operationId":"checks_api_cs_1219019593ce_seo_checks_get","parameters":[{"name":"url","in":"query","required":false,"schema":{"type":"string","default":"","title":"Url"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/unindexed":{"get":{"tags":["seo-geo"],"summary":"Unindexed","operationId":"unindexed_api_cs_1219019593ce_seo_unindexed_get","parameters":[{"name":"url","in":"query","required":false,"schema":{"type":"string","default":"","title":"Url"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/actions":{"get":{"tags":["seo-geo"],"summary":"Actions","operationId":"actions_api_cs_1219019593ce_seo_actions_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/actions/{action_id}/preview":{"post":{"tags":["seo-geo"],"summary":"Preview","operationId":"preview_api_cs_1219019593ce_seo_actions__action_id__preview_post","parameters":[{"name":"action_id","in":"path","required":true,"schema":{"type":"string","title":"Action Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/actions/{action_id}/run":{"post":{"tags":["seo-geo"],"summary":"Run","operationId":"run_api_cs_1219019593ce_seo_actions__action_id__run_post","parameters":[{"name":"action_id","in":"path","required":true,"schema":{"type":"string","title":"Action Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/jobs/current":{"get":{"tags":["seo-geo"],"summary":"Current Job","operationId":"current_job_api_cs_1219019593ce_seo_jobs_current_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/seo/jobs/{job_id}/log":{"get":{"tags":["seo-geo"],"summary":"Job Log","operationId":"job_log_api_cs_1219019593ce_seo_jobs__job_id__log_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/bestbus-seo/summary":{"get":{"tags":["bestbus-seo"],"summary":"Summary","operationId":"summary_api_cs_1219019593ce_bestbus_seo_summary_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/bestbus-seo/check":{"get":{"tags":["bestbus-seo"],"summary":"Check","operationId":"check_api_cs_1219019593ce_bestbus_seo_check_get","parameters":[{"name":"url","in":"query","required":false,"schema":{"type":"string","default":"","title":"Url"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs-1219019593ce/bestbus-seo/refresh":{"get":{"tags":["bestbus-seo"],"summary":"Refresh Status","operationId":"refresh_status_api_cs_1219019593ce_bestbus_seo_refresh_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["bestbus-seo"],"summary":"Refresh","operationId":"refresh_api_cs_1219019593ce_bestbus_seo_refresh_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/bestbus/home-preview/{lang}/":{"get":{"tags":["bestbus-avail"],"summary":"Home Preview","description":"Homepage design preview (build_home.py --look light --prefix /api/bestbus/home-preview), 30 Oct 2026. Under\n/api/ (Disallowed in robots.txt), noindex in the page and the X-Robots-Tag header; not linked from anywhere.","operationId":"home_preview_api_bestbus_home_preview__lang___get","parameters":[{"name":"lang","in":"path","required":true,"schema":{"type":"string","title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/bestbus/formlog":{"post":{"tags":["bestbus-avail"],"summary":"Formlog","description":"Passenger-form events from the booking app (rebrand hook 46, 30 Oct 2026): shown / error (field names only) /\nsubmit / hidden, per visitor id. No names, emails or phone numbers. Read with seo/form_report or by hand.","operationId":"formlog_api_bestbus_formlog_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/bestbus/avail":{"get":{"tags":["bestbus-avail"],"summary":"Avail","operationId":"avail_api_bestbus_avail_get","parameters":[{"name":"o","in":"query","required":false,"schema":{"type":"string","default":"","title":"O"}},{"name":"d","in":"query","required":false,"schema":{"type":"string","default":"","title":"D"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/.well-known/openai-apps-challenge":{"get":{"tags":["bestbus-mcp"],"summary":"Openai Challenge","description":"OpenAI app-directory domain check: the token from the portal, as plain text (data/bestbus/openai_apps_challenge.txt).","operationId":"openai_challenge__well_known_openai_apps_challenge_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/mcp-registry-auth":{"get":{"tags":["bestbus-mcp"],"summary":"Mcp Registry Auth","description":"Official MCP Registry domain proof (31 Oct 2026): the public key record from data/bestbus/mcp-registry-auth,\nplain text, no redirect. The private key stays in /etc/bestbus (root only).","operationId":"mcp_registry_auth__well_known_mcp_registry_auth_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/yandex_{code}.html":{"get":{"tags":["bestbus-mcp"],"summary":"Yandex Verification","description":"Yandex Webmaster site check (29 Oct 2026): serves data/bestbus/yandex_<code>.html when it exists.","operationId":"yandex_verification_yandex__code__html_get","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai/":{"get":{"tags":["bestbus-mcp"],"summary":"Ai Docs","description":"Public documentation of the MCP server (Claude connector directory requirement).","operationId":"ai_docs_ai__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/ai":{"get":{"tags":["bestbus-mcp"],"summary":"Ai Docs Slash","operationId":"ai_docs_slash_ai_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp":{"get":{"tags":["bestbus-mcp"],"summary":"Mcp Get","operationId":"mcp_get_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["bestbus-mcp"],"summary":"Mcp","operationId":"mcp_mcp_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/wa-book":{"get":{"summary":"Wa Book","description":"WhatsApp magic-link landing (app/channels/wa_link.py): verify the signed\ntoken and hand off to the booking web app bound to the customer's chat\nsession — WhatsApp's LIFF-equivalent. Fail-open: a bad/expired token lands\non a normal anonymous session; booking is never blocked.","operationId":"wa_book_wa_book_get","parameters":[{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ms-book":{"get":{"summary":"Ms Book","description":"Messenger magic-link landing (app/channels/ms_link.py): verify the signed\ntoken and hand off to the booking web app bound to the customer's chat\nsession — Messenger's LIFF-equivalent (docs/LIFF.md). Fail-open: a bad or\nexpired token lands on a normal anonymous session; booking is never blocked.","operationId":"ms_book_ms_book_get","parameters":[{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/healthz":{"get":{"summary":"Healthz","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/debug/translation_cache":{"get":{"summary":"Debug Translation Cache","operationId":"debug_translation_cache_debug_translation_cache_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/debug/token":{"get":{"summary":"Debug Token","operationId":"debug_token_debug_token_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/buyer/ask":{"post":{"summary":"Buyer Ask","description":"Claude-grounded Q&A.\n\nBody: { text, state?, user_id?, language?, save_to_history? }\n\n- `state` — booking context for grounding (passed when called from the\n  main booking composer's ? button).\n- `user_id` + `save_to_history=true` — persist both turns into the\n  user's server-side chat_history so staff see Concierge conversations\n  in the Agent Console. If admin_takeover is on for that user, we\n  DON'T call Claude — we just record the user message and return an\n  empty answer so the admin answers via poll.\nReturns: { answer, admin_takeover?, admin_agent_name? }","operationId":"buyer_ask_buyer_ask_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"}}}}}}},"/buyer/translate_text":{"post":{"summary":"Buyer Translate Text","description":"Translate one or more arbitrary English strings to the user's language.\n\nPowers the on-demand translation of carrier-supplied dynamic strings\n(terminal names, cabin codes, route titles) that aren't part of the\nkeyed UI_STRINGS_EN dict — those bypass the /buyer/ui_strings batch and\nused to leak through to non-English users in English.\n\nBody: { texts: [string, ...], lang: 'xx' }  or  { text: string, lang: 'xx' }\nReturns: { translations: { '<english>': '<translated>', ... } }","operationId":"buyer_translate_text_buyer_translate_text_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"}}}}}}},"/buyer/poll":{"get":{"summary":"Buyer Poll","description":"Lightweight poll for admin-initiated messages the customer hasn't\nactively fetched yet (e.g. admin typed while the user was idle).\n\nReturns any pending admin bubbles and the current takeover flag, and\nclears the pending queue server-side so each message is delivered once.\n\nOptional ui_lang query param: the customer's currently-effective UI\nlanguage as the React app computes it (manual pick → detected →\nbrowser → \"en\"). Persisting this lets the Agent Console translate\ncorrectly even before the customer has typed anything (chat_language\nis only set on the first user message).","operationId":"buyer_poll_buyer_poll_get","parameters":[{"name":"user_id","in":"query","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"ui_lang","in":"query","required":false,"schema":{"type":"string","default":"","title":"Ui Lang"}},{"name":"view","in":"query","required":false,"schema":{"type":"string","default":"","title":"View"}},{"name":"typing","in":"query","required":false,"schema":{"type":"string","default":"","title":"Typing"}},{"name":"sid","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/location":{"post":{"summary":"Buyer Location","description":"Customer voluntarily shares their GPS location (consent granted in the\nclient after an agent requested it). Stores \"lat,lng\" on the session for the\nCS console to display. Display-only, ephemeral — never written to the\nbookings DB. See frontend-web/src/dashboard/CUSTOMER_LOCATION_DECISION.md.","operationId":"buyer_location_buyer_location_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Buyer Location Buyer Location Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/chat":{"post":{"summary":"Buyer Chat","description":"Primary web-client endpoint.\n\nWeb clients carry server state in every request body.\nResponse is the full ChatEnvelope (React renders it natively).","operationId":"buyer_chat_buyer_chat_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Buyer Chat Buyer Chat Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/upload":{"post":{"summary":"Buyer Upload","description":"Customer-side attachment upload (web chat \"+\" button).\n\nAnonymous but strictly bounded: per-IP rate-limited, size/type-capped, and\nstored under a random filename. Returns the attachment reference the client\nthen echoes back on /buyer/attach.","operationId":"buyer_upload_buyer_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_buyer_upload_buyer_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/attach":{"post":{"summary":"Buyer Attach","description":"Record a customer-sent attachment into the session transcript so the\nAgent Console shows it. The booking pipeline is intentionally NOT run — an\nimage/file is a message to the human agent, not booking input. Optional\n`text` is a caption.","operationId":"buyer_attach_buyer_attach_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Buyer Attach Buyer Attach Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/liff/verify":{"post":{"summary":"Buyer Liff Verify","description":"Verify a LINE LIFF ID token; return how the sheet should join the chat.\n\nThe LIFF shell (frontend-web /liff) is a pure API consumer — operator\ndecoupling rule 2026-07-04: its frontend is never integrated into the\nbackend. We never trust a client-side userId claim: the token is verified\nagainst LINE's servers (audience = LIFF_CHANNEL_ID env).\n\nResponse modes:\n  * direct    — a chat session already exists under the token's `sub`\n                (same-provider case): adopt it immediately.\n  * handshake — cross-provider case: the sheet must send the one-time\n                BX-code into the chat, then claim it (/buyer/liff/claim).","operationId":"buyer_liff_verify_buyer_liff_verify_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Buyer Liff Verify Buyer Liff Verify Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/liff/claim":{"get":{"summary":"Buyer Liff Claim","description":"Poll the handshake: once the webhook has bound the BX-code to the chat\nuserId, hand it to the sheet (one-time) and stamp the LINE profile onto\nthat session for the Agent Console.","operationId":"buyer_liff_claim_buyer_liff_claim_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","default":"","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Buyer Liff Claim Buyer Liff Claim Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/react":{"post":{"summary":"Buyer React","description":"Customer reacts to a message (WhatsApp-style, one emoji per message).\nTargets a chat_history entry by its ts — in practice an agent message the\ncustomer received via /buyer/poll. Empty emoji removes the reaction.","operationId":"buyer_react_buyer_react_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Buyer React Buyer React Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat":{"post":{"summary":"Chat Legacy","operationId":"chat_legacy_chat_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Chat Legacy Chat Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/whatsapp/webhook":{"get":{"summary":"Wa Verify","description":"Meta webhook verification handshake.\n\nMeta sends dot-separated query params (hub.mode, hub.verify_token,\nhub.challenge) which FastAPI cannot bind to underscore parameters directly.\nRead them from the raw query string instead.","operationId":"wa_verify_channels_whatsapp_webhook_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Wa Webhook","description":"Receive WhatsApp messages and reply via Cloud API.","operationId":"wa_webhook_channels_whatsapp_webhook_post","parameters":[{"name":"x-hub-signature-256","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Hub-Signature-256"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Wa Webhook Channels Whatsapp Webhook Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/line/webhook":{"post":{"summary":"Line Webhook","description":"Receive a LINE delivery, spool it, ACK, and process afterwards.\n\nTHE ACK IS THE POINT. LINE allows a webhook about two seconds and then\naborts the connection, and it does not come back — webhook redelivery is a\nDevelopers Console setting for a channel nobody on the team can open, so a\ndelivery we answer too slowly is a customer message destroyed. Measured\n22-23 Sep 2026: 22 of 608 deliveries died that way.\n\nSo this function does the two things that must happen before the ack —\ncheck the signature, put the raw body somewhere durable — and nothing\nelse. Everything that touches Redis, the session locks and the transcript\nnow runs in a task, after LINE has already been told 200. Our response\ntime no longer depends on how busy this process is, which is what the\nwhole failure was made of.\n\nThe spool is what makes acking first safe. Nothing will ever redeliver, so\nif the process died between the ack and the record the message would be\ngone; `replay_pending` at startup finishes anything interrupted, and the\ndedupe makes a replay idempotent.","operationId":"line_webhook_channels_line_webhook_post","parameters":[{"name":"x-line-signature","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Line-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Line Webhook Channels Line Webhook Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/messenger/webhook":{"get":{"summary":"Messenger Verify","operationId":"messenger_verify_channels_messenger_webhook_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Messenger Webhook","description":"Receive Messenger messages and reply via Send API.","operationId":"messenger_webhook_channels_messenger_webhook_post","parameters":[{"name":"x-hub-signature-256","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Hub-Signature-256"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Messenger Webhook Channels Messenger Webhook Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/instagram/webhook":{"get":{"summary":"Instagram Verify","operationId":"instagram_verify_channels_instagram_webhook_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Instagram Webhook","description":"Receive Instagram Direct messages and reply via the Graph Send API.","operationId":"instagram_webhook_channels_instagram_webhook_post","parameters":[{"name":"x-hub-signature-256","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Hub-Signature-256"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Instagram Webhook Channels Instagram Webhook Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/reservation_details":{"post":{"summary":"Buyer Reservation Details","operationId":"buyer_reservation_details_buyer_reservation_details_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"}}}}}}},"/buyer/ticket/{booking_id}":{"get":{"summary":"Buyer View Ticket","description":"Customer-facing e-ticket view. Fetches the HTML (or PDF) ticket from\nBusX and serves it directly so the passenger can view / print / save.\n\nReturns either HTML (default, 200), a redirect to BusX's signed PDF URL\n(?format=pdf), or a small 'not ready' HTML page if the booking hasn't\nbeen paid yet. Never 500s — shows an error page instead.","operationId":"buyer_view_ticket_buyer_ticket__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","default":"html","title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/get_tickets":{"post":{"summary":"Buyer Get Tickets","operationId":"buyer_get_tickets_buyer_get_tickets_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"}}}}}}},"/buyer/unmark_seats":{"post":{"summary":"Buyer Unmark Seats","operationId":"buyer_unmark_seats_buyer_unmark_seats_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"}}}}}}},"/buyer/search_trips":{"post":{"summary":"Buyer Search Trips","operationId":"buyer_search_trips_buyer_search_trips_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"}}}}}}},"/buyer/bkk_sellable_terminals":{"post":{"summary":"Buyer Bkk Sellable Terminals","operationId":"buyer_bkk_sellable_terminals_buyer_bkk_sellable_terminals_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"}}}}}}},"/buyer/sellable_departures":{"post":{"summary":"Buyer Sellable Departures","operationId":"buyer_sellable_departures_buyer_sellable_departures_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"}}}}}}},"/buyer/suggest_alternatives":{"post":{"summary":"Buyer Suggest Alternatives","description":"AI-powered suggestions when search_trips returns no availability.\n\nBody: {\n    \"from_name\": \"Bangkok\",\n    \"to_name\": \"Phuket\",\n    \"departure_date\": \"2026-04-20\",\n    \"from_keyword_id\": 123 | null,\n    \"language\": \"en\" | \"th\" | ...\n}\nReturns: { \"suggestions\": [{ kind, label, reason, action }, ...] }","operationId":"buyer_suggest_alternatives_buyer_suggest_alternatives_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"}}}}}}},"/buyer/create_payment_link":{"post":{"summary":"Buyer Create Payment Link","description":"Create a BusX short payment link for an order.\n\nBody: { \"order_ref_id\": \"...\" }\nReturns: { \"payment_link\": \"https://bsx.me/...\", \"paycode\": \"...\", \"order_ref_id\": \"...\" }","operationId":"buyer_create_payment_link_buyer_create_payment_link_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"}}}}}}},"/buyer/find_bookings":{"post":{"summary":"Buyer Find Bookings","description":"Look up bookings by email or phone — for customers who lost their\nbrowser cache or are on a different device. Returns the booking list\n(no contact details echoed back) so a screenshot of the response is\nnot enough on its own to enumerate / impersonate.\n\nBody: { email?: str, phone?: str, phone_country?: str }","operationId":"buyer_find_bookings_buyer_find_bookings_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"}}}}}}},"/buyer/cancel_reservation":{"post":{"summary":"Buyer Cancel Reservation","operationId":"buyer_cancel_reservation_buyer_cancel_reservation_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"}}}}}}},"/buyer/request_refund":{"post":{"summary":"Buyer Request Refund","operationId":"buyer_request_refund_buyer_request_refund_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"}}}}}}},"/buyer/create_refund":{"post":{"summary":"Buyer Create Refund","operationId":"buyer_create_refund_buyer_create_refund_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"}}}}}}},"/buyer/manage_open_ended":{"post":{"summary":"Buyer Manage Open Ended","description":"Convert tickets on a booking to open-ended (flexible date).\n\nBody: { \"booking_id\": \"XXXBSXR...\" }\nFlow:\n  1. Fetch reservation to get global_ticket_numbers\n  2. request_open_ended_ticket → check allow_open per ticket\n  3. If any are allowed, create_open_ended_ticket to confirm\n  4. Return summary: { allow_open, open_valid_day, open_expires_at, tickets: [...] }","operationId":"buyer_manage_open_ended_buyer_manage_open_ended_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"}}}}}}},"/buyer/check_rebooking":{"post":{"summary":"Buyer Check Rebooking","description":"Step 1 of change-date: check whether tickets are eligible for rebooking.\n\nBody: { \"global_ticket_numbers\": [\"THGCADT6177\", ...] }\nReturns the raw request_rebookings response so the frontend can inspect\nset_travel_date.allow_rebooking per ticket.","operationId":"buyer_check_rebooking_buyer_check_rebooking_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"}}}}}}},"/buyer/search_trips_for_rebook":{"post":{"summary":"Buyer Search Trips For Rebook","description":"Resolve route from an existing booking then search trips for a new date.\n\nBody: { \"booking_id\": \"XXXBSXR3807299\", \"new_date\": \"2026-04-10\" }\nReturns: { \"trips\": [...raw BusX trip data...],\n           \"old_ticket_numbers\": [{\"global_ticket_number\": ..., \"seat_event_id\": ...}],\n           \"from_city\": \"...\", \"to_city\": \"...\" }","operationId":"buyer_search_trips_for_rebook_buyer_search_trips_for_rebook_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"}}}}}}},"/buyer/request_date_change":{"post":{"summary":"Buyer Request Date Change","description":"Step 3 of change-date: price the swap (request_set_travel_date).\n\nBody: {\n  \"new_fare_ref_id\": \"...\",\n  \"old_ticket_numbers\": [{\"global_ticket_number\": \"...\", \"seat_event_id\": \"...\"}]\n}\nReturns the rebooking quote including fare difference and fees.","operationId":"buyer_request_date_change_buyer_request_date_change_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"}}}}}}},"/buyer/confirm_date_change":{"post":{"summary":"Buyer Confirm Date Change","description":"Step 4 of change-date: confirm the rebooking (create_set_travel_date).\n\nBody: { \"rebooking_ref_ids\": [\"...\"] }\nReturns the updated reservation (same shape as get_reservation_details).","operationId":"buyer_confirm_date_change_buyer_confirm_date_change_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"}}}}}}},"/buyer/ui_strings":{"get":{"summary":"Buyer Ui Strings","description":"Return all frontend UI strings translated to the requested language.\n\nUses the shared Claude (Anthropic) translation — after pre-warm, responses are\ninstant (zero extra API calls). On a cold cache, one batched Claude Haiku call\ntranslates all strings at once.\n\nReturns 503 when translation is unavailable so the frontend knows not to\ncache the response and retries on the next render cycle.","operationId":"buyer_ui_strings_buyer_ui_strings_get","parameters":[{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"en","title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/popular_destinations":{"get":{"summary":"Buyer Popular Destinations","description":"Return curated popular destinations with names in the requested locale.\n\nUses the cached keyword-from lists (no extra BusX API calls on warm cache).\nResponse: [{id, name}]","operationId":"buyer_popular_destinations_buyer_popular_destinations_get","parameters":[{"name":"locale","in":"query","required":false,"schema":{"type":"string","default":"en_US","title":"Locale"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/keywords/from":{"get":{"summary":"Buyer Keywords From","operationId":"buyer_keywords_from_buyer_keywords_from_get","parameters":[{"name":"locale","in":"query","required":false,"schema":{"type":"string","default":"en_US","title":"Locale"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/buyer/keywords/to":{"get":{"summary":"Buyer Keywords To","operationId":"buyer_keywords_to_buyer_keywords_to_get","parameters":[{"name":"from_keyword_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"From Keyword Id"}},{"name":"locale","in":"query","required":false,"schema":{"type":"string","default":"en_US","title":"Locale"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/debug/concierge_health":{"get":{"summary":"Debug Concierge Health","operationId":"debug_concierge_health_debug_concierge_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/debug/cache_stats":{"get":{"summary":"Debug Cache Stats","description":"Live view of the Claude cache hit rates — useful for confirming the\n'we only call Anthropic for new things' invariant. Gated by the optional\nDEBUG_STATS_KEY (open if unset) since it exposes spend/model/credit state.","operationId":"debug_cache_stats_debug_cache_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/debug/reload_approved_translations":{"post":{"summary":"Debug Reload Approved Translations","description":"Re-read data/approved_translations.json without restarting the server.\nUse after editing the file with a new native-speaker batch. State-changing,\nso gated STRICTLY by DEBUG_STATS_KEY — hidden (404) in production when the key\nis unset, so it can never be triggered anonymously.","operationId":"debug_reload_approved_translations_debug_reload_approved_translations_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/debug/keywords/from":{"get":{"summary":"Debug Keywords From","operationId":"debug_keywords_from_debug_keywords_from_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/debug/keywords/to":{"get":{"summary":"Debug Keywords To","operationId":"debug_keywords_to_debug_keywords_to_get","parameters":[{"name":"from_keyword_id","in":"query","required":true,"schema":{"type":"integer","title":"From Keyword Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/debug/search_trips":{"post":{"summary":"Debug Search Trips","operationId":"debug_search_trips_debug_search_trips_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"}}}}}}}},"components":{"schemas":{"AdminMessageRequest":{"properties":{"text":{"type":"string","title":"Text","default":""},"agent_name":{"type":"string","title":"Agent Name","default":""},"quote_ts":{"type":"string","title":"Quote Ts","default":""},"original_text":{"type":"string","title":"Original Text","default":""},"attachment":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Attachment"},"sticker":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Sticker"}},"type":"object","title":"AdminMessageRequest"},"AgentCommandRequest":{"properties":{"query":{"type":"string","title":"Query"},"lang":{"type":"string","title":"Lang","default":"en"},"confirm_override":{"type":"boolean","title":"Confirm Override","default":false}},"type":"object","required":["query"],"title":"AgentCommandRequest"},"AnthropicModelRequest":{"properties":{"model":{"type":"string","title":"Model"}},"type":"object","required":["model"],"title":"AnthropicModelRequest"},"AnthropicToggleRequest":{"properties":{"disabled":{"type":"boolean","title":"Disabled"}},"type":"object","required":["disabled"],"title":"AnthropicToggleRequest"},"ArchiveBulkRequest":{"properties":{"user_ids":{"items":{"type":"string"},"type":"array","title":"User Ids","default":[]},"from_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Date"},"to_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To Date"}},"type":"object","title":"ArchiveBulkRequest"},"AssignRequest":{"properties":{"assignee":{"type":"string","title":"Assignee","default":""}},"type":"object","title":"AssignRequest"},"BackupCreateRequest":{"properties":{"note":{"type":"string","title":"Note","default":""}},"type":"object","title":"BackupCreateRequest"},"Body_buyer_upload_buyer_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_buyer_upload_buyer_upload_post"},"Body_upload_attachment_api_cs_1219019593ce_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_attachment_api_cs_1219019593ce_upload_post"},"CaseRequest":{"properties":{"case":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Case"}},"type":"object","title":"CaseRequest"},"ChannelAgentReplyRequest":{"properties":{"text":{"type":"string","title":"Text","default":""}},"type":"object","title":"ChannelAgentReplyRequest"},"ChannelConfigRequest":{"properties":{"updates":{"additionalProperties":{"type":"string"},"type":"object","title":"Updates","default":{}}},"type":"object","title":"ChannelConfigRequest"},"ChannelModeRequest":{"properties":{"mode":{"type":"string","title":"Mode"}},"type":"object","required":["mode"],"title":"ChannelModeRequest"},"ChannelWelcomeRequest":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":true},"text":{"type":"string","title":"Text","default":""}},"type":"object","title":"ChannelWelcomeRequest"},"DoneRequest":{"properties":{"done":{"type":"boolean","title":"Done","default":true},"case":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Case"}},"type":"object","title":"DoneRequest"},"FlagRequest":{"properties":{"urgent":{"type":"boolean","title":"Urgent","default":true},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"}},"type":"object","title":"FlagRequest"},"FlagResolveRequest":{"properties":{"resolved":{"type":"boolean","title":"Resolved","default":true}},"type":"object","title":"FlagResolveRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HintRequest":{"properties":{"hint":{"type":"string","title":"Hint"}},"type":"object","required":["hint"],"title":"HintRequest"},"KbApproveRequest":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"expires":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires"}},"type":"object","title":"KbApproveRequest"},"KbCreateRequest":{"properties":{"title":{"type":"string","title":"Title"},"content":{"type":"string","title":"Content"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"active":{"type":"boolean","title":"Active","default":true},"expires":{"type":"string","title":"Expires","default":""},"translations":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Translations"}},"type":"object","required":["title","content"],"title":"KbCreateRequest"},"KbTranslateRequest":{"properties":{"lang":{"type":"string","title":"Lang","default":"en"},"ids":{"items":{"type":"string"},"type":"array","title":"Ids","default":[]}},"type":"object","title":"KbTranslateRequest"},"KbUpdateRequest":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"},"expires":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires"},"translations":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Translations"}},"type":"object","title":"KbUpdateRequest"},"KeepRequest":{"properties":{"keep":{"type":"boolean","title":"Keep","default":true},"reason":{"type":"string","title":"Reason","default":""}},"type":"object","title":"KeepRequest"},"LanguageRequest":{"properties":{"lang":{"type":"string","title":"Lang","default":""}},"type":"object","title":"LanguageRequest"},"LoginRequest":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"}},"type":"object","required":["username","password"],"title":"LoginRequest"},"OrgSettingsRequest":{"properties":{"auto_archive_h":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Auto Archive H"}},"type":"object","title":"OrgSettingsRequest"},"ParseRequest":{"properties":{"text":{"type":"string","title":"Text"},"locale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locale","default":"en_US"},"time_zone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time Zone","default":"Asia/Bangkok"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency","default":"THB"}},"type":"object","required":["text"],"title":"ParseRequest"},"ParseResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"intent_envelope":{"additionalProperties":true,"type":"object","title":"Intent Envelope"},"missing_fields":{"items":{"type":"string"},"type":"array","title":"Missing Fields"},"notes":{"items":{"type":"string"},"type":"array","title":"Notes"}},"type":"object","title":"ParseResponse"},"ReactRequest":{"properties":{"ts":{"type":"string","title":"Ts"},"emoji":{"type":"string","title":"Emoji","default":""}},"type":"object","required":["ts"],"title":"ReactRequest"},"TakeoverRequest":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"agent_name":{"type":"string","title":"Agent Name","default":""}},"type":"object","required":["enabled"],"title":"TakeoverRequest"},"TranslateRequest":{"properties":{"text":{"type":"string","title":"Text"},"source_lang":{"type":"string","title":"Source Lang","default":"auto"},"target_lang":{"type":"string","title":"Target Lang","default":"en"}},"type":"object","required":["text"],"title":"TranslateRequest"},"UiStringsRequest":{"properties":{"lang":{"type":"string","title":"Lang","default":"en"},"strings":{"items":{"type":"string"},"type":"array","title":"Strings","default":[]}},"type":"object","title":"UiStringsRequest"},"UserUpsertRequest":{"properties":{"username":{"type":"string","title":"Username"},"role":{"type":"string","title":"Role"},"password":{"type":"string","title":"Password","default":""}},"type":"object","required":["username","role"],"title":"UserUpsertRequest"},"ValidateResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"errors":{"items":{"type":"string"},"type":"array","title":"Errors"}},"type":"object","title":"ValidateResponse"},"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"}}}}