{"openapi": "3.0.0", "paths": {"/api/admin/cron-jobs": {"get": {"description": "Jobs are executed by the trem-cron-scheduler Docker service using each job cron_expression.", "operationId": "list", "parameters": [], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCronJobListResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "List cron jobs (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateCronJobDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCronJobResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "Create cron job (admin)", "tags": ["admin"]}}, "/api/admin/cron-jobs/{id}": {"patch": {"operationId": "update", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateCronJobDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCronJobResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "Update cron job (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteCronJobResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "Delete cron job (admin)", "tags": ["admin"]}}, "/api/admin/cron-jobs/{id}/run-now": {"post": {"description": "Executes the job request_path once and updates last_run_* fields.", "operationId": "runNow", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCronJobRunResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "Run cron job immediately (admin)", "tags": ["admin"]}}, "/api/admin/auth/login": {"post": {"description": "Returns JWT access token and opaque refresh token. Access token includes role `admin`.", "operationId": "login", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LoginDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminLoginResponseDto"}}}}, "401": {"description": "Invalid credentials"}}, "summary": "Admin login (CMS)", "tags": ["admin"]}}, "/api/admin/auth/forgot-password": {"post": {"description": "Always returns success. If the email belongs to an admin, a one-time reset link is queued and sent. The link uses config `admin_password_reset_base_url` (or env ADMIN_PASSWORD_RESET_BASE_URL) with `reset_token`. Complete with POST /admin/auth/reset-password.", "operationId": "forgotPassword", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ForgotPasswordDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AuthMessageResponseDto"}}}}}, "summary": "Request a CMS admin password reset email", "tags": ["admin"]}}, "/api/admin/auth/reset-password": {"post": {"description": "Consumes a one-time reset token from the forgot-password email. Returns new access and refresh tokens. Other admin sessions are signed out.", "operationId": "resetPassword", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResetPasswordDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminLoginResponseDto"}}}}, "400": {"description": "Invalid or expired token"}}, "summary": "Set a new CMS admin password using the email token", "tags": ["admin"]}}, "/api/admin/auth/refresh-token": {"post": {"description": "Returns new access_token and refresh_token; old refresh token is invalidated.", "operationId": "refresh", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RefreshDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminRefreshResponseDto"}}}}, "401": {"description": "Invalid refresh token"}}, "summary": "Rotate admin refresh token", "tags": ["admin"]}}, "/api/admin/auth/logout": {"post": {"description": "Pass the refresh_token to revoke that session.", "operationId": "logout", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LogoutDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminLogoutResponseDto"}}}}}, "summary": "Invalidate an admin refresh token", "tags": ["admin"]}}, "/api/admin/shows": {"post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateShowDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminShowRowResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create show (admin)", "tags": ["admin"]}, "get": {"description": "Returns shows with pagination, search by name, and sorting by name/created_at.", "operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "description": "1-based page number", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "description": "Items per page (max 100)", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search query (matches name, case-insensitive)", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "description": "Sort field", "schema": {"default": "created_at", "type": "string", "enum": ["name", "created_at"]}}, {"name": "sort_order", "required": false, "in": "query", "description": "Sort order", "schema": {"default": "desc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminShowsListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List shows (admin)", "tags": ["admin"]}}, "/api/admin/shows/currencies": {"get": {"description": "Distinct ISO currency codes found on show pricing (tier/row/seat prices, categories, bands, add-ons).", "operationId": "listCurrencies", "parameters": [], "responses": {"200": {"description": "{ currencies: string[] }"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List currencies used by shows (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}": {"get": {"operationId": "get", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Same response shape as the public show details endpoint", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShowCardResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "Get show (admin)", "tags": ["admin"]}, "patch": {"description": "Use `application/json` for text-only updates. Use `multipart/form-data` to upload optional `show_image` (landscape preview), `image_poster` (card poster), and/or `videos` together with optional text fields. Uploaded images are stored under `/uploads/show-images/`; videos under `/uploads/show-videos/`. Max image size: SHOW_IMAGE_MAX_BYTES (default 5MB). Max each video: SHOW_VIDEO_MAX_BYTES (default 500MB).", "operationId": "update", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "description": "JSON (`AdminUpdateShowDto`) or `multipart/form-data` with the same optional text fields plus files `show_image` (one), `image_poster` (one), and `videos` (repeat for multiple).", "content": {"application/json": {"schema": {"type": "string"}}, "multipart/form-data": {"schema": {"type": "string"}}}}, "responses": {"200": {"description": "Full show object (same shape as GET /api/admin/shows/:showId). JSON or multipart.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShowCardResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "Update show (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteShowResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "Delete show (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/asset-directory": {"post": {"description": "Get-or-create `Shows / {showName} ({shortId})` in the asset manager and return the directory.", "operationId": "ensureAssetDirectory", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Asset directory row"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "Ensure Media Library folder for a show", "tags": ["admin"]}}, "/api/admin/shows/{showId}/apply-setup": {"post": {"description": "Copies performance sessions and venue seating prices (section/row/seat + bands) from this show onto selected target shows. Existing pricing on targets is overwritten. Sessions are synced by date+time; extra target sessions without bookings are removed. Targets must share the same venue (or have no venue \u2014 then the source venue is assigned).", "operationId": "applySetup", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Source show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminApplyShowSetupDto"}}}}, "responses": {"200": {"description": "Apply result with applied/skipped targets"}, "400": {"description": "Validation error"}}, "security": [{"bearer": []}], "summary": "Apply sessions + seating/pricing to other shows (admin)", "tags": ["admin"]}}, "/api/admin/shows/bulk": {"patch": {"description": "Partial-update many shows in one request (name, city, dates, venue, status, preview_mode, loyalty). Per-item errors are returned without aborting the whole batch.", "operationId": "bulkUpdate", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminBulkUpdateShowsDto"}}}}, "responses": {"200": {"description": "{ results: [{ id, ok, show|error }] }"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Bulk update shows (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/add-ons": {"get": {"operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "List add-ons for a show (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateShowAddOnDto"}}}}, "responses": {"201": {"description": "Add-on created"}}, "security": [{"bearer": []}], "summary": "Create show add-on (admin)", "tags": ["admin"]}}, "/api/admin/show-add-ons/{addOnId}": {"patch": {"operationId": "update", "parameters": [{"name": "addOnId", "required": true, "in": "path", "description": "Add-on UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateShowAddOnDto"}}}}, "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Update show add-on (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "addOnId", "required": true, "in": "path", "description": "Add-on UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Delete show add-on (admin)", "tags": ["admin"]}}, "/api/admin/show-add-ons/{addOnId}/image": {"post": {"description": "Multipart upload (field name: image). Max file size: ADD_ON_IMAGE_MAX_BYTES (default 2MB). Allowed types: jpg/jpeg/jfif, png, svg, webp.", "operationId": "uploadImage", "parameters": [{"name": "addOnId", "required": true, "in": "path", "description": "Add-on UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "properties": {"image": {"type": "string", "format": "binary"}}, "required": ["image"]}}}}, "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "summary": "Upload add-on image (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/pricing-categories": {"get": {"operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "List pricing categories for a show (base price + discount %)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateShowPricingCategoryDto"}}}}, "responses": {"201": {"description": "Pricing category created"}}, "security": [{"bearer": []}], "summary": "Create a pricing category for a show", "tags": ["admin"]}}, "/api/admin/pricing-categories/{categoryId}": {"patch": {"operationId": "update", "parameters": [{"name": "categoryId", "required": true, "in": "path", "description": "Pricing category UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateShowPricingCategoryDto"}}}}, "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Update a show pricing category", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "categoryId", "required": true, "in": "path", "description": "Pricing category UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Delete a show pricing category", "tags": ["admin"]}}, "/api/admin/users/{userId}/loyalty": {"get": {"operationId": "getSummary", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Loyalty summary"}, "401": {"description": "Unauthorized"}, "404": {"description": "User not found"}}, "security": [{"bearer": []}], "summary": "Get user loyalty points balance and recent transactions (admin)", "tags": ["admin"]}}, "/api/admin/users/{userId}/loyalty/adjust": {"post": {"operationId": "adjust", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminAdjustLoyaltyPointsDto"}}}}, "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "summary": "Manually adjust user loyalty points (admin)", "tags": ["admin"]}}, "/api/admin/categories": {"post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateCategoryDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCategoryResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create category (admin)", "tags": ["admin"]}, "get": {"description": "Pagination, search by id/label, and sorting by label/id/created_at.", "operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "description": "1-based page number", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "description": "Items per page (max 100)", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search query (matches id or label, case-insensitive)", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "description": "Sort field", "schema": {"default": "label", "type": "string", "enum": ["label", "id", "created_at"]}}, {"name": "sort_order", "required": false, "in": "query", "description": "Sort order", "schema": {"default": "asc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCategoriesListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List categories (admin)", "tags": ["admin"]}}, "/api/admin/categories/{categoryId}": {"get": {"operationId": "get", "parameters": [{"name": "categoryId", "required": true, "in": "path", "description": "Category id (string key)", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCategoryResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Category not found"}}, "security": [{"bearer": []}], "summary": "Get category (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "categoryId", "required": true, "in": "path", "description": "Category id (string key)", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateCategoryDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCategoryResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Category not found"}}, "security": [{"bearer": []}], "summary": "Update category (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "categoryId", "required": true, "in": "path", "description": "Category id (string key)", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteCategoryResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Category not found"}}, "security": [{"bearer": []}], "summary": "Delete category (admin)", "tags": ["admin"]}}, "/api/admin/cities": {"post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateCityDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCityResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create city (admin)", "tags": ["admin"]}, "get": {"description": "Pagination, search by id/name, and sorting by name/id/created_at.", "operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "description": "1-based page number", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "description": "Items per page (max 100)", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search query (matches id or name, case-insensitive)", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "description": "Sort field", "schema": {"default": "name", "type": "string", "enum": ["name", "id", "created_at"]}}, {"name": "sort_order", "required": false, "in": "query", "description": "Sort order", "schema": {"default": "asc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCitiesListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List cities (admin)", "tags": ["admin"]}}, "/api/admin/cities/{cityId}": {"get": {"operationId": "get", "parameters": [{"name": "cityId", "required": true, "in": "path", "description": "City id (uuid)", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCityResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "City not found"}}, "security": [{"bearer": []}], "summary": "Get city (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "cityId", "required": true, "in": "path", "description": "City id (uuid)", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateCityDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCityResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "City not found"}}, "security": [{"bearer": []}], "summary": "Update city (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "cityId", "required": true, "in": "path", "description": "City id (uuid)", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteCityResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "City not found"}}, "security": [{"bearer": []}], "summary": "Delete city (admin)", "tags": ["admin"]}}, "/api/admin/venues": {"post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateVenueDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminVenueResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create venue (admin)", "tags": ["admin"]}, "get": {"description": "Pagination, search by id/location_name/city name, filter by city_id, sort by location_name/city_name/created_at.", "operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "description": "1-based page number", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "description": "Items per page (max 100)", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search query (matches id, location_name, or city name)", "schema": {"type": "string"}}, {"name": "city_id", "required": false, "in": "query", "description": "Filter by city id", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "description": "Sort field", "schema": {"default": "location_name", "type": "string", "enum": ["location_name", "city_name", "created_at", "id"]}}, {"name": "sort_order", "required": false, "in": "query", "description": "Sort order", "schema": {"default": "asc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminVenuesListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List venues (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}": {"get": {"operationId": "get", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue id (uuid)", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminVenueResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Venue not found"}}, "security": [{"bearer": []}], "summary": "Get venue (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue id (uuid)", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateVenueDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminVenueResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Venue not found"}}, "security": [{"bearer": []}], "summary": "Update venue (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue id (uuid)", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteVenueResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Venue not found"}}, "security": [{"bearer": []}], "summary": "Delete venue (admin)", "tags": ["admin"]}}, "/api/admin/users": {"post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateUserDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create user (admin)", "tags": ["admin"]}, "get": {"description": "Pagination, search by id/email/name, and sorting by created_at/email/name/id.", "operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "description": "1-based page number", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "description": "Items per page (max 100)", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search query (matches id/email/name, case-insensitive)", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "description": "Sort field", "schema": {"default": "created_at", "type": "string", "enum": ["created_at", "email", "name", "id"]}}, {"name": "sort_order", "required": false, "in": "query", "description": "Sort order", "schema": {"default": "desc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUsersListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List users (admin)", "tags": ["admin"]}}, "/api/admin/users/{userId}/simulation-access-token": {"post": {"description": "Returns a short-lived user access token so CMS can call public APIs (shows, checkout) as the selected buyer.", "operationId": "issueSimulationAccessToken", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "App user UUID", "schema": {"type": "string"}}], "responses": {"401": {"description": "Unauthorized"}, "404": {"description": "User not found"}}, "security": [{"bearer": []}], "summary": "Issue app JWT for CMS checkout simulation", "tags": ["admin"]}}, "/api/admin/users/{userId}": {"get": {"operationId": "get", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "User not found"}}, "security": [{"bearer": []}], "summary": "Get user (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateUserDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "User not found"}}, "security": [{"bearer": []}], "summary": "Update user (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteUserResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "User not found"}}, "security": [{"bearer": []}], "summary": "Delete user (admin)", "tags": ["admin"]}}, "/api/admin/users/{userId}/membership": {"get": {"description": "Returns assigned tier and complimentary ticket balance, or null if none. Mirrors public GET /api/users/me/membership for the given year.", "operationId": "getMembership", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}, {"name": "year", "required": false, "in": "query", "schema": {"example": 2026, "type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserMembershipResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "User not found"}}, "security": [{"bearer": []}], "summary": "User membership for a calendar year (admin)", "tags": ["admin"]}, "put": {"description": "Upserts membership for user + year (default: current calendar year).", "operationId": "upsertMembership", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpsertUserMembershipDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserMembershipResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "User or tier not found"}}, "security": [{"bearer": []}], "summary": "Assign or update user membership (admin)", "tags": ["admin"]}, "delete": {"operationId": "removeMembership", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}, {"name": "year", "required": false, "in": "query", "schema": {"example": 2026, "type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteUserMembershipResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "User or membership not found"}}, "security": [{"bearer": []}], "summary": "Remove user membership for a year (admin)", "tags": ["admin"]}}, "/api/admin/users/{userId}/preferences": {"get": {"description": "Returns show_saved rows and show_notification preferences for the given user.", "operationId": "preferences", "parameters": [{"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Preferences payload"}, "401": {"description": "Unauthorized"}, "404": {"description": "User not found"}}, "security": [{"bearer": []}], "summary": "User preferences (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/reviews": {"get": {"operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminReviewListResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "List reviews for a show (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateReviewDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminReviewResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "Create review for a show (admin)", "tags": ["admin"]}}, "/api/admin/reviews/{reviewId}": {"patch": {"operationId": "update", "parameters": [{"name": "reviewId", "required": true, "in": "path", "description": "Review UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateReviewDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminReviewResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Review not found"}}, "security": [{"bearer": []}], "summary": "Update review (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "reviewId", "required": true, "in": "path", "description": "Review UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteReviewResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Review not found"}}, "security": [{"bearer": []}], "summary": "Delete review (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/video-reactions": {"get": {"operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminVideoReactionListResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "List video reactions for a show (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateVideoReactionDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminVideoReactionResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "Create video reaction for a show (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/video-reactions/upload": {"post": {"operationId": "uploadReactionVideo", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "properties": {"video": {"type": "string", "format": "binary"}}, "required": ["video"]}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminVideoReactionUploadResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "Upload a video for a reaction draft (admin)", "tags": ["admin"]}}, "/api/admin/video-reactions/{reactionId}": {"patch": {"operationId": "update", "parameters": [{"name": "reactionId", "required": true, "in": "path", "description": "Video reaction UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateVideoReactionDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminVideoReactionResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Video reaction not found"}}, "security": [{"bearer": []}], "summary": "Update video reaction (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "reactionId", "required": true, "in": "path", "description": "Video reaction UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteVideoReactionResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Video reaction not found"}}, "security": [{"bearer": []}], "summary": "Delete video reaction (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/tickets": {"get": {"operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTicketListResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "List tickets for a show (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateTicketDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTicketResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "Create ticket for a show (admin)", "tags": ["admin"]}}, "/api/admin/tickets/check-in": {"post": {"description": "Scans a ticket QR (validate URL or TRM:v1 token), returns booking/customer/seat details, and marks the ticket as used (attended). If already used, returns already_attended with message \"Customer already attended\".", "operationId": "checkIn", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTicketCheckInDto"}}}}, "responses": {"200": {"description": "Check-in result (ok or already attended)"}, "400": {"description": "Invalid QR or ticket not eligible"}, "401": {"description": "Unauthorized"}, "404": {"description": "Ticket not found"}}, "security": [{"bearer": []}], "summary": "Check in (attendance) from booking QR", "tags": ["admin"]}}, "/api/admin/tickets/{ticketId}": {"patch": {"operationId": "update", "parameters": [{"name": "ticketId", "required": true, "in": "path", "description": "Ticket UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateTicketDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTicketResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Ticket not found"}}, "security": [{"bearer": []}], "summary": "Update ticket (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "ticketId", "required": true, "in": "path", "description": "Ticket UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteTicketResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Ticket not found"}}, "security": [{"bearer": []}], "summary": "Delete ticket (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/ticket-categories": {"get": {"operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTicketCategoryListResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "List synced ticket categories for a show (read-only; set via venue-tier-prices)", "tags": ["admin"]}}, "/api/admin/checkout-orders": {"get": {"description": "Paginated checkout orders joined with buyer, show, and latest payment. Filter by payment status; search by order/payment/user id, email, or name; sort by date or amount.", "operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search checkout order id, payment id, user id, user email, or user name (case-insensitive)", "schema": {"type": "string"}}, {"name": "payment_status", "required": false, "in": "query", "description": "Filter by latest payment status", "schema": {"type": "string", "enum": ["pending", "processing", "succeeded", "failed", "cancelled"]}}, {"name": "sort_by", "required": false, "in": "query", "description": "Sort field", "schema": {"default": "created_at", "type": "string", "enum": ["created_at", "paid_at", "updated_at", "total_amount"]}}, {"name": "sort_order", "required": false, "in": "query", "schema": {"default": "desc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "Paged checkout orders"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List checkout orders with latest payment (admin)", "tags": ["admin"]}}, "/api/admin/checkout-orders/sandbox/complete": {"post": {"description": "JSON alternative to GET /api/checkout/return for CMS purchase simulation. Fulfills tickets on success.", "operationId": "completeSandboxPayment", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCompleteSandboxPaymentDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CheckoutCompleteResponseDto"}}}}, "400": {"description": "Invalid payment or result"}, "401": {"description": "Unauthorized"}, "404": {"description": "Payment not found"}, "409": {"description": "Checkout order already paid or payment already submitted for this order id"}}, "security": [{"bearer": []}], "summary": "Complete sandbox payment (CMS simulate)", "tags": ["admin"]}}, "/api/admin/payments": {"get": {"description": "Paginated payment records with buyer, show, session, and failure details. Search by payment id, Stripe PI id, checkout id, user, or show.", "operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search payment id, Stripe PI id, checkout id, user email/name, or show name (case-insensitive)", "schema": {"type": "string"}}, {"name": "status", "required": false, "in": "query", "description": "Filter by payment status", "schema": {"type": "string", "enum": ["pending", "processing", "succeeded", "failed", "cancelled"]}}, {"name": "gateway", "required": false, "in": "query", "description": "Filter by payment gateway", "schema": {"type": "string", "enum": ["sandbox", "stripe"]}}, {"name": "sort_by", "required": false, "in": "query", "description": "Sort field", "schema": {"default": "created_at", "type": "string", "enum": ["created_at", "completed_at", "amount"]}}, {"name": "sort_order", "required": false, "in": "query", "schema": {"default": "desc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "Paged payments"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List payments (admin)", "tags": ["admin"]}}, "/api/admin/payments/{paymentId}": {"get": {"description": "Full payment detail including customer, show/session choice, line items, reserved seats, Stripe fields, payment attempts, and issued e-tickets with `qr_payload` (`TRM:v1:{ticketNumber}:{qrCode}`) when the checkout is paid.", "operationId": "getDetail", "parameters": [{"name": "paymentId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": "Payment detail (includes issued_tickets + booking_reference when paid)"}, "401": {"description": "Unauthorized"}, "404": {"description": "Payment not found"}}, "security": [{"bearer": []}], "summary": "Payment detail (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/videos": {"post": {"description": "Multipart upload. Supports multiple files. Max file size: SHOW_VIDEO_MAX_BYTES (default 500MB). The reverse proxy must allow at least that body size (e.g. nginx client_max_body_size) or uploads return 413 before reaching this handler.", "operationId": "upload", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "properties": {"kind": {"type": "string", "example": "clip"}, "titles": {"type": "string", "description": "JSON array of titles aligned with uploaded files", "example": "[\"Highlight reel\",\"Trailer\"]"}, "videos": {"type": "array", "items": {"type": "string", "format": "binary"}}}, "required": ["videos"]}}}}, "responses": {"200": {"description": "Returns the full show object (same as GET /api/admin/shows/:showId) after inserts.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShowCardResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Upload show videos (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/videos/{videoId}": {"delete": {"description": "Deletes one record from shows_videos and attempts to remove the uploaded file (best-effort). Returns the full show object (same as GET /api/admin/shows/:showId) after deletion.", "operationId": "delete", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}, {"name": "videoId", "required": true, "in": "path", "description": "Shows video UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Returns the full show object (same as GET /api/admin/shows/:showId) after deletion.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShowCardResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Delete a show video (admin)", "tags": ["admin"]}, "patch": {"description": "Updates the display title for one shows_videos row.", "operationId": "updateVideo", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}, {"name": "videoId", "required": true, "in": "path", "description": "Shows video UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateShowVideoDto"}}}}, "responses": {"200": {"description": "Returns the full show object (same as GET /api/admin/shows/:showId) after update.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShowCardResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Update show video title (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/videos/{videoId}/main": {"patch": {"description": "Marks the given shows_videos row as main (is_main=true) and clears any previous main video for the show.", "operationId": "setMain", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}, {"name": "videoId", "required": true, "in": "path", "description": "Shows video UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Returns the full show object (same as GET /api/admin/shows/:showId) after update.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShowCardResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Set main show video (admin)", "tags": ["admin"]}}, "/api/admin/articles": {"post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateArticleDto"}}}}, "responses": {"201": {"description": "Created article"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create article (admin)", "tags": ["admin"]}, "get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "default": 20, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search title/city/highlight", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "schema": {"default": "news_date", "type": "string", "enum": ["news_date", "created_at", "title"]}}, {"name": "sort_order", "required": false, "in": "query", "schema": {"default": "desc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "Article list with pagination"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List articles (admin)", "tags": ["admin"]}}, "/api/admin/articles/{articleId}": {"get": {"operationId": "get", "parameters": [{"name": "articleId", "required": true, "in": "path", "description": "Article UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Article detail"}, "401": {"description": "Unauthorized"}, "404": {"description": "Article not found"}}, "security": [{"bearer": []}], "summary": "Get article (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "articleId", "required": true, "in": "path", "description": "Article UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateArticleDto"}}}}, "responses": {"200": {"description": "Updated article"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Article not found"}}, "security": [{"bearer": []}], "summary": "Update article (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "articleId", "required": true, "in": "path", "description": "Article UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Delete result"}, "401": {"description": "Unauthorized"}, "404": {"description": "Article not found"}}, "security": [{"bearer": []}], "summary": "Delete article (admin)", "tags": ["admin"]}}, "/api/admin/article-tags": {"post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateArticleTagDto"}}}}, "responses": {"201": {"description": "Created tag"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create article tag (admin)", "tags": ["admin"]}, "get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search by id or label", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "schema": {"default": "label", "type": "string", "enum": ["label", "id", "created_at"]}}, {"name": "sort_order", "required": false, "in": "query", "schema": {"default": "asc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "Tag list with pagination"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List article tags (admin)", "tags": ["admin"]}}, "/api/admin/article-tags/{tagId}": {"get": {"operationId": "get", "parameters": [{"name": "tagId", "required": true, "in": "path", "description": "Tag id", "schema": {"type": "string"}}], "responses": {"200": {"description": "Tag detail"}, "401": {"description": "Unauthorized"}, "404": {"description": "Tag not found"}}, "security": [{"bearer": []}], "summary": "Get article tag (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "tagId", "required": true, "in": "path", "description": "Tag id", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateArticleTagDto"}}}}, "responses": {"200": {"description": "Updated tag"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Tag not found"}}, "security": [{"bearer": []}], "summary": "Update article tag (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "tagId", "required": true, "in": "path", "description": "Tag id", "schema": {"type": "string"}}], "responses": {"200": {"description": "Delete result"}, "401": {"description": "Unauthorized"}, "404": {"description": "Tag not found"}}, "security": [{"bearer": []}], "summary": "Delete article tag (admin)", "tags": ["admin"]}}, "/api/admin/articles/{articleId}/image": {"post": {"description": "Multipart upload (field name: image). Max file size: ARTICLE_IMAGE_MAX_BYTES (default 1MB). Allowed types: jpg, png, svg. Returns `{ image_url }`.", "operationId": "upload", "parameters": [{"name": "articleId", "required": true, "in": "path", "description": "Article UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "properties": {"image": {"type": "string", "format": "binary"}}, "required": ["image"]}}}}, "responses": {"200": {"description": "Upload result"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Upload article image (admin)", "tags": ["admin"]}}, "/api/admin/casts": {"get": {"operationId": "list", "parameters": [], "responses": {"200": {"description": "Cast list (simple, non-paginated)"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List casts (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateCastDto"}}}}, "responses": {"201": {"description": "Created cast"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create cast (admin)", "tags": ["admin"]}}, "/api/admin/casts/{castId}": {"get": {"operationId": "get", "parameters": [{"name": "castId", "required": true, "in": "path", "description": "Cast UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Cast detail"}, "401": {"description": "Unauthorized"}, "404": {"description": "Cast not found"}}, "security": [{"bearer": []}], "summary": "Get cast (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "castId", "required": true, "in": "path", "description": "Cast UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateCastDto"}}}}, "responses": {"200": {"description": "Updated cast"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Cast not found"}}, "security": [{"bearer": []}], "summary": "Update cast (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "castId", "required": true, "in": "path", "description": "Cast UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Delete result"}, "401": {"description": "Unauthorized"}, "404": {"description": "Cast not found"}}, "security": [{"bearer": []}], "summary": "Delete cast (admin)", "tags": ["admin"]}}, "/api/admin/casts/{castId}/image": {"post": {"description": "Multipart upload (field name: image). Max file size: CAST_IMAGE_MAX_BYTES (default 1MB). Allowed types: jpg/jpeg/jfif, png, svg.", "operationId": "uploadImage", "parameters": [{"name": "castId", "required": true, "in": "path", "description": "Cast UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "properties": {"image": {"type": "string", "format": "binary"}}, "required": ["image"]}}}}, "responses": {"200": {"description": "Upload result"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Upload cast image (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/casts": {"get": {"operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Cast list for show"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List casts for show (admin)", "tags": ["admin"]}, "post": {"operationId": "link", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminLinkShowCastDto"}}}}, "responses": {"201": {"description": "Link result"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Link cast to show (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/casts/{castId}": {"delete": {"operationId": "unlink", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}, {"name": "castId", "required": true, "in": "path", "description": "Cast UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Unlink result"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Unlink cast from show (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/articles": {"get": {"operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Linked articles for show"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List articles linked to show (admin)", "tags": ["admin"]}, "post": {"operationId": "link", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminLinkShowArticleDto"}}}}, "responses": {"201": {"description": "Link result"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Link article to show (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/articles/{articleId}": {"delete": {"operationId": "unlink", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}, {"name": "articleId", "required": true, "in": "path", "description": "Article UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Unlink result"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Unlink article from show (admin)", "tags": ["admin"]}}, "/api/admin/shows/{showId}/show-sessions": {"get": {"description": "Includes session_name, description (promo badge), and aggregated tier seat counts. Section tiers sync from Tickets \u2192 Section prices.", "operationId": "list", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminShowSessionsListResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Show not found"}}, "security": [{"bearer": []}], "summary": "List show sessions for a show (admin)", "tags": ["admin"]}, "post": {"description": "Optional session_name (title) and description (promo badge on public session picker).", "operationId": "create", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateShowSessionDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminShowSessionResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create show session (admin)", "tags": ["admin"]}}, "/api/admin/show-sessions/{sessionId}": {"patch": {"operationId": "update", "parameters": [{"name": "sessionId", "required": true, "in": "path", "description": "Show session UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateShowSessionDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminShowSessionResponseDto"}}}}, "404": {"description": "Session not found"}}, "security": [{"bearer": []}], "summary": "Update show session (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "sessionId", "required": true, "in": "path", "description": "Show session UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteShowSessionResponseDto"}}}}, "404": {"description": "Session not found"}}, "security": [{"bearer": []}], "summary": "Delete show session (admin)", "tags": ["admin"]}}, "/api/admin/tags": {"post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateTagDto"}}}}, "responses": {"201": {"description": "Created tag"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create tag (show tags)", "tags": ["admin"]}, "get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search by id or label", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "schema": {"default": "label", "type": "string", "enum": ["label", "id", "created_at"]}}, {"name": "sort_order", "required": false, "in": "query", "schema": {"default": "asc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "Tag list with pagination"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List tags (show tags)", "tags": ["admin"]}}, "/api/admin/tags/{tagId}": {"get": {"operationId": "get", "parameters": [{"name": "tagId", "required": true, "in": "path", "description": "Tag id", "schema": {"type": "string"}}], "responses": {"200": {"description": "Tag detail"}, "401": {"description": "Unauthorized"}, "404": {"description": "Tag not found"}}, "security": [{"bearer": []}], "summary": "Get tag", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "tagId", "required": true, "in": "path", "description": "Tag id", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateTagDto"}}}}, "responses": {"200": {"description": "Updated tag"}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Tag not found"}}, "security": [{"bearer": []}], "summary": "Update tag", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "tagId", "required": true, "in": "path", "description": "Tag id", "schema": {"type": "string"}}], "responses": {"200": {"description": "Delete result"}, "401": {"description": "Unauthorized"}, "404": {"description": "Tag not found"}}, "security": [{"bearer": []}], "summary": "Delete tag", "tags": ["admin"]}}, "/api/admin/fcm/tokens": {"get": {"description": "Returns registered push tokens for the given app user UUID.", "operationId": "listTokens", "parameters": [{"name": "user_id", "required": true, "in": "query", "description": "App user UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminFcmTokenListResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized or not admin"}}, "security": [{"bearer": []}], "summary": "List FCM device tokens for a user (admin)", "tags": ["admin"]}}, "/api/admin/fcm/token": {"post": {"description": "Associates an FCM registration token with an app user. For CMS/testing when the mobile app is not used.", "operationId": "registerToken", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminRegisterFcmTokenDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RegisterFcmTokenResponseDto"}}}}, "400": {"description": "Validation error or unknown user_id"}, "401": {"description": "Unauthorized or not admin"}}, "security": [{"bearer": []}], "summary": "Register FCM device token for a user (admin)", "tags": ["admin"]}, "delete": {"description": "Removes a registration token for the given user.", "operationId": "unregisterToken", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUnregisterFcmTokenDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UnregisterFcmTokenResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized or not admin"}}, "security": [{"bearer": []}], "summary": "Unregister FCM device token for a user (admin)", "tags": ["admin"]}}, "/api/admin/fcm/send": {"post": {"description": "Sends a push notification via FCM to all registered devices of the given user IDs. Returns per-token success/failure counts.", "operationId": "send", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSendFcmDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSendFcmResponseDto"}}}}, "400": {"description": "Validation error (e.g. user_ids must be UUIDs)"}, "401": {"description": "Unauthorized or not admin"}}, "security": [{"bearer": []}], "summary": "Send FCM push notification (admin)", "tags": ["admin"]}}, "/api/admin/fcm/send-topic": {"post": {"description": "Delivers to every device subscribed to the FCM topic (mobile `subscribeToTopic`). Topic name must match the app subscription exactly (e.g. `xxx` for `event: xxx`).", "operationId": "sendTopic", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSendFcmTopicDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSendFcmTopicResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized or not admin"}}, "security": [{"bearer": []}], "summary": "Send FCM push to a topic (admin)", "tags": ["admin"]}}, "/api/admin/fcm/topic/subscribe": {"post": {"description": "Server-side `subscribeToTopic` for tokens in DB and/or explicit token list. Mobile apps usually subscribe on-device; use this for CMS web testing.", "operationId": "subscribeTopic", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSubscribeFcmTopicDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminFcmTopicSubscribeResponseDto"}}}}, "400": {"description": "Validation error"}}, "security": [{"bearer": []}], "summary": "Subscribe tokens to an FCM topic (admin)", "tags": ["admin"]}}, "/api/admin/fcm/topic/unsubscribe": {"post": {"operationId": "unsubscribeTopic", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUnsubscribeFcmTopicDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminFcmTopicSubscribeResponseDto"}}}}, "400": {"description": "Validation error"}}, "security": [{"bearer": []}], "summary": "Unsubscribe tokens from an FCM topic (admin)", "tags": ["admin"]}}, "/api/admin/error-logs": {"get": {"description": "Returns rows from `app_error_log` (HTTP exceptions captured by the global error filter). Use `level=error` for 5xx, `level=warn` for 4xx.", "operationId": "list", "parameters": [], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminErrorLogsListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List API error log entries (admin)", "tags": ["admin"]}}, "/api/admin/surveys": {"get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "default": 20, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search name or category", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "schema": {"default": "created_at", "type": "string", "enum": ["created_at", "name"]}}, {"name": "sort_order", "required": false, "in": "query", "schema": {"default": "desc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSurveysListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List surveys (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateSurveyDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSurveyResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create survey (admin)", "tags": ["admin"]}}, "/api/admin/surveys/{surveyId}": {"get": {"operationId": "get", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSurveyResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Survey not found"}}, "security": [{"bearer": []}], "summary": "Get survey (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateSurveyDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSurveyResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Survey not found"}}, "security": [{"bearer": []}], "summary": "Update survey (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteSurveyResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Survey not found"}}, "security": [{"bearer": []}], "summary": "Delete survey (admin)", "tags": ["admin"]}}, "/api/admin/surveys/{surveyId}/image": {"post": {"description": "Multipart upload (field name: image). Max: SURVEY_IMAGE_MAX_BYTES (default 1MB). jpg/png/svg.", "operationId": "uploadImage", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "properties": {"image": {"type": "string", "format": "binary"}}, "required": ["image"]}}}}, "responses": {"200": {"description": "Upload result"}}, "security": [{"bearer": []}], "summary": "Upload survey image (admin)", "tags": ["admin"]}}, "/api/admin/surveys/{surveyId}/video": {"post": {"description": "Multipart upload (field name: video). Max: SURVEY_VIDEO_MAX_BYTES (default 500MB). mp4/webm/quicktime.", "operationId": "uploadVideo", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "properties": {"video": {"type": "string", "format": "binary"}}, "required": ["video"]}}}}, "responses": {"200": {"description": "Upload result"}}, "security": [{"bearer": []}], "summary": "Upload survey video (admin)", "tags": ["admin"]}}, "/api/admin/surveys/{surveyId}/results": {"get": {"operationId": "list", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSurveyResultListResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Survey not found"}}, "security": [{"bearer": []}], "summary": "List survey results for a survey (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateSurveyResultDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSurveyResultResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Survey not found"}, "409": {"description": "User already answered this survey"}}, "security": [{"bearer": []}], "summary": "Create survey result (admin)", "tags": ["admin"]}}, "/api/admin/surveys/{surveyId}/results/{resultId}": {"get": {"operationId": "get", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}, {"name": "resultId", "required": true, "in": "path", "description": "Survey result UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSurveyResultResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Not found"}}, "security": [{"bearer": []}], "summary": "Get survey result (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}, {"name": "resultId", "required": true, "in": "path", "description": "Survey result UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateSurveyResultDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSurveyResultResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Not found"}, "409": {"description": "User already answered this survey"}}, "security": [{"bearer": []}], "summary": "Update survey result (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "surveyId", "required": true, "in": "path", "description": "Survey UUID", "schema": {"type": "string"}}, {"name": "resultId", "required": true, "in": "path", "description": "Survey result UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteSurveyResultResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Not found"}}, "security": [{"bearer": []}], "summary": "Delete survey result (admin)", "tags": ["admin"]}}, "/api/admin/configs": {"get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search name or value", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminConfigListResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "List configs (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateConfigDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminConfigResponseDto"}}}}, "401": {"description": ""}, "409": {"description": ""}}, "security": [{"bearer": []}], "summary": "Create config (admin)", "tags": ["admin"]}}, "/api/admin/configs/{configId}": {"get": {"operationId": "get", "parameters": [{"name": "configId", "required": true, "in": "path", "schema": {"format": "uuid", "type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminConfigResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Get config (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "configId", "required": true, "in": "path", "schema": {"format": "uuid", "type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateConfigDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminConfigResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}, "409": {"description": ""}}, "security": [{"bearer": []}], "summary": "Update config (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "configId", "required": true, "in": "path", "schema": {"format": "uuid", "type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteConfigResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Delete config (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/seating": {"get": {"operationId": "getTree", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Get venue seating tree + tiers (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/config/import": {"post": {"operationId": "importConfig", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}], "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "summary": "Import venue seating config from JSON body (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/config": {"delete": {"description": "Removes all sections, rows, seats, and tiers for this venue, clears the seat map SVG, and resets stored config JSON. Blocked when seats are sold or held, or when tickets/checkout orders still reference ticket categories for linked shows.", "operationId": "clearConfig", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Seating config cleared"}, "404": {"description": "Venue not found"}}, "security": [{"bearer": []}], "summary": "Clear published venue seating config (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/bookings": {"delete": {"description": "Deletes seat holds, tickets, checkout orders, and purchase records for shows linked to this venue (and tickets/holds on this venue\u2019s seats). Use before re-importing seating when sold/held seats block the import.", "operationId": "clearBookings", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "Bookings cleared"}, "404": {"description": "Venue not found"}}, "security": [{"bearer": []}], "summary": "Clear all bookings for a venue (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/shows": {"get": {"description": "Used by Live bookings to pick a show before selecting a session.", "operationId": "listShowsForVenue", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "List shows linked to a venue (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/sessions": {"get": {"description": "Filter by date and/or show_id. When show_id is set without date, use GET \u2026/shows/:showId/sessions instead for all sessions of that show.", "operationId": "listSessionsForDate", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}, {"name": "date", "required": false, "in": "query", "description": "YYYY-MM-DD. Defaults to today.", "schema": {"type": "string"}}, {"name": "show_id", "required": false, "in": "query", "description": "Optional show UUID to filter sessions on the given date.", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "List show sessions at a venue (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/shows/{showId}/sessions": {"get": {"description": "Returns sessions with sold/held counts for Live bookings search.", "operationId": "listSessionsForShow", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}, {"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "List all sessions for a show at this venue (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/occupancy": {"get": {"description": "Seat-by-seat availability with ticket/hold booking details for the selected show session.", "operationId": "getOccupancy", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}, {"name": "session_id", "required": true, "in": "query", "description": "Show session UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Get seat occupancy map for a venue session (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/occupancy/seats/{seatId}/block": {"post": {"description": "Makes an available seat unbookable until the requested expiry. Existing sold or actively held seats cannot be blocked.", "operationId": "blockSeat", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}, {"name": "seatId", "required": true, "in": "path", "description": "Venue seat UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminBlockVenueSeatDto"}}}}, "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "summary": "Temporarily block a seat for one show session (admin)", "tags": ["admin"]}, "delete": {"operationId": "unblockSeat", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}, {"name": "seatId", "required": true, "in": "path", "description": "Venue seat UUID", "schema": {"type": "string"}}, {"name": "session_id", "required": true, "in": "query", "description": "Show session UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Remove a temporary session seat block (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/occupancy/seats/{seatId}": {"delete": {"description": "Cancels an active ticket or removes the seat from an active hold for the given session.", "operationId": "clearSeatBooking", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}, {"name": "seatId", "required": true, "in": "path", "description": "Venue seat UUID", "schema": {"type": "string"}}, {"name": "session_id", "required": true, "in": "query", "description": "Show session UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Clear booking for one seat (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/occupancy/seats/{seatId}/assign": {"post": {"description": "Creates a ticket linked to venue_seat_id for an available seat. Requires a matching ticket category for the seat tier on that session.", "operationId": "assignSeat", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}, {"name": "seatId", "required": true, "in": "path", "description": "Venue seat UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminAssignVenueSeatDto"}}}}, "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "summary": "Assign a seat to a user (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/config/upload": {"post": {"operationId": "uploadConfig", "parameters": [{"name": "venueId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "summary": "Upload venue seating config JSON file (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/seat-map": {"post": {"operationId": "uploadSeatMap", "parameters": [{"name": "venueId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "summary": "Upload venue seat map SVG (admin)", "tags": ["admin"]}, "get": {"operationId": "getSeatMap", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Get venue seat map SVG content (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/section-images/{sectionKey}": {"post": {"operationId": "uploadSectionImage", "parameters": [{"name": "venueId", "required": true, "in": "path", "schema": {"type": "string"}}, {"name": "sectionKey", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "summary": "Upload section view image for venue (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/venue-layout": {"put": {"operationId": "upsertLayout", "parameters": [{"name": "venueId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpsertVenueLayoutDto"}}}}, "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Create or update venue layout shell (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/venue-sections": {"post": {"operationId": "createSection", "parameters": [{"name": "venueId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateVenueSectionDto"}}}}, "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}}, "/api/admin/venue-sections/{sectionId}": {"patch": {"operationId": "updateSection", "parameters": [{"name": "sectionId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateVenueSectionDto"}}}}, "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}, "delete": {"operationId": "deleteSection", "parameters": [{"name": "sectionId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}}, "/api/admin/venue-sections/{sectionId}/rows": {"post": {"operationId": "createRow", "parameters": [{"name": "sectionId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateVenueRowDto"}}}}, "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}}, "/api/admin/venue-rows/{rowId}": {"patch": {"operationId": "updateRow", "parameters": [{"name": "rowId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateVenueRowDto"}}}}, "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}, "delete": {"operationId": "deleteRow", "parameters": [{"name": "rowId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}}, "/api/admin/venue-rows/{rowId}/seats": {"post": {"operationId": "createSeats", "parameters": [{"name": "rowId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateVenueSeatsDto"}}}}, "responses": {"201": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}}, "/api/admin/venue-seats/{seatId}": {"patch": {"operationId": "updateSeat", "parameters": [{"name": "seatId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateVenueSeatDto"}}}}, "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}, "delete": {"operationId": "deleteSeat", "parameters": [{"name": "seatId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "tags": ["admin"]}}, "/api/admin/shows/{showId}/venue-tier-prices": {"get": {"description": "Returns tier prices plus seat map SVG. Pass optional `session_id` to load prices for one session (from ticket_categories).", "operationId": "listVenueTierPrices", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}, {"name": "session_id", "required": false, "in": "query", "description": "Load prices for one session", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "List venue section tiers and show prices (admin)", "tags": ["admin"]}, "put": {"description": "Pass `session_id` to save prices for one session only. Omit `session_id` or set `apply_to_all_sessions: true` to update every session.", "operationId": "upsertVenueTierPrices", "parameters": [{"name": "showId", "required": true, "in": "path", "description": "Show UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpsertShowVenueTierPricesDto"}}}}, "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Set prices per venue section tier for a show (admin)", "tags": ["admin"]}}, "/api/admin/venues/{venueId}/prices": {"get": {"description": "Returns venue-level default prices per section/row/seat plus the seat map SVG. Shows using this venue inherit these prices as defaults until they set their own.", "operationId": "listVenuePrices", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "List default seat prices for a venue (admin)", "tags": ["admin"]}, "put": {"description": "Saves venue-level default prices. `seat_pricing_mode` picks the granularity (tier=section, row, or seat). These act as defaults for shows using this venue.", "operationId": "upsertVenuePrices", "parameters": [{"name": "venueId", "required": true, "in": "path", "description": "Venue UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpsertVenuePricesDto"}}}}, "responses": {"200": {"description": ""}}, "security": [{"bearer": []}], "summary": "Set default seat prices for a venue (admin)", "tags": ["admin"]}}, "/api/admin/membership-tiers": {"get": {"operationId": "list", "parameters": [], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminMembershipTiersListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List membership tiers (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateMembershipTierDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminMembershipTierResponseDto"}}}}, "401": {"description": "Unauthorized"}, "409": {"description": "Tier code already exists"}}, "security": [{"bearer": []}], "summary": "Create membership tier (admin)", "tags": ["admin"]}}, "/api/admin/membership-tiers/{tierId}": {"patch": {"operationId": "update", "parameters": [{"name": "tierId", "required": true, "in": "path", "description": "Membership tier UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateMembershipTierDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminMembershipTierResponseDto"}}}}, "404": {"description": "Tier not found"}, "409": {"description": "Tier code already exists"}}, "security": [{"bearer": []}], "summary": "Update membership tier (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "tierId", "required": true, "in": "path", "description": "Membership tier UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteMembershipTierResponseDto"}}}}, "404": {"description": "Tier not found"}, "409": {"description": "Tier still assigned to users"}}, "security": [{"bearer": []}], "summary": "Delete membership tier (admin)", "tags": ["admin"]}}, "/api/admin/trendings": {"get": {"description": "Pagination, search by title/author/link_url, and sorting by title/status/created_at.", "operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "description": "1-based page number", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "description": "Items per page (max 100)", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search query (matches title/author/link_url, case-insensitive)", "schema": {"type": "string"}}, {"name": "sort_by", "required": false, "in": "query", "description": "Sort field", "schema": {"default": "created_at", "type": "string", "enum": ["title", "created_at", "status"]}}, {"name": "sort_order", "required": false, "in": "query", "description": "Sort order", "schema": {"default": "desc", "type": "string", "enum": ["asc", "desc"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTrendingListResponseDto"}}}}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List trendings (admin)", "tags": ["admin"]}, "post": {"description": "Create a trending item. Upload `video` (mp4, max 50MB) and `image_preview` (jpg/png/svg/gif, max 5MB) together with fields `title`, `author`, `link_url`, `status`, and `show_id`.", "operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/AdminCreateTrendingDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTrendingResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create trending (admin)", "tags": ["admin"]}}, "/api/admin/trendings/{id}": {"get": {"operationId": "get", "parameters": [{"name": "id", "required": true, "in": "path", "description": "Trending id (UUID)", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTrendingResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Trending not found"}}, "security": [{"bearer": []}], "summary": "Get trending (admin)", "tags": ["admin"]}, "patch": {"description": "JSON text-only update, or `multipart/form-data` to replace `video` (mp4) and/or `image_preview` (jpg/png/svg/gif).", "operationId": "update", "parameters": [{"name": "id", "required": true, "in": "path", "description": "Trending id (UUID)", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateTrendingDto"}}, "multipart/form-data": {"schema": {"$ref": "#/components/schemas/AdminUpdateTrendingDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTrendingResponseDto"}}}}, "400": {"description": "Validation error"}, "401": {"description": "Unauthorized"}, "404": {"description": "Trending not found"}}, "security": [{"bearer": []}], "summary": "Update trending (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "id", "required": true, "in": "path", "description": "Trending id (UUID)", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteTrendingResponseDto"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Trending not found"}}, "security": [{"bearer": []}], "summary": "Delete trending (admin)", "tags": ["admin"]}}, "/api/admin/messages/push": {"post": {"description": "Enqueues push for `user_ids`, all members of `user_group_id`, or a custom FCM `topic`. Per-user jobs send to the FCM topic named after each user UUID (`users.id`). Topic jobs send once to the given FCM topic. A cron worker delivers pending rows.", "operationId": "enqueuePush", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSendMessagePushDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSendMessageEnqueueResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "Queue push notification (admin)", "tags": ["admin"]}}, "/api/admin/messages/email": {"post": {"description": "Enqueues one email per user (to their account email). Pass `user_ids` or `user_group_id`. Sent via Resend by the email queue worker.", "operationId": "enqueueEmail", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSendMessageEmailDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminSendMessageEnqueueResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "Queue email for users (admin)", "tags": ["admin"]}}, "/api/admin/user-groups": {"get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search by name or topic", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserGroupsListResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "List user groups (admin)", "tags": ["admin"]}, "post": {"description": "Creates a group with a display name and FCM topic slug. Topic is auto-generated from the name when omitted.", "operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateUserGroupDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserGroupResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "Create user group (admin)", "tags": ["admin"]}}, "/api/admin/user-groups/{groupId}": {"get": {"operationId": "get", "parameters": [{"name": "groupId", "required": true, "in": "path", "description": "User group UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserGroupResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Get user group (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "groupId", "required": true, "in": "path", "description": "User group UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateUserGroupDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserGroupResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Update user group (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "groupId", "required": true, "in": "path", "description": "User group UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteUserGroupResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Delete user group (admin)", "tags": ["admin"]}}, "/api/admin/user-groups/{groupId}/members": {"get": {"operationId": "listMembers", "parameters": [{"name": "groupId", "required": true, "in": "path", "description": "User group UUID", "schema": {"type": "string"}}, {"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "default": 50, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search members by user id, email, or name", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserGroupMembersListResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "List members of a user group (admin)", "tags": ["admin"]}, "post": {"description": "Adds up to 1000 members total per group. Skips users already in the group.", "operationId": "addMembers", "parameters": [{"name": "groupId", "required": true, "in": "path", "description": "User group UUID", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserGroupMembersDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserGroupMembersAddResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Add users to a group (admin)", "tags": ["admin"]}}, "/api/admin/user-groups/{groupId}/members/{userId}": {"delete": {"operationId": "removeMember", "parameters": [{"name": "groupId", "required": true, "in": "path", "description": "User group UUID", "schema": {"type": "string"}}, {"name": "userId", "required": true, "in": "path", "description": "User UUID", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserGroupMemberRemoveResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Remove a user from a group (admin)", "tags": ["admin"]}}, "/api/admin/fcm-topic-types": {"get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"default": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"default": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search topic or description", "schema": {"type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminFcmTopicTypeListResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "List FCM topic types (admin)", "tags": ["admin"]}, "post": {"operationId": "create", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateFcmTopicTypeDto"}}}}, "responses": {"201": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminFcmTopicTypeResponseDto"}}}}, "401": {"description": ""}, "409": {"description": ""}}, "security": [{"bearer": []}], "summary": "Create FCM topic type (admin)", "tags": ["admin"]}}, "/api/admin/fcm-topic-types/{id}": {"get": {"operationId": "get", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"format": "uuid", "type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminFcmTopicTypeResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Get FCM topic type (admin)", "tags": ["admin"]}, "patch": {"operationId": "update", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"format": "uuid", "type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateFcmTopicTypeDto"}}}}, "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminFcmTopicTypeResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}, "409": {"description": ""}}, "security": [{"bearer": []}], "summary": "Update FCM topic type (admin)", "tags": ["admin"]}, "delete": {"operationId": "remove", "parameters": [{"name": "id", "required": true, "in": "path", "schema": {"format": "uuid", "type": "string"}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminDeleteFcmTopicTypeResponseDto"}}}}, "401": {"description": ""}, "404": {"description": ""}}, "security": [{"bearer": []}], "summary": "Delete FCM topic type (admin)", "tags": ["admin"]}}, "/api/admin/ticket-price-changes": {"get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "example": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "example": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search by ids (id/show_id/admin_id/ticket_category_id)", "schema": {"type": "string"}}, {"name": "direction", "required": false, "in": "query", "schema": {"type": "string", "enum": ["increase", "decrease"]}}, {"name": "notify", "required": false, "in": "query", "description": "Filter notify flag (0/1)", "schema": {"type": "string", "enum": ["0", "1"]}}, {"name": "fcm_sent", "required": false, "in": "query", "description": "Filter fcm_sent flag (0/1)", "schema": {"type": "string", "enum": ["0", "1"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTicketPriceChangeListResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "List ticket price change logs (admin)", "tags": ["admin"]}}, "/api/admin/ticket-extension-logs": {"get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "example": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "example": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search by ids or show name", "schema": {"type": "string"}}, {"name": "notify", "required": false, "in": "query", "description": "Filter notify flag (0/1)", "schema": {"type": "string", "enum": ["0", "1"]}}, {"name": "fcm_sent", "required": false, "in": "query", "description": "Filter fcm_sent flag (0/1)", "schema": {"type": "string", "enum": ["0", "1"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminTicketExtensionLogListResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "List ticket extension logs (admin)", "tags": ["admin"]}}, "/api/admin/show-low-availability-logs": {"get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "example": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "example": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search by show name or ids", "schema": {"type": "string"}}, {"name": "notify", "required": false, "in": "query", "description": "Filter notify flag (0/1)", "schema": {"type": "string", "enum": ["0", "1"]}}, {"name": "fcm_sent", "required": false, "in": "query", "description": "Filter fcm_sent flag (0/1)", "schema": {"type": "string", "enum": ["0", "1"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminShowLowAvailabilityLogListResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "List show low availability logs (admin)", "tags": ["admin"]}}, "/api/admin/show-final-week": {"get": {"operationId": "list", "parameters": [{"name": "page", "required": false, "in": "query", "schema": {"minimum": 1, "example": 1, "type": "number"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"minimum": 1, "maximum": 100, "example": 30, "type": "number"}}, {"name": "q", "required": false, "in": "query", "description": "Search by show name or ids", "schema": {"type": "string"}}, {"name": "notify", "required": false, "in": "query", "description": "Filter notify flag (0/1)", "schema": {"type": "string", "enum": ["0", "1"]}}, {"name": "fcm_sent", "required": false, "in": "query", "description": "Filter fcm_sent flag (0/1)", "schema": {"type": "string", "enum": ["0", "1"]}}], "responses": {"200": {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminShowFinalWeekListResponseDto"}}}}, "401": {"description": ""}}, "security": [{"bearer": []}], "summary": "List show final week logs (admin)", "tags": ["admin"]}}, "/api/admin/assets/settings": {"get": {"operationId": "settings", "parameters": [], "responses": {"200": {"description": "Current compression settings"}, "401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Asset compression settings (from configs)", "tags": ["admin"]}}, "/api/admin/assets/directories": {"get": {"operationId": "listDirectories", "parameters": [{"name": "parent_id", "required": false, "in": "query", "description": "Parent UUID, or empty/root", "schema": {"type": "string"}}], "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List asset directories under a parent (omit for root)", "tags": ["admin"]}, "post": {"operationId": "createDirectory", "parameters": [], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminCreateAssetDirectoryDto"}}}}, "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Create an asset directory", "tags": ["admin"]}}, "/api/admin/assets/directories/{directoryId}": {"patch": {"operationId": "renameDirectory", "parameters": [{"name": "directoryId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminRenameAssetDirectoryDto"}}}}, "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Rename an asset directory", "tags": ["admin"]}, "delete": {"operationId": "deleteDirectory", "parameters": [{"name": "directoryId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Delete an empty asset directory", "tags": ["admin"]}}, "/api/admin/assets": {"get": {"operationId": "listAssets", "parameters": [{"name": "directory_id", "required": false, "in": "query", "schema": {"type": "string"}}, {"name": "kind", "required": false, "in": "query", "schema": {"enum": ["image", "video"], "type": "string"}}, {"name": "q", "required": false, "in": "query", "schema": {"type": "string"}}, {"name": "page", "required": false, "in": "query", "schema": {"type": "string"}}, {"name": "per_page", "required": false, "in": "query", "schema": {"type": "string"}}], "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "List assets in a directory", "tags": ["admin"]}}, "/api/admin/assets/upload": {"post": {"description": "Multipart field `files` (multiple). Optional `directory_id`. Images become JPEG under asset_image_* configs; videos become H.264 MP4 under asset_video_* configs. Media Library root lists live OSS uploads.", "operationId": "upload", "parameters": [], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "properties": {"directory_id": {"type": "string", "nullable": true}, "files": {"type": "array", "items": {"type": "string", "format": "binary"}}}, "required": ["files"]}}}}, "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Upload and compress images/videos into asset library", "tags": ["admin"]}}, "/api/admin/assets/{assetId}/content": {"get": {"description": "Used by CMS when inserting an existing library item into a form. Avoids browser CORS on direct S3 URLs.", "operationId": "getContent", "parameters": [{"name": "assetId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Download asset bytes (authenticated proxy for Media Library picks)", "tags": ["admin"]}}, "/api/admin/assets/{assetId}": {"patch": {"operationId": "updateAsset", "parameters": [{"name": "assetId", "required": true, "in": "path", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AdminUpdateAssetDto"}}}}, "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Rename or move an asset", "tags": ["admin"]}, "delete": {"operationId": "deleteAsset", "parameters": [{"name": "assetId", "required": true, "in": "path", "schema": {"type": "string"}}], "responses": {"401": {"description": "Unauthorized"}}, "security": [{"bearer": []}], "summary": "Delete an asset and its stored file", "tags": ["admin"]}}}, "info": {"title": "Trem Admin API", "description": "Admin (CMS) endpoints, including FCM test send and paginated API error logs. Use `Authorization: Bearer <access_token>` from POST /api/admin/auth/login.", "version": "1.0", "contact": {}}, "tags": [], "servers": [{"url": "https://tremstgapi.axcova.com", "description": "Local"}, {"url": "https://tremdevapi.axcova.com", "description": "Dev"}], "components": {"securitySchemes": {"bearer": {"scheme": "bearer", "bearerFormat": "JWT", "type": "http", "description": "JWT access token from POST /api/admin/auth/login (role=admin)."}}, "schemas": {"AdminCronJobDto": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string", "example": "scheduler-heartbeat"}, "description": {"type": "object", "example": "Health check for trem-cron-scheduler"}, "cron_expression": {"type": "string", "example": "*/5 * * * *"}, "http_method": {"type": "string", "example": "GET"}, "request_path": {"type": "string", "example": "/api/internal/cron/ping"}, "enabled": {"type": "boolean", "example": true}, "notify_on_failure": {"type": "boolean", "example": false}, "last_run_at": {"type": "object"}, "last_run_status": {"type": "string", "enum": ["success", "failed", "skipped"]}, "last_run_error": {"type": "object"}, "created_at": {"type": "string"}, "updated_at": {"type": "string"}}, "required": ["id", "name", "cron_expression", "http_method", "request_path", "enabled", "notify_on_failure", "created_at", "updated_at"]}, "AdminCronJobListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminCronJobDto"}}}, "required": ["items"]}, "AdminCronJobListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminCronJobListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateCronJobDto": {"type": "object", "properties": {"name": {"type": "string", "example": "heartbeat"}, "description": {"type": "object", "example": "Scheduler health check"}, "cron_expression": {"type": "string", "example": "*/5 * * * *", "description": "Standard cron (5 fields) or with seconds (6 fields)"}, "http_method": {"type": "string", "example": "GET", "default": "POST"}, "request_path": {"type": "string", "example": "/api/internal/cron/ping", "description": "Must start with /api/internal/cron/"}, "enabled": {"type": "boolean", "default": true}, "notify_on_failure": {"type": "boolean", "default": false}}, "required": ["name", "cron_expression", "request_path"]}, "AdminCronJobResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminCronJobDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateCronJobDto": {"type": "object", "properties": {"name": {"type": "string", "example": "heartbeat"}, "description": {"type": "object"}, "cron_expression": {"type": "string", "example": "0 * * * *"}, "http_method": {"type": "string", "example": "GET"}, "request_path": {"type": "string", "example": "/api/internal/cron/ping"}, "enabled": {"type": "boolean"}, "notify_on_failure": {"type": "boolean"}}}, "AdminDeleteCronJobDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteCronJobResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteCronJobDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCronJobRunDataDto": {"type": "object", "properties": {"ok": {"type": "boolean", "example": true}, "status": {"type": "number", "example": 200}, "body": {"type": "string", "example": "{\"ok\":true}"}}, "required": ["ok", "status", "body"]}, "AdminCronJobRunResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminCronJobRunDataDto"}}, "required": ["success", "timestamp", "data"]}, "LoginDto": {"type": "object", "properties": {"email": {"type": "string", "format": "email", "example": "user@example.com"}, "password": {"type": "string", "format": "password", "minLength": 1, "example": "your-password"}}, "required": ["email", "password"]}, "AdminInfoDto": {"type": "object", "properties": {"id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "email": {"type": "string", "example": "demo@trem.com"}, "name": {"type": "string", "example": "Demo Admin"}}, "required": ["id", "email", "name"]}, "AdminLoginDataDto": {"type": "object", "properties": {"access_token": {"type": "string", "description": "JWT access token (role=admin)", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.***"}, "refresh_token": {"type": "string", "description": "Opaque refresh token", "example": "yG6t9tGfQ0gO1YdQe0mVh9h4d4wOQw0HkG9qGmQpKQw"}, "admin": {"$ref": "#/components/schemas/AdminInfoDto"}}, "required": ["access_token", "refresh_token", "admin"]}, "AdminLoginResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminLoginDataDto"}}, "required": ["success", "timestamp", "data"]}, "ForgotPasswordDto": {"type": "object", "properties": {"email": {"type": "string", "format": "email", "example": "user@example.com"}}, "required": ["email"]}, "AuthMessageDataDto": {"type": "object", "properties": {"message": {"type": "string", "example": "If an account exists for that email, we sent a reset link."}, "expires_in_seconds": {"type": "number", "example": 120, "description": "Present on the mobile reset-code response. Code lifetime in seconds."}}, "required": ["message"]}, "AuthMessageResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AuthMessageDataDto"}}, "required": ["success", "timestamp", "data"]}, "ResetPasswordDto": {"type": "object", "properties": {"token": {"type": "string", "description": "Raw token from the reset email link (query param `token`)", "example": "k7nQ2vR9pLwX\u2026"}, "password": {"type": "string", "format": "password", "minLength": 8, "description": "New password, at least 8 characters", "example": "securePass1"}}, "required": ["token", "password"]}, "RefreshDto": {"type": "object", "properties": {"refresh_token": {"type": "string", "description": "Opaque refresh token from login/register/oauth response", "minLength": 10}}, "required": ["refresh_token"]}, "AdminRefreshDataDto": {"type": "object", "properties": {"access_token": {"type": "string", "description": "JWT access token (role=admin)", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.***"}, "refresh_token": {"type": "string", "description": "Rotated opaque refresh token", "example": "m4N9k0d1P2fP6mV7w3jD8oE2Yx2mB1qW0cE9oQ0pQ1I"}}, "required": ["access_token", "refresh_token"]}, "AdminRefreshResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminRefreshDataDto"}}, "required": ["success", "timestamp", "data"]}, "LogoutDto": {"type": "object", "properties": {"refresh_token": {"type": "string", "description": "Refresh token to revoke", "minLength": 10}}, "required": ["refresh_token"]}, "AdminLogoutDataDto": {"type": "object", "properties": {"message": {"type": "string", "example": "Logged out"}}, "required": ["message"]}, "AdminLogoutResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminLogoutDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateShowDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Hamilton"}, "city_id": {"type": "string", "description": "City UUID (FK to cities.id)", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "venue_id": {"type": "object", "description": "Venue UUID (FK to venues.id). Required for Show Onboarding / seating prices.", "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "nullable": true}, "show_image_url": {"type": "string", "description": "Landscape / preview image URL (can be placeholder; poster uses image_poster)", "example": "https://example.com/show.jpg"}, "image_poster": {"type": "object", "description": "Portrait/card poster image URL", "example": "https://example.com/show-poster.jpg", "nullable": true}, "start_date": {"type": "string", "description": "Start date (YYYY-MM-DD)", "example": "2026-04-20"}, "end_date": {"type": "string", "description": "End date (YYYY-MM-DD); show is archived after this date", "example": "2026-05-20"}, "description": {"type": "object"}}, "required": ["name", "city_id", "show_image_url", "start_date", "end_date"]}, "AdminShowRowDto": {"type": "object", "properties": {"id": {"type": "string", "example": "75f842fc-27dd-4ac3-91e5-5e7b5c13a5f0"}, "name": {"type": "string", "example": "Hamilton"}, "city": {"type": "string", "example": "Singapore"}, "city_id": {"type": "string", "description": "City UUID (FK to cities.id)", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "start_date": {"type": "string", "example": "2026-04-20"}, "end_date": {"type": "string", "example": "2026-05-20", "description": "Show run end date; archive cron uses this"}, "category": {"type": "object", "example": "Musical"}, "categories": {"example": ["Musical", "Family"], "type": "array", "items": {"type": "string"}}, "color_scheme": {"type": "object", "example": "#7c3aed"}, "preview_mode": {"type": "string", "enum": ["video", "image"], "example": "video", "description": "Preview mode for the show card"}, "status": {"type": "number", "description": "Show visibility: 1 = active (visible in app), 0 = inactive", "enum": [0, 1], "example": 1}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.665Z"}, "ticket_available": {"type": "object", "description": "Combined remaining seats across all sessions and tiers (from show_ticket_balance.seat_balance)", "example": 42, "nullable": true}}, "required": ["id", "name", "city", "city_id", "start_date", "end_date", "categories", "preview_mode", "status", "created_at"]}, "AdminShowRowResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminShowRowDto"}}, "required": ["success", "timestamp", "data"]}, "AdminShowsListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminShowRowDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 123}, "total_pages": {"type": "number", "example": 5}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminShowsListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminShowsListDataDto"}}, "required": ["success", "timestamp", "data"]}, "ShowTagDto": {"type": "object", "properties": {"id": {"type": "string", "example": "headline"}, "label": {"type": "string", "example": "Headline"}}, "required": ["id", "label"]}, "ShowVideoDto": {"type": "object", "properties": {"id": {"type": "string", "example": "78850a4c-a1bb-4b59-9966-3f1b79e08e69"}, "video_url": {"type": "string", "example": "https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"}, "title": {"type": "object", "nullable": true, "example": "Opening night highlight", "description": "Display title for this video clip"}, "poster_image_url": {"type": "object", "nullable": true, "description": "Poster for this video only. A frame from the file is captured on upload.", "example": "https://picsum.photos/seed/nusantara-sv-hamilton-1/1280/720"}, "gif_url": {"type": "object", "nullable": true, "description": "Animated GIF from 10 random frames of this video, generated on upload.", "example": "https://cdn.example.com/uploads/show-videos/clip.gif"}, "duration_seconds": {"type": "number", "example": 18}, "kind": {"type": "string", "example": "clip"}, "sort_order": {"type": "number", "example": 10}, "is_main": {"type": "boolean", "example": false, "description": "Indicates the main/hero video for the show."}}, "required": ["id", "video_url", "duration_seconds", "kind", "sort_order", "is_main"]}, "ShowVideoReactionDto": {"type": "object", "properties": {"id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "video_url": {"type": "string", "example": "https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"}, "duration_secs": {"type": "number", "example": 12}, "moderation_status": {"type": "string", "example": "approved"}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.750Z"}, "approved_at": {"type": "object", "example": "2026-04-11T18:49:41.750Z"}}, "required": ["id", "video_url", "duration_secs", "moderation_status", "created_at"]}, "ShowReviewDto": {"type": "object", "properties": {"id": {"type": "string", "example": "9e1e1dbd-09d2-4900-981c-00eddc5fa468"}, "user_id": {"type": "object", "nullable": true, "description": "Reviewer user id", "example": "a439e524-3a8a-487a-b897-2c3a231a3fda"}, "user_name": {"type": "object", "nullable": true, "description": "Reviewer display name", "example": "Jane Doe"}, "star_rating": {"type": "object", "example": 5}, "written_review": {"type": "object", "example": "Absolutely incredible \u2014 the energy was unreal."}, "recommends": {"type": "boolean", "example": true}, "verified_purchase": {"type": "boolean", "example": false}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.750Z"}, "moderation_status": {"type": "string", "example": "approved"}, "video_reactions": {"type": "array", "items": {"$ref": "#/components/schemas/ShowVideoReactionDto"}}}, "required": ["id", "recommends", "verified_purchase", "created_at", "moderation_status", "video_reactions"]}, "ShowSessionDto": {"type": "object", "properties": {"id": {"type": "string", "example": "0395a9e6-f7d7-4e91-9684-6ac79dffeb1a"}, "session_date": {"type": "string", "example": "2026-06-15", "description": "Performance date (YYYY-MM-DD)"}, "session_time": {"type": "string", "example": "19:30", "description": "Performance time (HH:mm)"}}, "required": ["id", "session_date", "session_time"]}, "ShowNextSessionDto": {"type": "object", "properties": {"id": {"type": "string"}, "session_date": {"type": "string", "example": "2026-06-14"}, "session_time": {"type": "string", "example": "19:30"}, "ticket_price_from": {"type": "object", "example": 78, "nullable": true}}, "required": ["id", "session_date", "session_time"]}, "ShowUrgencyDto": {"type": "object", "properties": {"type": {"type": "string", "enum": ["selling_fast", "low_stock"]}, "message": {"type": "string", "example": "Limited S$138 seats remain for Sat 14 Jun 7:30pm"}, "session_id": {"type": "string"}, "session_date": {"type": "string"}, "session_time": {"type": "string"}, "price_from": {"type": "number", "example": 138}, "remaining_seats": {"type": "number", "example": 12}}, "required": ["type", "message", "session_id", "session_date", "session_time", "price_from", "remaining_seats"]}, "ShowArticleDto": {"type": "object", "properties": {"id": {"type": "string", "example": "75f842fc-27dd-4ac3-91e5-5e7b5c13a5f0"}, "title": {"type": "string", "example": "Hamilton opens in Singapore"}, "url": {"type": "string", "example": "https://example.com/article/hamilton"}, "image_url": {"type": "object", "example": "https://cdn.example.com/article.jpg", "nullable": true}, "city_venue": {"type": "string", "example": "Singapore \u00b7 Marina Bay Sands"}, "news_date": {"type": "string", "example": "2026-05-12"}, "highlight": {"type": "object", "example": "Must-read", "nullable": true}, "tags": {"type": "array", "items": {"$ref": "#/components/schemas/ShowTagDto"}}}, "required": ["id", "title", "url", "city_venue", "news_date", "tags"]}, "ShowCardDto": {"type": "object", "properties": {"id": {"type": "string", "example": "75f842fc-27dd-4ac3-91e5-5e7b5c13a5f0"}, "name": {"type": "string", "example": "Hamilton"}, "city": {"type": "string", "example": "Singapore"}, "city_id": {"type": "string", "description": "City UUID (FK to cities.id)", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "is_saved": {"type": "number", "description": "1 if the current user has saved this show (show_saved.status=1), else 0.", "example": 1}, "show_image_url": {"type": "string", "example": "https://picsum.photos/seed/nusantara-show-hamilton/1200/1600"}, "image_poster": {"type": "object", "description": "Portrait/card poster image URL", "example": "https://picsum.photos/seed/nusantara-show-hamilton-poster/800/1200", "nullable": true}, "description": {"type": "object", "example": null}, "venue_id": {"type": "object", "description": "FK to venues.id", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a", "nullable": true}, "venue_name": {"type": "object", "example": "Esplanade Theatre", "nullable": true}, "venue_lat": {"type": "object", "example": 1.28967, "nullable": true}, "venue_lng": {"type": "object", "example": 103.85007, "nullable": true}, "subtitles": {"example": [], "type": "array", "items": {"type": "string"}}, "start_date": {"type": "string", "example": "2026-05-12"}, "end_date": {"type": "string", "example": "2026-06-12", "description": "Show run end date"}, "rating_average": {"type": "number", "example": 4.8}, "rating_count": {"type": "number", "example": 245}, "categories": {"example": ["Musical"], "type": "array", "items": {"type": "string"}}, "tags": {"description": "Tags from the `tags` table (e.g. Headline, Latest)", "type": "array", "items": {"$ref": "#/components/schemas/ShowTagDto"}}, "preview_mode": {"type": "string", "enum": ["video", "image"], "example": "video", "description": "Preview mode for the show card"}, "status": {"type": "number", "description": "Show visibility: 1 = active (visible in app), 0 = inactive", "enum": [0, 1], "example": 1}, "videos": {"type": "array", "items": {"$ref": "#/components/schemas/ShowVideoDto"}}, "reviews": {"type": "array", "items": {"$ref": "#/components/schemas/ShowReviewDto"}}, "show_sessions": {"type": "array", "items": {"$ref": "#/components/schemas/ShowSessionDto"}}, "ticket_price_from": {"type": "object", "description": "Lowest ticket tier price for this show", "example": 88, "nullable": true}, "ticket_price_to": {"type": "object", "description": "Highest ticket tier price for this show", "example": 270, "nullable": true}, "next_session": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/ShowNextSessionDto"}]}, "urgency": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/ShowUrgencyDto"}]}, "articles": {"description": "Articles linked to this show", "type": "array", "items": {"$ref": "#/components/schemas/ShowArticleDto"}}}, "required": ["id", "name", "city", "city_id", "is_saved", "show_image_url", "subtitles", "start_date", "end_date", "rating_average", "rating_count", "categories", "tags", "preview_mode", "status", "videos", "reviews", "show_sessions", "articles"]}, "ShowCardResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/ShowCardDto"}}, "required": ["success", "timestamp", "data"]}, "AdminApplyShowSetupDto": {"type": "object", "properties": {"target_show_ids": {"description": "Target show UUIDs to receive sessions + seating/pricing from the source show", "example": ["11111111-1111-1111-1111-111111111111"], "type": "array", "items": {"type": "string"}}}, "required": ["target_show_ids"]}, "AdminBulkUpdateShowItemDto": {"type": "object", "properties": {"id": {"type": "string", "description": "Show UUID"}, "name": {"type": "string"}, "city_id": {"type": "string"}, "start_date": {"type": "string"}, "end_date": {"type": "string"}, "venue_id": {"type": "object", "nullable": true}, "status": {"type": "number", "enum": [0, 1]}, "preview_mode": {"type": "string", "enum": ["video", "image"]}, "loyalty_points_per_booking": {"type": "number"}}, "required": ["id"]}, "AdminBulkUpdateShowsDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminBulkUpdateShowItemDto"}}}, "required": ["items"]}, "AdminDeleteShowDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteShowResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteShowDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateShowAddOnDto": {"type": "object", "properties": {"type": {"type": "string", "example": "merchandise"}, "title": {"type": "string", "example": "Show programme"}, "description": {"type": "object", "example": "Collect at the foyer before the show."}, "price": {"type": "number", "example": 25}, "currency": {"type": "string", "example": "SGD"}, "sort_order": {"type": "number", "example": 0}, "is_active": {"type": "boolean", "example": true}, "image_url": {"type": "object", "example": null, "description": "Image URL (set via upload endpoint)"}}, "required": ["type", "title", "price"]}, "AdminUpdateShowAddOnDto": {"type": "object", "properties": {"type": {"type": "string", "example": "merchandise"}, "title": {"type": "string", "example": "Show programme"}, "description": {"type": "object"}, "price": {"type": "number", "example": 25}, "currency": {"type": "string", "example": "SGD"}, "sort_order": {"type": "number"}, "is_active": {"type": "boolean"}, "image_url": {"type": "object", "example": null, "description": "Image URL (set via upload endpoint)"}}}, "AdminCreateShowPricingCategoryDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Student"}, "base_price": {"type": "number", "minimum": 0, "example": 100}, "discount_percent": {"type": "number", "minimum": 0, "maximum": 100, "example": 20, "description": "Discount percent 0\u2013100"}, "currency": {"type": "string", "default": "SGD"}, "color": {"type": "string", "example": "#6366F1", "description": "Hex color #RRGGBB for seat map / category display"}, "sort_order": {"type": "number", "minimum": 0}, "parent_id": {"type": "string", "description": "Parent category id when creating a sub-category"}}, "required": ["name", "base_price", "discount_percent"]}, "AdminUpdateShowPricingCategoryDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Student"}, "base_price": {"type": "number", "minimum": 0}, "discount_percent": {"type": "number", "minimum": 0, "maximum": 100}, "currency": {"type": "string"}, "color": {"type": "string", "example": "#6366F1", "description": "Hex color #RRGGBB for seat map / category display"}, "sort_order": {"type": "number", "minimum": 0}, "parent_id": {"type": "object", "description": "Parent category id; null for top-level"}}}, "AdminAdjustLoyaltyPointsDto": {"type": "object", "properties": {"points_delta": {"type": "number", "description": "Points to add (positive) or deduct (negative)", "example": 100}, "note": {"type": "string", "example": "Manual goodwill credit"}}, "required": ["points_delta"]}, "AdminCreateCategoryDto": {"type": "object", "properties": {"id": {"type": "string", "description": "Category id (string key). Used by clients as a stable identifier.", "example": "musical"}, "label": {"type": "string", "example": "Musical"}, "icon_url": {"type": "string", "description": "Public icon URL", "example": "https://example.com/icons/musical.png"}, "filter_type": {"type": "string", "description": "Filter routing type", "enum": ["category", "all"], "example": "category"}, "filter_value": {"type": "object", "description": "Filter value used by discovery UI; required when filter_type=category", "example": "Musical"}}, "required": ["id", "label", "icon_url", "filter_type"]}, "AdminCategoryDto": {"type": "object", "properties": {"id": {"type": "string", "example": "musical"}, "label": {"type": "string", "example": "Musical"}, "icon_url": {"type": "string", "example": "https://example.com/icons/musical.png"}, "filter_type": {"type": "string", "enum": ["category", "all"], "example": "category"}, "filter_value": {"type": "object", "example": "Musical"}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.665Z"}}, "required": ["id", "label", "icon_url", "filter_type", "created_at"]}, "AdminCategoryResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminCategoryDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCategoriesListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminCategoryDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminCategoriesListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminCategoriesListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateCategoryDto": {"type": "object", "properties": {"label": {"type": "string", "example": "Musical"}, "icon_url": {"type": "string", "description": "Public icon URL", "example": "https://example.com/icons/musical.png"}, "filter_type": {"type": "string", "description": "Filter routing type", "enum": ["category", "all"], "example": "category"}, "filter_value": {"type": "object", "description": "Filter value used by discovery UI; required when filter_type=category", "example": "Musical"}}}, "AdminDeleteCategoryDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteCategoryResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteCategoryDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateCityDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Singapore"}}, "required": ["name"]}, "AdminCityDto": {"type": "object", "properties": {"id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "name": {"type": "string", "example": "Singapore"}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.665Z"}}, "required": ["id", "name", "created_at"]}, "AdminCityResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminCityDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCitiesListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminCityDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminCitiesListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminCitiesListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateCityDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Kuala Lumpur"}}}, "AdminDeleteCityDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteCityResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteCityDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateVenueDto": {"type": "object", "properties": {"location_name": {"type": "string", "example": "Esplanade Theatre"}, "lat": {"type": "object", "example": 1.28967, "nullable": true}, "lng": {"type": "object", "example": 103.85007, "description": "Longitude", "nullable": true}, "city_id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}}, "required": ["location_name", "city_id"]}, "AdminVenueDto": {"type": "object", "properties": {"id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "location_name": {"type": "string", "example": "Esplanade Theatre"}, "lat": {"type": "object", "example": 1.28967, "nullable": true}, "lng": {"type": "object", "example": 103.85007, "description": "Longitude", "nullable": true}, "city_id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "city_name": {"type": "string", "example": "Singapore"}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.665Z"}}, "required": ["id", "location_name", "city_id", "city_name", "created_at"]}, "AdminVenueResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminVenueDto"}}, "required": ["success", "timestamp", "data"]}, "AdminVenuesListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminVenueDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminVenuesListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminVenuesListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateVenueDto": {"type": "object", "properties": {"location_name": {"type": "string", "example": "Esplanade Theatre"}, "lat": {"type": "object", "example": 1.28967, "nullable": true}, "lng": {"type": "object", "example": 103.85007, "description": "Longitude", "nullable": true}, "city_id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}}}, "AdminDeleteVenueDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteVenueResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteVenueDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateUserDto": {"type": "object", "properties": {"email": {"type": "string", "format": "email", "example": "user@example.com"}, "name": {"type": "string", "example": "Jane Doe"}, "password": {"type": "string", "format": "password", "minLength": 8, "description": "If set, stores password_hash (bcrypt)."}, "city_id": {"type": "object", "description": "City UUID (FK to cities.id)", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "categories": {"description": "Category ids", "example": ["family", "theatre"], "type": "array", "items": {"type": "string"}}, "auth_provider": {"type": "string", "enum": ["email", "google", "apple"], "example": "email"}, "provider_subject": {"type": "object", "example": null}}, "required": ["email", "name"]}, "AdminUserDto": {"type": "object", "properties": {"id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "email": {"type": "string", "example": "user@example.com"}, "password_hash": {"type": "object", "example": null}, "name": {"type": "string", "example": "Jane Doe"}, "city": {"type": "object", "example": "Singapore"}, "city_id": {"type": "object", "description": "City UUID (FK to cities.id)", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "categories": {"description": "Category ids", "example": ["family", "theatre"], "type": "array", "items": {"type": "string"}}, "auth_provider": {"type": "string", "enum": ["email", "google", "apple"], "example": "email"}, "provider_subject": {"type": "object", "example": null}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.665Z"}, "updated_at": {"type": "string", "example": "2026-04-11T18:49:41.665Z"}}, "required": ["id", "email", "name", "categories", "auth_provider", "created_at", "updated_at"]}, "AdminUserResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminUserDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUsersListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUserDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminUsersListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminUsersListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUserMembershipDto": {"type": "object", "properties": {"id": {"type": "string"}, "user_id": {"type": "string"}, "tier_id": {"type": "string"}, "tier_code": {"type": "string", "example": "platinum"}, "tier_name": {"type": "string", "example": "BASE PLATINUM MEMBER"}, "year": {"type": "number", "example": 2026}, "tickets_remaining": {"type": "number", "example": 8}, "complimentary_tickets_per_year": {"type": "number", "example": 10}, "created_at": {"type": "string"}, "updated_at": {"type": "string"}}, "required": ["id", "user_id", "tier_id", "tier_code", "tier_name", "year", "tickets_remaining", "complimentary_tickets_per_year", "created_at", "updated_at"]}, "AdminUserMembershipResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"nullable": true, "description": "Null when user has no membership for the requested year", "allOf": [{"$ref": "#/components/schemas/AdminUserMembershipDto"}]}}, "required": ["success", "timestamp", "data"]}, "AdminUpsertUserMembershipDto": {"type": "object", "properties": {"tier_id": {"type": "string", "description": "Membership tier UUID"}, "year": {"type": "number", "example": 2026, "description": "Calendar year (defaults to current year)"}, "tickets_remaining": {"type": "number", "example": 8, "description": "Complimentary tickets left this year (defaults to tier annual quota on create)"}}, "required": ["tier_id"]}, "AdminDeleteUserMembershipDataDto": {"type": "object", "properties": {"id": {"type": "string"}}, "required": ["id"]}, "AdminDeleteUserMembershipResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteUserMembershipDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateUserDto": {"type": "object", "properties": {"email": {"type": "string", "format": "email", "example": "user@example.com"}, "name": {"type": "string", "example": "Jane Doe"}, "password": {"type": "string", "format": "password", "minLength": 8, "description": "If set, updates password_hash (bcrypt)."}, "city_id": {"type": "object", "description": "City UUID (FK to cities.id)", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "categories": {"description": "Category ids", "example": ["family", "theatre"], "type": "array", "items": {"type": "string"}}, "auth_provider": {"type": "string", "enum": ["email", "google", "apple"], "example": "email"}, "provider_subject": {"type": "object", "example": null}}}, "AdminDeleteUserDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteUserResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteUserDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminReviewDto": {"type": "object", "properties": {"id": {"type": "string", "example": "9e1e1dbd-09d2-4900-981c-00eddc5fa468"}, "show_id": {"type": "string", "example": "75f842fc-27dd-4ac3-91e5-5e7b5c13a5f0"}, "user_id": {"type": "object", "example": null}, "star_rating": {"type": "object", "example": 5}, "written_review": {"type": "object", "example": "Amazing show."}, "recommends": {"type": "boolean", "example": true}, "verified_purchase": {"type": "boolean", "example": false}, "moderation_status": {"type": "string", "example": "approved"}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.750Z"}, "video_reactions": {"type": "array", "items": {"$ref": "#/components/schemas/ShowVideoReactionDto"}}}, "required": ["id", "show_id", "recommends", "verified_purchase", "moderation_status", "created_at", "video_reactions"]}, "AdminReviewListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminReviewDto"}}}, "required": ["items"]}, "AdminReviewListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminReviewListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateReviewDto": {"type": "object", "properties": {"user_id": {"type": "object", "description": "Optional reviewer user id", "example": null}, "star_rating": {"type": "object", "minimum": 1, "maximum": 5, "example": 5}, "written_review": {"type": "object", "example": "Amazing show."}, "recommends": {"type": "boolean", "example": true, "default": false}, "verified_purchase": {"type": "boolean", "example": false, "default": false}, "moderation_status": {"type": "string", "enum": ["pending", "approved", "rejected"], "example": "approved"}}}, "AdminReviewResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminReviewDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateReviewDto": {"type": "object", "properties": {"user_id": {"type": "object", "description": "Optional reviewer user id", "example": null}, "star_rating": {"type": "object", "minimum": 1, "maximum": 5, "example": 4}, "written_review": {"type": "object", "example": "Updated review."}, "recommends": {"type": "boolean", "example": true}, "verified_purchase": {"type": "boolean", "example": false}, "moderation_status": {"type": "string", "enum": ["pending", "approved", "rejected"], "example": "approved"}}}, "AdminDeleteReviewDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteReviewResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteReviewDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminVideoReactionDto": {"type": "object", "properties": {"id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "show_id": {"type": "string", "example": "75f842fc-27dd-4ac3-91e5-5e7b5c13a5f0"}, "user_id": {"type": "object", "example": null}, "review_id": {"type": "object", "example": null}, "video_url": {"type": "string", "example": "https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"}, "duration_secs": {"type": "number", "example": 12}, "moderation_status": {"type": "string", "example": "approved"}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.750Z"}, "approved_at": {"type": "object", "example": "2026-04-11T18:49:41.750Z"}}, "required": ["id", "show_id", "video_url", "duration_secs", "moderation_status", "created_at"]}, "AdminVideoReactionListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminVideoReactionDto"}}}, "required": ["items"]}, "AdminVideoReactionListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminVideoReactionListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminVideoReactionUploadDataDto": {"type": "object", "properties": {"video_url": {"type": "string", "example": "/uploads/review-videos/example.mp4"}, "duration_secs": {"type": "number", "example": 12}}, "required": ["video_url", "duration_secs"]}, "AdminVideoReactionUploadResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminVideoReactionUploadDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateVideoReactionDto": {"type": "object", "properties": {"user_id": {"type": "object", "description": "Optional reviewer user id", "example": null}, "review_id": {"type": "object", "description": "Optional link to a review on this show", "example": null}, "video_url": {"type": "string", "example": "/uploads/review-videos/example.mp4"}, "duration_secs": {"type": "number", "example": 12, "minimum": 1}, "moderation_status": {"type": "string", "enum": ["pending", "approved", "rejected"], "example": "approved"}}, "required": ["video_url", "duration_secs"]}, "AdminVideoReactionResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminVideoReactionDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateVideoReactionDto": {"type": "object", "properties": {"user_id": {"type": "object", "description": "Optional reviewer user id", "example": null}, "review_id": {"type": "object", "description": "Optional link to a review on this show", "example": null}, "video_url": {"type": "string", "example": "/uploads/review-videos/example.mp4"}, "duration_secs": {"type": "number", "example": 12, "minimum": 1}, "moderation_status": {"type": "string", "enum": ["pending", "approved", "rejected"], "example": "approved"}}}, "AdminDeleteVideoReactionDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteVideoReactionResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteVideoReactionDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminTicketDto": {"type": "object", "properties": {"id": {"type": "string", "example": "0395a9e6-f7d7-4e91-9684-6ac79dffeb1a"}, "show_id": {"type": "string", "example": "75f842fc-27dd-4ac3-91e5-5e7b5c13a5f0"}, "user_id": {"type": "object", "example": null}, "show_session_id": {"type": "object", "example": null}, "session_id": {"type": "object", "description": "Alias for show_session_id", "example": null}, "category_id": {"type": "string", "example": "0395a9e6-f7d7-4e91-9684-6ac79dffeb1b"}, "tier_name": {"type": "string", "example": "Premium"}, "price": {"type": "number", "example": 168}, "currency": {"type": "string", "example": "SGD"}, "seat_number": {"type": "object", "example": "A-12"}, "ticket_status": {"type": "string", "example": "reserved"}, "qr_code": {"type": "object", "example": null}, "purchased_at": {"type": "object", "example": null}, "used_at": {"type": "object", "example": null}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.750Z"}}, "required": ["id", "show_id", "category_id", "tier_name", "price", "currency", "ticket_status", "created_at"]}, "AdminTicketListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminTicketDto"}}}, "required": ["items"]}, "AdminTicketListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminTicketListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateTicketDto": {"type": "object", "properties": {"category_id": {"type": "string", "description": "Ticket tier / category for this show", "format": "uuid"}, "user_id": {"type": "object", "description": "Buyer user id", "nullable": true, "format": "uuid"}, "show_session_id": {"type": "object", "description": "Show session id (FK to show_sessions)", "nullable": true, "format": "uuid"}, "session_id": {"type": "object", "description": "Alias for show_session_id", "nullable": true, "format": "uuid"}, "seat_number": {"type": "object", "example": "A-12", "nullable": true}, "ticket_status": {"type": "string", "enum": ["reserved", "purchased", "cancelled", "used"], "example": "reserved"}, "qr_code": {"type": "object", "nullable": true}, "purchased_at": {"type": "object", "example": "2026-04-28T12:00:00.000Z", "nullable": true}, "used_at": {"type": "object", "example": "2026-04-28T12:00:00.000Z", "nullable": true}}, "required": ["category_id"]}, "AdminTicketResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminTicketDto"}}, "required": ["success", "timestamp", "data"]}, "AdminTicketCheckInDto": {"type": "object", "properties": {"code": {"type": "string", "description": "Scanned QR payload \u2014 full validate URL or bare token TRM:v1:{ticketNumber}:{qrCode}", "example": "https://tremstgapi.axcova.com/api/tickets/validate?code=TRM%3Av1%3A00000042%3A22222222-2222-2222-2222-222222222222"}}, "required": ["code"]}, "AdminUpdateTicketDto": {"type": "object", "properties": {"category_id": {"type": "string", "description": "Ticket tier / category for this show", "format": "uuid"}, "user_id": {"type": "object", "nullable": true, "format": "uuid"}, "show_session_id": {"type": "object", "description": "Show session id (FK to show_sessions)", "nullable": true, "format": "uuid"}, "session_id": {"type": "object", "description": "Alias for show_session_id", "nullable": true, "format": "uuid"}, "seat_number": {"type": "object", "example": "A-12", "nullable": true}, "ticket_status": {"type": "string", "enum": ["reserved", "purchased", "cancelled", "used"]}, "qr_code": {"type": "object", "nullable": true}, "purchased_at": {"type": "object", "nullable": true}, "used_at": {"type": "object", "nullable": true}}}, "AdminDeleteTicketDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteTicketResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteTicketDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminTicketCategoryDto": {"type": "object", "properties": {"id": {"type": "string", "example": "0395a9e6-f7d7-4e91-9684-6ac79dffeb1a"}, "show_id": {"type": "string", "example": "75f842fc-27dd-4ac3-91e5-5e7b5c13a5f0"}, "session_id": {"type": "string", "example": "0395a9e6-f7d7-4e91-9684-6ac79dffeb1a"}, "tier_name": {"type": "string", "example": "Premium"}, "price": {"type": "number", "example": 168}, "currency": {"type": "string", "example": "SGD"}, "available_seats": {"type": "number", "example": 150}, "remaining_seats": {"type": "number", "example": 120}, "description": {"type": "object", "example": "Closer to stage"}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.750Z"}}, "required": ["id", "show_id", "session_id", "tier_name", "price", "currency", "available_seats", "remaining_seats", "created_at"]}, "AdminTicketCategoryListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminTicketCategoryDto"}}}, "required": ["items"]}, "AdminTicketCategoryListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminTicketCategoryListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCompleteSandboxPaymentDto": {"type": "object", "properties": {"payment_id": {"type": "string", "description": "Payment UUID from POST /api/checkout"}, "result": {"type": "string", "enum": ["success", "failed"]}}, "required": ["payment_id", "result"]}, "CheckoutCompleteDataDto": {"type": "object", "properties": {"checkout_id": {"type": "string"}, "payment_id": {"type": "string"}, "status": {"type": "string", "enum": ["paid", "failed"]}, "purchase_record_id": {"type": "object", "nullable": true}, "redirect_url": {"type": "string", "description": "Client return_url with checkout_id, payment_id, status query params"}, "payment_retry_until": {"type": "string", "nullable": true, "format": "date-time", "description": "After failure, seats held until this time for retry"}, "retry_available": {"type": "boolean", "description": "True when POST /checkout/retry is available"}, "guest_email": {"type": "object", "nullable": true}}, "required": ["checkout_id", "payment_id", "status", "redirect_url", "retry_available"]}, "CheckoutCompleteResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/CheckoutCompleteDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateShowVideoDto": {"type": "object", "properties": {"title": {"type": "string", "example": "Opening night highlight"}}, "required": ["title"]}, "AdminCreateArticleDto": {"type": "object", "properties": {"title": {"type": "string", "example": "Blackpink Adds Extra Show Date"}, "content_html": {"type": "string", "description": "HTML content from WYSIWYG editor", "example": "<p>Hello world</p>"}, "image_url": {"type": "object", "description": "Optional uploaded image URL (e.g. /uploads/article-images/xxx.png)", "example": "/uploads/article-images/abc.png", "nullable": true}, "url": {"type": "string", "description": "Article link (free text)", "example": "https://example.com/news/blackpink-osaka"}, "city_venue": {"type": "string", "example": "Osaka, Japan"}, "news_date": {"type": "string", "description": "News date (YYYY-MM-DD)", "example": "2026-04-28"}, "highlight": {"type": "object", "example": "Just Announced", "nullable": true}, "tag_ids": {"description": "List of tag ids", "example": ["concert", "announcement"], "type": "array", "items": {"type": "string"}}, "main": {"type": "boolean", "description": "Is this a main article for its tags? Only one main article is allowed per tag.", "default": false}, "show_in_homepage": {"type": "boolean", "description": "Show this article on the homepage.", "default": true}}, "required": ["title", "content_html", "url", "city_venue", "news_date"]}, "AdminUpdateArticleDto": {"type": "object", "properties": {"title": {"type": "string", "example": "Blackpink Adds Extra Show Date"}, "content_html": {"type": "string", "description": "HTML content from WYSIWYG editor", "example": "<p>Hello world</p>"}, "image_url": {"type": "object", "description": "Optional uploaded image URL (e.g. /uploads/article-images/xxx.png)", "example": "/uploads/article-images/abc.png", "nullable": true}, "url": {"type": "string", "description": "Article link (free text)", "example": "https://example.com/news/blackpink-osaka"}, "city_venue": {"type": "string", "example": "Osaka, Japan"}, "news_date": {"type": "string", "description": "News date (YYYY-MM-DD)", "example": "2026-04-28"}, "highlight": {"type": "object", "example": "Just Announced", "nullable": true}, "tag_ids": {"description": "Replace tags with this list of tag ids. Omit to keep unchanged.", "example": ["concert", "announcement"], "type": "array", "items": {"type": "string"}}, "main": {"type": "boolean", "description": "Is this a main article for its tags? Only one main article is allowed per tag."}, "show_in_homepage": {"type": "boolean", "description": "Show this article on the homepage."}}}, "AdminCreateArticleTagDto": {"type": "object", "properties": {"id": {"type": "string", "example": "announcement"}, "label": {"type": "string", "example": "Announcement"}}, "required": ["id", "label"]}, "AdminUpdateArticleTagDto": {"type": "object", "properties": {"label": {"type": "string", "example": "Announcement"}}}, "AdminCreateCastDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Hugh Jackman"}, "character_name": {"type": "string", "example": "Jean Valjean"}, "image_url": {"type": "object", "description": "Optional uploaded image URL (e.g. /uploads/cast-images/xxx.png)", "nullable": true}, "description": {"type": "object", "description": "Optional bio or notes", "nullable": true}, "instagram_url": {"type": "object", "example": "https://instagram.com/name", "nullable": true}, "x_url": {"type": "object", "example": "https://x.com/name", "nullable": true}, "tiktok_url": {"type": "object", "example": "https://www.tiktok.com/@name", "nullable": true}}, "required": ["name", "character_name"]}, "AdminUpdateCastDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Hugh Jackman"}, "character_name": {"type": "string", "example": "Jean Valjean"}, "image_url": {"type": "object", "description": "Optional uploaded image URL (e.g. /uploads/cast-images/xxx.png)", "nullable": true}, "description": {"type": "object", "description": "Optional bio or notes", "nullable": true}, "instagram_url": {"type": "object", "example": "https://instagram.com/name", "nullable": true}, "x_url": {"type": "object", "example": "https://x.com/name", "nullable": true}, "tiktok_url": {"type": "object", "example": "https://www.tiktok.com/@name", "nullable": true}}}, "AdminLinkShowCastDto": {"type": "object", "properties": {"cast_id": {"type": "string", "description": "Cast UUID", "example": "b7d1d1a0-1c35-4d2c-8d45-2b0bb6a4bb1a"}}, "required": ["cast_id"]}, "AdminLinkShowArticleDto": {"type": "object", "properties": {"article_id": {"type": "string", "description": "Article UUID", "example": "b7d1d1a0-1c35-4d2c-8d45-2b0bb6a4bb1a"}}, "required": ["article_id"]}, "AdminShowSessionDto": {"type": "object", "properties": {"id": {"type": "string"}, "show_id": {"type": "string"}, "session_date": {"type": "string", "example": "2026-06-15"}, "session_time": {"type": "string", "example": "19:30"}, "session_name": {"type": "string", "nullable": true, "example": "Evening Session", "description": "Custom title; public API uses as session_label when set"}, "description": {"type": "string", "nullable": true, "example": "Best value", "description": "Promo badge text returned on public GET /api/shows/:showId/sessions"}, "available_seats": {"type": "number", "description": "Sum of tier capacities (ticket_categories.available_seats)"}, "remaining_seats": {"type": "number", "description": "Sum of tier remaining seats for this session"}, "created_at": {"type": "string"}}, "required": ["id", "show_id", "session_date", "session_time", "available_seats", "remaining_seats", "created_at"]}, "AdminShowSessionsListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminShowSessionDto"}}}, "required": ["items"]}, "AdminShowSessionsListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminShowSessionsListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateShowSessionDto": {"type": "object", "properties": {"session_date": {"type": "string", "example": "2026-06-15", "description": "Performance date (YYYY-MM-DD)"}, "session_time": {"type": "string", "example": "19:30", "description": "Performance time (HH:mm or HH:mm:ss)"}, "session_name": {"type": "string", "nullable": true, "example": "Evening", "description": "Optional display name (e.g. Matinee, Evening). Falls back to time-based label if omitted."}, "description": {"type": "string", "nullable": true, "example": "Best value", "description": "Promo badge on session picker (public GET \u2026/sessions returns this as description)"}}, "required": ["session_date", "session_time"]}, "AdminShowSessionResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminShowSessionDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateShowSessionDto": {"type": "object", "properties": {"session_date": {"type": "string", "example": "2026-06-15"}, "session_time": {"type": "string", "example": "20:00"}, "session_name": {"type": "string", "nullable": true, "example": "Matinee", "description": "Custom session display name (empty clears it)"}, "description": {"type": "string", "nullable": true, "example": "Best value", "description": "Promo badge on session picker (public GET \u2026/sessions)"}}}, "AdminDeleteShowSessionDataDto": {"type": "object", "properties": {"id": {"type": "string", "description": "Deleted show session UUID"}}, "required": ["id"]}, "AdminDeleteShowSessionResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteShowSessionDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateTagDto": {"type": "object", "properties": {"id": {"type": "string", "example": "headline"}, "label": {"type": "string", "example": "Headline"}}, "required": ["id", "label"]}, "AdminUpdateTagDto": {"type": "object", "properties": {"label": {"type": "string", "example": "Headline"}}}, "AdminFcmTokenListItemDto": {"type": "object", "properties": {"id": {"type": "string"}, "user_id": {"type": "string"}, "platform": {"type": "string", "nullable": true, "enum": ["android", "ios", "web"]}, "updated_at": {"type": "string"}, "token_preview": {"type": "string", "description": "Truncated FCM token for display"}, "token": {"type": "string", "description": "Full FCM token (admin list only; for unregister in CMS)"}}, "required": ["id", "user_id", "platform", "updated_at", "token_preview", "token"]}, "AdminFcmTokenListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminFcmTokenListItemDto"}}}, "required": ["items"]}, "AdminFcmTokenListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminFcmTokenListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminRegisterFcmTokenDto": {"type": "object", "properties": {"user_id": {"type": "string", "description": "App user UUID to attach the FCM device token to", "example": "a439e524-3a8a-487a-b897-2c3a231a3fda"}, "token": {"type": "string", "description": "FCM registration token from the client app or Firebase console"}, "platform": {"type": "string", "enum": ["android", "ios", "web"]}}, "required": ["user_id", "token"]}, "RegisterFcmTokenDataDto": {"type": "object", "properties": {"id": {"type": "string", "description": "user_fcm_token.id"}, "user_id": {"type": "string", "description": "FK to users.id"}, "platform": {"type": "string", "nullable": true, "enum": ["android", "ios", "web"]}, "updated_at": {"type": "string", "description": "ISO timestamp"}}, "required": ["id", "user_id", "platform", "updated_at"]}, "RegisterFcmTokenResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/RegisterFcmTokenDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUnregisterFcmTokenDto": {"type": "object", "properties": {"user_id": {"type": "string", "description": "App user UUID", "example": "a439e524-3a8a-487a-b897-2c3a231a3fda"}, "token": {"type": "string", "description": "FCM registration token to remove for this user"}}, "required": ["user_id", "token"]}, "UnregisterFcmTokenDataDto": {"type": "object", "properties": {"removed": {"type": "boolean", "description": "Whether a matching row was deleted"}}, "required": ["removed"]}, "UnregisterFcmTokenResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/UnregisterFcmTokenDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminSendFcmDto": {"type": "object", "properties": {"user_ids": {"description": "List of user UUIDs to send the push notification to", "example": ["def36a9f-6600-48d4-9922-be6b1a00c04c"], "type": "array", "items": {"type": "string"}}, "title": {"type": "string", "description": "Notification title", "example": "New show added!"}, "body": {"type": "string", "description": "Notification body text", "example": "Check out the latest show near you."}, "data": {"type": "object", "description": "Optional key-value data payload (all values must be strings)", "example": {"screen": "show_detail", "show_id": "abc-123"}}}, "required": ["user_ids", "title", "body"]}, "AdminSendFcmDataDto": {"type": "object", "properties": {"fcm_enabled": {"type": "boolean", "description": "Whether FCM is configured on the server"}, "success_count": {"type": "number", "description": "Number of tokens that received the message"}, "failure_count": {"type": "number", "description": "Number of tokens that failed"}}, "required": ["fcm_enabled", "success_count", "failure_count"]}, "AdminSendFcmResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminSendFcmDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminSendFcmTopicDto": {"type": "object", "properties": {"topic": {"type": "string", "description": "FCM topic name. Use the same string as the mobile `subscribeToTopic` call (e.g. `xxx` or `event: xxx`).", "example": "xxx"}, "title": {"type": "string", "example": "Event alert"}, "body": {"type": "string", "example": "Something happened for event xxx"}, "data": {"type": "object", "example": {"event": "xxx", "screen": "test"}, "description": "Optional data payload (all values must be strings)"}}, "required": ["topic", "title", "body"]}, "AdminSendFcmTopicDataDto": {"type": "object", "properties": {"fcm_enabled": {"type": "boolean"}, "topic": {"type": "string", "description": "Normalized FCM topic name used for delivery"}, "message_id": {"type": "object", "description": "Firebase message id when send succeeded"}}, "required": ["fcm_enabled", "topic"]}, "AdminSendFcmTopicResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminSendFcmTopicDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminSubscribeFcmTopicDto": {"type": "object", "properties": {"topic": {"type": "string", "description": "FCM topic (must match mobile `subscribeToTopic`)", "example": "xxx"}, "user_ids": {"description": "Subscribe all tokens for these users (server-side via FCM)", "type": "array", "items": {"type": "string"}}, "tokens": {"description": "Subscribe explicit FCM tokens (e.g. from CMS web receiver)", "type": "array", "items": {"type": "string"}}}, "required": ["topic"]}, "AdminFcmTopicSubscribeDataDto": {"type": "object", "properties": {"fcm_enabled": {"type": "boolean"}, "topic": {"type": "string"}, "success_count": {"type": "number", "description": "Tokens successfully subscribed"}, "failure_count": {"type": "number", "description": "Tokens that failed to subscribe"}}, "required": ["fcm_enabled", "topic", "success_count", "failure_count"]}, "AdminFcmTopicSubscribeResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminFcmTopicSubscribeDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUnsubscribeFcmTopicDto": {"type": "object", "properties": {"topic": {"type": "string", "description": "FCM topic (must match mobile `subscribeToTopic`)", "example": "xxx"}, "user_ids": {"description": "Subscribe all tokens for these users (server-side via FCM)", "type": "array", "items": {"type": "string"}}, "tokens": {"description": "Subscribe explicit FCM tokens (e.g. from CMS web receiver)", "type": "array", "items": {"type": "string"}}}, "required": ["topic"]}, "AdminErrorLogRowDto": {"type": "object", "properties": {"id": {"type": "string"}, "created_at": {"type": "string"}, "level": {"type": "string", "enum": ["error", "warn"]}, "http_method": {"type": "string"}, "path": {"type": "string"}, "status_code": {"type": "number"}, "message": {"type": "string"}, "stack": {"type": "string", "nullable": true}, "actor_user_id": {"type": "string", "nullable": true}, "actor_email": {"type": "string", "nullable": true}, "actor_role": {"type": "string", "nullable": true}}, "required": ["id", "created_at", "level", "http_method", "path", "status_code", "message", "stack", "actor_user_id", "actor_email", "actor_role"]}, "AdminErrorLogsListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminErrorLogRowDto"}}, "page": {"type": "number"}, "per_page": {"type": "number"}, "total": {"type": "number"}, "total_pages": {"type": "number"}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminErrorLogsListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminErrorLogsListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminSurveyDto": {"type": "object", "properties": {"id": {"type": "string", "description": "Survey UUID"}, "name": {"type": "string"}, "category_id": {"type": "string"}, "category_label": {"type": "string"}, "launch_date": {"type": "string", "example": "2026-01-01"}, "expiry_date": {"type": "string", "example": "2026-12-31"}, "status": {"type": "number", "description": "Survey visibility: 1 = active (visible in app), 0 = inactive", "enum": [0, 1], "example": 1}, "video_url": {"type": "string", "description": "CDN URL when configured"}, "image_url": {"type": "object", "nullable": true}, "created_at": {"type": "string"}, "updated_at": {"type": "string"}}, "required": ["id", "name", "category_id", "category_label", "launch_date", "expiry_date", "status", "video_url", "created_at", "updated_at"]}, "AdminSurveysListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminSurveyDto"}}, "page": {"type": "number"}, "per_page": {"type": "number"}, "total": {"type": "number"}, "total_pages": {"type": "number"}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminSurveysListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminSurveysListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateSurveyDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Would you attend this show?"}, "category_id": {"type": "string", "example": "musical", "description": "Category id from categories table"}, "launch_date": {"type": "string", "description": "Launch date (YYYY-MM-DD)", "example": "2026-05-17"}, "expiry_date": {"type": "string", "description": "Expiry date (YYYY-MM-DD)", "example": "2026-12-31"}, "video_url": {"type": "string", "description": "Video URL (optional if uploading a file via POST \u2026/video after create)", "example": "/uploads/survey-videos/abc.mp4"}, "image_url": {"type": "object", "description": "Optional image URL (e.g. /uploads/survey-images/xxx.png)", "nullable": true}, "status": {"type": "number", "description": "Survey visibility: 1 = active, 0 = inactive", "enum": [0, 1], "default": 1}}, "required": ["name", "category_id", "launch_date", "expiry_date"]}, "AdminSurveyResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminSurveyDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateSurveyDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Would you attend this show?"}, "category_id": {"type": "string", "example": "musical"}, "launch_date": {"type": "string", "description": "Launch date (YYYY-MM-DD)", "example": "2026-05-17"}, "expiry_date": {"type": "string", "description": "Expiry date (YYYY-MM-DD)", "example": "2026-12-31"}, "video_url": {"type": "object", "description": "Video URL (optional; upload via POST \u2026/video or set null to clear)", "example": "/uploads/survey-videos/abc.mp4", "nullable": true}, "image_url": {"type": "object", "nullable": true}, "status": {"type": "number", "description": "Survey visibility: 1 = active, 0 = inactive", "enum": [0, 1]}}}, "AdminDeleteSurveyDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteSurveyResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteSurveyDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminSurveyResultDto": {"type": "object", "properties": {"id": {"type": "string"}, "user_id": {"type": "string"}, "survey_id": {"type": "string"}, "answer": {"type": "string", "enum": ["yes", "no", "maybe"]}, "notify": {"type": "boolean"}, "created_at": {"type": "string"}, "user_email": {"type": "string"}, "user_name": {"type": "string"}}, "required": ["id", "user_id", "survey_id", "answer", "notify", "created_at", "user_email", "user_name"]}, "AdminSurveyResultListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminSurveyResultDto"}}}, "required": ["items"]}, "AdminSurveyResultListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminSurveyResultListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateSurveyResultDto": {"type": "object", "properties": {"user_id": {"type": "string", "description": "App user UUID"}, "answer": {"type": "string", "enum": ["yes", "no", "maybe"], "example": "yes"}, "notify": {"type": "boolean", "default": false, "description": "Notify flag for this result"}}, "required": ["user_id", "answer"]}, "AdminSurveyResultResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminSurveyResultDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateSurveyResultDto": {"type": "object", "properties": {"user_id": {"type": "string", "description": "App user UUID"}, "answer": {"type": "string", "enum": ["yes", "no", "maybe"], "example": "maybe"}, "notify": {"type": "boolean", "description": "Notify flag for this result"}}}, "AdminDeleteSurveyResultDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteSurveyResultResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteSurveyResultDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminConfigDto": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string", "example": "low_stock_alert_threshold"}, "value": {"type": "string", "example": "10"}}, "required": ["id", "name", "value"]}, "AdminConfigListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminConfigDto"}}, "page": {"type": "number"}, "per_page": {"type": "number"}, "total": {"type": "number"}, "total_pages": {"type": "number"}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminConfigListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminConfigListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminConfigResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminConfigDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateConfigDto": {"type": "object", "properties": {"name": {"type": "string", "example": "low_stock_alert_threshold"}, "value": {"type": "string", "example": "10"}}, "required": ["name", "value"]}, "AdminUpdateConfigDto": {"type": "object", "properties": {"name": {"type": "string", "example": "low_stock_alert_threshold"}, "value": {"type": "string", "example": "10"}}}, "AdminDeleteConfigDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteConfigResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteConfigDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminBlockVenueSeatDto": {"type": "object", "properties": {"session_id": {"type": "string", "description": "Show session UUID"}, "duration_minutes": {"type": "number", "description": "How long the seat remains unavailable (minutes). Required if expires_at is omitted.", "minimum": 1, "maximum": 525600, "example": 60}, "expires_at": {"type": "string", "description": "Absolute expiry (ISO-8601). Future only; max 365 days from now. Required if duration_minutes is omitted.", "example": "2026-09-01T23:59:59.000Z"}, "reason": {"type": "string", "maxLength": 500, "example": "Reserved for production guest"}}, "required": ["session_id"]}, "AdminAssignVenueSeatDto": {"type": "object", "properties": {"session_id": {"type": "string", "description": "Show session UUID"}, "user_id": {"type": "string", "description": "User UUID to assign the seat to"}, "ticket_status": {"type": "string", "enum": ["purchased", "reserved"], "default": "purchased", "description": "Ticket status to create (default purchased)"}}, "required": ["session_id", "user_id"]}, "AdminUpsertVenueLayoutDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Main"}}}, "AdminCreateVenueSectionDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Stalls Centre"}, "code": {"type": "object", "example": "SC"}, "map_region_id": {"type": "object", "example": "stalls_centre", "description": "Figma map region id (stalls_front, dress_circle, box_1, \u2026). Null = resolve from code/name."}, "level": {"type": "object", "example": 1, "description": "1=ground, 2=first, 3=second"}, "sort_order": {"type": "number", "example": 0}, "map_x": {"type": "object", "example": 0.25}, "map_y": {"type": "object", "example": 0.5}, "map_width": {"type": "object", "example": 0.3}, "map_height": {"type": "object", "example": 0.2}}, "required": ["name"]}, "AdminUpdateVenueSectionDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Stalls Centre"}, "code": {"type": "object", "example": "SC"}, "map_region_id": {"type": "object", "example": "stalls_centre", "description": "Figma map region id (stalls_front, dress_circle, box_1, \u2026). Null = resolve from code/name."}, "level": {"type": "object", "example": 1, "description": "1=ground, 2=first, 3=second"}, "sort_order": {"type": "number", "example": 0}, "map_x": {"type": "object", "example": 0.25}, "map_y": {"type": "object", "example": 0.5}, "map_width": {"type": "object", "example": 0.3}, "map_height": {"type": "object", "example": 0.2}}}, "AdminCreateVenueRowDto": {"type": "object", "properties": {"row_label": {"type": "string", "example": "FF"}, "sort_order": {"type": "number", "example": 0}, "distance_from_stage_m": {"type": "object", "example": 12}, "sightline_rating": {"type": "object", "example": 5, "minimum": 1, "maximum": 5}}, "required": ["row_label"]}, "AdminUpdateVenueRowDto": {"type": "object", "properties": {"row_label": {"type": "string", "example": "FF"}, "sort_order": {"type": "number", "example": 0}, "distance_from_stage_m": {"type": "object", "example": 12}, "sightline_rating": {"type": "object", "example": 5, "minimum": 1, "maximum": 5}}}, "AdminCreateVenueSeatsDto": {"type": "object", "properties": {"seat_numbers": {"description": "Seat numbers to create in this row", "example": [19, 20, 21, 22, 23], "type": "array", "items": {"type": "number"}}, "tier_name": {"type": "object", "description": "Ticket tier name (resolved per session at booking \u2014 Approach A). Must match ticket_categories.tier_name.", "example": "Stalls Centre"}, "status": {"type": "string", "example": "active", "enum": ["active", "blocked", "wheelchair"]}}, "required": ["seat_numbers"]}, "AdminUpdateVenueSeatDto": {"type": "object", "properties": {"seat_number": {"type": "number", "example": 21}, "status": {"type": "string", "enum": ["active", "blocked", "wheelchair"]}, "tier_name": {"type": "object", "example": "Stalls Centre", "nullable": true}}}, "AdminUpsertShowVenueTierPriceItemDto": {"type": "object", "properties": {"venue_tier_id": {"type": "string", "format": "uuid"}, "price": {"type": "number", "minimum": 0}, "currency": {"type": "string", "default": "SGD"}, "price_category_id": {"type": "object", "format": "uuid", "nullable": true, "description": "Optional show pricing category id"}}, "required": ["venue_tier_id", "price"]}, "AdminUpsertShowVenueRowPriceItemDto": {"type": "object", "properties": {"row_id": {"type": "string", "format": "uuid"}, "price": {"type": "number", "minimum": 0}, "currency": {"type": "string", "default": "SGD"}, "price_category_id": {"type": "object", "format": "uuid", "nullable": true, "description": "Optional show pricing category id"}}, "required": ["row_id", "price"]}, "AdminUpsertShowVenueSeatPriceItemDto": {"type": "object", "properties": {"seat_id": {"type": "string", "format": "uuid"}, "price": {"type": "number", "minimum": 0}, "currency": {"type": "string", "default": "SGD"}, "price_category_id": {"type": "object", "format": "uuid", "nullable": true, "description": "Optional show pricing category id"}}, "required": ["seat_id", "price"]}, "AdminUpsertShowPriceBandItemDto": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid", "description": "Existing band id (omit to create)"}, "name": {"type": "string", "description": "Band name shown to buyers, e.g. \"Below RM 100\""}, "min_price": {"type": "number", "minimum": 0, "description": "Displayed range minimum price"}, "max_price": {"type": "number", "minimum": 0, "description": "Displayed range maximum price"}, "currency": {"type": "string", "default": "SGD"}, "sort_order": {"type": "number", "minimum": 0}, "tier_ids": {"description": "Venue tier (section) ids grouped into this band", "type": "array", "items": {"type": "string", "format": "uuid"}}}, "required": ["name", "min_price", "max_price", "tier_ids"]}, "AdminUpsertShowVenueTierPricesDto": {"type": "object", "properties": {"session_id": {"type": "string", "format": "uuid", "description": "When set, prices apply to this session only. Omit (or use apply_to_all_sessions) to update every session."}, "apply_to_all_sessions": {"type": "boolean", "default": false, "description": "When true with session_id, copies the submitted prices to all sessions on the show."}, "use_venue_default_pricing": {"type": "boolean", "default": false, "description": "When true, the show uses the venue default prices (a snapshot is copied on save). Custom items/row/seat prices are ignored."}, "items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUpsertShowVenueTierPriceItemDto"}}, "seat_pricing_mode": {"type": "string", "enum": ["tier", "row", "seat"], "default": "tier"}, "row_prices": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUpsertShowVenueRowPriceItemDto"}}, "seat_prices": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUpsertShowVenueSeatPriceItemDto"}}, "bands": {"description": "Price bands for this show. Each band groups sections and carries a min/max display range.", "type": "array", "items": {"$ref": "#/components/schemas/AdminUpsertShowPriceBandItemDto"}}}}, "AdminUpsertVenueTierPriceItemDto": {"type": "object", "properties": {"venue_tier_id": {"type": "string", "format": "uuid"}, "price": {"type": "number", "minimum": 0}, "currency": {"type": "string", "default": "SGD"}, "price_category_id": {"type": "object", "format": "uuid", "nullable": true, "description": "Optional venue pricing category id"}}, "required": ["venue_tier_id", "price"]}, "AdminUpsertVenueRowPriceItemDto": {"type": "object", "properties": {"row_id": {"type": "string", "format": "uuid"}, "price": {"type": "number", "minimum": 0}, "currency": {"type": "string", "default": "SGD"}, "price_category_id": {"type": "object", "format": "uuid", "nullable": true, "description": "Optional venue pricing category id"}}, "required": ["row_id", "price"]}, "AdminUpsertVenueSeatPriceItemDto": {"type": "object", "properties": {"seat_id": {"type": "string", "format": "uuid"}, "price": {"type": "number", "minimum": 0}, "currency": {"type": "string", "default": "SGD"}, "price_category_id": {"type": "object", "format": "uuid", "nullable": true, "description": "Optional venue pricing category id"}}, "required": ["seat_id", "price"]}, "AdminUpsertVenuePricesDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUpsertVenueTierPriceItemDto"}}, "seat_pricing_mode": {"type": "string", "enum": ["tier", "row", "seat"], "default": "tier"}, "row_prices": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUpsertVenueRowPriceItemDto"}}, "seat_prices": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUpsertVenueSeatPriceItemDto"}}}, "required": ["items"]}, "AdminMembershipTierDto": {"type": "object", "properties": {"id": {"type": "string"}, "code": {"type": "string", "example": "platinum"}, "name": {"type": "string", "example": "BASE PLATINUM MEMBER"}, "complimentary_tickets_per_year": {"type": "number", "example": 10}, "created_at": {"type": "string"}}, "required": ["id", "code", "name", "complimentary_tickets_per_year", "created_at"]}, "AdminMembershipTiersListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminMembershipTierDto"}}}, "required": ["items"]}, "AdminMembershipTiersListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminMembershipTiersListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateMembershipTierDto": {"type": "object", "properties": {"code": {"type": "string", "example": "gold", "description": "Unique tier code (public API tier field)"}, "name": {"type": "string", "example": "GOLD MEMBER"}, "complimentary_tickets_per_year": {"type": "number", "example": 5, "description": "Complimentary tickets granted per calendar year"}}, "required": ["code", "name", "complimentary_tickets_per_year"]}, "AdminMembershipTierResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminMembershipTierDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateMembershipTierDto": {"type": "object", "properties": {"code": {"type": "string", "example": "platinum"}, "name": {"type": "string", "example": "BASE PLATINUM MEMBER"}, "complimentary_tickets_per_year": {"type": "number", "example": 10}}}, "AdminDeleteMembershipTierDataDto": {"type": "object", "properties": {"id": {"type": "string"}}, "required": ["id"]}, "AdminDeleteMembershipTierResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteMembershipTierDataDto"}}, "required": ["success", "timestamp", "data"]}, "TrendingShowSummaryDto": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid"}, "name": {"type": "string", "example": "Hamilton"}, "show_image_url": {"type": "string", "example": "https://cdn.example.com/shows/hamilton.jpg"}, "status": {"type": "number", "enum": [0, 1], "example": 1}, "start_date": {"type": "object", "nullable": true, "example": "2026-09-01"}, "end_date": {"type": "object", "nullable": true, "example": "2026-12-31"}}, "required": ["id", "name", "show_image_url", "status"]}, "AdminTrendingDto": {"type": "object", "properties": {"id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "video_url": {"type": "string", "example": "/uploads/trending-videos/abc.mp4"}, "image_preview": {"type": "object", "nullable": true, "example": "/uploads/trending-images/abc.jpg", "description": "Preview image URL (jpg/png/svg/gif)"}, "title": {"type": "string", "example": "Trending video title"}, "author": {"type": "string", "example": "John Doe"}, "link_url": {"type": "string", "example": "https://example.com"}, "show_id": {"type": "object", "nullable": true, "format": "uuid", "example": "8f3c2a1b-4d5e-6f70-8192-a3b4c5d6e7f8", "description": "Show opened from this trending reel"}, "status": {"type": "string", "enum": ["active", "inactive"], "example": "active"}, "show": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/TrendingShowSummaryDto"}]}, "created_at": {"type": "string", "example": "2026-04-11T18:49:41.665Z"}, "updated_at": {"type": "string", "example": "2026-04-11T18:49:41.665Z"}}, "required": ["id", "video_url", "image_preview", "title", "author", "link_url", "show_id", "status", "created_at", "updated_at"]}, "AdminTrendingListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminTrendingDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminTrendingListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminTrendingListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminTrendingResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminTrendingDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateTrendingDto": {"type": "object", "properties": {"title": {"type": "string", "example": "Trending video title"}, "author": {"type": "string", "example": "John Doe"}, "link_url": {"type": "string", "example": "https://example.com"}, "status": {"type": "string", "enum": ["active", "inactive"], "example": "active"}, "show_id": {"type": "object", "nullable": true, "format": "uuid", "description": "Show to open from this trending reel. Empty string or null clears the link."}}, "required": ["title", "author", "link_url", "status"]}, "AdminUpdateTrendingDto": {"type": "object", "properties": {"title": {"type": "string", "example": "Trending video title"}, "author": {"type": "string", "example": "John Doe"}, "link_url": {"type": "string", "example": "https://example.com"}, "status": {"type": "string", "enum": ["active", "inactive"], "example": "active"}, "show_id": {"type": "object", "nullable": true, "format": "uuid", "description": "Show to open from this trending reel. Pass empty string or null to clear."}}}, "AdminDeleteTrendingDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}}, "required": ["deleted"]}, "AdminDeleteTrendingResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteTrendingDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminSendMessagePushDto": {"type": "object", "properties": {"topic": {"type": "string", "description": "Send to a custom FCM topic (alternative to user_ids / user_group_id). Use the same name as mobile `subscribeToTopic` (e.g. `xxx` or `event: xxx`).", "example": "xxx"}, "user_group_id": {"type": "string", "description": "Send to all members of this user group (alternative to user_ids / topic)", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "user_ids": {"description": "Target user UUIDs (ignored when user_group_id or topic is set)", "example": ["b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"], "type": "array", "items": {"type": "string"}}, "title": {"type": "string", "example": "New show alert"}, "body": {"type": "string", "example": "Tickets are on sale now."}, "show_id": {"type": "string", "description": "FCM data.show_id (optional; used with topic sends)", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "show_color": {"type": "string", "description": "FCM data.show_color (optional; used with topic sends)", "example": "#1E3A5F"}, "type": {"type": "string", "description": "FCM data.type (optional; used with topic sends)", "example": "ticket_extension_log"}}, "required": ["title", "body"]}, "AdminSendMessageEnqueueDataDto": {"type": "object", "properties": {"queued": {"type": "number", "example": 3}, "ids": {"type": "array", "items": {"type": "string"}}}, "required": ["queued", "ids"]}, "AdminSendMessageEnqueueResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminSendMessageEnqueueDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminSendMessageEmailDto": {"type": "object", "properties": {"user_group_id": {"type": "string", "description": "Send to all members of this user group (alternative to user_ids)", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "user_ids": {"description": "Target user UUIDs (email taken from user record; ignored when user_group_id is set)", "example": ["b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"], "type": "array", "items": {"type": "string"}}, "subject": {"type": "string", "example": "Weekly update"}, "html_body": {"type": "string", "example": "<p>Hello from TREM</p>"}}, "required": ["subject", "html_body"]}, "AdminUserGroupDto": {"type": "object", "properties": {"id": {"type": "string", "example": "cc800633-5e29-476f-bfc4-da3aa3e13c9a"}, "name": {"type": "string", "example": "VIP subscribers"}, "topic": {"type": "string", "example": "vipsubscribers", "description": "FCM topic slug (lowercase letters/digits only)"}, "member_count": {"type": "number", "example": 42, "description": "Current member count (max 1000)"}, "created_at": {"type": "string", "example": "2026-06-18T12:00:00.000Z"}, "updated_at": {"type": "string", "example": "2026-06-18T12:00:00.000Z"}}, "required": ["id", "name", "topic", "member_count", "created_at", "updated_at"]}, "AdminUserGroupsListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUserGroupDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 5}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminUserGroupsListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminUserGroupsListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateUserGroupDto": {"type": "object", "properties": {"name": {"type": "string", "example": "VIP subscribers"}, "topic": {"type": "string", "description": "FCM topic slug (lowercase letters/digits only). Auto-generated from name when omitted.", "example": "vipsubscribers"}}, "required": ["name"]}, "AdminUserGroupResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminUserGroupDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUpdateUserGroupDto": {"type": "object", "properties": {"name": {"type": "string", "example": "VIP subscribers"}, "topic": {"type": "string", "description": "FCM topic slug (lowercase letters/digits only)", "example": "vipsubscribers"}}}, "AdminDeleteUserGroupDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean"}, "id": {"type": "string"}}, "required": ["deleted", "id"]}, "AdminDeleteUserGroupResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteUserGroupDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUserGroupMemberDto": {"type": "object", "properties": {"user_id": {"type": "string"}, "email": {"type": "string"}, "name": {"type": "string"}, "added_at": {"type": "string"}}, "required": ["user_id", "email", "name", "added_at"]}, "AdminUserGroupMembersListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminUserGroupMemberDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 50}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminUserGroupMembersListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminUserGroupMembersListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUserGroupMembersDto": {"type": "object", "properties": {"user_ids": {"description": "User UUIDs to add to the group", "example": ["cc800633-5e29-476f-bfc4-da3aa3e13c9a"], "type": "array", "items": {"type": "string"}}}, "required": ["user_ids"]}, "AdminUserGroupMembersAddDataDto": {"type": "object", "properties": {"added": {"type": "number", "example": 3}, "skipped": {"type": "number", "example": 1, "description": "Users already in the group"}, "member_count": {"type": "number", "example": 12}}, "required": ["added", "skipped", "member_count"]}, "AdminUserGroupMembersAddResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminUserGroupMembersAddDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminUserGroupMemberRemoveDataDto": {"type": "object", "properties": {"removed": {"type": "boolean"}, "member_count": {"type": "number", "example": 11}}, "required": ["removed", "member_count"]}, "AdminUserGroupMemberRemoveResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminUserGroupMemberRemoveDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminFcmTopicTypeDto": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid"}, "topic": {"type": "string", "example": "ticket-price-drop"}, "description": {"type": "string", "example": "Ticket price drop notifications"}}, "required": ["id", "topic", "description"]}, "AdminFcmTopicTypeListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminFcmTopicTypeDto"}}, "page": {"type": "number"}, "per_page": {"type": "number"}, "total": {"type": "number"}, "total_pages": {"type": "number"}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminFcmTopicTypeListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminFcmTopicTypeListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminFcmTopicTypeResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminFcmTopicTypeDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateFcmTopicTypeDto": {"type": "object", "properties": {"topic": {"type": "string", "example": "ticket-price-drop"}, "description": {"type": "string", "example": "Ticket price drop notifications"}}, "required": ["topic"]}, "AdminUpdateFcmTopicTypeDto": {"type": "object", "properties": {"topic": {"type": "string", "example": "ticket-price-drop"}, "description": {"type": "string", "example": "Ticket price drop notifications"}}}, "AdminDeleteFcmTopicTypeDataDto": {"type": "object", "properties": {"deleted": {"type": "boolean", "example": true}, "id": {"type": "string", "format": "uuid"}}, "required": ["deleted", "id"]}, "AdminDeleteFcmTopicTypeResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminDeleteFcmTopicTypeDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminTicketPriceChangeDto": {"type": "object", "properties": {"id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "ticket_category_id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "show_id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "admin_id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "changed_at": {"type": "string", "example": "2026-06-01T12:00:00.000Z"}, "price_from": {"type": "string", "example": "250000.00"}, "price_to": {"type": "string", "example": "200000.00"}, "direction": {"type": "string", "enum": ["increase", "decrease"], "example": "decrease"}, "notify": {"type": "boolean", "example": false}, "fcm_sent": {"type": "number", "enum": [0, 1], "example": 0}}, "required": ["id", "ticket_category_id", "show_id", "admin_id", "changed_at", "price_from", "price_to", "direction", "notify", "fcm_sent"]}, "AdminTicketPriceChangeListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminTicketPriceChangeDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminTicketPriceChangeListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminTicketPriceChangeListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminTicketExtensionLogDto": {"type": "object", "properties": {"id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "show_id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "show_name": {"type": "object", "example": "Hamilton", "nullable": true}, "admin_id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "changed_at": {"type": "string", "example": "2026-06-01T12:00:00.000Z"}, "end_date_from": {"type": "string", "example": "2026-06-15"}, "end_date_to": {"type": "string", "example": "2026-06-30"}, "notify": {"type": "boolean", "example": true}, "fcm_sent": {"type": "number", "enum": [0, 1], "example": 0}}, "required": ["id", "show_id", "show_name", "admin_id", "changed_at", "end_date_from", "end_date_to", "notify", "fcm_sent"]}, "AdminTicketExtensionLogListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminTicketExtensionLogDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminTicketExtensionLogListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminTicketExtensionLogListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminShowLowAvailabilityLogDto": {"type": "object", "properties": {"id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "show_id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "show_name": {"type": "object", "example": "Hamilton", "nullable": true}, "seat_balance": {"type": "number", "example": 12}, "seat_total": {"type": "number", "example": 100}, "threshold": {"type": "number", "example": 20}, "logged_at": {"type": "string", "example": "2026-06-01T12:00:00.000Z"}, "notify": {"type": "boolean", "example": true}, "fcm_sent": {"type": "number", "enum": [0, 1], "example": 0}}, "required": ["id", "show_id", "show_name", "seat_balance", "seat_total", "threshold", "logged_at", "notify", "fcm_sent"]}, "AdminShowLowAvailabilityLogListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminShowLowAvailabilityLogDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminShowLowAvailabilityLogListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminShowLowAvailabilityLogListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminShowFinalWeekDto": {"type": "object", "properties": {"id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "show_id": {"type": "string", "example": "b3b0a2c1-7f2b-4aa9-9c88-9d5a4c0e9b10"}, "show_name": {"type": "object", "example": "Hamilton", "nullable": true}, "end_date": {"type": "string", "example": "2026-10-17"}, "days_before_end": {"type": "number", "example": 7, "description": "Days-before-end window used when logged"}, "logged_at": {"type": "string", "example": "2026-06-01T12:00:00.000Z"}, "notify": {"type": "boolean", "example": true}, "fcm_sent": {"type": "number", "enum": [0, 1], "example": 0}}, "required": ["id", "show_id", "show_name", "end_date", "days_before_end", "logged_at", "notify", "fcm_sent"]}, "AdminShowFinalWeekListDataDto": {"type": "object", "properties": {"items": {"type": "array", "items": {"$ref": "#/components/schemas/AdminShowFinalWeekDto"}}, "page": {"type": "number", "example": 1}, "per_page": {"type": "number", "example": 30}, "total": {"type": "number", "example": 12}, "total_pages": {"type": "number", "example": 1}}, "required": ["items", "page", "per_page", "total", "total_pages"]}, "AdminShowFinalWeekListResponseDto": {"type": "object", "properties": {"success": {"type": "boolean", "example": true}, "timestamp": {"type": "string", "example": "2026-04-20T13:35:43.727Z"}, "data": {"$ref": "#/components/schemas/AdminShowFinalWeekListDataDto"}}, "required": ["success", "timestamp", "data"]}, "AdminCreateAssetDirectoryDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Campaigns"}, "parent_id": {"type": "object", "nullable": true, "description": "Parent directory UUID. Omit or null for root."}}, "required": ["name"]}, "AdminRenameAssetDirectoryDto": {"type": "object", "properties": {"name": {"type": "string", "example": "Summer campaign"}}, "required": ["name"]}, "AdminUpdateAssetDto": {"type": "object", "properties": {"name": {"type": "string", "example": "hero-banner"}, "directory_id": {"type": "object", "nullable": true, "description": "Move into this directory. Null moves to root."}}}}}}