{"openapi":"3.1.0","info":{"title":"Selecto API","version":"1.0"},"paths":{"/v1/admin/owners":{"get":{"tags":["admin"],"summary":"List Admin Owners","description":"List owners with admin flag and credits (for admin UI).","operationId":"list_admin_owners_v1_admin_owners_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Admin Owners V1 Admin Owners Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/owners/{target_owner_id}/credits":{"patch":{"tags":["admin"],"summary":"Patch Owner Credits","description":"Set owner credits: full admin any amount; Selecto (field) at most 20 total per account.","operationId":"patch_owner_credits_v1_admin_owners__target_owner_id__credits_patch","parameters":[{"name":"target_owner_id","in":"path","required":true,"schema":{"type":"string","title":"Target Owner Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchCreditsBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Patch Owner Credits V1 Admin Owners  Target Owner Id  Credits Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/owners/{target_owner_id}/tier":{"patch":{"tags":["admin"],"summary":"Patch Owner Tier","description":"Set owner subscription tier (admin only).","operationId":"patch_owner_tier_v1_admin_owners__target_owner_id__tier_patch","parameters":[{"name":"target_owner_id","in":"path","required":true,"schema":{"type":"string","title":"Target Owner Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTierBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Patch Owner Tier V1 Admin Owners  Target Owner Id  Tier Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/diners/{target_diner_id}/credits":{"patch":{"tags":["admin"],"summary":"Patch Diner Credits","description":"Set diner credits: full admin any amount; Selecto (field) at most 20 total per account.","operationId":"patch_diner_credits_v1_admin_diners__target_diner_id__credits_patch","parameters":[{"name":"target_diner_id","in":"path","required":true,"schema":{"type":"string","title":"Target Diner Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchCreditsBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Patch Diner Credits V1 Admin Diners  Target Diner Id  Credits Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/owners/{target_owner_id}/admin":{"post":{"tags":["admin"],"summary":"Post Owner Admin Flag","description":"Grant or revoke admin on an owner (admin only).","operationId":"post_owner_admin_flag_v1_admin_owners__target_owner_id__admin_post","parameters":[{"name":"target_owner_id","in":"path","required":true,"schema":{"type":"string","title":"Target Owner Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetAdminBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Owner Admin Flag V1 Admin Owners  Target Owner Id  Admin Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/owners/{target_owner_id}/selecto-field":{"post":{"tags":["admin"],"summary":"Post Owner Selecto Field","description":"Grant Selecto field read-only /admin (until a full admin revokes), or revoke (issue #190). Full admin only.","operationId":"post_owner_selecto_field_v1_admin_owners__target_owner_id__selecto_field_post","parameters":[{"name":"target_owner_id","in":"path","required":true,"schema":{"type":"string","title":"Target Owner Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelectoFieldBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Owner Selecto Field V1 Admin Owners  Target Owner Id  Selecto Field Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/diners":{"get":{"tags":["admin"],"summary":"List Admin Diners","description":"List diners with credits (for admin UI).","operationId":"list_admin_diners_v1_admin_diners_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Admin Diners V1 Admin Diners Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/me":{"get":{"tags":["admin"],"summary":"Get Admin Me","description":"Confirm session is admin (after owner Bearer); includes mode for read-only Selecto field.","operationId":"get_admin_me_v1_admin_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Admin Me V1 Admin Me Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/stats":{"get":{"tags":["admin"],"summary":"Get Admin Stats","description":"Counts for dashboard: owners, diners, restaurants, menus.","operationId":"get_admin_stats_v1_admin_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Admin Stats V1 Admin Stats Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/usage/metrics/global":{"get":{"tags":["admin"],"summary":"Get Admin Usage Global","description":"Site-wide menu usage aggregates (QR vs app, searches, etc.).","operationId":"get_admin_usage_global_v1_admin_usage_metrics_global_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Admin Usage Global V1 Admin Usage Metrics Global Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/usage/metrics/menus/batch":{"post":{"tags":["admin"],"summary":"Post Admin Usage Menus Batch","description":"Per-menu usage for many menus in one request (avoids N parallel admin hub calls).","operationId":"post_admin_usage_menus_batch_v1_admin_usage_metrics_menus_batch_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MenuUsageBatchBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Admin Usage Menus Batch V1 Admin Usage Metrics Menus Batch Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/usage/metrics/menus/{menu_id}":{"get":{"tags":["admin"],"summary":"Get Admin Usage For Menu","description":"Per-menu usage aggregates.","operationId":"get_admin_usage_for_menu_v1_admin_usage_metrics_menus__menu_id__get","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Admin Usage For Menu V1 Admin Usage Metrics Menus  Menu Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/menus/{menu_id}":{"get":{"tags":["admin"],"summary":"Get Admin Menu","description":"Return full menu document for admin edit.","operationId":"get_admin_menu_v1_admin_menus__menu_id__get","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Admin Menu V1 Admin Menus  Menu Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["admin"],"summary":"Put Admin Menu","description":"Update menu like owner PUT without ownership checks.","operationId":"put_admin_menu_v1_admin_menus__menu_id__put","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Put Admin Menu V1 Admin Menus  Menu Id  Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete Admin Menu","description":"Delete any menu by id (admin only).","operationId":"delete_admin_menu_v1_admin_menus__menu_id__delete","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/owner-invites":{"post":{"tags":["admin"],"summary":"Post Create Owner Invite","description":"Issue a single-use owner invite. Full admin (any restaurant) or Selecto field (own restaurants).","operationId":"post_create_owner_invite_v1_admin_owner_invites_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOwnerInviteBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Create Owner Invite V1 Admin Owner Invites Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/restaurants/{restaurant_id}/owner-invite":{"delete":{"tags":["admin"],"summary":"Delete Restaurant Owner Invite","description":"Revoke the current owner-invite for this restaurant (if any) and clear the pointer.","operationId":"delete_restaurant_owner_invite_v1_admin_restaurants__restaurant_id__owner_invite_delete","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Restaurant Owner Invite V1 Admin Restaurants  Restaurant Id  Owner Invite Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/owner-invites/{token}":{"delete":{"tags":["admin"],"summary":"Delete Revoke Owner Invite","description":"Full admin may revoke any invite; field rep may revoke invites they created.","operationId":"delete_revoke_owner_invite_v1_admin_owner_invites__token__delete","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/restaurants":{"get":{"tags":["admin"],"summary":"List Admin Restaurants","description":"List all restaurants with nested menu summaries.","operationId":"list_admin_restaurants_v1_admin_restaurants_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Admin Restaurants V1 Admin Restaurants Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/restaurants/{restaurant_id}/owner":{"patch":{"tags":["admin"],"summary":"Patch Admin Restaurant Owner","description":"Reassign restaurant (and its menus) to another owner.","operationId":"patch_admin_restaurant_owner_v1_admin_restaurants__restaurant_id__owner_patch","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchRestaurantOwnerBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Patch Admin Restaurant Owner V1 Admin Restaurants  Restaurant Id  Owner Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/restaurants/{restaurant_id}/unofficial":{"patch":{"tags":["admin"],"summary":"Patch Admin Restaurant Unofficial","description":"Set or clear diner-submitted (unofficial) flag via source_reference.","operationId":"patch_admin_restaurant_unofficial_v1_admin_restaurants__restaurant_id__unofficial_patch","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchRestaurantUnofficialBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Patch Admin Restaurant Unofficial V1 Admin Restaurants  Restaurant Id  Unofficial Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/restaurants/{restaurant_id}":{"delete":{"tags":["admin"],"summary":"Delete Admin Restaurant","description":"Delete restaurant and its menus (admin only).","operationId":"delete_admin_restaurant_v1_admin_restaurants__restaurant_id__delete","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/restaurants/{restaurant_id}/menus":{"get":{"summary":"List Admin Restaurant Menus","description":"List all menus for a restaurant (every status).","operationId":"list_admin_restaurant_menus_v1_admin_restaurants__restaurant_id__menus_get","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Admin Restaurant Menus V1 Admin Restaurants  Restaurant Id  Menus Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/menus/{menu_id}/public-preview":{"get":{"summary":"Get Admin Menu Public Preview","description":"Same JSON as GET /v1/menus/:id but for any status (admin preview in client app).","operationId":"get_admin_menu_public_preview_v1_admin_menus__menu_id__public_preview_get","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"BCP-47 language override","title":"Lang"},"description":"BCP-47 language override"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Admin Menu Public Preview V1 Admin Menus  Menu Id  Public Preview Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/client-errors":{"get":{"tags":["admin"],"summary":"List Admin Client Errors","description":"List client-side error reports (newest first). Issue #168.","operationId":"list_admin_client_errors_v1_admin_client_errors_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Admin Client Errors V1 Admin Client Errors Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete All Admin Client Errors","description":"Delete every client-error record. Returns the deleted count.","operationId":"delete_all_admin_client_errors_v1_admin_client_errors_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete All Admin Client Errors V1 Admin Client Errors Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/client-errors/{error_id}":{"delete":{"tags":["admin"],"summary":"Delete Admin Client Error","description":"Delete one client-error record (GDPR erasure / admin cleanup).","operationId":"delete_admin_client_error_v1_admin_client_errors__error_id__delete","parameters":[{"name":"error_id","in":"path","required":true,"schema":{"type":"string","title":"Error Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/client-errors/{error_id}/anonymize":{"post":{"tags":["admin"],"summary":"Anonymize Admin Client Error","description":"Strip PII (owner_id, device_name, user_agent, stack) from a single client-error row.","operationId":"anonymize_admin_client_error_v1_admin_client_errors__error_id__anonymize_post","parameters":[{"name":"error_id","in":"path","required":true,"schema":{"type":"string","title":"Error Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Anonymize Admin Client Error V1 Admin Client Errors  Error Id  Anonymize Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/client-errors/anonymize":{"post":{"tags":["admin"],"summary":"Anonymize All Admin Client Errors","description":"Strip PII from every client-error row. Returns the updated count.","operationId":"anonymize_all_admin_client_errors_v1_admin_client_errors_anonymize_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Anonymize All Admin Client Errors V1 Admin Client Errors Anonymize Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/oauth/state":{"get":{"tags":["auth"],"summary":"Get Oauth State","description":"Issue signed OAuth state for authorize URLs (CSRF + return path).","operationId":"get_oauth_state_v1_auth_oauth_state_get","parameters":[{"name":"provider","in":"query","required":false,"schema":{"type":"string","description":"google or apple","default":"google","title":"Provider"},"description":"google or apple"},{"name":"return_to","in":"query","required":false,"schema":{"type":"string","description":"owner, signin, or admin","default":"owner","title":"Return To"},"description":"owner, signin, or admin"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Get Oauth State V1 Auth Oauth State Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/callback":{"post":{"tags":["auth"],"summary":"Auth Callback","description":"Exchange OAuth code for session. Body: provider, code, state.\nReturns { owner?: { id, oauth_id, name, email }, diner: { id, ... } }.\nOwner only created/returned when signing in from owner flow; diner-only flow (state ends with\n:signin) does not create owner, so plain diners do not see owner UI or subscription.","operationId":"auth_callback_v1_auth_callback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallbackBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Auth Callback V1 Auth Callback Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/dev-login":{"post":{"tags":["auth"],"summary":"Dev Login","description":"Get or create a dev owner and diner (oauth_id=dev-test). Same response shape as /callback.\nOnly available when SELECTO_DEV_USER is set (e.g. for phone testing over LAN without OAuth).","operationId":"dev_login_v1_auth_dev_login_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Dev Login V1 Auth Dev Login Post"}}}}}}},"/v1/owner/billing/checkout-session":{"post":{"tags":["owner-billing"],"summary":"Post Checkout Session","description":"Start Stripe Checkout (pack or Pro / Premium subscription).","operationId":"post_checkout_session_v1_owner_billing_checkout_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Post Checkout Session V1 Owner Billing Checkout Session Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/billing/portal-session":{"post":{"tags":["owner-billing"],"summary":"Post Portal Session","description":"Open Stripe customer portal (subscription / payment method).","operationId":"post_portal_session_v1_owner_billing_portal_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Post Portal Session V1 Owner Billing Portal Session Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/stripe/webhook":{"post":{"tags":["billing"],"summary":"Stripe Webhook","description":"Handle Stripe webhooks; verify signature. Idempotent per event id in DynamoDB.","operationId":"stripe_webhook_v1_billing_stripe_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Stripe Webhook V1 Billing Stripe Webhook Post"}}}}}}},"/v1/auth/apple/callback":{"post":{"tags":["auth-apple"],"summary":"Apple Callback Post","description":"Apple form_post callback. Verifies id_token, get/create owner, redirect to frontend with token.","operationId":"apple_callback_post_v1_auth_apple_callback_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/auth/apple/session":{"get":{"tags":["auth-apple"],"summary":"Apple Session","description":"Exchange one-time token for owner (and diner when state ends with :signin).","operationId":"apple_session_v1_auth_apple_session_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Apple Session V1 Auth Apple Session Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/menus/from-image-quota":{"get":{"tags":["me"],"summary":"Get Me Menus From Image Quota","description":"Return image credits after daily refill (UTC): credits, credits_max, image_credits_unlimited,\ncredits_reset_at (YYYY-MM-DD UTC for the current daily period).\nAuth: Authorization: Bearer diner:<uuid> or X-Diner-Id.","operationId":"get_me_menus_from_image_quota_v1_me_menus_from_image_quota_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"anyOf":[{"type":"integer"},{"type":"boolean"},{"type":"string"},{"type":"null"}]},"title":"Response Get Me Menus From Image Quota V1 Me Menus From Image Quota Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/menus/from-image":{"post":{"tags":["me"],"summary":"Post Me Menus From Image","description":"Ingest menu from uploaded image (signed-in diner). Costs one image credit (daily refill).\nReturns owner-menu-import shape for viewing in app. Auth: Bearer diner:<uuid> or X-Diner-Id.","operationId":"post_me_menus_from_image_v1_me_menus_from_image_post","parameters":[{"name":"output_language","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MenuImageOutputLanguage","description":"BCP-47 language code for extracted text (single language).","default":"en"},"description":"BCP-47 language code for extracted text (single language)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_post_me_menus_from_image_v1_me_menus_from_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Me Menus From Image V1 Me Menus From Image Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/menus/merge":{"post":{"tags":["me"],"summary":"Post Me Menus Merge","description":"Merge multiple owner-menu-import payloads (from prior POST /menus/from-image calls).\nDoes not deduct image credits. Auth: Bearer diner:<uuid> or X-Diner-Id.","operationId":"post_me_menus_merge_v1_me_menus_merge_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Me Menus Merge V1 Me Menus Merge Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/menus/from-images":{"post":{"tags":["me"],"summary":"Post Me Menus From Images","description":"Ingest multiple menu photos as a diner; merge into one template. One credit per image.\nAuth: Bearer diner:<uuid> or X-Diner-Id.","operationId":"post_me_menus_from_images_v1_me_menus_from_images_post","parameters":[{"name":"output_language","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MenuImageOutputLanguage","description":"BCP-47 language code for extracted text (single language).","default":"en"},"description":"BCP-47 language code for extracted text (single language)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_post_me_menus_from_images_v1_me_menus_from_images_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Me Menus From Images V1 Me Menus From Images Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/diner":{"get":{"tags":["me"],"summary":"Get Me Diner","description":"Return current diner profile (id, name, email, subscription_tier, credits, preferences).\nAuth: Authorization: Bearer diner:<uuid> or X-Diner-Id.","operationId":"get_me_diner_v1_me_diner_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Me Diner V1 Me Diner Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["me"],"summary":"Patch Me Diner","description":"Update diner preferences (diets, allergens, fav_restaurant_ids). Partial update.\nAuth: Authorization: Bearer diner:<uuid> or X-Diner-Id.","operationId":"patch_me_diner_v1_me_diner_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchDinerBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Patch Me Diner V1 Me Diner Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/reverse-geocode":{"get":{"tags":["me"],"summary":"Get Me Reverse Geocode","description":"Reverse geocode coordinates to a display address (for \"Use my location\" when saving unofficial menu).\nAuth: Bearer diner:<uuid>.","operationId":"get_me_reverse_geocode_v1_me_reverse_geocode_get","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","maximum":90,"minimum":-90,"title":"Lat"}},{"name":"lon","in":"query","required":true,"schema":{"type":"number","maximum":180,"minimum":-180,"title":"Lon"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"title":"Response Get Me Reverse Geocode V1 Me Reverse Geocode Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/place-details":{"get":{"tags":["me"],"summary":"Get Me Place Details","description":"Fetch Google Place details (opening hours, website, etc.) for display. Used when user expands place details.\nAuth: Bearer diner:<uuid>.","operationId":"get_me_place_details_v1_me_place_details_get","parameters":[{"name":"place_id","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Place Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Me Place Details V1 Me Place Details Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/unofficial-menus/check":{"get":{"tags":["me"],"summary":"Get Me Unofficial Menus Check","description":"Check whether saving this address + restaurant name would conflict with an existing same-name restaurant within 10 km.\nReturns { \"existing_restaurant\": {...} } if conflict (with replace_available, menu_id, etc.) or { \"existing_restaurant\": null }.\nUse when showing the save form so UI can show conflict/replace state before the user clicks Save.\nAuth: Bearer diner:<uuid>.","operationId":"get_me_unofficial_menus_check_v1_me_unofficial_menus_check_get","parameters":[{"name":"address","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Address"}},{"name":"restaurant_name","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Restaurant Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Me Unofficial Menus Check V1 Me Unofficial Menus Check Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/unofficial-menus":{"post":{"tags":["me"],"summary":"Post Me Unofficial Menus","description":"Save ephemeral menu as unofficial (restaurant + menu) when no restaurant within 10km.\nGeocodes address; returns 409 with existing_restaurant if one exists within 10km.\nIf replace_restaurant_id is set and that restaurant is unofficial and within 10km, replace its menu.\nAuth: Bearer diner:<uuid>.","operationId":"post_me_unofficial_menus_v1_me_unofficial_menus_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnofficialMenuBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Post Me Unofficial Menus V1 Me Unofficial Menus Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/reports":{"post":{"tags":["me"],"summary":"Post Me Reports","description":"Report an unofficial restaurant as problematic. Signed-in diner only.\nReturns report_id. Fails if restaurant is not unofficial.","operationId":"post_me_reports_v1_me_reports_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportRestaurantBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Post Me Reports V1 Me Reports Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/geocode":{"get":{"tags":["owner-geocode"],"summary":"Geocode","description":"Geocode an address to latitude/longitude using OpenStreetMap Nominatim.\nOwner-only. Returns { latitude, longitude } or 404 if not found.","operationId":"geocode_v1_owner_geocode_get","parameters":[{"name":"address","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Address to geocode","title":"Address"},"description":"Address to geocode"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/place-details":{"get":{"tags":["owner-place-details"],"summary":"Get Owner Place Details","description":"Fetch Google Place details (name, address, phone, website, opening hours) for display.\nOwner-only. Used when adding a restaurant via \"Find on Google Maps\".","operationId":"get_owner_place_details_v1_owner_place_details_get","parameters":[{"name":"place_id","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Place Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Owner Place Details V1 Owner Place Details Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/place-menus":{"get":{"tags":["owner-place-details"],"summary":"Get Owner Place Menus","description":"Fetch Google Place menu links for the given place_id (website or menu URL when available).\nOwner-only. Returns { \"menus\": [ { \"uri\", \"label\" } ] }.","operationId":"get_owner_place_menus_v1_owner_place_menus_get","parameters":[{"name":"place_id","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Place Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Owner Place Menus V1 Owner Place Menus Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/unofficial-restaurants":{"get":{"tags":["owner-unofficial"],"summary":"List Unofficial Restaurants","description":"List all unofficial (diner-submitted) restaurants. Admin only.\nReturns id, name, address, menu_count, report_count.","operationId":"list_unofficial_restaurants_v1_owner_unofficial_restaurants_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Unofficial Restaurants V1 Owner Unofficial Restaurants Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/unofficial-restaurants/{restaurant_id}":{"delete":{"tags":["owner-unofficial"],"summary":"Delete Unofficial Restaurant","description":"Delete an unofficial restaurant and its menus and reports. Admin only.\nReturns 403 if the restaurant is not unofficial.","operationId":"delete_unofficial_restaurant_v1_owner_unofficial_restaurants__restaurant_id__delete","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/unofficial-restaurants/{restaurant_id}/reports":{"get":{"tags":["owner-unofficial"],"summary":"List Unofficial Restaurant Reports","description":"List reports for an unofficial restaurant. Admin only.\nReturns list of { report_id, diner_id, created_at, reason? }.","operationId":"list_unofficial_restaurant_reports_v1_owner_unofficial_restaurants__restaurant_id__reports_get","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Unofficial Restaurant Reports V1 Owner Unofficial Restaurants  Restaurant Id  Reports Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/claim-from-owner-invite":{"post":{"tags":["owner"],"summary":"Post Claim From Owner Invite","description":"Binds the signed-in owner account to the restaurant in the invite (single use).","operationId":"post_claim_from_owner_invite_v1_owner_claim_from_owner_invite_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimFromInviteBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Claim From Owner Invite V1 Owner Claim From Owner Invite Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/menu-templates":{"get":{"tags":["owner-menus"],"summary":"Get Menu Templates Catalog","description":"List built-in design templates (id, name, preview_thumbnail_url).","operationId":"get_menu_templates_catalog_v1_owner_menus_menu_templates_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Menu Templates Catalog V1 Owner Menus Menu Templates Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/{menu_id}/versions":{"get":{"tags":["owner-menus"],"summary":"List Menu Versions","description":"List saved snapshots for this menu (newest first).","operationId":"list_menu_versions_v1_owner_menus__menu_id__versions_get","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Menu Versions V1 Owner Menus  Menu Id  Versions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["owner-menus"],"summary":"Create Menu Version","description":"Save current editor state as an immutable snapshot (does not change the live menu).\nBody may include the same fields as PUT /menus/:id for one-shot snapshot content.","operationId":"create_menu_version_v1_owner_menus__menu_id__versions_post","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Menu Version V1 Owner Menus  Menu Id  Versions Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/{menu_id}/live-version":{"patch":{"tags":["owner-menus"],"summary":"Set Live Menu Version","description":"Set which saved version diners see for official ACTIVE menus.","operationId":"set_live_menu_version_v1_owner_menus__menu_id__live_version_patch","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Set Live Menu Version V1 Owner Menus  Menu Id  Live Version Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus":{"get":{"tags":["owner-menus"],"summary":"List Menus","description":"List owner menus with item_count and section_counts.","operationId":"list_menus_v1_owner_menus_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["owner-menus"],"summary":"Create Menu","description":"Create a menu. Body: restaurant_id, status (optional DRAFT|ACTIVE), selecto_creation.\nReturns menu id and id for public URL.","operationId":"create_menu_v1_owner_menus_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/from-image":{"post":{"tags":["owner-menus"],"summary":"Menu From One Image","description":"Ingest one menu image; returns owner-menu-import payload. Use with /merge for multiple images\nto avoid Lambda timeout (one Bedrock call per request).","operationId":"menu_from_one_image_v1_owner_menus_from_image_post","parameters":[{"name":"output_language","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MenuImageOutputLanguage","description":"BCP-47 language code for extracted text (single language).","default":"en"},"description":"BCP-47 language code for extracted text (single language)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_menu_from_one_image_v1_owner_menus_from_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Menu From One Image V1 Owner Menus From Image Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/parse-selecto":{"post":{"tags":["owner-menus"],"summary":"Menu Parse Selecto","description":"Parse a Selecto YAML file into owner-menu-import payload. Use with /merge to combine with image payloads.","operationId":"menu_parse_selecto_v1_owner_menus_parse_selecto_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_menu_parse_selecto_v1_owner_menus_parse_selecto_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Menu Parse Selecto V1 Owner Menus Parse Selecto Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/merge":{"post":{"tags":["owner-menus"],"summary":"Menu Merge Payloads","description":"Merge multiple owner-menu-import payloads into one (dedupe, reorder, validate).\nConsumes one multi-image-merge quota when payloads length >= 2.","operationId":"menu_merge_payloads_v1_owner_menus_merge_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Menu Merge Payloads V1 Owner Menus Merge Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/from-images-quota":{"get":{"tags":["owner-menus"],"summary":"Get From Images Quota","description":"Return owner image-credit balance (no UTC daily auto-refill; issue #169).","operationId":"get_from_images_quota_v1_owner_menus_from_images_quota_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"anyOf":[{"type":"integer"},{"type":"boolean"},{"type":"string"},{"type":"null"}]},"title":"Response Get From Images Quota V1 Owner Menus From Images Quota Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/from-images":{"post":{"tags":["owner-menus"],"summary":"Menu From Images","description":"Ingest menu from multiple uploaded images: extract each, merge into one template, remove duplicates.\nOne image credit per file (daily refill applies). Returns owner-menu-import shape.","operationId":"menu_from_images_v1_owner_menus_from_images_post","parameters":[{"name":"output_language","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MenuImageOutputLanguage","description":"BCP-47 language code for extracted text (single language).","default":"en"},"description":"BCP-47 language code for extracted text (single language)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_menu_from_images_v1_owner_menus_from_images_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Menu From Images V1 Owner Menus From Images Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/from-import":{"post":{"tags":["owner-menus"],"summary":"Menu From Import","description":"Ingest Selecto file and/or menu images; merge and reorder sections. Returns payload only (no menu created).\nUser can review on edit screen and save to create the menu. Each menu image costs one credit.","operationId":"menu_from_import_v1_owner_menus_from_import_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_menu_from_import_v1_owner_menus_from_import_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Menu From Import V1 Owner Menus From Import Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/translate-selecto-stream":{"post":{"tags":["owner-menus"],"summary":"Translate Selecto Stream","description":"Stream translation for a selecto payload without a persisted menu.\nOwner must own restaurant_id. Does not write to the registry; final event includes selecto_creation.\nConsumes one image credit per successfully generated target language.","operationId":"translate_selecto_stream_v1_owner_menus_translate_selecto_stream_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/infer-item-tags":{"post":{"tags":["owner-menus"],"summary":"Post Infer Item Tags","description":"Infer allergens/diets from item name+description using server keywords and filters.\nMatches ingest _apply_keyword_matching (diet excludes, explicit markers, inferred ingredients).","operationId":"post_infer_item_tags_v1_owner_menus_infer_item_tags_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"title":"Response Post Infer Item Tags V1 Owner Menus Infer Item Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/rescan-tags":{"post":{"tags":["owner-menus"],"summary":"Post Rescan Menu Tags","description":"Batch keyword inference over all items in selecto_creation; returns items whose allergens/diets\nwould change (optional_diets / removable_allergens are not modified server-side).","operationId":"post_rescan_menu_tags_v1_owner_menus_rescan_tags_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Rescan Menu Tags V1 Owner Menus Rescan Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/usage-summary":{"get":{"tags":["owner-menus"],"summary":"Get Owner Menus Usage Summary","description":"Aggregated usage per menu for the signed-in owner (QR vs other channels).","operationId":"get_owner_menus_usage_summary_v1_owner_menus_usage_summary_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/{menu_id}/usage":{"get":{"tags":["owner-menus"],"summary":"Get Menu Usage For Owner","description":"Aggregated usage for one menu (owner must own it).","operationId":"get_menu_usage_for_owner_v1_owner_menus__menu_id__usage_get","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/{menu_id}":{"get":{"tags":["owner-menus"],"summary":"Get Menu","description":"Get a menu by id for editing. Owner must own the menu's restaurant.\nReturns menu id, restaurant_id, status, selecto_creation.","operationId":"get_menu_v1_owner_menus__menu_id__get","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["owner-menus"],"summary":"Update Menu","description":"Update a menu. Body: optional status (DRAFT|ACTIVE), optional selecto_creation.\nReturns menu id and public_id.","operationId":"update_menu_v1_owner_menus__menu_id__put","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["owner-menus"],"summary":"Delete Menu","description":"Delete an archived menu. Irreversible.\nReturns 204 on success.","operationId":"delete_menu_v1_owner_menus__menu_id__delete","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/{menu_id}/generate-translations":{"post":{"tags":["owner-menus"],"summary":"Generate Menu Translations","description":"Generate and save translated menu variants for target languages. One image credit per target language.","operationId":"generate_menu_translations_v1_owner_menus__menu_id__generate_translations_post","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/{menu_id}/generate-translations-stream":{"post":{"tags":["owner-menus"],"summary":"Generate Menu Translations Stream","description":"Stream server-side translation progress while generating menu translations. One image credit per generated language.","operationId":"generate_menu_translations_stream_v1_owner_menus__menu_id__generate_translations_stream_post","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/{menu_id}/items/{item_index}/availability":{"patch":{"tags":["owner-menus"],"summary":"Patch Menu Item Availability","description":"Lightweight availability toggle by flat item index (order in sections[].items).\nBody: availability (active|hidden|special), optional available_until, hidden_reason,\nuse_default_expiry (bool, for special).","operationId":"patch_menu_item_availability_v1_owner_menus__menu_id__items__item_index__availability_patch","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"item_index","in":"path","required":true,"schema":{"type":"integer","title":"Item Index"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/menus/{menu_id}/sections/{section_index}/availability":{"patch":{"tags":["owner-menus"],"summary":"Patch Menu Section Availability","description":"Section-level availability: active | hidden | special with optional available_until.\nBody matches item availability PATCH (availability, use_default_expiry, available_until, hidden_reason).","operationId":"patch_menu_section_availability_v1_owner_menus__menu_id__sections__section_index__availability_patch","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"section_index","in":"path","required":true,"schema":{"type":"integer","title":"Section Index"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/restaurants":{"post":{"tags":["owner-restaurants"],"summary":"Create Restaurant","description":"Create a restaurant. Body: name, currency (optional), address?, image_url?, place_id? (optional).\nIf place_id is set, fetches details from Places and fills name, address, location, phone, etc.\nReturns { id }.","operationId":"create_restaurant_v1_owner_restaurants_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["owner-restaurants"],"summary":"List Restaurants","description":"List restaurants for the current owner.\nReturns list of { id, name, currency, location?, archived? }.","operationId":"list_restaurants_v1_owner_restaurants_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/restaurants/{restaurant_id}":{"put":{"tags":["owner-restaurants"],"summary":"Update Restaurant","description":"Update a restaurant. Body: name?, currency?, address?, location? (latitude, longitude), archived?.\nReturns { id }.","operationId":"update_restaurant_v1_owner_restaurants__restaurant_id__put","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["owner-restaurants"],"summary":"Delete Restaurant","description":"Delete a restaurant and its menus. Allowed if the restaurant is archived or has no menus. Irreversible.\nReturns 204 on success.","operationId":"delete_restaurant_v1_owner_restaurants__restaurant_id__delete","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/owner/upload-image":{"post":{"tags":["owner-upload"],"summary":"Upload Image","description":"Upload an image for a menu item. Returns { url } for use in image_url.\nUses S3 when SELECTO_UPLOADS_BUCKET is set (e.g. Lambda); otherwise local disk.","operationId":"upload_image_v1_owner_upload_image_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_image_v1_owner_upload_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Upload Image V1 Owner Upload Image Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/menu-filters":{"get":{"tags":["public-filters"],"summary":"Get Menu Filters","description":"Return allergen and diet options for filtering and tagging menu items.","operationId":"get_menu_filters_v1_menu_filters_get","parameters":[{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Menu Filters V1 Menu Filters Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/menus/from-image":{"post":{"tags":["public-menus"],"summary":"Menu From Image","description":"Ingest menu from an uploaded image. Returns owner-menu-import shape (name?, menu_type?, currency?,\ndescription?, address?, sections) for use in Import flow: show as YAML, then convert to menu / edit / cancel.\nRate-limited.","operationId":"menu_from_image_v1_menus_from_image_post","parameters":[{"name":"output_language","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MenuImageOutputLanguage","description":"BCP-47 language code for extracted text (single language).","default":"en"},"description":"BCP-47 language code for extracted text (single language)."},{"name":"X-Vision-Public-Client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Vision-Public-Client"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_menu_from_image_v1_menus_from_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Menu From Image V1 Menus From Image Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/menus/from-url":{"post":{"tags":["public-menus"],"summary":"Menu From Url","description":"Ingest menu from external URL; return same shape as GET /v1/menus/:id (ephemeral id).\nRate-limited and URL-validated.","operationId":"menu_from_url_v1_menus_from_url_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Menu From Url V1 Menus From Url Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/menus/{menu_id}":{"get":{"tags":["public-menus"],"summary":"Get Public Menu","description":"Return a published menu by id. No auth. 404 if not found or not ACTIVE.\nPayload: id, locale, sections with items (name, description, allergens, price).","operationId":"get_public_menu_v1_menus__menu_id__get","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"}},{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Owner-only: preview a saved version UUID","title":"Version"},"description":"Owner-only: preview a saved version UUID"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Public Menu V1 Menus  Menu Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/places/autocomplete":{"post":{"tags":["public-places"],"summary":"Places Autocomplete","description":"Proxy for Google Places Autocomplete. Returns [{placeId, mainText, secondaryText}].\nRequires diner or owner auth. Key is server-side only.\nWhen location is set, radius_meters (optional) restricts to that radius in metres.","operationId":"places_autocomplete_v1_places_autocomplete_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Diner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Diner-Id"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutocompleteBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response Places Autocomplete V1 Places Autocomplete Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/owner-invite":{"get":{"tags":["public"],"summary":"Get Public Owner Invite","description":"No auth. Returns display fields when token is still usable; status otherwise.","operationId":"get_public_owner_invite_v1_public_owner_invite_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","minLength":8,"description":"Opaque invite token from /welcome?token=","title":"Token"},"description":"Opaque invite token from /welcome?token="}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Public Owner Invite V1 Public Owner Invite Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/owner-invite/menu-preview":{"get":{"tags":["public"],"summary":"Get Public Owner Invite Menu Preview","description":"No auth. Returns the same shape as GET /v1/menus/:id for the menu when the invite is valid\nand the menu belongs to the invite's restaurant (drafts allowed; uses head or live snapshot\nper build_public_response_for_menu).","operationId":"get_public_owner_invite_menu_preview_v1_public_owner_invite_menu_preview_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","minLength":8,"description":"Owner-invite token from /welcome?token=","title":"Token"},"description":"Owner-invite token from /welcome?token="},{"name":"menu_id","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Menu id to preview for this restaurant","title":"Menu Id"},"description":"Menu id to preview for this restaurant"},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Public Owner Invite Menu Preview V1 Public Owner Invite Menu Preview Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/restaurants/{restaurant_id}":{"get":{"tags":["public-restaurants"],"summary":"Get Public Restaurant","description":"Get one restaurant by id with its published menus. 404 if not found or archived or no active menus.","operationId":"get_public_restaurant_v1_restaurants__restaurant_id__get","parameters":[{"name":"restaurant_id","in":"path","required":true,"schema":{"type":"string","title":"Restaurant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Public Restaurant V1 Restaurants  Restaurant Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/restaurants":{"get":{"tags":["public-restaurants"],"summary":"List Public Restaurants","description":"List restaurants with published menus. Optional search, diet/allergen filters, official_only.\n\nWhen exclude_allergens or diets are set, restaurants with matching_dish_count == 0 are\ndropped; restaurants with matching_dish_count == null (pre-advisory menus) are kept.","operationId":"list_public_restaurants_v1_restaurants_get","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by restaurant name (case-insensitive)","title":"Search"},"description":"Filter by restaurant name (case-insensitive)"},{"name":"exclude_allergens","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated allergens to exclude; when set with diets, adds matching_dish_count","title":"Exclude Allergens"},"description":"Comma-separated allergens to exclude; when set with diets, adds matching_dish_count"},{"name":"diets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated diets (item must have at least one); when set with exclude_allergens, adds matching_dish_count","title":"Diets"},"description":"Comma-separated diets (item must have at least one); when set with exclude_allergens, adds matching_dish_count"},{"name":"official_only","in":"query","required":false,"schema":{"type":"boolean","description":"When true, exclude restaurants added by diners (unofficial).","default":false,"title":"Official Only"},"description":"When true, exclude restaurants added by diners (unofficial)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Public Restaurants V1 Restaurants Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/menu-usage/events":{"post":{"tags":["menu-usage"],"summary":"Post Menu Usage Event","description":"Record one usage event into aggregate counters (rate-limited).","operationId":"post_menu_usage_event_v1_menu_usage_events_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MenuUsageEventBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Post Menu Usage Event V1 Menu Usage Events Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/qr/m/{menu_id}":{"get":{"tags":["menu-usage"],"summary":"Qr Menu Redirect","description":"QR entry: 302 to canonical public menu path; records qr_used + menu_shown (channel=qr).\n\nResponse is not indexable (X-Robots-Tag). HTML clients are rare; SPA uses /app/m/:id.","operationId":"qr_menu_redirect_v1_qr_m__menu_id__get","parameters":[{"name":"menu_id","in":"path","required":true,"schema":{"type":"string","title":"Menu Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/robots.txt":{"get":{"tags":["menu-usage"],"summary":"Robots Txt","description":"Disallow QR tracking entry path; allow everything else at site root when served by API.","operationId":"robots_txt_robots_txt_get","responses":{"200":{"description":"Successful Response"}}}},"/v1/vision-jobs":{"post":{"tags":["vision-jobs"],"summary":"Post Create Vision Job","description":"Submit a menu image for async extraction; returns job_id immediately.\nAuth: X-Vision-Job-User owner|diner|public (default public). Owner/diner must also send auth headers.","operationId":"post_create_vision_job_v1_vision_jobs_post","parameters":[{"name":"output_language","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MenuImageOutputLanguage","default":"en"}},{"name":"X-Vision-Job-User","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Vision-Job-User"}},{"name":"X-Vision-Public-Client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Vision-Public-Client"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_post_create_vision_job_v1_vision_jobs_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/vision-jobs/{job_id}":{"get":{"tags":["vision-jobs"],"summary":"Get Vision Job Status","description":"Poll job status; completed jobs include the same payload shape as sync from-image.","operationId":"get_vision_job_status_v1_vision_jobs__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"X-Vision-Job-User","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Vision-Job-User"}},{"name":"X-Vision-Public-Client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Vision-Public-Client"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Vision Job Status V1 Vision Jobs  Job Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/translation-jobs":{"post":{"tags":["translation-jobs"],"summary":"Post Translation Job","description":"Submit translation work; returns job_id immediately. Poll GET until completed or failed.","operationId":"post_translation_job_v1_translation_jobs_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Post Translation Job V1 Translation Jobs Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/translation-jobs/{job_id}":{"get":{"tags":["translation-jobs"],"summary":"Get Translation Job","description":"Poll job status; completed jobs include payload (shape depends on mode).","operationId":"get_translation_job_v1_translation_jobs__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Translation Job V1 Translation Jobs  Job Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/client-errors":{"post":{"tags":["client-errors"],"summary":"Post Client Error","description":"Store client error (owner auth required) and send Telegram notification. Issue #168.","operationId":"post_client_error_v1_client_errors_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Owner-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Owner-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientErrorBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Client Error V1 Client Errors Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","description":"Liveness/readiness. Returns API version for display in frontend.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AutocompleteBody":{"properties":{"input":{"type":"string","title":"Input"},"session_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Token"},"location":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Location"},"radius_meters":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Radius Meters"}},"type":"object","required":["input"],"title":"AutocompleteBody","description":"Body for POST /v1/places/autocomplete."},"Body_menu_from_image_v1_menus_from_image_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"Menu image (JPEG, PNG, WebP, GIF)"}},"type":"object","required":["file"],"title":"Body_menu_from_image_v1_menus_from_image_post"},"Body_menu_from_images_v1_owner_menus_from_images_post":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files","description":"Menu images (2–10) to merge into one menu"}},"type":"object","required":["files"],"title":"Body_menu_from_images_v1_owner_menus_from_images_post"},"Body_menu_from_import_v1_owner_menus_from_import_post":{"properties":{"restaurant_id":{"type":"string","title":"Restaurant Id","description":"Restaurant id"},"selecto_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Selecto File","description":"Optional Selecto file (.yaml, .yml)"},"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files","description":"Optional menu images","default":[]},"output_language":{"$ref":"#/components/schemas/MenuImageOutputLanguage","default":"en"}},"type":"object","required":["restaurant_id"],"title":"Body_menu_from_import_v1_owner_menus_from_import_post"},"Body_menu_from_one_image_v1_owner_menus_from_image_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"Single menu image (JPEG, PNG, WebP, GIF)"}},"type":"object","required":["file"],"title":"Body_menu_from_one_image_v1_owner_menus_from_image_post"},"Body_menu_parse_selecto_v1_owner_menus_parse_selecto_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"Selecto YAML file"}},"type":"object","required":["file"],"title":"Body_menu_parse_selecto_v1_owner_menus_parse_selecto_post"},"Body_post_create_vision_job_v1_vision_jobs_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_post_create_vision_job_v1_vision_jobs_post"},"Body_post_me_menus_from_image_v1_me_menus_from_image_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"Menu image (JPEG, PNG, WebP, GIF)"}},"type":"object","required":["file"],"title":"Body_post_me_menus_from_image_v1_me_menus_from_image_post"},"Body_post_me_menus_from_images_v1_me_menus_from_images_post":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files","description":"Menu images (2–10) to merge into one menu"}},"type":"object","required":["files"],"title":"Body_post_me_menus_from_images_v1_me_menus_from_images_post"},"Body_upload_image_v1_owner_upload_image_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"Image for menu item (JPEG, PNG, WebP, GIF)"}},"type":"object","required":["file"],"title":"Body_upload_image_v1_owner_upload_image_post"},"CallbackBody":{"properties":{"provider":{"type":"string","title":"Provider","default":"google"},"code":{"type":"string","title":"Code","default":""},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},"type":"object","title":"CallbackBody","description":"POST body for /v1/auth/callback: provider, code, optional state."},"CheckoutBody":{"properties":{"kind":{"type":"string","title":"Kind","description":"pack | basic | pro | ultimate"}},"type":"object","required":["kind"],"title":"CheckoutBody"},"ClaimFromInviteBody":{"properties":{"token":{"type":"string","minLength":8,"title":"Token"}},"type":"object","required":["token"],"title":"ClaimFromInviteBody","description":"Body for POST .../claim-from-owner-invite."},"ClientErrorBody":{"properties":{"error_type":{"type":"string","maxLength":64,"minLength":1,"title":"Error Type"},"message":{"type":"string","maxLength":2000,"minLength":1,"title":"Message"},"consent":{"type":"boolean","title":"Consent","default":false},"stack":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Stack"},"menu_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Menu Id"},"device_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Device Name"}},"type":"object","required":["error_type","message"],"title":"ClientErrorBody","description":"Payload for a browser-side error report."},"CreateOwnerInviteBody":{"properties":{"restaurant_id":{"type":"string","minLength":1,"title":"Restaurant Id"},"valid_days":{"type":"integer","maximum":90.0,"minimum":1.0,"title":"Valid Days","default":90}},"type":"object","required":["restaurant_id"],"title":"CreateOwnerInviteBody","description":"Body for POST /v1/admin/owner-invites."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"MenuImageOutputLanguage":{"type":"string","enum":["en","es","fr","ru"],"title":"MenuImageOutputLanguage","description":"Target language for extracted menu text (single locale in payload)."},"MenuUsageBatchBody":{"properties":{"menu_ids":{"items":{"type":"string"},"type":"array","maxItems":200,"minItems":1,"title":"Menu Ids"}},"type":"object","required":["menu_ids"],"title":"MenuUsageBatchBody","description":"Body for POST .../usage/metrics/menus/batch (admin menus hub)."},"MenuUsageEventBody":{"properties":{"event":{"type":"string","title":"Event","description":"menu_shown | qr_used | item_opened | filter_applied | restaurant_search"},"menu_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Menu Id","description":"Published menu UUID for menu-scoped events"},"entry_channel":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entry Channel","description":"qr | app | direct | unknown"},"search_area":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search Area","description":"home | near_me for restaurant_search"}},"type":"object","required":["event"],"title":"MenuUsageEventBody","description":"One client-reported usage event (aggregated server-side; no raw PII stored)."},"PatchCreditsBody":{"properties":{"credits":{"type":"integer","maximum":1000000.0,"minimum":0.0,"title":"Credits"}},"type":"object","required":["credits"],"title":"PatchCreditsBody","description":"Body for PATCH .../credits."},"PatchDinerBody":{"properties":{"diets":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Diets"},"allergens":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allergens"},"fav_restaurant_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Fav Restaurant Ids"}},"type":"object","title":"PatchDinerBody","description":"Optional fields for PATCH /v1/me/diner."},"PatchRestaurantOwnerBody":{"properties":{"owner_id":{"type":"string","minLength":1,"title":"Owner Id"}},"type":"object","required":["owner_id"],"title":"PatchRestaurantOwnerBody","description":"Body for PATCH .../owner (reassign restaurant to another owner)."},"PatchRestaurantUnofficialBody":{"properties":{"unofficial":{"type":"boolean","title":"Unofficial"}},"type":"object","required":["unofficial"],"title":"PatchRestaurantUnofficialBody","description":"Body for PATCH .../unofficial."},"PatchTierBody":{"properties":{"subscription_tier":{"type":"string","maxLength":32,"minLength":1,"title":"Subscription Tier"}},"type":"object","required":["subscription_tier"],"title":"PatchTierBody","description":"Body for PATCH .../tier."},"ReportRestaurantBody":{"properties":{"restaurant_id":{"type":"string","title":"Restaurant Id"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["restaurant_id"],"title":"ReportRestaurantBody","description":"Body for POST /v1/me/reports: report an unofficial restaurant as problematic."},"SelectoFieldBody":{"properties":{"grant":{"type":"boolean","title":"Grant"}},"type":"object","required":["grant"],"title":"SelectoFieldBody","description":"Grant or revoke Selecto (field) read-only admin (issue #190)."},"SetAdminBody":{"properties":{"is_admin":{"type":"boolean","title":"Is Admin"}},"type":"object","required":["is_admin"],"title":"SetAdminBody","description":"Body for POST .../admin flag."},"UnofficialMenuBody":{"properties":{"place_id":{"type":"string","title":"Place Id"},"restaurant_name":{"type":"string","title":"Restaurant Name","default":""},"address":{"type":"string","title":"Address","default":""},"selecto_creation":{"additionalProperties":true,"type":"object","title":"Selecto Creation"},"replace_restaurant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Replace Restaurant Id"}},"type":"object","required":["place_id","selecto_creation"],"title":"UnofficialMenuBody","description":"Body for POST /v1/me/unofficial-menus: place_id required; save ephemeral menu when no restaurant in 10km."},"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"}}}}