{ "openapi": "3.0.0", "paths": { "/api/v1/auth/request-otp": { "post": { "description": "Creates an OTP request for login or registration and sends the code by SMS. If a still-valid login/register OTP already exists for this mobile, the previous code is reused (`alreadySent: true`) and no new SMS is sent.", "operationId": "AuthController_requestOtp", "parameters": [], "requestBody": { "required": true, "description": "Mobile number that should receive the OTP code.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestOtpDto" } } } }, "responses": { "201": { "description": "OTP request accepted for delivery.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OtpRequestResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "Request SMS OTP", "tags": ["Auth"] } }, "/api/v1/auth/verify-otp": { "post": { "description": "Verifies an SMS OTP, creates a pending user on first registration when needed, and returns a JWT access token. The refresh token is set as an httpOnly cookie.", "operationId": "AuthController_verifyOtp", "parameters": [], "requestBody": { "required": true, "description": "OTP verification details.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VerifyOtpDto" } } } }, "responses": { "201": { "description": "OTP verified and tokens issued.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthTokensDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "Verify OTP", "tags": ["Auth"] } }, "/api/v1/auth/refresh": { "post": { "description": "Rotates the httpOnly refresh-token cookie and returns a new access token. The refresh token is never returned in the JSON body.", "operationId": "AuthController_refresh", "parameters": [], "responses": { "201": { "description": "Token rotation succeeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthTokensDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "Refresh JWT tokens", "tags": ["Auth"] } }, "/api/v1/auth/logout": { "post": { "description": "Revokes the refresh-token session associated with the current JWT.", "operationId": "AuthController_logout", "parameters": [], "responses": { "200": { "description": "Current session revoked successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoggedOutResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Logout current session", "tags": ["Auth"] } }, "/api/v1/auth/logout-all": { "post": { "description": "Revokes all active refresh-token sessions for the current user.", "operationId": "AuthController_logoutAll", "parameters": [], "responses": { "200": { "description": "All user sessions revoked successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoggedOutResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Logout all sessions", "tags": ["Auth"] } }, "/api/v1/auth/sessions": { "get": { "description": "Returns active refresh-token sessions for the current authenticated user.", "operationId": "AuthController_listSessions", "parameters": [], "responses": { "200": { "description": "Active sessions for the current user.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/SessionResponseDto" } } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List active sessions", "tags": ["Auth"] } }, "/api/v1/auth/sessions/{id}": { "delete": { "description": "Revokes one active refresh-token session owned by the current user.", "operationId": "AuthController_revokeSession", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Refresh-token session identifier to revoke.", "schema": { "format": "uuid", "example": "0c60c25d-831b-4d43-a5fd-6e8127d81134", "type": "string" } } ], "responses": { "200": { "description": "Session revoked successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RevokedResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Revoke a session", "tags": ["Auth"] } }, "/api/v1/internal/sentry-webhook": { "post": { "description": "Receives Sentry issue/alert webhooks and forwards a short ops alarm to Telegram. Auth: required header X-Ghabilee-Sentry-Secret (same value as SENTRY_WEBHOOK_SECRET). Query ?token= is rejected (secrets must not appear in access logs).", "operationId": "OpsAlertsController_handle", "parameters": [ { "name": "x-ghabilee-sentry-secret", "in": "header", "description": "Shared secret; must match SENTRY_WEBHOOK_SECRET on the VPS.", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" } } } } } } }, "summary": "Sentry → Telegram alert relay", "tags": ["Ops"] } }, "/api/v1/internal/jibit-transaction-webhook": { "post": { "description": "Receives Jibit CoBank transaction notifications (deposit, corrective withdrawal, transfer) and forwards a short Persian summary to the ops Telegram group. Auth: HMAC-SHA256 of the raw body (X-Jibit-Signature / X-Hub-Signature-256) keyed by JIBIT_TRANSACTION_WEBHOOK_SECRET, or header X-Jibit-App-Secret with the same value. Query ?token= is rejected (secrets must not appear in access logs).", "operationId": "JibitTransactionWebhookController_handle", "parameters": [ { "name": "x-jibit-signature", "in": "header", "description": "HMAC-SHA256 of the raw JSON body, keyed by the App Secret (hex or sha256=hex).", "required": false, "schema": { "type": "string" } }, { "name": "x-jibit-app-secret", "in": "header", "description": "App Secret from the Jibit panel (same as JIBIT_TRANSACTION_WEBHOOK_SECRET). Optional when Jibit sends an HMAC signature header instead.", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" } } } } } } }, "summary": "Jibit account-transaction → Telegram relay", "tags": ["Ops"] } }, "/api/v1/users/public/{id}": { "get": { "operationId": "UsersController_getPublicOrganizer", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicOrganizerResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get a public user profile; host stats are included only for verified hosts", "tags": ["Users"] } }, "/api/v1/users/public/{id}/events": { "get": { "description": "Paginated upcoming or held events. Non-verified profiles return an empty list.", "operationId": "UsersController_listPublicOrganizerEvents", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "status", "required": false, "in": "query", "schema": { "default": "upcoming", "type": "string", "enum": ["upcoming", "held"] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/PublicOrganizerEventDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List discoverable events for a verified public host", "tags": ["Users"] } }, "/api/v1/users/public/{id}/viewer-state": { "get": { "operationId": "UsersController_getOrganizerViewerState", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrganizerViewerStateResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get private organizer state for the current viewer", "tags": ["Users"] } }, "/api/v1/users/me": { "get": { "description": "Returns the authenticated user profile and account capability fields.", "operationId": "UsersController_getMe", "parameters": [], "responses": { "200": { "description": "Current user profile.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserProfileResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get current user profile", "tags": ["Users"] }, "patch": { "description": "Updates editable profile fields for the authenticated user only.", "operationId": "UsersController_updateMe", "parameters": [], "requestBody": { "required": true, "description": "Profile fields to update.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateUserProfileDto" } } } }, "responses": { "200": { "description": "Updated user profile.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserProfileResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Update current user profile", "tags": ["Users"] } }, "/api/v1/users/me/profile-summary": { "get": { "description": "Returns wallet balance, social/event/bookmark/notification counts, and short previews for the consumer profile dashboard.", "operationId": "UsersController_getProfileSummary", "parameters": [], "responses": { "200": { "description": "Profile hub summary.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserProfileSummaryResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get current user profile hub summary", "tags": ["Users"] } }, "/api/v1/users/{id}/attendee-contact-links": { "get": { "description": "Returns non-public links only when the current user owns the organizer profile or has a confirmed booking for one of its events.", "operationId": "UsersController_getAttendeeContactLinks", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PublicOrganizerContactLinkDto" } } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get attendee-only contact links for an organizer", "tags": ["Users"] } }, "/api/v1/users/me/complete-profile": { "post": { "description": "Creates the user profile and activates a pending account. Only callable while status is pending.", "operationId": "UsersController_completeProfile", "parameters": [], "requestBody": { "required": true, "description": "Required profile fields for first-time registration.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompleteProfileDto" } } } }, "responses": { "200": { "description": "Profile completed and account activated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserProfileResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Complete user profile after OTP verification", "tags": ["Users"] } }, "/api/v1/admin/users": { "get": { "description": "Staff-only paginated list of platform users for the admin panel.", "operationId": "AdminUsersController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt, lastLoginAt, mobile (prefix - for desc).", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Admin user filters via filters[key]=value (userType, role, status, identityStatus, mobile, firstName, lastName, gender, cityId, walletBalance, lastLoginAt, createdAt). userType is guest|host|admin. Date ranges and walletBalance use from,to.", "schema": { "additionalProperties": { "type": "string" }, "example": { "userType": "host", "status": "active", "gender": "female", "cityId": "1", "walletBalance": "0,500000" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[createdAt]", "required": false, "in": "query", "description": "Registration date range as from,to (YYYY-MM-DD).", "schema": { "example": "2026-07-01,2026-07-31", "type": "string" } }, { "name": "filters[lastLoginAt]", "required": false, "in": "query", "description": "Last login date range as from,to (YYYY-MM-DD).", "schema": { "example": "2026-07-01,2026-07-31", "type": "string" } }, { "name": "filters[walletBalance]", "required": false, "in": "query", "description": "Wallet balance range in Tomans as from,to.", "schema": { "example": "0,500000", "type": "string" } }, { "name": "filters[cityId]", "required": false, "in": "query", "description": "Filter by home city id.", "schema": { "example": "1", "type": "string" } }, { "name": "filters[gender]", "required": false, "in": "query", "description": "Filter by gender: male, female, or other.", "schema": { "example": "female", "type": "string" } }, { "name": "filters[mobile]", "required": false, "in": "query", "description": "Partial mobile number match.", "schema": { "example": "98912", "type": "string" } }, { "name": "filters[identityStatus]", "required": false, "in": "query", "description": "Filter by latest identity status: none, pending, verified, or rejected.", "schema": { "example": "verified", "type": "string" } }, { "name": "filters[status]", "required": false, "in": "query", "description": "Filter by account status: active, suspended, or deleted.", "schema": { "example": "active", "type": "string" } }, { "name": "filters[role]", "required": false, "in": "query", "description": "Raw account role: user or admin. Prefer filters[userType] for guest/host/admin.", "schema": { "example": "user", "type": "string" } }, { "name": "filters[userType]", "required": false, "in": "query", "description": "Derived user type for admin triage: guest, host (identity verified), or admin.", "schema": { "example": "host", "type": "string" } } ], "responses": { "200": { "description": "Paginated users for admin management.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserListItemDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Paginated admin users list", "tags": ["Admin - Users"] } }, "/api/v1/admin/users/{id}/mobile": { "patch": { "description": "Changes the login identifier, clears mobile verification, cancels pending OTPs for the old and new numbers, and revokes every active session. Historical SMS/OTP destination fields are not rewritten.", "operationId": "AdminUsersController_changeMobile", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminChangeUserMobileDto" } } } }, "responses": { "200": { "description": "Updated user. The mobile is returned in normalized format.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserProfileResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Change a user's login mobile (admin)", "tags": ["Admin - Users"] } }, "/api/v1/admin/users/{id}": { "patch": { "description": "Edits profile fields (only once the user has completed their own profile), status ('active'/'suspended' only), and/or host plan. An admin cannot change their own status through this endpoint. For read access to a single user, see GET /admin/users/{id} in the admin-user-detail module instead — this controller intentionally has no GET :id of its own to avoid a duplicate route.", "operationId": "AdminUsersController_update", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "description": "Any subset of profile fields, status, and/or host plan.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminUpdateUserDto" } } } }, "responses": { "200": { "description": "Updated user.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserProfileResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Edit a user (admin)", "tags": ["Admin - Users"] }, "get": { "description": "Staff-only: full profile, identity status, wallet balance, and a summary count for every category (hosted events, bookings, payments, reviews, follows, reports, blocks). See the dedicated sub-endpoints (hosted-events, bookings, payments, reviews, follows, reports, blocks) for the paginated lists themselves.", "operationId": "AdminUserDetailController_getDetail", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminUserDetailResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Full user detail for admin", "tags": ["Admin - User Detail"] } }, "/api/v1/users/me/contact-links": { "get": { "operationId": "UserContactLinksController_list", "parameters": [], "responses": { "200": { "description": "Contact links ordered by displayOrder.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserContactLinkResponseDto" } } } } } }, "security": [ { "bearer": [] } ], "summary": "Current user's contact links (unpaginated)", "tags": ["users"] }, "post": { "operationId": "UserContactLinksController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateUserContactLinkDto" } } } }, "responses": { "201": { "description": "Contact link created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserContactLinkResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Add a contact link for the current user", "tags": ["users"] } }, "/api/v1/users/me/contact-links/{id}": { "patch": { "operationId": "UserContactLinksController_update", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateUserContactLinkDto" } } } }, "responses": { "200": { "description": "Contact link updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserContactLinkResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Update a contact link owned by the current user", "tags": ["users"] }, "delete": { "operationId": "UserContactLinksController_remove", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Contact link removed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Remove a contact link owned by the current user", "tags": ["users"] } }, "/api/v1/identity/rejection-reasons": { "get": { "description": "Returns active structured rejection reasons for the host identity submission form.", "operationId": "IdentityController_getRejectionReasons", "parameters": [], "responses": { "200": { "description": "Active rejection reasons sorted by display order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IdentityRejectionReasonResponseDto" } } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "List active identity rejection reasons", "tags": ["Identity"] } }, "/api/v1/identity/verifications/request-otp": { "post": { "description": "Sends an OTP to the authenticated user’s registered mobile after they accept the host cooperation contract and provide a national code.", "operationId": "IdentityController_requestContractOtp", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestHostContractOtpDto" } } } }, "responses": { "200": { "description": "OTP accepted for delivery.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostContractOtpRequestedDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Request OTP to confirm host cooperation contract", "tags": ["Identity"] } }, "/api/v1/identity/verifications/confirm-otp": { "post": { "description": "Verifies the host-contract OTP and creates a pending identity verification with electronic contract acceptance metadata.", "operationId": "IdentityController_confirmContractOtp", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfirmHostContractOtpDto" } } } }, "responses": { "201": { "description": "Identity verification request created with pending status.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IdentityVerificationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Confirm host-contract OTP and submit identity verification", "tags": ["Identity"] } }, "/api/v1/identity/verifications/me": { "get": { "description": "Returns the authenticated user’s own bounded identity verification history.", "operationId": "IdentityController_listMine", "parameters": [], "responses": { "200": { "description": "Identity verification submissions for the current user.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IdentityVerificationResponseDto" } } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List my identity verification submissions", "tags": ["Identity"] } }, "/api/v1/admin/identity/verifications": { "get": { "description": "Staff-only paginated queue of host identity verification submissions across all users.", "operationId": "AdminIdentityController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt, -createdAt, status, or -status.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[userId]", "required": false, "in": "query", "description": "Filter by applicant user identifier.", "schema": { "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "type": "string" } }, { "name": "filters[status]", "required": false, "in": "query", "description": "Filter by verification status: pending, verified, or rejected.", "schema": { "example": "pending", "type": "string" } } ], "responses": { "200": { "description": "Paginated identity verification review queue.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminIdentityVerificationResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List identity verification review queue", "tags": ["Admin - Identity"] } }, "/api/v1/admin/identity/verifications/{id}/jibit-inquiry": { "post": { "description": "Calls Jibit matching for the pending submission. Saves the result. matched=true auto-approves; matched=false auto-rejects with national_mobile_mismatch.", "operationId": "AdminIdentityController_inquireJibit", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Identity verification request identifier.", "schema": { "format": "uuid", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "type": "string" } } ], "responses": { "200": { "description": "Inquiry stored; status may become verified or rejected.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IdentityVerificationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Run Jibit national-code/mobile matching inquiry", "tags": ["Admin - Identity"] } }, "/api/v1/admin/identity/verifications/{id}/approve": { "patch": { "description": "Staff-only endpoint that approves a pending host verification request.", "operationId": "AdminIdentityController_approve", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Identity verification request identifier.", "schema": { "format": "uuid", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "type": "string" } } ], "responses": { "200": { "description": "Verification request approved and user identity status synced by database trigger.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IdentityVerificationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Approve identity verification", "tags": ["Admin - Identity"] } }, "/api/v1/admin/identity/verifications/{id}/reject": { "patch": { "description": "Staff-only endpoint that rejects a pending host verification request with a structured and/or free-text reason.", "operationId": "AdminIdentityController_reject", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Identity verification request identifier.", "schema": { "format": "uuid", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "type": "string" } } ], "requestBody": { "required": true, "description": "Rejection reason. At least one of reasonId or rejectionReason is required.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RejectIdentityVerificationDto" } } } }, "responses": { "200": { "description": "Verification request rejected and user identity status synced by database trigger.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IdentityVerificationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Reject identity verification", "tags": ["Admin - Identity"] } }, "/api/v1/notifications/me": { "get": { "description": "Paginated in-app notification feed for the authenticated user.", "operationId": "NotificationsController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt or -createdAt.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[referenceType]", "required": false, "in": "query", "description": "Filter by polymorphic reference type (e.g. booking, event).", "schema": { "example": "booking", "type": "string" } } ], "responses": { "200": { "description": "Paginated in-app notification feed for the current user.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/NotificationResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List my in-app notifications", "tags": ["Notifications"] } }, "/api/v1/notifications/me/unread-count": { "get": { "description": "Returns how many in-app notifications the current user has not read yet.", "operationId": "NotificationsController_unreadCount", "parameters": [], "responses": { "200": { "description": "Unread in-app notification count for the current user.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NotificationUnreadCountResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get unread in-app notification count", "tags": ["Notifications"] } }, "/api/v1/notifications/{id}/read": { "patch": { "description": "Sets read_at for the current user on the given in-app notification.", "operationId": "NotificationsController_markAsRead", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "In-app notification id (same as parent notification id).", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "The notification after marking it as read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NotificationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Mark an in-app notification as read", "tags": ["Notifications"] } }, "/api/v1/notifications/push/vapid-public-key": { "get": { "description": "Returns the server VAPID public key used by the PWA service worker to subscribe via PushManager.", "operationId": "NotificationsController_vapidPublicKey", "parameters": [], "responses": { "200": { "description": "VAPID public key and whether Web Push is enabled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PushVapidPublicKeyResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get VAPID public key for Web Push subscription", "tags": ["Notifications"] } }, "/api/v1/notifications/push/deliveries/receipt": { "post": { "description": "Called by the service worker after a push is displayed or opened, using identifiers embedded in the push payload.", "operationId": "NotificationsController_recordPushReceipt", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PushDeliveryReceiptDto" } } } }, "responses": { "201": { "description": "Updated receipt timestamps for the targeted push delivery row.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PushDeliveryReceiptResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Record a browser Web Push receipt event", "tags": ["Notifications"] } }, "/api/v1/notifications/push/subscribe": { "post": { "description": "Stores or updates the PushSubscription keys for the authenticated user device. Rate-limited per user via PUSH_SUBSCRIBE_RATE_LIMIT / PUSH_SUBSCRIBE_RATE_WINDOW_SECONDS.", "operationId": "NotificationsController_subscribePush", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PushSubscribeDto" } } } }, "responses": { "201": { "description": "The stored push subscription.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PushSubscriptionResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "429": { "description": "Too many subscription requests — try again later." }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Register a browser Web Push subscription", "tags": ["Notifications"] }, "delete": { "description": "Called on logout to stop push delivery to all user devices.", "operationId": "NotificationsController_unsubscribeAllPush", "parameters": [], "responses": { "200": { "description": "All subscriptions removed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeletedCountResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "429": { "description": "Too many subscription requests — try again later." }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Remove all Web Push subscriptions for current user", "tags": ["Notifications"] } }, "/api/v1/notifications/push/subscribe/{id}": { "delete": { "description": "Deletes a single push subscription owned by the current user.", "operationId": "NotificationsController_unsubscribePush", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Subscription removed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeletedResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "429": { "description": "Too many subscription requests — try again later." }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Remove a browser Web Push subscription", "tags": ["Notifications"] } }, "/api/v1/admin/sms-messages": { "get": { "description": "Staff-only paginated view of SMS delivery status for troubleshooting.", "operationId": "AdminSmsMessagesController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt or -createdAt.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[mobile]", "required": false, "in": "query", "description": "Partial mobile match on the SMS destination number.", "schema": { "example": "98915", "type": "string" } }, { "name": "filters[userId]", "required": false, "in": "query", "description": "Filter by recipient user identifier.", "schema": { "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "type": "string" } }, { "name": "filters[status]", "required": false, "in": "query", "description": "Filter by SMS status: pending, sent, delivered, or failed.", "schema": { "example": "pending", "type": "string" } } ], "responses": { "200": { "description": "Paginated SMS delivery records for admin troubleshooting.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/SmsMessageResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List SMS delivery records", "tags": ["Admin - Notifications"] } }, "/api/v1/admin/notification-rules": { "get": { "operationId": "AdminNotificationRulesController_list", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/NotificationRuleResponseDto" } } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List admin-managed notification rules", "tags": ["Admin - Notification Rules"] } }, "/api/v1/admin/notification-rules/{eventKey}": { "patch": { "operationId": "AdminNotificationRulesController_update", "parameters": [ { "name": "eventKey", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateNotificationRuleDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NotificationRuleResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Update delivery and templates for a notification event", "tags": ["Admin - Notification Rules"] } }, "/api/v1/admin/manual-notifications/request-otp": { "post": { "description": "Sends a confirmation OTP to the authenticated admin mobile. Required before manual SMS/both sends to two or more recipients.", "operationId": "AdminManualNotificationsController_requestBulkSmsOtp", "parameters": [], "responses": { "201": { "description": "OTP accepted for delivery.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminBulkSmsOtpResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Request OTP before sending a group SMS", "tags": ["Admin - Notifications"] } }, "/api/v1/admin/manual-notifications": { "post": { "description": "Staff-only send for one or more existing users, resolved by user id and/or mobile. Channel may be in_app (also Web Push), sms, or both. Group SMS/both sends require a prior OTP via POST /request-otp.", "operationId": "AdminManualNotificationsController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminSendManualNotificationDto" } } } }, "responses": { "201": { "description": "Notification rows created for each resolved recipient.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminManualNotificationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Send a manual notification to selected users", "tags": ["Admin - Notifications"] } }, "/api/v1/admin/in-app-notifications": { "get": { "description": "Staff-only paginated view of in-app inbox rows and whether the recipient has read them.", "operationId": "AdminInAppNotificationsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt or -createdAt.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[readStatus]", "required": false, "in": "query", "description": "Filter by read state: read or unread.", "schema": { "example": "unread", "type": "string" } }, { "name": "filters[category]", "required": false, "in": "query", "description": "Filter by notification category.", "schema": { "example": "admin_manual", "type": "string" } }, { "name": "filters[mobile]", "required": false, "in": "query", "description": "Partial mobile match on the recipient user.", "schema": { "example": "98915", "type": "string" } }, { "name": "filters[userId]", "required": false, "in": "query", "description": "Filter by recipient user identifier.", "schema": { "example": "c3c921d5-4418-42ed-bf39-0f04b1d5123b", "type": "string" } } ], "responses": { "200": { "description": "Paginated in-app inbox rows for admin troubleshooting.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminInAppNotificationResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List in-app notifications", "tags": ["Admin - Notifications"] } }, "/api/v1/admin/push-deliveries": { "get": { "description": "Staff-only paginated troubleshooting view of Web Push provider handoff plus browser display/open receipts.", "operationId": "AdminPushDeliveriesController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt or -createdAt.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[mobile]", "required": false, "in": "query", "description": "Partial mobile match on the recipient user.", "schema": { "example": "98915", "type": "string" } }, { "name": "filters[notificationId]", "required": false, "in": "query", "description": "Filter by parent notification identifier.", "schema": { "example": "80e4a3ce-4544-4534-b712-36553261528a", "type": "string" } }, { "name": "filters[userId]", "required": false, "in": "query", "description": "Filter by recipient user identifier.", "schema": { "example": "c3c921d5-4418-42ed-bf39-0f04b1d5123b", "type": "string" } }, { "name": "filters[status]", "required": false, "in": "query", "description": "Filter by push provider handoff status: sent or failed.", "schema": { "example": "failed", "type": "string" } } ], "responses": { "200": { "description": "Paginated Web Push delivery rows for admin troubleshooting.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/PushDeliveryResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List Web Push delivery records", "tags": ["Admin - Notifications"] } }, "/api/v1/event-categories": { "get": { "operationId": "EventCategoriesController_list", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/EventCategoryResponseDto" } } } } } }, "summary": "Active event categories (flat list, unpaginated)", "tags": ["Event Categories"] } }, "/api/v1/event-categories/{slug}": { "get": { "operationId": "EventCategoriesController_findBySlug", "parameters": [ { "name": "slug", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventCategoryResponseDto" } } } } }, "summary": "Single active category by slug", "tags": ["Event Categories"] } }, "/api/v1/admin/event-categories/flat": { "get": { "operationId": "AdminEventCategoriesController_listFlat", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/EventCategorySummaryResponseDto" } } } } } }, "security": [ { "bearer": [] } ], "summary": "Flat list of all categories for admin filters", "tags": ["Admin - Event Categories"] } }, "/api/v1/admin/event-categories": { "get": { "operationId": "AdminEventCategoriesController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/EventCategoryResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated category management list", "tags": ["Admin - Event Categories"] }, "post": { "operationId": "AdminEventCategoriesController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateEventCategoryDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventCategoryResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create event category", "tags": ["Admin - Event Categories"] } }, "/api/v1/admin/event-categories/{id}": { "patch": { "operationId": "AdminEventCategoriesController_update", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "number" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateEventCategoryDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventCategoryResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Update event category", "tags": ["Admin - Event Categories"] }, "delete": { "operationId": "AdminEventCategoriesController_remove", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "number" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Soft-delete event category", "tags": ["Admin - Event Categories"] } }, "/api/v1/geography/provinces": { "get": { "operationId": "GeographyController_listProvinces", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ProvinceResponseDto" } } } } } }, "summary": "List all provinces (unpaginated)", "tags": ["Geography"] } }, "/api/v1/geography/cities": { "get": { "operationId": "GeographyController_listAllCities", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CityResponseDto" } } } } } }, "summary": "List all cities (unpaginated, flat)", "tags": ["Geography"] } }, "/api/v1/geography/provinces/{provinceId}/cities": { "get": { "operationId": "GeographyController_listCities", "parameters": [ { "name": "provinceId", "required": true, "in": "path", "schema": { "type": "number" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CityResponseDto" } } } } } }, "summary": "List cities in a province (unpaginated)", "tags": ["Geography"] } }, "/api/v1/geography/cities/{slug}": { "get": { "operationId": "GeographyController_findCityBySlug", "parameters": [ { "name": "slug", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CityDetailResponseDto" } } } } }, "summary": "Single city by slug (SEO city landing pages)", "tags": ["Geography"] } }, "/api/v1/my-events": { "get": { "description": "Guest bookings are always available. Hosted events are returned only when the current user has a verified hosting identity.", "operationId": "MyEventsListController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "role", "required": false, "in": "query", "schema": { "default": "guest", "type": "string", "enum": ["guest", "host"] } }, { "name": "status", "required": false, "in": "query", "schema": { "default": "upcoming", "type": "string", "enum": ["upcoming", "held", "cancelled", "pending_review", "published", "rejected"] } } ], "responses": { "200": { "description": "One paginated lifecycle category for the current user.", "content": { "application/json": { "schema": { "type": "object", "required": ["role", "status", "canHost", "availableRoles", "items", "response"], "properties": { "role": { "type": "string", "enum": ["guest", "host"] }, "status": { "type": "string", "enum": ["upcoming", "held", "cancelled", "pending_review", "published", "rejected"] }, "canHost": { "type": "boolean" }, "availableRoles": { "type": "array", "items": { "type": "string", "enum": ["guest", "host"] } }, "items": { "type": "array", "items": { "oneOf": [ { "$ref": "#/components/schemas/MyEventsHostItemDto" }, { "$ref": "#/components/schemas/MyEventsGuestItemDto" } ] } }, "response": { "$ref": "#/components/schemas/PaginationMetaDto" } } } } } } }, "security": [ { "bearer": [] } ], "summary": "List the current user’s guest or hosted events by lifecycle", "tags": ["My Events"] } }, "/api/v1/events/{eventId}/revisions": { "post": { "operationId": "EventRevisionsController_submit", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubmitEventRevisionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventRevisionResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Submit a pending revision for a published/full event (admin must re-approve before changes go live)", "tags": ["My Events"] } }, "/api/v1/events/{eventId}/revisions/pending": { "get": { "operationId": "EventRevisionsController_getPending", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventRevisionResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Get the current pending revision for own live event", "tags": ["My Events"] }, "delete": { "operationId": "EventRevisionsController_withdraw", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "example": { "success": true } } } } } }, "security": [ { "bearer": [] } ], "summary": "Withdraw the pending revision for own live event", "tags": ["My Events"] } }, "/api/v1/events": { "post": { "operationId": "MyEventsController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateEventDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create a draft event (verified host)", "tags": ["My Events"] }, "get": { "operationId": "EventsController_discovery", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Discovery filters use filters[key]=value query params", "schema": { "additionalProperties": { "type": "string" }, "example": { "categoryId": "1", "isFree": "true" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/DiscoveryEventResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "summary": "Public event discovery list", "tags": ["Events"] } }, "/api/v1/events/mine": { "get": { "operationId": "MyEventsController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Discovery filters use filters[key]=value query params", "schema": { "additionalProperties": { "type": "string" }, "example": { "categoryId": "1", "isFree": "true" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/EventResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Organizer's own events", "tags": ["My Events"] } }, "/api/v1/events/{id}/for-edit": { "get": { "description": "Owner or admin. Returns unmasked address and works for draft/non-listed events. Public GET /events/:id remains the guest-facing detail.", "operationId": "MyEventsController_findOneForEdit", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Full event payload for edit/clone forms", "tags": ["My Events"] } }, "/api/v1/events/{id}/owner-insights": { "get": { "operationId": "MyEventsController_ownerInsights", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OwnerEventInsightsResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Get engagement insights for own event", "tags": ["My Events"] } }, "/api/v1/events/{id}/management-bootstrap": { "get": { "operationId": "MyEventsController_managementBootstrap", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventManagementBootstrapResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Get all initial data for managing an owned event", "tags": ["My Events"] } }, "/api/v1/events/{id}/attendees": { "get": { "operationId": "MyEventsController_attendees", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "search", "required": false, "in": "query", "description": "Search by guest first name, last name, full name, or booking code. Mobile is never searchable for hosts.", "schema": { "maxLength": 100, "type": "string" } }, { "name": "attendance", "required": false, "in": "query", "schema": { "$ref": "#/components/schemas/EventAttendeeFilter" } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/EventAttendeeResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Search and list registrations for own event attendance desk", "tags": ["My Events"] } }, "/api/v1/events/{id}/reserved-capacity": { "patch": { "description": "Absolute reservedCapacity. Occupancy is bookedCount + reservedCapacity and cannot exceed capacity. Pending payments already occupy seats.", "operationId": "MyEventsController_updateReservedCapacity", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateEventReservedCapacityDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Set off-platform reserved seat count for own event", "tags": ["My Events"] } }, "/api/v1/events/{id}": { "patch": { "operationId": "MyEventsController_update", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateEventDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Update own event", "tags": ["My Events"] }, "delete": { "operationId": "MyEventsController_softDelete", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Soft-delete own event when it has no active bookings (booked_count = 0)", "tags": ["My Events"] }, "get": { "operationId": "EventsController_findOne", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "summary": "Single event detail", "tags": ["Events"] } }, "/api/v1/events/{id}/publish": { "patch": { "operationId": "MyEventsController_publish", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Publish draft event", "tags": ["My Events"] } }, "/api/v1/events/{id}/cancel": { "patch": { "operationId": "MyEventsController_cancel", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Cancel published/full event", "tags": ["My Events"] } }, "/api/v1/events/{id}/complete": { "patch": { "operationId": "MyEventsController_complete", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Mark event completed (creates group chat)", "tags": ["My Events"] } }, "/api/v1/events/{id}/clone": { "post": { "operationId": "MyEventsController_clone", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Clone event to new draft", "tags": ["My Events"] } }, "/api/v1/events/bookmarks": { "get": { "operationId": "EventBookmarksController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/EventResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List events bookmarked by current user", "tags": ["Event Bookmarks"] } }, "/api/v1/events/bookmarks/status": { "post": { "operationId": "EventBookmarksController_batchStatus", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookmarkStatusDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookmarkBatchStatusResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get bookmark status for a batch of events", "tags": ["Event Bookmarks"] } }, "/api/v1/events/{id}/bookmark": { "get": { "operationId": "EventBookmarksController_status", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookmarkStateResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get bookmark status for current user", "tags": ["Event Bookmarks"] }, "post": { "operationId": "EventBookmarksController_bookmark", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookmarkStateResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Bookmark an event", "tags": ["Event Bookmarks"] }, "delete": { "operationId": "EventBookmarksController_remove", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookmarkStateResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Remove an event bookmark", "tags": ["Event Bookmarks"] } }, "/api/v1/events/by-slug/{slug}/bootstrap": { "get": { "operationId": "EventsController_bootstrapBySlug", "parameters": [ { "name": "slug", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventLandingBootstrapResponseDto" } } } } }, "summary": "Public event landing bootstrap payload", "tags": ["Events"] } }, "/api/v1/events/by-slug/{slug}": { "get": { "operationId": "EventsController_findBySlug", "parameters": [ { "name": "slug", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "summary": "Single discoverable event by slug (SEO event landing page)", "tags": ["Events"] } }, "/api/v1/events/{id}/viewer-state": { "get": { "operationId": "EventsController_viewerState", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventViewerStateResponseDto" } } } } }, "summary": "Authenticated viewer state for an event landing", "tags": ["Events"] } }, "/api/v1/admin/events/{eventId}/revisions/pending": { "get": { "operationId": "AdminEventRevisionsController_getPending", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventRevisionResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Get pending revision for any live event", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{eventId}/revisions/{revisionId}/approve": { "patch": { "operationId": "AdminEventRevisionsController_approve", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "revisionId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Approve a pending revision and apply it to the live event immediately (no host confirm)", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{eventId}/revisions/{revisionId}/reject": { "patch": { "operationId": "AdminEventRevisionsController_reject", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "revisionId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RejectEventRevisionDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventRevisionResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Reject a pending revision; live event content stays unchanged", "tags": ["Admin - Events"] } }, "/api/v1/admin/events": { "get": { "operationId": "AdminEventsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Discovery filters use filters[key]=value query params", "schema": { "additionalProperties": { "type": "string" }, "example": { "categoryId": "1", "isFree": "true" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminEventResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated admin events list", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}": { "get": { "operationId": "AdminEventsController_findOne", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminEventDetailResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Admin event detail including bookmark count", "tags": ["Admin - Events"] }, "patch": { "operationId": "AdminEventsController_update", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateEventDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Update any event as admin (same field locks as host edit)", "tags": ["Admin - Events"] }, "delete": { "operationId": "AdminEventsController_softDelete", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Soft-delete any event as admin when it has no active bookings (booked_count = 0)", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}/reserved-capacity": { "patch": { "operationId": "AdminEventsController_updateReservedCapacity", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateEventReservedCapacityDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Set off-platform reserved seat count as admin", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}/commission": { "patch": { "description": "Admin-only. commissionPercent: null resets the event to the platform default. Never exposed on the host-facing event-edit endpoint.", "operationId": "AdminEventsController_updateCommission", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateEventCommissionDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Set or clear this event’s platform commission override", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}/insights": { "get": { "operationId": "AdminEventsController_insights", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OwnerEventInsightsResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Get engagement insights for any event as admin", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}/approve": { "patch": { "operationId": "AdminEventsController_approve", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Approve a draft or pending-review event; publishes once the host has also requested publication", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}/publish": { "patch": { "operationId": "AdminEventsController_publish", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Force-publish any organizer’s draft or pending-review event as admin, bypassing the request/approve flow entirely", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}/reject": { "patch": { "operationId": "AdminEventsController_reject", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RejectEventDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Reject a pending-review event with a reason", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}/cancel": { "patch": { "operationId": "AdminEventsController_cancel", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Cancel any organizer’s published/full event as admin", "tags": ["Admin - Events"] } }, "/api/v1/admin/events/{id}/complete": { "patch": { "operationId": "AdminEventsController_complete", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Mark any organizer’s event completed as admin", "tags": ["Admin - Events"] } }, "/api/v1/discovery/cities": { "get": { "operationId": "DiscoveryBootstrapController_listCities", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CityResponseDto" } } } } } }, "summary": "Cached city list for discovery/home", "tags": ["Events"] } }, "/api/v1/discovery/categories": { "get": { "operationId": "DiscoveryBootstrapController_listCategories", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/DiscoveryCategorySummaryDto" } } } } } }, "summary": "Cached active category tree summary for discovery/home", "tags": ["Events"] } }, "/api/v1/discovery/home-feed": { "get": { "operationId": "DiscoveryBootstrapController_getHomeFeed", "parameters": [ { "name": "cityId", "required": false, "in": "query", "description": "Optional city filter for popular + category previews. City strip previews stay nationwide.", "schema": { "type": "number" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HomeFeedResponseDto" } } } } }, "summary": "Home page event sections (popular, category previews, city previews)", "tags": ["Events"] } }, "/api/v1/discovery/bootstrap": { "get": { "deprecated": true, "operationId": "DiscoveryBootstrapController_getBootstrap", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscoveryBootstrapResponseDto" } } } } }, "summary": "Legacy combined reference data for event discovery", "tags": ["Events"] } }, "/api/v1/organizers/me/following": { "get": { "operationId": "OrganizerFollowMeController_getFollowing", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[organizerId]", "required": false, "in": "query", "description": "Filter by organizer identifier.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Paginated list of followed organizers.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/FollowResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get organizers followed by current user", "tags": ["Organizer Follows"] } }, "/api/v1/organizers/me/following/events": { "get": { "operationId": "OrganizerFollowMeController_getFollowedOrganizersEvents", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list of upcoming events from followed organizers.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/FollowResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get upcoming events from followed organizers", "tags": ["Organizer Follows"] } }, "/api/v1/organizers/me/followers": { "get": { "operationId": "OrganizerFollowMeController_getFollowers", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list of followers.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/FollowResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get followers of the current user (as organizer)", "tags": ["Organizer Follows"] } }, "/api/v1/organizers/{organizerId}/follow": { "post": { "operationId": "OrganizerFollowsController_follow", "parameters": [ { "name": "organizerId", "required": true, "in": "path", "description": "Organizer user identifier.", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Successfully followed organizer.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FollowResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Follow an organizer", "tags": ["Organizer Follows"] }, "delete": { "operationId": "OrganizerFollowsController_unfollow", "parameters": [ { "name": "organizerId", "required": true, "in": "path", "description": "Organizer user identifier.", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Successfully unfollowed organizer.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FollowResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Unfollow an organizer", "tags": ["Organizer Follows"] } }, "/api/v1/organizers/{organizerId}/followers": { "get": { "operationId": "OrganizerFollowsController_getFollowers", "parameters": [ { "name": "organizerId", "required": true, "in": "path", "description": "Organizer user identifier.", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list of followers.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/FollowResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "summary": "Get followers of an organizer", "tags": ["Organizer Follows"] } }, "/api/v1/organizers/{organizerId}/is-following": { "get": { "operationId": "OrganizerFollowsController_isFollowing", "parameters": [ { "name": "organizerId", "required": true, "in": "path", "description": "Organizer user identifier.", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Follow status for the authenticated user.", "content": { "application/json": { "schema": { "type": "object", "properties": { "isFollowing": { "type": "boolean", "example": true } } } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Check if current user is following an organizer", "tags": ["Organizer Follows"] } }, "/api/v1/organizers/{organizerId}/follow-settings": { "patch": { "operationId": "OrganizerFollowsController_updateFollowSettings", "parameters": [ { "name": "organizerId", "required": true, "in": "path", "description": "Organizer user identifier.", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateFollowSettingsDto" } } } }, "responses": { "200": { "description": "Updated follow settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FollowResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Update follow notification settings", "tags": ["Organizer Follows"] } }, "/api/v1/events/{eventId}/media": { "get": { "operationId": "EventMediaController_list", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/EventMediaResponseDto" } } } } } }, "summary": "List media for an event (unpaginated)", "tags": ["Event Extras"] }, "post": { "operationId": "EventMediaController_create", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateEventMediaDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventMediaResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Add media to own event (or any event as admin)", "tags": ["Event Extras"] } }, "/api/v1/events/{eventId}/media/{id}": { "patch": { "operationId": "EventMediaController_update", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateEventMediaDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventMediaResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Update event media (owner or admin)", "tags": ["Event Extras"] }, "delete": { "operationId": "EventMediaController_remove", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Remove event media (owner or admin)", "tags": ["Event Extras"] } }, "/api/v1/events/{eventId}/faqs": { "get": { "operationId": "EventFaqsController_list", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/EventFaqResponseDto" } } } } } }, "summary": "List FAQs for an event (unpaginated)", "tags": ["Event Extras"] }, "post": { "operationId": "EventFaqsController_create", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateEventFaqDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventFaqResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Add FAQ to own event (or any event as admin)", "tags": ["Event Extras"] } }, "/api/v1/events/{eventId}/faqs/{id}": { "patch": { "operationId": "EventFaqsController_update", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateEventFaqDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventFaqResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Update event FAQ (owner or admin)", "tags": ["Event Extras"] }, "delete": { "operationId": "EventFaqsController_remove", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Remove event FAQ (owner or admin)", "tags": ["Event Extras"] } }, "/api/v1/organizer-guest-lists/previous-attendees": { "get": { "operationId": "OrganizerGuestListsController_previousAttendees", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PreviousAttendeeResponseDto" } } } } } }, "security": [ { "bearer": [] } ], "summary": "Organizer's deduplicated checked-in attendees", "tags": ["Event Extras"] } }, "/api/v1/organizer-guest-lists": { "get": { "operationId": "OrganizerGuestListsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/OrganizerGuestListResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Organizer's own guest lists (paginated)", "tags": ["Event Extras"] }, "post": { "operationId": "OrganizerGuestListsController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateOrganizerGuestListDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrganizerGuestListResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create a guest list", "tags": ["Event Extras"] } }, "/api/v1/organizer-guest-lists/{id}": { "delete": { "operationId": "OrganizerGuestListsController_removeList", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Soft-delete a whole guest list", "tags": ["Event Extras"] } }, "/api/v1/organizer-guest-lists/{id}/items": { "get": { "description": "Unlike event_faqs/event_media, this is paginated from the start — a guest list can realistically grow into the hundreds of contacts.", "operationId": "OrganizerGuestListsController_listItems", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/GuestListItemResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "A guest list's contacts (paginated)", "tags": ["Event Extras"] }, "post": { "operationId": "OrganizerGuestListsController_addItem", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AddGuestListItemDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestListItemResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Add a contact to a guest list", "tags": ["Event Extras"] } }, "/api/v1/organizer-guest-lists/{id}/items/{itemId}": { "delete": { "operationId": "OrganizerGuestListsController_removeItem", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "itemId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Remove a contact from a guest list", "tags": ["Event Extras"] } }, "/api/v1/events/{eventId}/guest-list-links/invited-guests": { "put": { "description": "Full replace of the invite set from the host previous-attendees pool. An empty array clears the invite set. Does not send SMS while the event is still a draft — guest_list_invitation is dispatched automatically when the event is published. If the event is already published/full and has not been notified yet, dispatch runs immediately.", "operationId": "EventGuestListLinksController_setInvitedGuests", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetEventInvitedGuestsDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Set previous attendees invited to this event", "tags": ["Event Extras"] }, "get": { "operationId": "EventGuestListLinksController_getInvitedGuests", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/InvitedGuestResponseDto" } } } } } }, "security": [ { "bearer": [] } ], "summary": "Guests selected to invite to this event, for its organizer", "tags": ["Event Extras"] } }, "/api/v1/events/{eventId}/guest-list-links/invited-guests/notify": { "post": { "description": "Dispatches guest_list_invitation to every resolved platform user on the current invite list and stamps notified_at. The event must already be published. Use to re-send after the automatic publish-time SMS, or when the invite set was updated after that send.", "operationId": "EventGuestListLinksController_notifyInvitedGuests", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestListNotifyResultDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Send invitation notifications for the event invite set", "tags": ["Event Extras"] } }, "/api/v1/events/{eventId}/guest-list-links": { "get": { "description": "Unpaginated, same documented exception as event_faqs/event_media — an organizer linking dozens of guest lists to one event is not a realistic scenario.", "operationId": "EventGuestListLinksController_list", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/GuestListLinkResponseDto" } } } } } }, "security": [ { "bearer": [] } ], "summary": "Guest lists linked to this event (unpaginated)", "tags": ["Event Extras"] }, "post": { "operationId": "EventGuestListLinksController_link", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkGuestListToEventDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestListLinkResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Attach a guest list to an event (stores link only — call notify to send invitations)", "tags": ["Event Extras"] } }, "/api/v1/events/{eventId}/guest-list-links/{listId}/notify": { "post": { "operationId": "EventGuestListLinksController_notify", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "listId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestListNotifyResultDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Send invitation notifications for a guest list linked to this event", "tags": ["Event Extras"] } }, "/api/v1/events/{eventId}/guest-list-links/{listId}": { "delete": { "operationId": "EventGuestListLinksController_unlink", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "listId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Unlink a guest list from an event", "tags": ["Event Extras"] } }, "/api/v1/admin/guest-lists": { "get": { "description": "Admin visibility into guest lists across all organizers — decided with Ali, overriding the owner-only design this module started with. Filter by organizerId to scope to one organizer.", "operationId": "AdminGuestListsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/OrganizerGuestListResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Every organizer's guest lists (paginated)", "tags": ["Admin - Event Extras"] } }, "/api/v1/admin/guest-lists/{id}/items": { "get": { "operationId": "AdminGuestListsController_listItems", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/GuestListItemResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Any guest list's contacts, admin view (paginated)", "tags": ["Admin - Event Extras"] } }, "/api/v1/admin/events/{eventId}/guest-list-links": { "get": { "description": "Same unpaginated reasoning as the organizer-facing version — admin visibility override, decided with Ali.", "operationId": "AdminEventGuestListLinksController_list", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/GuestListLinkResponseDto" } } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Guest lists linked to any event, admin view (unpaginated)", "tags": ["Admin - Event Extras"] } }, "/api/v1/admin/events/{eventId}/invited-guests": { "get": { "description": "Admin-facing invite set for one event: platform users linked via a host-owned guest list. Empty array if none set yet.", "operationId": "AdminEventInvitedGuestsController_get", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/InvitedGuestResponseDto" } } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Guests selected to invite to this event (unpaginated)", "tags": ["Admin - Event Extras"] }, "put": { "description": "userIds must all be existing platform users. Full replace, not incremental. An empty array clears the invite set. Does not send notifications — use POST .../notify.", "operationId": "AdminEventInvitedGuestsController_set", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetEventInvitedGuestsDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/InvitedGuestResponseDto" } } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Replace the invite set for this event", "tags": ["Admin - Event Extras"] } }, "/api/v1/admin/events/{eventId}/invited-guests/notify": { "post": { "description": "Dispatches guest_list_invitation notifications to every resolved platform user on the invite list and sets notified_at on the link.", "operationId": "AdminEventInvitedGuestsController_notify", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestListNotifyResultDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Send invitation notifications to the event invite set", "tags": ["Admin - Event Extras"] } }, "/api/v1/bookings/{bookingId}/payments": { "post": { "operationId": "PaymentsController_create", "parameters": [ { "name": "bookingId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePaymentDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePaymentResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create payment attempt for a booking", "tags": ["Financial"] } }, "/api/v1/bookings/{bookingId}/discount-preview": { "post": { "operationId": "PaymentsController_previewDiscount", "parameters": [ { "name": "bookingId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PreviewDiscountDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscountPreviewResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Preview a discount code against a booking", "tags": ["Financial"] } }, "/api/v1/payments/me": { "get": { "operationId": "PaymentsController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Current user's payments", "tags": ["Financial"] } }, "/api/v1/payments/{id}/receipt": { "get": { "operationId": "PaymentsController_receipt", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentReceiptResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Payment receipt (owner only)", "tags": ["Financial"] } }, "/api/v1/admin/payments": { "get": { "operationId": "AdminPaymentsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminPaymentResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated admin payment list, all users", "tags": ["Financial"] } }, "/api/v1/admin/wallet-deposits": { "get": { "operationId": "AdminWalletDepositsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminWalletDepositResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated admin wallet deposit list (gateway sessions)", "tags": ["Financial"] } }, "/api/v1/users/me/wallet": { "get": { "operationId": "WalletController_getMyWallet", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WalletResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Current user's wallet", "tags": ["Financial"] } }, "/api/v1/users/me/wallet/transactions": { "get": { "operationId": "WalletController_listMyTransactions", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/WalletTransactionResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Current user's wallet transactions", "tags": ["Financial"] } }, "/api/v1/users/me/wallet/deposits": { "post": { "operationId": "WalletController_createDeposit", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWalletDepositDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWalletDepositResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Start a wallet top-up via payment gateway", "tags": ["Financial"] } }, "/api/v1/users/me/wallet/deposits/{id}": { "get": { "operationId": "WalletController_getMyDeposit", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WalletDepositResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Get one of the current user wallet deposits", "tags": ["Financial"] } }, "/api/v1/wallet/deposits/{id}/gateway-return": { "get": { "operationId": "WalletController_depositGatewayReturnGet", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "302": { "description": "Redirects to the frontend payment result page.", "headers": { "Location": { "description": "Allowlisted frontend payment result URL.", "schema": { "type": "string", "format": "uri" } } } } }, "summary": "Jibit browser return (GET) — verifies payment server-side then redirects to the frontend result page", "tags": ["Financial"] }, "post": { "operationId": "WalletController_depositGatewayReturnPost", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "302": { "description": "Redirects to the frontend payment result page.", "headers": { "Location": { "description": "Allowlisted frontend payment result URL.", "schema": { "type": "string", "format": "uri" } } } } }, "summary": "Jibit browser return (form POST from PPG) — verifies payment server-side then redirects to the frontend result page", "tags": ["Financial"] } }, "/api/v1/wallet/deposits/{id}/gateway-callback": { "patch": { "operationId": "WalletController_depositGatewayCallback", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GatewayCallbackDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WalletDepositResponseDto" } } } } }, "summary": "Console/dev HMAC callback for wallet deposits (disabled when PAYMENT_GATEWAY_PROVIDER=jibit)", "tags": ["Financial"] } }, "/api/v1/bank-accounts": { "post": { "operationId": "BankAccountsController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBankAccountDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BankAccountResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create bank account for withdrawals/settlements", "tags": ["Financial"] } }, "/api/v1/bank-accounts/me": { "get": { "operationId": "BankAccountsController_listMine", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/BankAccountResponseDto" } } } } } }, "security": [ { "bearer": [] } ], "summary": "Current user's bank accounts", "tags": ["Financial"] } }, "/api/v1/admin/bank-accounts": { "get": { "operationId": "AdminBankAccountsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/BankAccountResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated admin bank account verification queue", "tags": ["Financial"] } }, "/api/v1/admin/bank-accounts/{id}/jibit-inquiry": { "patch": { "description": "Saves the Jibit result. Admin cannot approve unless this inquiry returns matched=true.", "operationId": "AdminBankAccountsController_inquireJibit", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BankAccountResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Run Jibit IBAN/national-code/birth-date matching inquiry", "tags": ["Financial"] } }, "/api/v1/admin/bank-accounts/{id}/approve": { "patch": { "operationId": "AdminBankAccountsController_approve", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BankAccountResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Approve bank account after a successful Jibit match", "tags": ["Financial"] } }, "/api/v1/admin/bank-accounts/{id}/reject": { "patch": { "operationId": "AdminBankAccountsController_reject", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RejectBankAccountDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BankAccountResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Reject bank account with a reason", "tags": ["Financial"] } }, "/api/v1/withdrawal-requests": { "post": { "operationId": "WithdrawalRequestsController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWithdrawalRequestDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WithdrawalResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create withdrawal request from wallet", "tags": ["Financial"] } }, "/api/v1/withdrawal-requests/me": { "get": { "operationId": "WithdrawalRequestsController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/WithdrawalResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Current user's withdrawal requests", "tags": ["Financial"] } }, "/api/v1/admin/withdrawal-requests": { "get": { "operationId": "AdminWithdrawalRequestsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/WithdrawalResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated admin withdrawal queue", "tags": ["Financial"] } }, "/api/v1/admin/withdrawal-requests/{id}/process": { "patch": { "operationId": "AdminWithdrawalRequestsController_process", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WithdrawalResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Mark withdrawal request as processing", "tags": ["Financial"] } }, "/api/v1/admin/withdrawal-requests/{id}/complete": { "patch": { "operationId": "AdminWithdrawalRequestsController_complete", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompleteManualPayoutDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WithdrawalResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Complete withdrawal with manual payment evidence", "tags": ["Financial"] } }, "/api/v1/admin/withdrawal-requests/{id}/reject": { "patch": { "operationId": "AdminWithdrawalRequestsController_reject", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RejectWithdrawalRequestDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WithdrawalResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Reject withdrawal request and reverse hold", "tags": ["Financial"] } }, "/api/v1/organizers/me/earnings": { "get": { "operationId": "OrganizerEarningsController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/OrganizerEarningResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Current organizer's earnings ledger", "tags": ["Financial"] } }, "/api/v1/settlements/me": { "get": { "operationId": "OrganizerEarningsController_listSettlementsMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/SettlementResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Current organizer's settlement batches", "tags": ["Financial"] } }, "/api/v1/admin/settlements": { "post": { "operationId": "AdminSettlementsController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateSettlementDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettlementResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create organizer settlement batch", "tags": ["Financial"] }, "get": { "operationId": "AdminSettlementsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/SettlementResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated settlement batches", "tags": ["Financial"] } }, "/api/v1/admin/settlements/{id}": { "get": { "description": "Items are inlined (not a separately paginated sub-resource) — a batch is capped by construction to one organizer's weekly earnings, bounded the same way event_faqs/event_media are, just for a business-rule reason instead of a UI one.", "operationId": "AdminSettlementsController_findOne", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettlementResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Settlement batch detail, with items inlined", "tags": ["Financial"] } }, "/api/v1/admin/settlements/{id}/process": { "patch": { "operationId": "AdminSettlementsController_process", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettlementResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Mark settlement batch as processing", "tags": ["Financial"] } }, "/api/v1/admin/settlements/{id}/complete": { "patch": { "operationId": "AdminSettlementsController_complete", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompleteManualPayoutDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettlementResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Complete settlement with manual payment evidence", "tags": ["Financial"] } }, "/api/v1/admin/settlements/{id}/fail": { "patch": { "description": "Releases every earning the batch had claimed back to available (settlement_item_id cleared) so a future batch can pick them up.", "operationId": "AdminSettlementsController_fail", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FailSettlementDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettlementResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Mark settlement batch as failed and release its earnings", "tags": ["Financial"] } }, "/api/v1/events/{eventId}/discount-codes": { "post": { "operationId": "DiscountCodesController_create", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateDiscountCodesDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkCreateDiscountCodesResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Bulk-create discount codes for an event (host or admin)", "tags": ["Discount Codes"] }, "get": { "operationId": "DiscountCodesController_list", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/DiscountCodeResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "An event's discount codes (paginated)", "tags": ["Discount Codes"] } }, "/api/v1/events/{eventId}/discount-codes/management-bootstrap": { "get": { "operationId": "DiscountCodesController_managementBootstrap", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscountManagementBootstrapResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "All initial data for an event's discount management tab", "tags": ["Discount Codes"] } }, "/api/v1/events/{eventId}/discount-codes/report": { "get": { "operationId": "DiscountCodesController_report", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscountReportSummaryDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Summary of an event's discount usage", "tags": ["Discount Codes"] } }, "/api/v1/events/{eventId}/discount-codes/redemptions": { "get": { "operationId": "DiscountCodesController_redemptions", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/DiscountRedemptionResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "An event's discount redemptions (paginated)", "tags": ["Discount Codes"] } }, "/api/v1/discount-codes/{id}": { "patch": { "operationId": "DiscountCodesController_update", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateDiscountCodeDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscountCodeResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Enable/disable a discount code (host or admin)", "tags": ["Discount Codes"] }, "delete": { "operationId": "DiscountCodesController_remove", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Delete an unused discount code (host or admin)", "tags": ["Discount Codes"] } }, "/api/v1/events/{eventId}/bookings": { "post": { "operationId": "BookingsController_create", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBookingDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create a booking for an event", "tags": ["Bookings"] } }, "/api/v1/bookings/me": { "get": { "operationId": "BookingsController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/BookingResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Current user's booking history", "tags": ["Bookings"] } }, "/api/v1/bookings/{id}": { "get": { "operationId": "BookingsController_findOne", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Single booking detail (owner only)", "tags": ["Bookings"] } }, "/api/v1/bookings/{id}/cancel": { "patch": { "description": "Organizers cannot cancel an individual booking (business-rules.md: \"Host cancels single booking: NOT allowed\") — cancelling the whole event (PATCH /events/:id/cancel) is the only organizer-initiated path.", "operationId": "BookingsController_cancel", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CancelBookingDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Cancel a booking (guest only)", "tags": ["Bookings"] } }, "/api/v1/bookings/{id}/check-in": { "patch": { "operationId": "BookingsController_checkIn", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Check in a guest (event organizer only)", "tags": ["Bookings"] } }, "/api/v1/events/{eventId}/waitlist": { "post": { "operationId": "WaitlistController_join", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JoinWaitlistDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WaitlistResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Join the waitlist for a full event", "tags": ["Waitlist"] }, "get": { "operationId": "WaitlistController_listForEvent", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 10, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/WaitlistResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Host view of the waiting queue for their own event (used to hand-pick who gets a seat when settings.waitlistAutoOffer is false)", "tags": ["Waitlist"] } }, "/api/v1/waitlist/me": { "get": { "operationId": "WaitlistController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/WaitlistResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Current user's waitlist entries", "tags": ["Waitlist"] } }, "/api/v1/events/{eventId}/waitlist/{entryId}/offer": { "post": { "operationId": "WaitlistController_offerToEntry", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "entryId", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WaitlistResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Host manually offers an opened seat to a specific waitlist entry", "tags": ["Waitlist"] } }, "/api/v1/waitlist/{id}/accept": { "patch": { "operationId": "WaitlistController_accept", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WaitlistAcceptResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Accept a notified waitlist offer", "tags": ["Waitlist"] } }, "/api/v1/admin/bookings": { "get": { "operationId": "AdminBookingsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminBookingResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated admin bookings list", "tags": ["Admin - Bookings"] } }, "/api/v1/admin/bookings/{id}/check-in": { "patch": { "operationId": "AdminBookingsController_checkIn", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Check in a guest for any organizer’s event as admin", "tags": ["Admin - Bookings"] } }, "/api/v1/events/{eventId}/reviews": { "get": { "description": "Public paginated list of published reviews for a single event.", "operationId": "ReviewsController_listByEvent", "parameters": [ { "name": "eventId", "required": true, "in": "path", "description": "Event identifier.", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt, -createdAt, rating, or -rating.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[rating]", "required": false, "in": "query", "description": "Filter by exact star rating (1–5).", "schema": { "example": 5, "type": "number" } } ], "responses": { "200": { "description": "Published reviews for the event.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ReviewResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "List published reviews for an event", "tags": ["Reviews"] }, "post": { "description": "Authenticated guests with a confirmed booking may review once after the event is completed.", "operationId": "ReviewsController_create", "parameters": [ { "name": "eventId", "required": true, "in": "path", "description": "Event identifier.", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateReviewDto" } } } }, "responses": { "201": { "description": "Review created and published.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Create a review for a completed event", "tags": ["Reviews"] } }, "/api/v1/users/{userId}/reviews": { "get": { "description": "Public paginated list of published reviews across all events hosted by the user.", "operationId": "ReviewsController_listByOrganizer", "parameters": [ { "name": "userId", "required": true, "in": "path", "description": "Organizer user identifier.", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt, -createdAt, rating, or -rating.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Published reviews for the organizer.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/OrganizerReviewResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "List published reviews for an organizer", "tags": ["Reviews"] } }, "/api/v1/reviews/{id}": { "patch": { "description": "Guests may edit their rating/body until the organizer has replied.", "operationId": "ReviewsController_update", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Review identifier.", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateReviewDto" } } } }, "responses": { "200": { "description": "Review updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Update own review", "tags": ["Reviews"] } }, "/api/v1/reviews/{id}/host-reply": { "patch": { "description": "Event organizer may post or update a single reply on a review for their event.", "operationId": "ReviewsController_hostReply", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Review identifier.", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostReplyDto" } } } }, "responses": { "200": { "description": "Host reply saved.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Reply to a review as organizer", "tags": ["Reviews"] } }, "/api/v1/admin/reviews": { "get": { "description": "Unlike the public event review list, this includes reviews of every status (published, hidden, deleted) so admins can see what has already been moderated.", "operationId": "AdminReviewsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt, -createdAt, rating, or -rating.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[rating]", "required": false, "in": "query", "description": "Filter by exact star rating (1–5).", "schema": { "type": "number" } }, { "name": "filters[status]", "required": false, "in": "query", "description": "Filter by status: published, hidden, or deleted.", "schema": { "type": "string" } }, { "name": "filters[eventId]", "required": false, "in": "query", "description": "Filter by event id.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Reviews of any status.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminReviewResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Paginated review moderation list", "tags": ["Admin - Reviews"] } }, "/api/v1/admin/reviews/{id}/hide": { "patch": { "description": "Reversible — sets status to hidden. Idempotent if already hidden. Rejects with 409 if the review is deleted.", "operationId": "AdminReviewsController_hide", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Review hidden.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Hide a review from public view", "tags": ["Admin - Reviews"] } }, "/api/v1/admin/reviews/{id}/restore": { "patch": { "description": "Idempotent if already published. Rejects with 409 if the review is deleted — deleted is a terminal state in this workflow.", "operationId": "AdminReviewsController_restore", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Review restored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Restore a hidden review to published", "tags": ["Admin - Reviews"] } }, "/api/v1/admin/reviews/{id}": { "delete": { "description": "Terminal — sets status to deleted and deleted_at. Idempotent if already deleted. No restore endpoint exists for this state by design.", "operationId": "AdminReviewsController_remove", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Review deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Soft-delete a review", "tags": ["Admin - Reviews"] } }, "/api/v1/conversations/me/unread-count": { "get": { "description": "Returns the number of unread messages across all conversations for the authenticated user.", "operationId": "ChatController_unreadCount", "parameters": [], "responses": { "200": { "description": "Unread count.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnreadCountResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get total unread message count", "tags": ["Chat"] } }, "/api/v1/conversations/me": { "get": { "description": "Paginated list of conversations the authenticated user participates in. Closed event groups (closesAt in the past) always sort after open conversations; within each group, default order is most recent message.", "operationId": "ChatController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field within open/closed buckets: lastMessageAt, -lastMessageAt, createdAt, or -createdAt. Closed event groups always appear after open ones.", "schema": { "example": "-lastMessageAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[type]", "required": false, "in": "query", "description": "Filter by conversation type: direct or event_group.", "schema": { "example": "direct", "type": "string" } } ], "responses": { "200": { "description": "Conversations for the current user.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ConversationResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List my conversations", "tags": ["Chat"] } }, "/api/v1/conversations/direct/{otherUserId}": { "post": { "description": "Returns the existing direct conversation between the current user and another user, or lazily creates an empty one without sending a message.", "operationId": "ChatController_ensureDirect", "parameters": [ { "name": "otherUserId", "required": true, "in": "path", "description": "Other participant user identifier.", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Direct conversation.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConversationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get or create a direct conversation", "tags": ["Chat"] } }, "/api/v1/conversations/direct/{otherUserId}/messages": { "post": { "description": "Creates or reuses the single direct conversation between the current user and another user, then sends a message.", "operationId": "ChatController_sendDirect", "parameters": [ { "name": "otherUserId", "required": true, "in": "path", "description": "Recipient user identifier.", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SendMessageDto" } } } }, "responses": { "201": { "description": "Direct message sent.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MessageResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Send a direct message", "tags": ["Chat"] } }, "/api/v1/conversations/{id}": { "get": { "description": "Returns a single conversation the authenticated user participates in.", "operationId": "ChatController_getOne", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Conversation identifier.", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Conversation details.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConversationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get a conversation", "tags": ["Chat"] } }, "/api/v1/conversations/{id}/participants": { "get": { "description": "Paginated member list for a conversation the authenticated user participates in.", "operationId": "ChatController_listParticipants", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Conversation identifier.", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 50, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: joinedAt or -joinedAt.", "schema": { "example": "joinedAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Participants in the conversation.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ConversationParticipantResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List conversation participants", "tags": ["Chat"] } }, "/api/v1/conversations/{id}/read": { "post": { "description": "Updates last_read_at for the current user and returns the new total unread count.", "operationId": "ChatController_markRead", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Conversation identifier.", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MarkConversationReadDto" } } } }, "responses": { "200": { "description": "Conversation marked as read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnreadCountResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Mark conversation as read", "tags": ["Chat"] } }, "/api/v1/conversations/{id}/messages": { "get": { "description": "Paginated message history for a conversation the user participates in. Use at most one of `before`, `after`, or `around` (400 CHAT_MESSAGE_CURSOR_CONFLICT if combined). `around` returns a centered ASC window; continue with separate `before`/`after` requests using olderCursor/newerCursor.", "operationId": "ChatController_listMessages", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Conversation identifier.", "schema": { "format": "uuid", "type": "string" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Number of messages to return.", "schema": { "minimum": 1, "maximum": 100, "default": 50, "example": 20, "type": "number" } }, { "name": "before", "required": false, "in": "query", "description": "Load older messages than this id. Mutually exclusive with after/around.", "schema": { "format": "uuid", "type": "string" } }, { "name": "after", "required": false, "in": "query", "description": "Load newer messages than this id. Mutually exclusive with before/around.", "schema": { "format": "uuid", "type": "string" } }, { "name": "around", "required": false, "in": "query", "description": "Center an ASC window on this message id. Mutually exclusive with before/after.", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Messages in the conversation.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/MessageResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List messages in a conversation", "tags": ["Chat"] }, "post": { "description": "Sends a message in an existing conversation (typically an event group chat).", "operationId": "ChatController_sendGroup", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Conversation identifier.", "schema": { "format": "uuid", "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SendMessageDto" } } } }, "responses": { "201": { "description": "Message sent.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MessageResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Send a message in a conversation", "tags": ["Chat"] } }, "/api/v1/admin/conversations": { "get": { "operationId": "AdminChatController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[createdAt]", "required": false, "in": "query", "schema": { "type": "string" } }, { "name": "filters[search]", "required": false, "in": "query", "schema": { "type": "string" } }, { "name": "filters[userId]", "required": false, "in": "query", "schema": { "format": "uuid", "type": "string" } }, { "name": "filters[eventId]", "required": false, "in": "query", "schema": { "format": "uuid", "type": "string" } }, { "name": "filters[type]", "required": false, "in": "query", "schema": { "enum": ["direct", "event_group"], "type": "string" } } ], "responses": { "200": { "description": "All chat conversations.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminConversationResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List all conversations for read-only admin oversight", "tags": ["Admin - Chat Oversight"] } }, "/api/v1/admin/conversations/{id}": { "get": { "operationId": "AdminChatController_getOne", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminConversationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get a conversation without joining or changing read state", "tags": ["Admin - Chat Oversight"] } }, "/api/v1/admin/conversations/{id}/participants": { "get": { "operationId": "AdminChatController_listParticipants", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Conversation participants.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminChatUserDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List conversation participants for admin oversight", "tags": ["Admin - Chat Oversight"] } }, "/api/v1/admin/conversations/{id}/messages": { "get": { "operationId": "AdminChatController_listMessages", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "format": "uuid", "type": "string" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Number of messages to return.", "schema": { "minimum": 1, "maximum": 100, "default": 50, "type": "number" } }, { "name": "before", "required": false, "in": "query", "description": "Load messages strictly older than this id (DESC page, returned ASC). Mutually exclusive with `after` and `around`.", "schema": { "format": "uuid", "type": "string" } }, { "name": "after", "required": false, "in": "query", "description": "Load messages strictly newer than this id (ASC). Mutually exclusive with `before` and `around`. Use after an `around` jump to scroll toward the live edge.", "schema": { "format": "uuid", "type": "string" } }, { "name": "around", "required": false, "in": "query", "description": "Center a contiguous ASC window on this message id. Mutually exclusive with `before` and `after`. Soft-deleted targets are rejected (400 `CHAT_REPLY_TARGET_INVALID`).", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Visible message history.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminMessageResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Read message history without joining or changing read state", "tags": ["Admin - Chat Oversight"] } }, "/api/v1/contact-messages": { "post": { "description": "Stores a /contact form submission after Arcaptcha verification. No login required.", "operationId": "ContactMessagesController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateContactMessageDto" } } } }, "responses": { "201": { "description": "Contact message stored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactMessageResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "Submit a public contact message", "tags": ["Contact Messages"] } }, "/api/v1/admin/contact-messages": { "get": { "description": "Staff-only paginated inbox of public /contact messages.", "operationId": "AdminContactMessagesController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt, -createdAt, readAt, or -readAt.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[readStatus]", "required": false, "in": "query", "description": "Filter by read state: unread or read.", "schema": { "example": "unread", "type": "string" } } ], "responses": { "200": { "description": "Paginated contact message inbox.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ContactMessageResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List contact form submissions", "tags": ["Admin - Contact Messages"] } }, "/api/v1/admin/contact-messages/{id}": { "get": { "description": "Staff-only detail of a public /contact submission.", "operationId": "AdminContactMessagesController_findOne", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Contact message identifier.", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Contact message.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactMessageResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get one contact message", "tags": ["Admin - Contact Messages"] } }, "/api/v1/admin/contact-messages/{id}/read": { "patch": { "description": "Staff-only. Idempotent if the message is already read.", "operationId": "AdminContactMessagesController_markRead", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Contact message identifier.", "schema": { "format": "uuid", "type": "string" } } ], "responses": { "200": { "description": "Contact message marked read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContactMessageResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Mark contact message as read", "tags": ["Admin - Contact Messages"] } }, "/api/v1/traffic-visits": { "post": { "description": "Fire-and-forget beacon for tagged share links (`?src=instagram|telegram`). Duplicates for the same visitor+page+source are ignored.", "operationId": "TrafficVisitsController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateTrafficVisitDto" } } } }, "responses": { "204": { "description": "Visit recorded or already counted." }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "Record a unique attributed page visit", "tags": ["Traffic Visits"] } }, "/api/v1/user-blocks": { "post": { "description": "Creates a directed block record. Self-block is rejected with 400 before the database CHECK fires.", "operationId": "UserBlocksController_create", "parameters": [], "requestBody": { "required": true, "description": "Identifier of the user to block.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateUserBlockDto" } } } }, "responses": { "201": { "description": "User blocked successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserBlockResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Block another user", "tags": ["User Blocks"] } }, "/api/v1/user-blocks/{blockedId}": { "delete": { "description": "Removes the block record for the authenticated blocker and the given blocked user.", "operationId": "UserBlocksController_unblock", "parameters": [ { "name": "blockedId", "required": true, "in": "path", "description": "Identifier of the user to unblock.", "schema": { "format": "uuid", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "type": "string" } } ], "responses": { "200": { "description": "User unblocked successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserBlockResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Unblock a user", "tags": ["User Blocks"] } }, "/api/v1/user-blocks/me": { "get": { "description": "Paginated list of block records initiated by the authenticated user.", "operationId": "UserBlocksController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt or -createdAt.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[blockedId]", "required": false, "in": "query", "description": "Filter by blocked user identifier.", "schema": { "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "type": "string" } } ], "responses": { "200": { "description": "Paginated list of blocked users.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/UserBlockResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List users I have blocked", "tags": ["User Blocks"] } }, "/api/v1/report-reasons": { "get": { "description": "Returns active structured report reasons for the user report form.", "operationId": "UserReportsController_listReportReasons", "parameters": [], "responses": { "200": { "description": "Active report reasons sorted by display order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ReportReasonResponseDto" } } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "List active report reasons", "tags": ["User Reports"] } }, "/api/v1/user-reports": { "post": { "description": "Creates a pending user report. Self-report is rejected with 400 before the database CHECK fires.", "operationId": "UserReportsController_create", "parameters": [], "requestBody": { "required": true, "description": "Report payload. At least one of reasonId or description is required.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateUserReportDto" } } } }, "responses": { "201": { "description": "User report created with pending status.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserReportResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Report another user", "tags": ["User Reports"] } }, "/api/v1/admin/user-reports": { "get": { "description": "Staff-only paginated queue of user reports across all users.", "operationId": "AdminUserReportsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt, -createdAt, status, or -status.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[reportedId]", "required": false, "in": "query", "description": "Filter by reported user identifier.", "schema": { "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "type": "string" } }, { "name": "filters[reporterId]", "required": false, "in": "query", "description": "Filter by reporting user identifier.", "schema": { "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "type": "string" } }, { "name": "filters[status]", "required": false, "in": "query", "description": "Filter by report status: pending, reviewed, or dismissed.", "schema": { "example": "pending", "type": "string" } } ], "responses": { "200": { "description": "Paginated user report review queue.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserReportResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List user report review queue", "tags": ["Admin - User Reports"] } }, "/api/v1/admin/user-reports/{id}/review": { "patch": { "description": "Staff-only endpoint that marks a pending user report as reviewed.", "operationId": "AdminUserReportsController_review", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "User report identifier.", "schema": { "format": "uuid", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "type": "string" } } ], "responses": { "200": { "description": "Report marked as reviewed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserReportResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Mark user report as reviewed", "tags": ["Admin - User Reports"] } }, "/api/v1/admin/user-reports/{id}/dismiss": { "patch": { "description": "Staff-only endpoint that dismisses a pending user report.", "operationId": "AdminUserReportsController_dismiss", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "User report identifier.", "schema": { "format": "uuid", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "type": "string" } } ], "responses": { "200": { "description": "Report dismissed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserReportResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Dismiss user report", "tags": ["Admin - User Reports"] } }, "/api/v1/admin/user-reports/{id}/block": { "post": { "description": "Staff-only endpoint. While reviewing a pending report, creates (or confirms an already-existing) reporter -> reported block row and marks the report reviewed in one combined, idempotent action. See Flow D in docs/workflows/user-blocking-reporting.md.", "operationId": "AdminUserReportsController_blockOnBehalf", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "User report identifier.", "schema": { "format": "uuid", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "type": "string" } } ], "responses": { "200": { "description": "The block row (created or already existing) and the report, now reviewed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminUserReportBlockResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Block reported user on the reporter's behalf", "tags": ["Admin - User Reports"] } }, "/api/v1/support-tickets": { "post": { "operationId": "SupportTicketsController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateSupportTicketDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SupportTicketResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Create a support ticket", "tags": ["Support Tickets"] } }, "/api/v1/support-tickets/me": { "get": { "operationId": "SupportTicketsController_listMine", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "My support tickets.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/SupportTicketResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List my support tickets", "tags": ["Support Tickets"] } }, "/api/v1/support-tickets/{id}": { "get": { "operationId": "SupportTicketsController_getMine", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SupportTicketResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get my support ticket thread", "tags": ["Support Tickets"] } }, "/api/v1/support-tickets/{id}/replies": { "post": { "operationId": "SupportTicketsController_reply", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReplySupportTicketDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SupportTicketResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Reply to my support ticket", "tags": ["Support Tickets"] } }, "/api/v1/admin/support-tickets": { "get": { "operationId": "AdminSupportTicketsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Support queue.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/SupportTicketResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List the support queue", "tags": ["Admin - Support Tickets"] } }, "/api/v1/admin/support-tickets/{id}": { "get": { "operationId": "AdminSupportTicketsController_get", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SupportTicketResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Get a support ticket thread", "tags": ["Admin - Support Tickets"] } }, "/api/v1/admin/support-tickets/{id}/replies": { "post": { "operationId": "AdminSupportTicketsController_reply", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReplySupportTicketDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SupportTicketResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Reply as support and send the user an SMS", "tags": ["Admin - Support Tickets"] } }, "/api/v1/admin/support-tickets/{id}/status": { "patch": { "operationId": "AdminSupportTicketsController_updateStatus", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateSupportTicketStatusDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SupportTicketResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Change support ticket status", "tags": ["Admin - Support Tickets"] } }, "/api/v1/admin/reports/overview": { "get": { "description": "Staff-only numeric report: user counts, event counts (by status), booking counts (by status), identity verification counts, and financial totals. All-time totals, computed live on every request — see docs/workflows/admin-dashboard-reports.md.", "operationId": "AdminReportsController_getOverview", "parameters": [], "responses": { "200": { "description": "Platform-wide overview KPIs.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminReportsOverviewDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Platform-wide overview KPIs", "tags": ["Admin - Reports"] } }, "/api/v1/admin/users/{id}/wallet/credit": { "post": { "description": "Inserts a deposit wallet_transaction (amount in Toman). Balance is updated only by trg_apply_wallet_transaction — never write wallets.balance from application code.", "operationId": "AdminUserDetailController_creditWallet", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminCreditWalletDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminCreditWalletResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Manually credit a user wallet", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/users/{id}/identity/attest": { "post": { "description": "Staff-only: marks the user verified even if they never submitted KYC. If a pending request exists, it is approved instead. Does not write users.identity_status — trg_sync_user_identity_status handles that.", "operationId": "AdminUserDetailController_attestIdentity", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IdentityVerificationResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Verify host identity without a user submission", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/users/{id}/hosted-events": { "get": { "operationId": "AdminUserDetailController_getHostedEvents", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated hosted events", "content": { "application/json": { "schema": { "type": "object", "properties": { "events": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserHostedEventDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["events", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Events this user has hosted (organizer_id = :id)", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/users/{id}/bookings": { "get": { "operationId": "AdminUserDetailController_getBookings", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated user bookings", "content": { "application/json": { "schema": { "type": "object", "properties": { "bookings": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserBookingDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["bookings", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Bookings this user made as a guest", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/users/{id}/payments": { "get": { "operationId": "AdminUserDetailController_getPayments", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated user payments", "content": { "application/json": { "schema": { "type": "object", "properties": { "payments": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserPaymentDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["payments", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Payments this user made", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/users/{id}/reviews": { "get": { "operationId": "AdminUserDetailController_getReviews", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "type", "required": false, "in": "query", "description": "written (default): authored by this user. received: on events this user organized.", "schema": { "enum": ["written", "received"], "type": "string" } } ], "responses": { "200": { "description": "Paginated user reviews", "content": { "application/json": { "schema": { "type": "object", "properties": { "reviews": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserReviewDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["reviews", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Reviews written by this user or received on their events", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/users/{id}/follows": { "get": { "operationId": "AdminUserDetailController_getFollows", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "type", "required": false, "in": "query", "description": "following (default): organizers this user follows. followers: users following this user.", "schema": { "enum": ["following", "followers"], "type": "string" } } ], "responses": { "200": { "description": "Paginated user follows", "content": { "application/json": { "schema": { "type": "object", "properties": { "follows": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserFollowDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["follows", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Organizers this user follows, or users following this user", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/users/{id}/reports": { "get": { "operationId": "AdminUserDetailController_getReports", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "direction", "required": false, "in": "query", "description": "filed (default): reports this user submitted. received: reports submitted about this user.", "schema": { "enum": ["filed", "received"], "type": "string" } } ], "responses": { "200": { "description": "Paginated user reports", "content": { "application/json": { "schema": { "type": "object", "properties": { "reports": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserReportDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["reports", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "User reports this user filed, or reports filed about them", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/users/{id}/blocks": { "get": { "operationId": "AdminUserDetailController_getBlocks", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "direction", "required": false, "in": "query", "description": "blocking (default): users this user blocked. blockedBy: users who blocked this user.", "schema": { "enum": ["blocking", "blockedBy"], "type": "string" } } ], "responses": { "200": { "description": "Paginated user blocks", "content": { "application/json": { "schema": { "type": "object", "properties": { "blocks": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserBlockDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["blocks", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Users this user has blocked, or users who blocked this user", "tags": ["Admin - User Detail"] } }, "/api/v1/admin/audit-logs": { "get": { "description": "Staff-only paginated log of mutating requests and explicitly audited sensitive reads on admin-only routes: who acted, on which route, and the resulting status code. Request bodies are not captured.", "operationId": "AdminAuditLogsController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page number.", "schema": { "minimum": 1, "default": 1, "example": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Rows per page, maximum 100.", "schema": { "minimum": 1, "maximum": 100, "default": 20, "example": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field: createdAt or -createdAt.", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } }, { "name": "filters[path]", "required": false, "in": "query", "description": "Filter by route path fragment.", "schema": { "type": "string" } }, { "name": "filters[method]", "required": false, "in": "query", "description": "Filter by HTTP method (POST, PATCH, PUT, DELETE).", "schema": { "example": "PATCH", "type": "string" } }, { "name": "filters[adminUserId]", "required": false, "in": "query", "description": "Filter by admin user id.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Paginated admin audit log.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AdminAuditLogResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Admin role required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "404": { "description": "Not Found - requested resource does not exist or is not visible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "409": { "description": "Conflict - request violates a business rule or uniqueness constraint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "List admin action audit trail", "tags": ["Admin - Audit Logs"] } }, "/api/v1/uploads": { "post": { "description": "Accepts a multipart image (JPEG, PNG, or WebP) and returns a public URL. When UPLOAD_FORWARD_URL is set, the file is stored on the VPS; otherwise on UPLOAD_DIR.", "operationId": "UploadsController_upload", "parameters": [], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "string", "format": "binary" } } } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadFileResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Upload an image file", "tags": ["Uploads"] } }, "/api/v1/uploads/identity": { "post": { "operationId": "UploadsController_uploadIdentity", "parameters": [], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "string", "format": "binary" } } } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadFileResponseDto" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Upload a private identity document image", "tags": ["Uploads"] } }, "/api/v1/uploads/identity/{ownerId}/{filename}": { "get": { "operationId": "UploadsController_readIdentity", "parameters": [ { "name": "ownerId", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "filename", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Private identity image bytes.", "content": { "image/jpeg": { "schema": { "type": "string", "format": "binary" } }, "image/png": { "schema": { "type": "string", "format": "binary" } }, "image/webp": { "schema": { "type": "string", "format": "binary" } } } } }, "security": [ { "JWT-auth": [] } ], "summary": "Read an authorized private identity document", "tags": ["Uploads"] } }, "/api/v1/uploads/internal": { "post": { "description": "Used by local/dev Nest to store files on the VPS. Requires X-Ghabilee-Upload-Secret.", "operationId": "UploadsController_uploadInternal", "parameters": [ { "name": "x-ghabilee-upload-scope", "required": true, "in": "header", "schema": { "type": "string" } }, { "name": "x-ghabilee-upload-owner", "required": true, "in": "header", "schema": { "type": "string" } }, { "name": "x-ghabilee-upload-secret", "in": "header", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "string", "format": "binary" } } } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadFileResponseDto" } } } }, "400": { "description": "Validation error - request body, parameters, or query string are invalid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "401": { "description": "Unauthorized - invalid or missing JWT token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "403": { "description": "Forbidden - Insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "Internal upload from trusted app instances", "tags": ["Uploads"] } }, "/api/v1/blog-articles": { "get": { "operationId": "BlogArticlesController_list", "parameters": [ { "name": "categorySlug", "required": false, "in": "query", "schema": { "type": "string" } }, { "name": "citySlug", "required": false, "in": "query", "schema": { "type": "string" } }, { "name": "eventCategorySlug", "required": false, "in": "query", "schema": { "type": "string" } }, { "name": "featured", "required": false, "in": "query", "schema": { "type": "boolean" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/BlogArticleSummaryResponseDto" } } } } } }, "summary": "Published blog articles (unpaginated, for hub/sitemap pages)", "tags": ["Blog Articles"] } }, "/api/v1/blog-articles/{slug}/related": { "get": { "operationId": "BlogArticlesController_related", "parameters": [ { "name": "slug", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "limit", "required": false, "in": "query", "schema": { "example": 3, "type": "number" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/BlogArticleSummaryResponseDto" } } } } } }, "summary": "Related published articles by slug", "tags": ["Blog Articles"] } }, "/api/v1/blog-articles/{slug}": { "get": { "operationId": "BlogArticlesController_findBySlug", "parameters": [ { "name": "slug", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlogArticleResponseDto" } } } } }, "summary": "Single published article by slug", "tags": ["Blog Articles"] } }, "/api/v1/admin/blog-articles": { "get": { "operationId": "AdminBlogArticlesController_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "1-based page", "schema": { "minimum": 1, "default": 1, "type": "number" } }, { "name": "pageSize", "required": false, "in": "query", "description": "Page size (alias accepted: limit)", "schema": { "minimum": 1, "maximum": 100, "default": 20, "type": "number" } }, { "name": "sort", "required": false, "in": "query", "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", "schema": { "example": "-createdAt", "type": "string" } }, { "name": "filters", "required": false, "in": "query", "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", "schema": { "additionalProperties": { "type": "string" }, "type": "object" } }, { "name": "resultType", "required": false, "in": "query", "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", "schema": { "type": "number", "enum": [0, 1] } } ], "responses": { "200": { "description": "Paginated list response", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/BlogArticleResponseDto" }, "description": "Array of items for the current page." }, "response": { "$ref": "#/components/schemas/PaginationMetaDto", "description": "Pagination metadata for the current query." } }, "required": ["items", "response"] } } } } }, "security": [ { "bearer": [] } ], "summary": "Paginated article management list", "tags": ["Admin - Blog Articles"] }, "post": { "operationId": "AdminBlogArticlesController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBlogArticleDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlogArticleResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Create blog article", "tags": ["Admin - Blog Articles"] } }, "/api/v1/admin/blog-articles/{id}": { "get": { "operationId": "AdminBlogArticlesController_findOne", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "number" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlogArticleResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Single article by id (draft or published)", "tags": ["Admin - Blog Articles"] }, "patch": { "operationId": "AdminBlogArticlesController_update", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "number" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateBlogArticleDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlogArticleResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Update blog article", "tags": ["Admin - Blog Articles"] }, "delete": { "operationId": "AdminBlogArticlesController_remove", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "number" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponseDto" } } } } }, "security": [ { "bearer": [] } ], "summary": "Soft-delete blog article", "tags": ["Admin - Blog Articles"] } }, "/health": { "get": { "description": "Returns a simple service health indicator for uptime checks.", "operationId": "HealthController_health", "parameters": [], "responses": { "200": { "description": "Service is reachable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponseDto" } } } } }, "summary": "Health check", "tags": ["Health"] } }, "/health/ready": { "get": { "description": "Reports whether the API can reach its required PostgreSQL database.", "operationId": "HealthController_readiness", "parameters": [], "responses": { "200": { "description": "Service dependencies are ready.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponseDto" } } } }, "503": { "description": "A required service dependency is unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponseDto" } } } } }, "summary": "Readiness check", "tags": ["Health"] } } }, "info": { "title": "Ghabilee Backend API", "description": "Professional OpenAPI documentation for the Ghabilee event discovery, booking, payments, and community platform.\n\nAll responses are wrapped by the standard API envelope at runtime.\n\nAuthentication uses JWT bearer tokens issued by the OTP auth flow.", "version": "1.0.0", "contact": {} }, "tags": [ { "name": "Health", "description": "Service health and readiness endpoints" }, { "name": "Auth", "description": "OTP authentication, token refresh, logout, and session management" }, { "name": "Users", "description": "Current user profile management" }, { "name": "Admin - Users", "description": "Staff-only user management" }, { "name": "Identity", "description": "Host identity verification submission and public rejection reasons" }, { "name": "Admin - Identity", "description": "Staff-only host identity verification review workflow" }, { "name": "Event Categories", "description": "Public event category discovery" }, { "name": "Tags", "description": "Public event tag discovery" }, { "name": "Admin - Event Categories", "description": "Staff-only event category management" }, { "name": "Events", "description": "Public event discovery and event details" }, { "name": "My Events", "description": "Organizer-owned event management" }, { "name": "Admin - Events", "description": "Staff-only event management and moderation" }, { "name": "Event Extras", "description": "Event media, FAQs, and organizer guest lists" }, { "name": "Bookings", "description": "User booking lifecycle and organizer check-in actions" }, { "name": "Admin - Bookings", "description": "Staff-only booking management" }, { "name": "Waitlist", "description": "Event waitlist join, offer, and acceptance lifecycle" }, { "name": "Financial", "description": "Payments, wallet, earnings, bank accounts, withdrawals, and settlements" }, { "name": "Notifications", "description": "In-app notifications for the current user" }, { "name": "Admin - Notifications", "description": "Staff-only SMS delivery diagnostics and notification review" }, { "name": "Reviews", "description": "Event reviews and host replies" }, { "name": "Chat", "description": "Direct and event-group conversations and messages" }, { "name": "User Blocks", "description": "Direct-message blocking controls" }, { "name": "User Reports", "description": "User reporting by authenticated users" }, { "name": "Admin - User Reports", "description": "Staff-only user report review workflow" }, { "name": "Admin - Audit Logs", "description": "Staff-only audit trail of admin actions" } ], "servers": [], "components": { "securitySchemes": { "JWT-auth": { "scheme": "bearer", "bearerFormat": "JWT", "type": "http", "description": "JWT access token returned by POST /api/v1/auth/verify-otp." } }, "schemas": { "RequestOtpDto": { "type": "object", "properties": { "mobile": { "type": "string", "description": "Iranian mobile number used for OTP login or registration.", "example": "09121234567", "minLength": 10, "maxLength": 13, "pattern": "^(\\+?98|0)?9\\d{9}$", "format": "phone" } }, "required": ["mobile"] }, "OtpRequestResponseDto": { "type": "object", "properties": { "purpose": { "type": "string", "description": "Whether this OTP flow is for an existing or new account.", "enum": ["login", "register"], "example": "login" }, "expiresIn": { "type": "number", "description": "OTP validity window in seconds.", "example": 300 }, "alreadySent": { "type": "boolean", "description": "True when a still-valid login/register OTP already existed for this mobile. No new SMS is sent and the previous code stays usable until expiresIn elapses.", "example": false } }, "required": ["purpose", "expiresIn", "alreadySent"] }, "ApiErrorCode": { "type": "string", "enum": [ "VALIDATION_FAILED", "UNAUTHORIZED", "FORBIDDEN", "NOT_FOUND", "CONFLICT", "OTP_RATE_LIMITED", "OTP_INVALID", "OTP_EXPIRED", "OTP_ATTEMPTS_EXCEEDED", "TERMS_ACCEPTANCE_REQUIRED", "RATE_LIMITED", "PUSH_SUBSCRIPTION_LIMIT_EXCEEDED", "ACCOUNT_SUSPENDED", "ACCOUNT_DELETED", "EVENT_FULL", "MESSAGING_BLOCKED", "ADMIN_REQUIRED", "PRICE_LOCKED", "EVENT_NOT_EDITABLE", "EVENT_FIELD_LOCKED", "EVENT_LIVE_EDIT_REQUIRES_REVISION", "EVENT_REVISION_NOT_FOUND", "CAPACITY_BELOW_BOOKED", "IDENTITY_REQUIRED", "HOST_EVENT_LIMIT_REACHED", "NOT_CHECKED_IN", "BOOKING_NOT_CONFIRMED", "REVIEW_LOCKED", "REVIEW_WINDOW_EXPIRED", "HOST_REPLY_WINDOW_EXPIRED", "SELF_ACTION_FORBIDDEN", "WAITLIST_NOT_AVAILABLE", "EVENT_NOT_BOOKABLE", "EVENT_GENDER_RESTRICTED", "EVENT_AGE_RESTRICTED", "EVENT_CITY_RESTRICTED", "EVENT_HAS_ACTIVE_BOOKINGS", "RESERVED_CAPACITY_EXCEEDS_REMAINING", "CHAT_CLOSED", "CHAT_REPLY_TARGET_INVALID", "CHAT_MESSAGE_CURSOR_CONFLICT", "NOT_PARTICIPANT", "SERVICE_UNAVAILABLE", "CAPTCHA_INVALID", "INTERNAL_ERROR" ], "description": "Stable machine-readable error code for client-side handling." }, "ErrorResponseDto": { "type": "object", "properties": { "success": { "type": "boolean", "description": "Indicates whether the request succeeded. Always false for error responses.", "example": false }, "code": { "description": "Stable machine-readable error code for client-side handling.", "example": "VALIDATION_FAILED", "allOf": [ { "$ref": "#/components/schemas/ApiErrorCode" } ] }, "message": { "type": "string", "description": "Human-readable error message safe to show to API clients.", "example": "Validation failed", "minLength": 1, "maxLength": 500 }, "errors": { "type": "object", "description": "Optional field-level validation errors keyed by request field name.", "example": { "mobile": ["mobile must be a valid Iranian mobile number"] }, "additionalProperties": { "type": "array", "items": { "type": "string" } } } }, "required": ["success", "code", "message"] }, "VerifyOtpDto": { "type": "object", "properties": { "mobile": { "type": "string", "description": "Iranian mobile number that received the OTP code.", "example": "09121234567", "minLength": 10, "maxLength": 13, "pattern": "^(\\+?98|0)?9\\d{9}$", "format": "phone" }, "code": { "type": "string", "description": "Four-digit one-time password sent by SMS.", "example": "1234", "minLength": 4, "maxLength": 4, "pattern": "^\\d{4}$" }, "deviceInfo": { "type": "string", "description": "Human-readable device label for session management.", "example": "iPhone 15 Safari", "maxLength": 500 }, "termsAccepted": { "type": "boolean", "description": "Must be true when this OTP creates a new account. Existing-user logins do not require it.", "example": true }, "termsVersion": { "type": "string", "description": "Deprecated and ignored. The server stamps its own CURRENT_TERMS_VERSION into the consent audit record.", "example": "2026-08-01", "maxLength": 50, "deprecated": true } }, "required": ["mobile", "code"] }, "UserStatus": { "type": "string", "enum": ["active", "pending", "suspended", "deleted"], "description": "Account status after OTP verification; pending users must complete profile." }, "AuthTokensDto": { "type": "object", "properties": { "accessToken": { "type": "string", "description": "Short-lived JWT access token for protected API endpoints.", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.access" }, "sessionId": { "type": "string", "description": "Refresh-token session identifier.", "example": "0c60c25d-831b-4d43-a5fd-6e8127d81134", "format": "uuid" }, "expiresAt": { "type": "string", "description": "Natural expiry timestamp for the refresh token session.", "example": "2026-09-14T09:00:00.000Z", "format": "date-time" }, "status": { "description": "Account status after OTP verification; pending users must complete profile.", "example": "active", "allOf": [ { "$ref": "#/components/schemas/UserStatus" } ] } }, "required": ["accessToken", "sessionId", "expiresAt", "status"] }, "LoggedOutResponseDto": { "type": "object", "properties": { "loggedOut": { "type": "boolean", "example": true } }, "required": ["loggedOut"] }, "SessionResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Refresh token session identifier.", "example": "0c60c25d-831b-4d43-a5fd-6e8127d81134", "format": "uuid" }, "deviceInfo": { "type": "string", "description": "Device label captured when the session was created.", "example": "iPhone 15 Safari" }, "ipAddress": { "type": "string", "description": "IP address from which the session was created.", "example": "203.0.113.10", "format": "ipv4" }, "createdAt": { "type": "string", "description": "Session creation timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "expiresAt": { "type": "string", "description": "Natural expiry timestamp for the refresh token session.", "example": "2026-09-14T09:00:00.000Z", "format": "date-time" } }, "required": ["id", "createdAt", "expiresAt"] }, "RevokedResponseDto": { "type": "object", "properties": { "revoked": { "type": "boolean", "example": true } }, "required": ["revoked"] }, "Gender": { "type": "string", "enum": ["male", "female", "other"], "description": "Gender for placeholder avatars when no photo is set." }, "ContactChannel": { "type": "string", "enum": ["instagram", "telegram", "twitter", "linkedin", "youtube", "website", "support_phone"] }, "PublicOrganizerContactLinkDto": { "type": "object", "properties": { "channel": { "allOf": [ { "$ref": "#/components/schemas/ContactChannel" } ] }, "label": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "value": { "type": "string", "description": "Raw stored value (username, URL, or phone digits)." } }, "required": ["channel", "label", "url", "value"] }, "PublicOrganizerResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "avatarUrl": { "type": "object", "format": "uri", "nullable": true }, "gender": { "nullable": true, "description": "Gender for placeholder avatars when no photo is set.", "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "bio": { "type": "object", "nullable": true }, "cityName": { "type": "object", "nullable": true }, "defaultAddress": { "type": "object", "nullable": true }, "followersCount": { "type": "number", "minimum": 0, "description": "Follower count; 0 for non-verified guests." }, "isVerified": { "type": "boolean", "description": "True when the user has a verified hosting identity." }, "memberSince": { "format": "date-time", "type": "string" }, "pastEventsCount": { "type": "number", "minimum": 0, "description": "Discoverable held events count for verified hosts; 0 for guests." }, "totalGuestsCount": { "type": "number", "minimum": 0, "description": "Confirmed bookings across the host’s events; 0 for non-verified guests." }, "avgRating": { "type": "number", "nullable": true, "description": "Mean published rating; null for guests or when unrated." }, "reviewsCount": { "type": "number", "minimum": 0, "description": "Published review count; 0 for non-verified guests." }, "contactLinks": { "type": "array", "items": { "$ref": "#/components/schemas/PublicOrganizerContactLinkDto" } } }, "required": [ "id", "firstName", "lastName", "followersCount", "isVerified", "memberSince", "pastEventsCount", "totalGuestsCount", "reviewsCount", "contactLinks" ] }, "PublicOrganizerEventDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "slug": { "type": "string" }, "title": { "type": "string" }, "shortDescription": { "type": "object", "nullable": true }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "startsAt": { "format": "date-time", "type": "string" }, "endsAt": { "format": "date-time", "type": "string" }, "cityName": { "type": "string" }, "isFree": { "type": "boolean" }, "price": { "type": "number" }, "isPast": { "type": "boolean" } }, "required": ["id", "slug", "title", "posterUrl", "startsAt", "endsAt", "cityName", "isFree", "price", "isPast"] }, "ListResultType": { "type": "number", "enum": [0, 1], "description": "Result type requested by the client: 0 for JSON list, 1 for Excel export." }, "PaginationMetaDto": { "type": "object", "properties": { "page": { "type": "number", "description": "Current page number using a 1-based index.", "example": 1, "minimum": 1 }, "pageSize": { "type": "number", "description": "Number of items requested per page.", "example": 20, "minimum": 1, "maximum": 100 }, "totalItemsCount": { "type": "number", "description": "Total number of items matching the current query.", "example": 42, "minimum": 0 }, "totalPages": { "type": "number", "description": "Total number of pages available for the current query.", "example": 3, "minimum": 0 }, "sort": { "type": "string", "description": "Applied sort expression. Prefix the field with - for descending order.", "example": "-createdAt", "minLength": 1, "maxLength": 100 }, "filters": { "type": "object", "description": "Whitelisted filters applied to the list endpoint.", "example": { "status": "pending" }, "additionalProperties": { "type": "string" } }, "resultType": { "description": "Result type requested by the client: 0 for JSON list, 1 for Excel export.", "example": 0, "default": 0, "allOf": [ { "$ref": "#/components/schemas/ListResultType" } ] }, "fileData": { "type": "string", "description": "Base64 encoded export file. Present only when resultType is 1.", "example": "UEsDBBQAAAAIA..." }, "nextCursor": { "type": "string", "description": "Deprecated for chat message history — use `olderCursor` / `newerCursor` instead. Kept for other cursor-paginated endpoints.", "format": "uuid", "nullable": true }, "olderCursor": { "type": "string", "description": "Message id to pass as `before` to load older messages. Same meaning in every list-messages mode; null when there is no older page.", "format": "uuid", "nullable": true }, "newerCursor": { "type": "string", "description": "Message id to pass as `after` to load newer messages. Same meaning in every list-messages mode; null when there is no newer page.", "format": "uuid", "nullable": true }, "aroundMessageId": { "type": "string", "description": "Present when `around` was requested — the message id the window was centered on.", "format": "uuid" }, "hasMore": { "type": "boolean", "description": "Whether another cursor page is available (legacy; prefer hasMoreBefore/hasMoreAfter for chat)." }, "hasMoreBefore": { "type": "boolean", "description": "True when older messages exist beyond this page (pass `olderCursor` as `before`)." }, "hasMoreAfter": { "type": "boolean", "description": "True when newer messages exist beyond this page (pass `newerCursor` as `after`)." } }, "required": ["page", "pageSize", "totalItemsCount", "totalPages", "sort", "filters"] }, "OrganizerViewerStateResponseDto": { "type": "object", "properties": { "isFollowing": { "type": "boolean" }, "attendeeContactLinks": { "type": "array", "items": { "$ref": "#/components/schemas/PublicOrganizerContactLinkDto" } } }, "required": ["isFollowing", "attendeeContactLinks"] }, "IdentityVerificationStatus": { "type": "string", "enum": ["none", "pending", "verified", "rejected"], "description": "Host identity verification status; verified users may create events." }, "UserRole": { "type": "string", "enum": ["user", "admin"], "description": "User role. Admin is staff-only and manually assigned." }, "UserProfileResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "User account identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "mobile": { "type": "string", "description": "Verified Iranian mobile number used as the login identifier.", "example": "989123456789", "format": "phone", "pattern": "^98\\d{10}$" }, "firstName": { "type": "string", "description": "User given name.", "example": "Ali", "nullable": true, "minLength": 1, "maxLength": 100 }, "lastName": { "type": "string", "description": "User family name.", "example": "SaZa", "nullable": true, "minLength": 1, "maxLength": 100 }, "gender": { "description": "User gender.", "example": "male", "nullable": true, "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "cityId": { "type": "number", "description": "Home city identifier.", "example": 1, "minimum": 1, "nullable": true }, "cityName": { "type": "string", "description": "Home city display name.", "example": "مشهد", "nullable": true }, "dateOfBirth": { "type": "string", "description": "Date of birth as an ISO calendar date (YYYY-MM-DD). Null for profiles completed before the field was required.", "example": "1991-03-21", "format": "date", "nullable": true }, "avatarUrl": { "type": "string", "description": "Profile image URL.", "example": "https://storage.ghabilee.com/users/avatar-123.jpg", "format": "uri", "nullable": true }, "bio": { "type": "string", "description": "Short profile biography.", "example": "Event lover and workshop organizer in Tehran.", "maxLength": 2000, "nullable": true }, "defaultAddress": { "type": "string", "description": "Fixed physical address for verified hosts.", "example": "تهران، خیابان ولیعصر، پلاک ۱", "maxLength": 500, "nullable": true }, "status": { "description": "Account status.", "example": "active", "allOf": [ { "$ref": "#/components/schemas/UserStatus" } ] }, "identityStatus": { "description": "Host identity verification status; verified users may create events.", "example": "verified", "allOf": [ { "$ref": "#/components/schemas/IdentityVerificationStatus" } ] }, "role": { "description": "User role. Admin is staff-only and manually assigned.", "example": "user", "allOf": [ { "$ref": "#/components/schemas/UserRole" } ] }, "followersCount": { "type": "number", "description": "Number of users following this account.", "example": 27, "minimum": 0 }, "followingCount": { "type": "number", "description": "Number of organizers this account follows.", "example": 12, "minimum": 0 } }, "required": [ "id", "mobile", "firstName", "lastName", "gender", "cityId", "cityName", "dateOfBirth", "avatarUrl", "bio", "defaultAddress", "status", "identityStatus", "role", "followersCount", "followingCount" ] }, "ProfileSummaryTicketDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "subject": { "type": "string", "example": "مشکل در پرداخت" }, "status": { "type": "string", "example": "open" }, "category": { "type": "string", "example": "payment" }, "lastMessageAt": { "type": "string", "format": "date-time", "nullable": true }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": ["id", "subject", "status", "category", "lastMessageAt", "updatedAt"] }, "ProfileSummaryEventPreviewDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string", "example": "ورکشاپ عکاسی" }, "slug": { "type": "string", "example": "workshop-akkasi", "nullable": true }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "squarePosterUrl": { "type": "string", "format": "uri", "nullable": true }, "startsAt": { "type": "string", "format": "date-time" } }, "required": ["id", "title", "slug", "posterUrl", "squarePosterUrl", "startsAt"] }, "ProfileSummaryRegisteredPreviewDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string", "example": "ورکشاپ عکاسی" }, "slug": { "type": "string", "example": "workshop-akkasi", "nullable": true }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "squarePosterUrl": { "type": "string", "format": "uri", "nullable": true }, "startsAt": { "type": "string", "format": "date-time" }, "bookingId": { "type": "string", "format": "uuid", "description": "Booking id" } }, "required": ["id", "title", "slug", "posterUrl", "squarePosterUrl", "startsAt", "bookingId"] }, "UserProfileSummaryResponseDto": { "type": "object", "properties": { "walletBalance": { "type": "number", "example": 150000, "minimum": 0 }, "followersCount": { "type": "number", "example": 12, "minimum": 0 }, "followingCount": { "type": "number", "example": 8, "minimum": 0 }, "hostedEventsCount": { "type": "number", "example": 3, "minimum": 0 }, "registeredEventsCount": { "type": "number", "example": 5, "minimum": 0 }, "bookmarksCount": { "type": "number", "example": 7, "minimum": 0 }, "unreadNotificationsCount": { "type": "number", "example": 2, "minimum": 0 }, "recentTickets": { "type": "array", "items": { "$ref": "#/components/schemas/ProfileSummaryTicketDto" } }, "recentHostedEvents": { "type": "array", "items": { "$ref": "#/components/schemas/ProfileSummaryEventPreviewDto" } }, "recentRegisteredEvents": { "type": "array", "items": { "$ref": "#/components/schemas/ProfileSummaryRegisteredPreviewDto" } }, "recentBookmarks": { "type": "array", "items": { "$ref": "#/components/schemas/ProfileSummaryEventPreviewDto" } } }, "required": [ "walletBalance", "followersCount", "followingCount", "hostedEventsCount", "registeredEventsCount", "bookmarksCount", "unreadNotificationsCount", "recentTickets", "recentHostedEvents", "recentRegisteredEvents", "recentBookmarks" ] }, "UpdateUserProfileDto": { "type": "object", "properties": { "firstName": { "type": "string", "description": "User given name shown on the profile.", "example": "Ali", "minLength": 1, "maxLength": 100 }, "lastName": { "type": "string", "description": "User family name shown on the profile.", "example": "SaZa", "minLength": 1, "maxLength": 100 }, "gender": { "description": "User gender used for profile display and personalization.", "example": "male", "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "cityId": { "type": "number", "description": "Home city identifier from the cities reference table.", "example": 1, "minimum": 1, "nullable": true }, "avatarUrl": { "type": "string", "description": "HTTPS URL of the user profile image.", "example": "https://storage.ghabilee.com/users/avatar-123.jpg", "format": "uri", "maxLength": 500 }, "bio": { "type": "string", "description": "Short user biography displayed on the profile.", "example": "Event lover and workshop organizer in Tehran.", "maxLength": 2000 }, "defaultAddress": { "type": "string", "description": "Fixed physical address for verified hosts. Null or empty clears it. Requires verified identity.", "example": "تهران، خیابان ولیعصر، پلاک ۱", "maxLength": 500, "nullable": true } } }, "CompleteProfileDto": { "type": "object", "properties": { "firstName": { "type": "string", "description": "User given name.", "example": "Ali", "minLength": 1, "maxLength": 100 }, "lastName": { "type": "string", "description": "User family name.", "example": "SaZa", "minLength": 1, "maxLength": 100 }, "gender": { "description": "User gender used for profile personalization.", "example": "male", "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "dateOfBirth": { "type": "string", "description": "Date of birth as an ISO calendar date (YYYY-MM-DD). Must fall in Jalali years 1330–1395.", "example": "1991-03-21", "format": "date" }, "cityId": { "type": "number", "description": "Home city identifier from the cities reference table.", "example": 1, "minimum": 1 } }, "required": ["firstName", "lastName", "gender", "dateOfBirth", "cityId"] }, "AdminUserListItemDto": { "type": "object", "properties": { "id": { "type": "string", "description": "User account identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "mobile": { "type": "string", "description": "Verified Iranian mobile number used as the login identifier.", "example": "989123456789", "format": "phone", "pattern": "^98\\d{10}$" }, "firstName": { "type": "string", "description": "User given name.", "example": "Ali", "nullable": true, "minLength": 1, "maxLength": 100 }, "lastName": { "type": "string", "description": "User family name.", "example": "SaZa", "nullable": true, "minLength": 1, "maxLength": 100 }, "gender": { "description": "User gender.", "example": "male", "nullable": true, "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "cityId": { "type": "number", "description": "Home city identifier.", "example": 1, "minimum": 1, "nullable": true }, "cityName": { "type": "string", "description": "Home city display name.", "example": "مشهد", "nullable": true }, "dateOfBirth": { "type": "string", "description": "Date of birth as an ISO calendar date (YYYY-MM-DD). Null for profiles completed before the field was required.", "example": "1991-03-21", "format": "date", "nullable": true }, "avatarUrl": { "type": "string", "description": "Profile image URL.", "example": "https://storage.ghabilee.com/users/avatar-123.jpg", "format": "uri", "nullable": true }, "bio": { "type": "string", "description": "Short profile biography.", "example": "Event lover and workshop organizer in Tehran.", "maxLength": 2000, "nullable": true }, "defaultAddress": { "type": "string", "description": "Fixed physical address for verified hosts.", "example": "تهران، خیابان ولیعصر، پلاک ۱", "maxLength": 500, "nullable": true }, "status": { "description": "Account status.", "example": "active", "allOf": [ { "$ref": "#/components/schemas/UserStatus" } ] }, "identityStatus": { "description": "Host identity verification status; verified users may create events.", "example": "verified", "allOf": [ { "$ref": "#/components/schemas/IdentityVerificationStatus" } ] }, "role": { "description": "User role. Admin is staff-only and manually assigned.", "example": "user", "allOf": [ { "$ref": "#/components/schemas/UserRole" } ] }, "followersCount": { "type": "number", "description": "Number of users following this account.", "example": 27, "minimum": 0 }, "followingCount": { "type": "number", "description": "Number of organizers this account follows.", "example": 12, "minimum": 0 }, "userType": { "type": "string", "description": "Derived admin-facing user type: admin staff, verified host, or guest.", "enum": ["guest", "host", "admin"], "example": "host" }, "walletBalance": { "type": "number", "description": "Current wallet balance in Tomans.", "example": 150000, "minimum": 0 } }, "required": [ "id", "mobile", "firstName", "lastName", "gender", "cityId", "cityName", "dateOfBirth", "avatarUrl", "bio", "defaultAddress", "status", "identityStatus", "role", "followersCount", "followingCount", "userType", "walletBalance" ] }, "AdminChangeUserMobileDto": { "type": "object", "properties": { "mobile": { "type": "string", "description": "New Iranian mobile number. It is normalized to the 98XXXXXXXXXX format.", "example": "09121231231", "minLength": 10, "maxLength": 13, "pattern": "^(\\+?98|0)?9\\d{9}$", "format": "phone" } }, "required": ["mobile"] }, "AdminUpdateUserDto": { "type": "object", "properties": { "firstName": { "type": "string", "description": "User given name shown on the profile.", "example": "Ali", "minLength": 1, "maxLength": 100 }, "lastName": { "type": "string", "description": "User family name shown on the profile.", "example": "SaZa", "minLength": 1, "maxLength": 100 }, "gender": { "description": "User gender used for profile display and personalization.", "example": "male", "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "cityId": { "type": "number", "description": "Home city identifier from the cities reference table.", "example": 1, "minimum": 1, "nullable": true }, "avatarUrl": { "type": "string", "description": "HTTPS URL of the user profile image.", "example": "https://storage.ghabilee.com/users/avatar-123.jpg", "format": "uri", "maxLength": 500 }, "bio": { "type": "string", "description": "Short user biography displayed on the profile.", "example": "Event lover and workshop organizer in Tehran.", "maxLength": 2000 }, "defaultAddress": { "type": "string", "description": "Fixed physical address for verified hosts. Null or empty clears it. Requires verified identity.", "example": "تهران، خیابان ولیعصر، پلاک ۱", "maxLength": 500, "nullable": true }, "status": { "type": "string", "description": "Suspend or reactivate the account. 'pending' and 'deleted' are not settable here — see the field-level note in this DTO.", "enum": ["active", "suspended"], "example": "suspended" }, "hostPlan": { "type": "string", "description": "Free hosts can create 10 events; unlimited removes the quota.", "enum": ["free", "unlimited"] } } }, "UserContactLinkResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "channel": { "type": "string", "enum": ["instagram", "telegram", "twitter", "linkedin", "youtube", "website", "support_phone"] }, "value": { "type": "string" }, "isPublic": { "type": "boolean" }, "displayOrder": { "type": "number" }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": ["id", "channel", "value", "isPublic", "displayOrder", "createdAt", "updatedAt"] }, "CreateUserContactLinkDto": { "type": "object", "properties": { "channel": { "type": "string", "enum": ["instagram", "telegram", "twitter", "linkedin", "youtube", "website", "support_phone"], "example": "instagram" }, "value": { "type": "string", "example": "ghabilee_official", "description": "Username, full https URL (website), or phone (support_phone, normalized to 98…)." }, "isPublic": { "type": "boolean", "example": true }, "displayOrder": { "type": "number", "example": 0 } }, "required": ["channel", "value"] }, "UpdateUserContactLinkDto": { "type": "object", "properties": { "value": { "type": "string", "example": "ghabilee_official", "description": "Username, full https URL (website), or phone (support_phone, normalized to 98…)." }, "isPublic": { "type": "boolean", "example": true }, "displayOrder": { "type": "number", "example": 0 } } }, "SuccessResponseDto": { "type": "object", "properties": { "success": { "type": "boolean", "example": true } }, "required": ["success"] }, "IdentityRejectionReasonResponseDto": { "type": "object", "properties": { "id": { "type": "number", "description": "Rejection reason identifier.", "example": 1, "minimum": 1 }, "code": { "type": "string", "description": "Stable rejection reason code for UI handling.", "example": "blurry_image", "minLength": 1, "maxLength": 100 }, "label": { "type": "string", "description": "Human-readable rejection reason label.", "example": "Blurry image", "minLength": 1, "maxLength": 255 }, "sortOrder": { "type": "number", "description": "Display order for rejection reasons.", "example": 10, "minimum": 0 } }, "required": ["id", "code", "label", "sortOrder"] }, "RequestHostContractOtpDto": { "type": "object", "properties": { "nationalCode": { "type": "string", "example": "0012345678", "description": "Iranian national ID number (exactly 10 digits)", "minLength": 10, "maxLength": 10, "pattern": "^\\d{10}$" }, "contractAccepted": { "type": "boolean", "example": true, "description": "Must be true — applicant confirms they read and accept the host cooperation contract." }, "contractVersion": { "type": "string", "example": "2026-08-22", "description": "Must match the currently published host cooperation contract version." } }, "required": ["nationalCode", "contractAccepted", "contractVersion"] }, "HostContractOtpRequestedDto": { "type": "object", "properties": { "purpose": { "type": "string", "description": "OTP purpose used for this challenge.", "example": "host_contract" }, "expiresIn": { "type": "number", "description": "Seconds until the OTP expires.", "example": 300 }, "contractVersion": { "type": "string", "description": "Contract version that will be recorded on successful confirmation.", "example": "2026-08-22" } }, "required": ["purpose", "expiresIn", "contractVersion"] }, "ConfirmHostContractOtpDto": { "type": "object", "properties": { "code": { "type": "string", "example": "1234", "description": "OTP code sent to the authenticated user's registered mobile (4 digits).", "minLength": 4, "maxLength": 4 } }, "required": ["code"] }, "IdentityVerificationSource": { "type": "string", "enum": ["host_submission", "admin_attested"], "description": "host_submission means the user sent KYC fields; admin_attested means staff verified the user without a host request." }, "IdentityVerificationResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Identity verification request identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "userId": { "type": "string", "description": "User who submitted the verification request.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "status": { "description": "Verification status - pending means awaiting admin review, verified means hosting is enabled, rejected means the user may re-apply.", "example": "pending", "allOf": [ { "$ref": "#/components/schemas/IdentityVerificationStatus" } ] }, "source": { "description": "host_submission means the user sent KYC fields; admin_attested means staff verified the user without a host request.", "example": "host_submission", "allOf": [ { "$ref": "#/components/schemas/IdentityVerificationSource" } ] }, "nationalCode": { "type": "string", "description": "Iranian national ID number submitted by the user. Null on admin-attested rows.", "example": "0012345678", "minLength": 10, "maxLength": 10, "pattern": "^\\d{10}$" }, "birthDate": { "type": "string", "description": "Birth date submitted by the user. Null on admin-attested rows.", "example": "1990-05-20", "format": "date" }, "idCardImageUrl": { "type": "string", "description": "Authenticated API URL of the private national ID card image. Legacy submissions only — current host flow no longer collects an ID image.", "example": "https://ghabilee.ir/api/v1/uploads/identity/6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1/16e33f70-d3cc-492c-a8fa-e2317fbfb37b.jpg", "format": "uri" }, "contractVersion": { "type": "string", "description": "Host cooperation contract version accepted at submission.", "example": "2026-08-22" }, "contractAcceptedAt": { "type": "string", "description": "When the host electronically accepted the cooperation contract.", "example": "2026-08-22T09:00:00.000Z", "format": "date-time" }, "contractAcceptanceId": { "type": "string", "description": "Immutable electronic acceptance identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "jibitMatched": { "type": "boolean", "description": "Jibit nationalCode↔mobile match result. Null until an admin runs inquiry.", "example": true }, "jibitInquiredAt": { "type": "string", "description": "When an admin last ran the Jibit matching inquiry.", "example": "2026-08-22T10:00:00.000Z", "format": "date-time" }, "jibitInquiredBy": { "type": "string", "description": "Admin user who ran the Jibit matching inquiry.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "jibitErrorCode": { "type": "string", "description": "Jibit error code when the matching call failed.", "example": "daily_limit.reached" }, "reasonId": { "type": "number", "description": "Structured rejection reason identifier when the request is rejected.", "example": 1, "minimum": 1 }, "rejectionReason": { "type": "string", "description": "Free-text rejection explanation when provided by an admin.", "example": "The uploaded national ID card image is blurry.", "maxLength": 2000 }, "reason": { "description": "Structured rejection reason joined from identity_rejection_reasons when reasonId is set. Present on both user and admin responses so applicants see the label after a reasonId-only reject.", "allOf": [ { "$ref": "#/components/schemas/IdentityRejectionReasonResponseDto" } ] }, "reviewedAt": { "type": "string", "description": "Admin review timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "reviewedBy": { "type": "string", "description": "Admin user identifier who reviewed the request.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "createdAt": { "type": "string", "description": "Request creation timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" }, "updatedAt": { "type": "string", "description": "Last update timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" } }, "required": ["id", "userId", "status", "source", "createdAt", "updatedAt"] }, "AdminIdentityVerificationResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Identity verification request identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "userId": { "type": "string", "description": "User who submitted the verification request.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "status": { "description": "Verification status - pending means awaiting admin review, verified means hosting is enabled, rejected means the user may re-apply.", "example": "pending", "allOf": [ { "$ref": "#/components/schemas/IdentityVerificationStatus" } ] }, "source": { "description": "host_submission means the user sent KYC fields; admin_attested means staff verified the user without a host request.", "example": "host_submission", "allOf": [ { "$ref": "#/components/schemas/IdentityVerificationSource" } ] }, "nationalCode": { "type": "string", "description": "Iranian national ID number submitted by the user. Null on admin-attested rows.", "example": "0012345678", "minLength": 10, "maxLength": 10, "pattern": "^\\d{10}$" }, "birthDate": { "type": "string", "description": "Birth date submitted by the user. Null on admin-attested rows.", "example": "1990-05-20", "format": "date" }, "idCardImageUrl": { "type": "string", "description": "Authenticated API URL of the private national ID card image. Legacy submissions only — current host flow no longer collects an ID image.", "example": "https://ghabilee.ir/api/v1/uploads/identity/6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1/16e33f70-d3cc-492c-a8fa-e2317fbfb37b.jpg", "format": "uri" }, "contractVersion": { "type": "string", "description": "Host cooperation contract version accepted at submission.", "example": "2026-08-22" }, "contractAcceptedAt": { "type": "string", "description": "When the host electronically accepted the cooperation contract.", "example": "2026-08-22T09:00:00.000Z", "format": "date-time" }, "contractAcceptanceId": { "type": "string", "description": "Immutable electronic acceptance identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "jibitMatched": { "type": "boolean", "description": "Jibit nationalCode↔mobile match result. Null until an admin runs inquiry.", "example": true }, "jibitInquiredAt": { "type": "string", "description": "When an admin last ran the Jibit matching inquiry.", "example": "2026-08-22T10:00:00.000Z", "format": "date-time" }, "jibitInquiredBy": { "type": "string", "description": "Admin user who ran the Jibit matching inquiry.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "jibitErrorCode": { "type": "string", "description": "Jibit error code when the matching call failed.", "example": "daily_limit.reached" }, "reasonId": { "type": "number", "description": "Structured rejection reason identifier when the request is rejected.", "example": 1, "minimum": 1 }, "rejectionReason": { "type": "string", "description": "Free-text rejection explanation when provided by an admin.", "example": "The uploaded national ID card image is blurry.", "maxLength": 2000 }, "reason": { "description": "Structured rejection reason joined from identity_rejection_reasons when reasonId is set. Present on both user and admin responses so applicants see the label after a reasonId-only reject.", "allOf": [ { "$ref": "#/components/schemas/IdentityRejectionReasonResponseDto" } ] }, "reviewedAt": { "type": "string", "description": "Admin review timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "reviewedBy": { "type": "string", "description": "Admin user identifier who reviewed the request.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "createdAt": { "type": "string", "description": "Request creation timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" }, "updatedAt": { "type": "string", "description": "Last update timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "user": { "type": "object", "description": "Minimal applicant profile shown to staff reviewers.", "example": { "mobile": "989123456789", "firstName": "Ali", "lastName": "SaZa" } } }, "required": ["id", "userId", "status", "source", "createdAt", "updatedAt", "user"] }, "RejectIdentityVerificationDto": { "type": "object", "properties": { "reasonId": { "type": "number", "description": "Structured reason id from GET /identity/rejection-reasons", "example": 1, "minimum": 1 }, "rejectionReason": { "type": "string", "description": "Free-text rejection explanation visible to the applicant.", "example": "The uploaded national ID card image is blurry. Please upload a clearer photo.", "maxLength": 2000 } } }, "NotificationStatus": { "type": "string", "enum": ["pending", "sent", "failed", "cancelled"], "description": "Parent notification delivery status." }, "NotificationResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "In-app notification identifier (same as parent notification id).", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "category": { "type": "string", "description": "Notification category (app-defined type).", "example": "booking_confirmed", "maxLength": 50 }, "title": { "type": "string", "description": "Notification headline.", "example": "Booking confirmed", "maxLength": 200 }, "body": { "type": "string", "description": "Notification body text.", "example": "Your booking for Friday Night Jazz has been confirmed." }, "actionUrl": { "type": "string", "description": "Deep link URL when the user taps the notification.", "example": "/bookings/6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1" }, "referenceType": { "type": "string", "description": "Polymorphic reference label (e.g. booking, event).", "example": "booking", "maxLength": 50 }, "referenceId": { "type": "string", "description": "Identifier of the referenced entity.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "status": { "description": "Parent notification delivery status.", "example": "sent", "allOf": [ { "$ref": "#/components/schemas/NotificationStatus" } ] }, "sentAt": { "type": "string", "description": "When the notification was sent to the user.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "createdAt": { "type": "string", "description": "When the in-app notification was created.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "readAt": { "type": "string", "description": "When the user read this in-app notification. Null means unread.", "example": "2026-08-15T10:30:00.000Z", "format": "date-time" } }, "required": ["id", "category", "title", "body", "status", "createdAt"] }, "NotificationUnreadCountResponseDto": { "type": "object", "properties": { "totalUnread": { "type": "number", "description": "Total unread in-app notifications for the current user.", "example": 2 } }, "required": ["totalUnread"] }, "PushVapidPublicKeyResponseDto": { "type": "object", "properties": { "publicKey": { "type": "string", "description": "VAPID public key for PushManager.subscribe applicationServerKey.", "example": "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U" }, "enabled": { "type": "boolean", "description": "Whether Web Push is configured on the server.", "example": true } }, "required": ["publicKey", "enabled"] }, "PushDeliveryReceiptDto": { "type": "object", "properties": { "notificationId": { "type": "string", "description": "Notification identifier embedded in the push payload.", "example": "80e4a3ce-4544-4534-b712-36553261528a", "format": "uuid" }, "subscriptionId": { "type": "string", "description": "Push subscription identifier embedded in the push payload.", "example": "937b8ddb-7dd7-4d8d-af2b-69690e5fa39b", "format": "uuid" }, "eventType": { "type": "string", "description": "displayed = browser showed the notification, opened = user tapped it.", "enum": ["displayed", "opened"], "example": "opened" }, "receiptHmac": { "type": "string", "description": "HMAC-SHA256 of notificationId:subscriptionId using OTP_HASH_PEPPER, hex-encoded.", "example": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } }, "required": ["notificationId", "subscriptionId", "eventType", "receiptHmac"] }, "PushDeliveryReceiptResponseDto": { "type": "object", "properties": { "recorded": { "type": "boolean", "description": "Whether this request changed any stored receipt timestamp.", "example": true }, "notificationId": { "type": "string", "description": "Notification identifier for the delivery row.", "example": "80e4a3ce-4544-4534-b712-36553261528a", "format": "uuid" }, "subscriptionId": { "type": "string", "description": "Subscription identifier for the delivery row.", "example": "937b8ddb-7dd7-4d8d-af2b-69690e5fa39b", "format": "uuid" }, "displayedAt": { "type": "object", "description": "First time the browser reported display.", "example": "2026-08-18T09:04:00.111Z", "format": "date-time", "nullable": true }, "openedAt": { "type": "object", "description": "First time the user tapped the notification.", "example": "2026-08-18T09:05:12.000Z", "format": "date-time", "nullable": true } }, "required": ["recorded", "notificationId", "subscriptionId"] }, "PushSubscribeDto": { "type": "object", "properties": { "endpoint": { "type": "string", "description": "Push service endpoint URL from PushSubscription.endpoint.", "example": "https://fcm.googleapis.com/fcm/send/..." }, "p256dh": { "type": "string", "description": "p256dh key from PushSubscription.getKey(\"p256dh\"), base64url-encoded." }, "auth": { "type": "string", "description": "auth key from PushSubscription.getKey(\"auth\"), base64url-encoded." }, "userAgent": { "type": "string", "description": "Browser user-agent string for debugging multi-device subscriptions." } }, "required": ["endpoint", "p256dh", "auth"] }, "PushSubscriptionResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "endpoint": { "type": "string" }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "userId", "endpoint", "createdAt"] }, "DeletedResponseDto": { "type": "object", "properties": { "deleted": { "type": "boolean", "example": true } }, "required": ["deleted"] }, "DeletedCountResponseDto": { "type": "object", "properties": { "deletedCount": { "type": "number", "example": 2, "minimum": 0 } }, "required": ["deletedCount"] }, "SmsStatus": { "type": "string", "enum": ["pending", "sent", "failed", "delivered"], "description": "SMS delivery status." }, "SmsRecipientDto": { "type": "object", "properties": { "firstName": { "type": "string", "description": "Recipient first name (null if the user has no profile).", "example": "Sara", "nullable": true }, "lastName": { "type": "string", "description": "Recipient last name (null if the user has no profile).", "example": "Ahmadi", "nullable": true } }, "required": ["firstName", "lastName"] }, "SmsMessageResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "SMS delivery record identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "notificationId": { "type": "string", "description": "Parent notification identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "userId": { "type": "string", "description": "Recipient user identifier.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "mobile": { "type": "string", "description": "Destination mobile number (98XXXXXXXXXX).", "example": "989123456789", "maxLength": 15 }, "messageBody": { "type": "string", "description": "SMS text handed off to the provider.", "example": "Your booking for Friday Night Jazz has been confirmed." }, "status": { "description": "SMS delivery status.", "example": "sent", "allOf": [ { "$ref": "#/components/schemas/SmsStatus" } ] }, "provider": { "type": "string", "description": "SMS gateway provider name.", "example": "kavenegar", "maxLength": 50 }, "providerRef": { "type": "string", "description": "Provider tracking reference.", "example": "kn-123456789", "maxLength": 100 }, "errorMessage": { "type": "string", "description": "Failure detail when status is failed.", "example": "Provider timeout" }, "sentAt": { "type": "string", "description": "When the SMS was handed off to the provider.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "deliveredAt": { "type": "string", "description": "When delivery to the handset was confirmed.", "example": "2026-08-15T09:00:05.000Z", "format": "date-time" }, "createdAt": { "type": "string", "description": "Record creation timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "updatedAt": { "type": "string", "description": "Last update timestamp.", "example": "2026-08-15T09:00:05.000Z", "format": "date-time" }, "recipient": { "description": "Recipient display-name fields, joined from the user profile (may be absent if the user has no profile).", "allOf": [ { "$ref": "#/components/schemas/SmsRecipientDto" } ] } }, "required": ["id", "notificationId", "userId", "mobile", "messageBody", "status", "createdAt", "updatedAt"] }, "NotificationRuleResponseDto": { "type": "object", "properties": { "eventKey": { "type": "string", "example": "event.submitted_for_review" }, "displayName": { "type": "string" }, "description": { "type": "string" }, "enabled": { "type": "boolean" }, "channel": { "type": "string", "enum": ["in_app", "sms", "both"] }, "titleTemplate": { "type": "string" }, "bodyTemplate": { "type": "string" }, "actionUrlTemplate": { "type": "object", "nullable": true }, "allowedVariables": { "type": "array", "items": { "type": "string" } }, "sampleVariables": { "type": "object", "additionalProperties": { "type": "string" } }, "smsAllowed": { "type": "boolean" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": [ "eventKey", "displayName", "description", "enabled", "channel", "titleTemplate", "bodyTemplate", "allowedVariables", "sampleVariables", "smsAllowed", "updatedAt" ] }, "UpdateNotificationRuleDto": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "channel": { "type": "string", "enum": ["in_app", "sms", "both"] }, "titleTemplate": { "type": "string" }, "bodyTemplate": { "type": "string" }, "actionUrlTemplate": { "type": "string", "nullable": true } } }, "AdminBulkSmsOtpResponseDto": { "type": "object", "properties": { "purpose": { "type": "string", "description": "OTP purpose used when confirming a bulk SMS send.", "enum": ["admin_bulk_sms"], "example": "admin_bulk_sms" }, "expiresIn": { "type": "number", "description": "OTP validity window in seconds.", "example": 300 } }, "required": ["purpose", "expiresIn"] }, "NotificationChannel": { "type": "string", "enum": ["in_app", "sms", "both"], "description": "Delivery channel. `in_app` also dispatches Web Push when the user has an active subscription. Defaults to `in_app`." }, "AdminSendManualNotificationDto": { "type": "object", "properties": { "channel": { "description": "Delivery channel. `in_app` also dispatches Web Push when the user has an active subscription. Defaults to `in_app`.", "example": "in_app", "allOf": [ { "$ref": "#/components/schemas/NotificationChannel" } ] }, "title": { "type": "string", "description": "Notification title. Required for `in_app` and `both`. For `sms` it is stored on the parent row and defaults to پیامک when omitted.", "example": "یادآوری تکمیل پروفایل", "maxLength": 200 }, "body": { "type": "string", "description": "Notification body text.", "example": "برای استفاده کامل از امکانات قبیله، لطفاً پروفایل خود را تکمیل کنید.", "maxLength": 2000 }, "actionUrl": { "type": "string", "description": "Optional internal app path opened when the recipient taps the notification.", "example": "/profile/account", "nullable": true, "maxLength": 500 }, "userIds": { "description": "Recipient user ids. At least one of userIds or mobiles must be provided.", "example": ["c3c921d5-4418-42ed-bf39-0f04b1d5123b"], "type": "array", "items": { "type": "string" } }, "mobiles": { "description": "Recipient mobile numbers (0912..., 98912..., or +98912...). At least one of userIds or mobiles must be provided.", "example": ["09153641196", "989121234567"], "type": "array", "items": { "type": "string" } }, "otpCode": { "type": "string", "description": "Required when sending SMS/both to two or more recipients. Confirms the admin bulk SMS OTP sent to the admin mobile.", "example": "1234", "maxLength": 10 } }, "required": ["body"] }, "AdminManualNotificationRecipientDto": { "type": "object", "properties": { "userId": { "type": "string", "description": "Recipient user identifier.", "example": "c3c921d5-4418-42ed-bf39-0f04b1d5123b", "format": "uuid" }, "mobile": { "type": "string", "description": "Recipient normalized mobile number.", "example": "989153641196" }, "firstName": { "type": "object", "description": "Recipient first name if present on profile.", "example": "علی", "nullable": true }, "lastName": { "type": "object", "description": "Recipient last name if present on profile.", "example": "ساعی", "nullable": true }, "notificationId": { "type": "string", "description": "Created notification identifier for this recipient.", "example": "80e4a3ce-4544-4534-b712-36553261528a", "format": "uuid" } }, "required": ["userId", "mobile", "notificationId"] }, "AdminManualNotificationResponseDto": { "type": "object", "properties": { "recipientCount": { "type": "number", "description": "How many distinct users received the notification.", "example": 2 }, "recipients": { "description": "Distinct users that were targeted and resolved successfully.", "type": "array", "items": { "$ref": "#/components/schemas/AdminManualNotificationRecipientDto" } }, "missingUserIds": { "description": "Requested user ids that did not resolve to a user row.", "example": ["11111111-1111-1111-1111-111111111111"], "type": "array", "items": { "type": "string" } }, "missingMobiles": { "description": "Requested mobile numbers that did not resolve to a user row after normalization.", "example": ["989199999999"], "type": "array", "items": { "type": "string" } } }, "required": ["recipientCount", "recipients", "missingUserIds", "missingMobiles"] }, "AdminInAppRecipientDto": { "type": "object", "properties": { "firstName": { "type": "object", "description": "Recipient display-name first part if present.", "example": "سارا", "nullable": true }, "lastName": { "type": "object", "description": "Recipient display-name last part if present.", "example": "احمدی", "nullable": true } }, "required": ["firstName", "lastName"] }, "AdminInAppNotificationResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "In-app notification identifier (same as parent notification id).", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "userId": { "type": "string", "description": "Recipient user identifier.", "example": "c3c921d5-4418-42ed-bf39-0f04b1d5123b", "format": "uuid" }, "mobile": { "type": "string", "description": "Recipient mobile number.", "example": "989153641196" }, "category": { "type": "string", "description": "Notification category.", "example": "admin_manual" }, "title": { "type": "string", "description": "Notification headline.", "example": "یادآوری تکمیل پروفایل" }, "body": { "type": "string", "description": "Notification body text.", "example": "پروفایل خود را تکمیل کنید." }, "actionUrl": { "type": "object", "description": "Internal destination path opened on click.", "example": "/profile/account", "nullable": true }, "status": { "description": "Parent notification delivery status.", "example": "sent", "allOf": [ { "$ref": "#/components/schemas/NotificationStatus" } ] }, "readAt": { "type": "object", "description": "When the in-app row was marked read. Null means unread.", "example": "2026-08-18T09:05:12.000Z", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string", "description": "When the in-app notification was created.", "example": "2026-08-18T09:04:00.106Z" }, "recipient": { "description": "Recipient display-name fields when a profile exists.", "allOf": [ { "$ref": "#/components/schemas/AdminInAppRecipientDto" } ] } }, "required": ["id", "userId", "mobile", "category", "title", "body", "status", "createdAt"] }, "PushDeliveryRecipientDto": { "type": "object", "properties": { "firstName": { "type": "object", "description": "Recipient display-name first part if present.", "example": "سارا", "nullable": true }, "lastName": { "type": "object", "description": "Recipient display-name last part if present.", "example": "احمدی", "nullable": true } }, "required": ["firstName", "lastName"] }, "PushDeliveryResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Push delivery row identifier.", "example": "292f665d-9c83-4c7b-a8b7-fc73a889b2b7", "format": "uuid" }, "notificationId": { "type": "string", "description": "Parent notification identifier.", "example": "80e4a3ce-4544-4534-b712-36553261528a", "format": "uuid" }, "userId": { "type": "string", "description": "Recipient user identifier.", "example": "c3c921d5-4418-42ed-bf39-0f04b1d5123b", "format": "uuid" }, "subscriptionId": { "type": "string", "description": "Subscription identifier.", "example": "937b8ddb-7dd7-4d8d-af2b-69690e5fa39b", "format": "uuid" }, "mobile": { "type": "string", "description": "Recipient mobile number.", "example": "989153641196" }, "category": { "type": "string", "description": "Notification category.", "example": "admin_manual" }, "title": { "type": "string", "description": "Notification title.", "example": "یادآوری تکمیل پروفایل" }, "body": { "type": "string", "description": "Notification body text.", "example": "پروفایل خود را تکمیل کنید." }, "actionUrl": { "type": "object", "description": "Internal destination path opened on click.", "example": "/profile/account", "nullable": true }, "status": { "type": "string", "description": "Push provider handoff status.", "example": "sent" }, "errorMessage": { "type": "object", "description": "Failure detail if provider handoff failed.", "example": "Subscription is no longer valid", "nullable": true }, "userAgent": { "type": "object", "description": "Browser user-agent stored with the subscription.", "example": "Mozilla/5.0 (Linux; Android 10; K)...", "nullable": true }, "displayedAt": { "type": "object", "description": "When the browser confirmed display.", "example": "2026-08-18T09:04:00.111Z", "format": "date-time", "nullable": true }, "openedAt": { "type": "object", "description": "When the user tapped the notification.", "example": "2026-08-18T09:05:12.000Z", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string", "description": "Delivery row creation timestamp.", "example": "2026-08-18T09:04:00.106Z" }, "recipient": { "description": "Recipient display-name fields when a profile exists.", "allOf": [ { "$ref": "#/components/schemas/PushDeliveryRecipientDto" } ] } }, "required": ["id", "notificationId", "userId", "subscriptionId", "mobile", "category", "title", "body", "status", "createdAt"] }, "EventCategoryResponseDto": { "type": "object", "properties": { "id": { "type": "number", "example": 1 }, "name": { "type": "string", "example": "موسیقی" }, "slug": { "type": "string", "example": "music" }, "parentId": { "type": "number", "nullable": true, "example": null }, "shortDescription": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "icon": { "type": "string", "nullable": true }, "color": { "type": "string", "nullable": true, "example": "#7c3aed" }, "coverImageUrl": { "type": "string", "nullable": true, "format": "uri" }, "bannerImageUrl": { "type": "string", "nullable": true, "format": "uri" }, "metaTitle": { "type": "string", "nullable": true }, "metaDescription": { "type": "string", "nullable": true }, "metaKeywords": { "type": "string", "nullable": true }, "ogImageUrl": { "type": "string", "nullable": true, "format": "uri" }, "sortOrder": { "type": "number", "example": 0 }, "isActive": { "type": "boolean", "example": true }, "isFeatured": { "type": "boolean", "example": false }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": [ "id", "name", "slug", "parentId", "shortDescription", "description", "icon", "color", "coverImageUrl", "bannerImageUrl", "metaTitle", "metaDescription", "metaKeywords", "ogImageUrl", "sortOrder", "isActive", "isFeatured", "createdAt", "updatedAt" ] }, "EventCategorySummaryResponseDto": { "type": "object", "properties": { "id": { "type": "number", "example": 1 }, "name": { "type": "string", "example": "موسیقی" } }, "required": ["id", "name"] }, "CreateEventCategoryDto": { "type": "object", "properties": { "name": { "type": "string", "example": "ورکشاپ" }, "slug": { "type": "string", "example": "workshop" }, "shortDescription": { "type": "string", "example": "کارگاه‌های آموزشی", "nullable": true }, "description": { "type": "string", "nullable": true }, "icon": { "type": "string", "example": "palette", "nullable": true }, "color": { "type": "string", "example": "#FF5733", "nullable": true }, "coverImageUrl": { "type": "string", "example": "https://cdn.example.com/cover.jpg" }, "bannerImageUrl": { "type": "string", "example": "https://cdn.example.com/banner.jpg" }, "metaTitle": { "type": "string", "example": "ورکشاپ‌ها | Ghabilee", "nullable": true }, "metaDescription": { "type": "string", "nullable": true }, "metaKeywords": { "type": "string", "nullable": true }, "ogImageUrl": { "type": "string", "nullable": true }, "sortOrder": { "type": "number", "example": 0 }, "isActive": { "type": "boolean", "example": true }, "isFeatured": { "type": "boolean", "example": false }, "parentId": { "type": "number", "example": null, "nullable": true } }, "required": ["name", "slug"] }, "UpdateEventCategoryDto": { "type": "object", "properties": { "name": { "type": "string", "example": "ورکشاپ" }, "slug": { "type": "string", "example": "workshop" }, "shortDescription": { "type": "string", "example": "کارگاه‌های آموزشی", "nullable": true }, "description": { "type": "string", "nullable": true }, "icon": { "type": "string", "example": "palette", "nullable": true }, "color": { "type": "string", "example": "#FF5733", "nullable": true }, "coverImageUrl": { "type": "string", "example": "https://cdn.example.com/cover.jpg" }, "bannerImageUrl": { "type": "string", "example": "https://cdn.example.com/banner.jpg" }, "metaTitle": { "type": "string", "example": "ورکشاپ‌ها | Ghabilee", "nullable": true }, "metaDescription": { "type": "string", "nullable": true }, "metaKeywords": { "type": "string", "nullable": true }, "ogImageUrl": { "type": "string", "nullable": true }, "sortOrder": { "type": "number", "example": 0 }, "isActive": { "type": "boolean", "example": true }, "isFeatured": { "type": "boolean", "example": false }, "parentId": { "type": "number", "example": null, "nullable": true } } }, "ProvinceResponseDto": { "type": "object", "properties": { "id": { "type": "number", "example": 8 }, "name": { "type": "string", "example": "تهران" }, "lat": { "type": "number", "example": 35.6892 }, "lng": { "type": "number", "example": 51.389 } }, "required": ["id", "name", "lat", "lng"] }, "CityResponseDto": { "type": "object", "properties": { "id": { "type": "number", "example": 301 }, "provinceId": { "type": "number", "example": 8 }, "name": { "type": "string", "example": "تهران" }, "slug": { "type": "string", "example": "tehran" }, "lat": { "type": "number", "example": 35.6892 }, "lng": { "type": "number", "example": 51.389 } }, "required": ["id", "provinceId", "name", "slug"] }, "CityDetailResponseDto": { "type": "object", "properties": { "id": { "type": "number", "example": 301 }, "provinceId": { "type": "number", "example": 8 }, "name": { "type": "string", "example": "تهران" }, "slug": { "type": "string", "example": "tehran" }, "lat": { "type": "number", "example": 35.6892 }, "lng": { "type": "number", "example": 51.389 }, "provinceName": { "type": "string", "example": "استان تهران" }, "provinceSlug": { "type": "string", "example": "tehran-province" } }, "required": ["id", "provinceId", "name", "slug", "provinceName", "provinceSlug"] }, "EventStatus": { "type": "string", "enum": ["draft", "pending_review", "published", "full", "rejected", "cancelled", "completed"] }, "MyEventsHostItemDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string" }, "slug": { "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "startsAt": { "format": "date-time", "type": "string" }, "endsAt": { "format": "date-time", "type": "string" }, "address": { "type": "string" }, "isFree": { "type": "boolean" }, "price": { "type": "number" }, "capacity": { "type": "number" }, "bookedCount": { "type": "number" }, "reservedCapacity": { "type": "number" }, "posterUrl": { "type": "string", "nullable": true }, "squarePosterUrl": { "type": "string", "nullable": true }, "rejectionReason": { "type": "string", "nullable": true }, "categoryId": { "type": "number" }, "categoryName": { "type": "string" } }, "required": [ "id", "title", "slug", "status", "startsAt", "endsAt", "address", "isFree", "price", "capacity", "bookedCount", "reservedCapacity", "categoryId", "categoryName" ] }, "BookingStatus": { "type": "string", "enum": ["pending_payment", "confirmed", "cancelled", "expired", "refunded", "no_show"] }, "MyEventsGuestItemDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "bookingCode": { "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/BookingStatus" } ] }, "expiresAt": { "type": "string", "format": "date-time", "nullable": true }, "eventTitle": { "type": "string" }, "eventPosterUrl": { "type": "string", "format": "uri", "nullable": true }, "eventStartsAt": { "format": "date-time", "type": "string" }, "eventEndsAt": { "format": "date-time", "type": "string" }, "eventStatus": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "eventAddress": { "type": "string", "nullable": true }, "eventPrice": { "type": "number" }, "eventCancellationFeePercent": { "type": "number" }, "eventCancellationFeePercent12To24Hours": { "type": "number" }, "eventCancellationFeePercentMoreThan24Hours": { "type": "number" }, "eventEffectiveCommissionPercent": { "type": "number" }, "payableAmount": { "type": "number", "nullable": true }, "eventCategoryId": { "type": "number" }, "eventCategoryName": { "type": "string" } }, "required": [ "id", "eventId", "bookingCode", "status", "eventTitle", "eventPosterUrl", "eventStartsAt", "eventEndsAt", "eventStatus", "eventAddress", "eventPrice", "eventCancellationFeePercent", "eventCancellationFeePercent12To24Hours", "eventCancellationFeePercentMoreThan24Hours", "eventEffectiveCommissionPercent", "payableAmount", "eventCategoryId", "eventCategoryName" ] }, "EventGenderRestriction": { "type": "string", "enum": ["open", "female_only", "male_only"] }, "EventAgeRestriction": { "type": "string", "enum": ["open", "age_15_19", "age_20_24", "age_25_plus"] }, "EventSettingsDto": { "type": "object", "properties": { "isDiscoverable": { "type": "boolean", "default": false, "description": "Whether the event appears in public search/discovery. Replaces the old top-level isDiscoverable field." }, "autoCreateGroup": { "type": "boolean", "default": true, "description": "Whether the post-event group chat is auto-created when the event completes." }, "addressVisibility": { "type": "string", "enum": ["public", "attendees_only"], "default": "public", "description": "When 'attendees_only', only the organizer/admin and guests with a confirmed booking see the exact address/lat/lng; everyone else sees generalArea instead." }, "generalArea": { "type": "string", "example": "مشهد، خیابان هفت‌تیر", "description": "Coarse location shown instead of the exact address when addressVisibility is 'attendees_only'. Required in that case." }, "waitlistAutoOffer": { "type": "boolean", "default": true, "description": "Whether an opened seat is automatically offered FIFO to the next waitlist entry. When false, the host must manually pick who gets it." }, "sendReviewRequestSms": { "type": "boolean", "default": false, "description": "When true, confirmed guests without a review also receive an SMS (in addition to the in-app notification) after the event is completed. Default is in-app only." } } }, "CreateEventMediaDto": { "type": "object", "properties": { "mediaType": { "type": "string", "enum": ["image", "video"], "example": "image" }, "url": { "type": "string", "description": "Full-resolution media URL" }, "thumbnailUrl": { "type": "string", "description": "Thumbnail/preview URL (especially for video)" }, "sortOrder": { "type": "number", "default": 0, "minimum": 0 }, "isPoster": { "type": "boolean", "default": false }, "isSquarePoster": { "type": "boolean", "default": false, "description": "Marks the 1:1 square card poster. Exactly one image per event." } }, "required": ["mediaType", "url"] }, "CreateEventFaqDto": { "type": "object", "properties": { "question": { "type": "string", "example": "آیا پارکینگ دارد؟", "maxLength": 500 }, "answer": { "type": "string", "example": "بله، پارکینگ رایگان در محل برگزاری موجود است.", "maxLength": 5000 }, "sortOrder": { "type": "number", "default": 0, "minimum": 0 } }, "required": ["question", "answer"] }, "SubmitEventRevisionDto": { "type": "object", "properties": { "title": { "type": "string", "example": "کارگاه عکاسی" }, "slug": { "type": "string", "example": "photography-workshop" }, "shortDescription": { "type": "string" }, "description": { "type": "string" }, "categoryId": { "type": "number", "example": 1 }, "startsAt": { "type": "string", "example": "2026-08-01T10:00:00.000Z" }, "endsAt": { "type": "string", "example": "2026-08-01T14:00:00.000Z" }, "provinceId": { "type": "number", "example": 1 }, "cityId": { "type": "number", "example": 1 }, "address": { "type": "string", "example": "خیابان ولیعصر", "maxLength": 500 }, "lat": { "type": "number", "example": 35.7219 }, "lng": { "type": "number", "example": 51.4084 }, "isFree": { "type": "boolean", "example": false }, "price": { "type": "number", "example": 500000 }, "capacity": { "type": "number", "example": 20 }, "genderRestriction": { "default": "open", "allOf": [ { "$ref": "#/components/schemas/EventGenderRestriction" } ] }, "ageRestriction": { "default": "open", "allOf": [ { "$ref": "#/components/schemas/EventAgeRestriction" } ] }, "cancellationFeePercent": { "type": "number", "example": 30, "default": 30, "description": "Last-minute cancellation fee when 0 < remaining time <= 12 hours." }, "cancellationFeePercent12To24Hours": { "type": "number", "example": 20, "default": 20, "description": "Cancellation fee when 12 < remaining time <= 24 hours." }, "cancellationFeePercentMoreThan24Hours": { "type": "number", "example": 10, "default": 10, "description": "Early cancellation fee when remaining time > 24 hours." }, "settings": { "$ref": "#/components/schemas/EventSettingsDto" }, "media": { "minItems": 2, "description": "Full media set to apply on approve (vertical + square poster required).", "type": "array", "items": { "$ref": "#/components/schemas/CreateEventMediaDto" } }, "faqs": { "type": "array", "items": { "$ref": "#/components/schemas/CreateEventFaqDto" } } }, "required": ["media"] }, "EventRevisionResponseDto": { "type": "object", "properties": { "id": { "type": "string" }, "eventId": { "type": "string" }, "status": { "type": "string", "enum": ["pending", "approved", "rejected", "superseded"] }, "payload": { "type": "object", "description": "Proposed snapshot (scalars + media + faqs)" }, "createdBy": { "type": "string" }, "reviewedBy": { "type": "object" }, "rejectionReason": { "type": "object" }, "submittedAt": { "format": "date-time", "type": "string" }, "decidedAt": { "type": "object" } }, "required": ["id", "eventId", "status", "payload", "createdBy", "submittedAt"] }, "CreateEventDto": { "type": "object", "properties": { "title": { "type": "string", "example": "کارگاه عکاسی" }, "slug": { "type": "string", "example": "photography-workshop" }, "shortDescription": { "type": "string" }, "description": { "type": "string" }, "categoryId": { "type": "number", "example": 1 }, "startsAt": { "type": "string", "example": "2026-08-01T10:00:00.000Z" }, "endsAt": { "type": "string", "example": "2026-08-01T14:00:00.000Z" }, "provinceId": { "type": "number", "example": 1 }, "cityId": { "type": "number", "example": 1 }, "address": { "type": "string", "example": "خیابان ولیعصر", "maxLength": 500 }, "lat": { "type": "number", "example": 35.7219 }, "lng": { "type": "number", "example": 51.4084 }, "isFree": { "type": "boolean", "example": false }, "price": { "type": "number", "example": 500000 }, "capacity": { "type": "number", "example": 20 }, "reservedCapacity": { "type": "number", "example": 0, "default": 0, "description": "Off-platform holds (regular guests, Instagram, etc.). Occupies seats with bookedCount. Must be >= 0 and <= capacity." }, "genderRestriction": { "default": "open", "allOf": [ { "$ref": "#/components/schemas/EventGenderRestriction" } ] }, "ageRestriction": { "default": "open", "allOf": [ { "$ref": "#/components/schemas/EventAgeRestriction" } ] }, "cancellationFeePercent": { "type": "number", "example": 30, "default": 30, "description": "Last-minute cancellation fee when 0 < remaining time <= 12 hours." }, "cancellationFeePercent12To24Hours": { "type": "number", "example": 20, "default": 20, "description": "Cancellation fee when 12 < remaining time <= 24 hours." }, "cancellationFeePercentMoreThan24Hours": { "type": "number", "example": 10, "default": 10, "description": "Early cancellation fee when remaining time > 24 hours." }, "settings": { "$ref": "#/components/schemas/EventSettingsDto" }, "media": { "minItems": 2, "description": "Requires a vertical poster (isPoster) and a square poster (isSquarePoster). Extra gallery images are allowed with both flags false.", "type": "array", "items": { "$ref": "#/components/schemas/CreateEventMediaDto" } } }, "required": [ "title", "slug", "categoryId", "startsAt", "endsAt", "provinceId", "cityId", "address", "lat", "lng", "isFree", "price", "capacity", "media" ] }, "EventSettingsResponseDto": { "type": "object", "properties": { "isDiscoverable": { "type": "boolean" }, "autoCreateGroup": { "type": "boolean" }, "addressVisibility": { "type": "string", "enum": ["public", "attendees_only"] }, "generalArea": { "type": "string", "nullable": true }, "waitlistAutoOffer": { "type": "boolean" }, "sendReviewRequestSms": { "type": "boolean", "description": "When true, review_request after completion is sent as in-app + SMS; otherwise in-app only." } }, "required": ["isDiscoverable", "autoCreateGroup", "addressVisibility", "generalArea", "waitlistAutoOffer", "sendReviewRequestSms"] }, "EventDiscoveryPlacementDto": { "type": "object", "properties": { "placement": { "type": "string" }, "priority": { "type": "number", "minimum": 0 } }, "required": ["placement", "priority"] }, "EventResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "organizerId": { "type": "string", "format": "uuid" }, "categoryId": { "type": "number" }, "clonedFromEventId": { "type": "string", "format": "uuid", "nullable": true }, "title": { "type": "string" }, "slug": { "type": "string" }, "shortDescription": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "startsAt": { "format": "date-time", "type": "string" }, "endsAt": { "format": "date-time", "type": "string" }, "provinceId": { "type": "number" }, "cityId": { "type": "number" }, "categoryName": { "type": "string", "description": "Display name of the event category (avoids a second taxonomy round-trip)." }, "cityName": { "type": "string", "description": "Display name of the event city (avoids a second geography round-trip)." }, "address": { "type": "string", "nullable": true, "description": "Exact street address. null when settings.addressVisibility is 'attendees_only' and the current viewer is not the organizer/admin and has no confirmed booking — see generalArea in settings." }, "lat": { "type": "number", "nullable": true, "description": "Same visibility rule as address." }, "lng": { "type": "number", "nullable": true, "description": "Same visibility rule as address." }, "isFree": { "type": "boolean" }, "price": { "type": "number" }, "capacity": { "type": "number" }, "bookedCount": { "type": "number" }, "reservedCapacity": { "type": "number", "description": "Off-platform / pre-held seats (regular guests, Instagram). Occupies capacity with bookedCount." }, "genderRestriction": { "allOf": [ { "$ref": "#/components/schemas/EventGenderRestriction" } ] }, "ageRestriction": { "allOf": [ { "$ref": "#/components/schemas/EventAgeRestriction" } ] }, "cancellationFeePercent": { "type": "number", "description": "Last-minute fee for 0 < remaining time <= 12 hours." }, "cancellationFeePercent12To24Hours": { "type": "number", "description": "Fee for 12 < remaining time <= 24 hours." }, "cancellationFeePercentMoreThan24Hours": { "type": "number", "description": "Early fee for remaining time > 24 hours." }, "commissionPercent": { "type": "number", "nullable": true, "description": "Admin override of platform commission for this event. null means the event follows the platform default." }, "effectiveCommissionPercent": { "type": "number", "description": "Resolved commission percent actually applied — commissionPercent if set, else the platform default." }, "status": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "isFeatured": { "type": "boolean" }, "settings": { "$ref": "#/components/schemas/EventSettingsResponseDto" }, "discoveryPlacements": { "type": "array", "items": { "$ref": "#/components/schemas/EventDiscoveryPlacementDto" } }, "publishedAt": { "type": "string", "format": "date-time", "nullable": true }, "adminApprovedAt": { "type": "string", "format": "date-time", "nullable": true }, "rejectionReason": { "type": "string", "nullable": true }, "avgRating": { "type": "number", "nullable": true, "description": "Mean published guest rating (1–5), or null when none." }, "reviewsCount": { "type": "number", "description": "Count of published guest reviews for this event.", "minimum": 0 }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "squarePosterUrl": { "type": "string", "format": "uri", "nullable": true, "description": "1:1 square card poster URL." } }, "required": [ "id", "organizerId", "categoryId", "clonedFromEventId", "title", "slug", "shortDescription", "description", "startsAt", "endsAt", "provinceId", "cityId", "categoryName", "cityName", "address", "lat", "lng", "isFree", "price", "capacity", "bookedCount", "reservedCapacity", "genderRestriction", "ageRestriction", "cancellationFeePercent", "cancellationFeePercent12To24Hours", "cancellationFeePercentMoreThan24Hours", "commissionPercent", "effectiveCommissionPercent", "status", "isFeatured", "settings", "discoveryPlacements", "publishedAt", "adminApprovedAt", "rejectionReason", "reviewsCount", "createdAt", "updatedAt", "posterUrl", "squarePosterUrl" ] }, "OwnerEventRegistrationInsightsDto": { "type": "object", "properties": { "bookedCount": { "type": "number", "minimum": 0 }, "reservedCapacity": { "type": "number", "minimum": 0 }, "capacity": { "type": "number", "minimum": 0 }, "remainingCapacity": { "type": "number", "minimum": 0 }, "pendingPaymentCount": { "type": "number", "minimum": 0 }, "confirmedCount": { "type": "number", "minimum": 0 }, "checkedInCount": { "type": "number", "minimum": 0 }, "notCheckedInCount": { "type": "number", "minimum": 0 }, "cancelledCount": { "type": "number", "minimum": 0 }, "expiredCount": { "type": "number", "minimum": 0 }, "refundedCount": { "type": "number", "minimum": 0 }, "noShowCount": { "type": "number", "minimum": 0 }, "waitlistCount": { "type": "number", "minimum": 0 } }, "required": [ "bookedCount", "reservedCapacity", "capacity", "remainingCapacity", "pendingPaymentCount", "confirmedCount", "checkedInCount", "notCheckedInCount", "cancelledCount", "expiredCount", "refundedCount", "noShowCount", "waitlistCount" ] }, "OwnerEventFinancialInsightsDto": { "type": "object", "properties": { "grossAmount": { "type": "number", "minimum": 0, "description": "Successful ticket sales." }, "commissionAmount": { "type": "number", "minimum": 0 }, "guestRefundAmount": { "type": "number", "minimum": 0 }, "netAmount": { "type": "number", "minimum": 0, "description": "Organizer net after refunds." }, "heldAmount": { "type": "number", "minimum": 0 }, "availableAmount": { "type": "number", "minimum": 0 }, "settledAmount": { "type": "number", "minimum": 0 } }, "required": ["grossAmount", "commissionAmount", "guestRefundAmount", "netAmount", "heldAmount", "availableAmount", "settledAmount"] }, "OwnerEventTrafficChannelInsightsDto": { "type": "object", "properties": { "uniqueVisits": { "type": "number", "minimum": 0 }, "confirmedBookings": { "type": "number", "minimum": 0 } }, "required": ["uniqueVisits", "confirmedBookings"] }, "OwnerEventTrafficInsightsDto": { "type": "object", "properties": { "instagram": { "$ref": "#/components/schemas/OwnerEventTrafficChannelInsightsDto" }, "telegram": { "$ref": "#/components/schemas/OwnerEventTrafficChannelInsightsDto" } }, "required": ["instagram", "telegram"] }, "OwnerEventInsightsResponseDto": { "type": "object", "properties": { "bookmarkCount": { "type": "number", "minimum": 0 }, "registrations": { "$ref": "#/components/schemas/OwnerEventRegistrationInsightsDto" }, "financial": { "$ref": "#/components/schemas/OwnerEventFinancialInsightsDto" }, "traffic": { "$ref": "#/components/schemas/OwnerEventTrafficInsightsDto" } }, "required": ["bookmarkCount", "registrations", "financial", "traffic"] }, "EventMediaType": { "type": "string", "enum": ["image", "video"] }, "EventMediaResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "mediaType": { "allOf": [ { "$ref": "#/components/schemas/EventMediaType" } ] }, "url": { "type": "string", "format": "uri" }, "thumbnailUrl": { "type": "string", "format": "uri", "nullable": true }, "sortOrder": { "type": "number" }, "isPoster": { "type": "boolean" }, "isSquarePoster": { "type": "boolean" }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "eventId", "mediaType", "url", "thumbnailUrl", "sortOrder", "isPoster", "isSquarePoster", "createdAt"] }, "EventFaqResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "question": { "type": "string" }, "answer": { "type": "string" }, "sortOrder": { "type": "number" }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": ["id", "eventId", "question", "answer", "sortOrder", "createdAt", "updatedAt"] }, "EventManagementBootstrapResponseDto": { "type": "object", "properties": { "event": { "$ref": "#/components/schemas/EventResponseDto" }, "insights": { "$ref": "#/components/schemas/OwnerEventInsightsResponseDto" }, "categoryName": { "type": "string" }, "cityName": { "type": "string" }, "provinceName": { "type": "string" }, "media": { "type": "array", "items": { "$ref": "#/components/schemas/EventMediaResponseDto" } }, "faqs": { "type": "array", "items": { "$ref": "#/components/schemas/EventFaqResponseDto" } } }, "required": ["event", "insights", "categoryName", "cityName", "provinceName", "media", "faqs"] }, "EventAttendeeResponseDto": { "type": "object", "properties": { "bookingId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "bookingCode": { "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/BookingStatus" } ] }, "firstName": { "type": "object", "nullable": true }, "lastName": { "type": "object", "nullable": true }, "confirmedAt": { "type": "object", "format": "date-time", "nullable": true }, "checkedInAt": { "type": "object", "format": "date-time", "nullable": true }, "noShowAt": { "type": "object", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["bookingId", "userId", "bookingCode", "status", "createdAt"] }, "EventAttendeeFilter": { "type": "string", "enum": ["all", "confirmed", "checked_in", "not_checked_in", "pending_payment", "cancelled"] }, "UpdateEventReservedCapacityDto": { "type": "object", "properties": { "reservedCapacity": { "type": "number", "example": 3, "minimum": 0, "description": "Absolute off-platform reserved seat count. Occupancy is bookedCount + reservedCapacity and cannot exceed capacity. Pending payments already occupy seats." } }, "required": ["reservedCapacity"] }, "UpdateEventDto": { "type": "object", "properties": { "title": { "type": "string", "example": "کارگاه عکاسی" }, "slug": { "type": "string", "example": "photography-workshop" }, "shortDescription": { "type": "string" }, "description": { "type": "string" }, "categoryId": { "type": "number", "example": 1 }, "startsAt": { "type": "string", "example": "2026-08-01T10:00:00.000Z" }, "endsAt": { "type": "string", "example": "2026-08-01T14:00:00.000Z" }, "provinceId": { "type": "number", "example": 1 }, "cityId": { "type": "number", "example": 1 }, "address": { "type": "string", "example": "خیابان ولیعصر", "maxLength": 500 }, "lat": { "type": "number", "example": 35.7219 }, "lng": { "type": "number", "example": 51.4084 }, "isFree": { "type": "boolean", "example": false }, "price": { "type": "number", "example": 500000 }, "capacity": { "type": "number", "example": 20 }, "genderRestriction": { "default": "open", "allOf": [ { "$ref": "#/components/schemas/EventGenderRestriction" } ] }, "ageRestriction": { "default": "open", "allOf": [ { "$ref": "#/components/schemas/EventAgeRestriction" } ] }, "cancellationFeePercent": { "type": "number", "example": 30, "default": 30, "description": "Last-minute cancellation fee when 0 < remaining time <= 12 hours." }, "cancellationFeePercent12To24Hours": { "type": "number", "example": 20, "default": 20, "description": "Cancellation fee when 12 < remaining time <= 24 hours." }, "cancellationFeePercentMoreThan24Hours": { "type": "number", "example": 10, "default": 10, "description": "Early cancellation fee when remaining time > 24 hours." }, "settings": { "$ref": "#/components/schemas/EventSettingsDto" } } }, "BookmarkStatusDto": { "type": "object", "properties": { "eventIds": { "maxItems": 100, "type": "array", "items": { "type": "string", "format": "uuid" } } }, "required": ["eventIds"] }, "BookmarkBatchStatusResponseDto": { "type": "object", "properties": { "bookmarkedEventIds": { "type": "array", "items": { "type": "string", "format": "uuid" } } }, "required": ["bookmarkedEventIds"] }, "BookmarkStateResponseDto": { "type": "object", "properties": { "isBookmarked": { "type": "boolean" } }, "required": ["isBookmarked"] }, "DiscoveryEventOrganizerDto": { "type": "object", "properties": { "firstName": { "type": "string", "nullable": true }, "lastName": { "type": "string", "nullable": true } }, "required": ["firstName", "lastName"] }, "DiscoveryEventResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "organizerId": { "type": "string", "format": "uuid" }, "categoryId": { "type": "number" }, "clonedFromEventId": { "type": "string", "format": "uuid", "nullable": true }, "title": { "type": "string" }, "slug": { "type": "string" }, "shortDescription": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "startsAt": { "format": "date-time", "type": "string" }, "endsAt": { "format": "date-time", "type": "string" }, "provinceId": { "type": "number" }, "cityId": { "type": "number" }, "categoryName": { "type": "string", "description": "Display name of the event category (avoids a second taxonomy round-trip)." }, "cityName": { "type": "string", "description": "Display name of the event city (avoids a second geography round-trip)." }, "address": { "type": "string", "nullable": true, "description": "Exact street address. null when settings.addressVisibility is 'attendees_only' and the current viewer is not the organizer/admin and has no confirmed booking — see generalArea in settings." }, "lat": { "type": "number", "nullable": true, "description": "Same visibility rule as address." }, "lng": { "type": "number", "nullable": true, "description": "Same visibility rule as address." }, "isFree": { "type": "boolean" }, "price": { "type": "number" }, "capacity": { "type": "number" }, "bookedCount": { "type": "number" }, "reservedCapacity": { "type": "number", "description": "Off-platform / pre-held seats (regular guests, Instagram). Occupies capacity with bookedCount." }, "genderRestriction": { "allOf": [ { "$ref": "#/components/schemas/EventGenderRestriction" } ] }, "ageRestriction": { "allOf": [ { "$ref": "#/components/schemas/EventAgeRestriction" } ] }, "cancellationFeePercent": { "type": "number", "description": "Last-minute fee for 0 < remaining time <= 12 hours." }, "cancellationFeePercent12To24Hours": { "type": "number", "description": "Fee for 12 < remaining time <= 24 hours." }, "cancellationFeePercentMoreThan24Hours": { "type": "number", "description": "Early fee for remaining time > 24 hours." }, "commissionPercent": { "type": "number", "nullable": true, "description": "Admin override of platform commission for this event. null means the event follows the platform default." }, "effectiveCommissionPercent": { "type": "number", "description": "Resolved commission percent actually applied — commissionPercent if set, else the platform default." }, "status": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "isFeatured": { "type": "boolean" }, "settings": { "$ref": "#/components/schemas/EventSettingsResponseDto" }, "discoveryPlacements": { "type": "array", "items": { "$ref": "#/components/schemas/EventDiscoveryPlacementDto" } }, "publishedAt": { "type": "string", "format": "date-time", "nullable": true }, "adminApprovedAt": { "type": "string", "format": "date-time", "nullable": true }, "rejectionReason": { "type": "string", "nullable": true }, "avgRating": { "type": "number", "nullable": true, "description": "Mean published guest rating (1–5), or null when none." }, "reviewsCount": { "type": "number", "description": "Count of published guest reviews for this event.", "minimum": 0 }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "squarePosterUrl": { "type": "string", "format": "uri", "nullable": true, "description": "1:1 square card poster URL." }, "organizer": { "$ref": "#/components/schemas/DiscoveryEventOrganizerDto" } }, "required": [ "id", "organizerId", "categoryId", "clonedFromEventId", "title", "slug", "shortDescription", "description", "startsAt", "endsAt", "provinceId", "cityId", "categoryName", "cityName", "address", "lat", "lng", "isFree", "price", "capacity", "bookedCount", "reservedCapacity", "genderRestriction", "ageRestriction", "cancellationFeePercent", "cancellationFeePercent12To24Hours", "cancellationFeePercentMoreThan24Hours", "commissionPercent", "effectiveCommissionPercent", "status", "isFeatured", "settings", "discoveryPlacements", "publishedAt", "adminApprovedAt", "rejectionReason", "reviewsCount", "createdAt", "updatedAt", "posterUrl", "squarePosterUrl", "organizer" ] }, "EventLandingCategoryDto": { "type": "object", "properties": { "id": { "type": "number" }, "name": { "type": "string" }, "slug": { "type": "string" } }, "required": ["id", "name", "slug"] }, "EventLandingCityDto": { "type": "object", "properties": { "id": { "type": "number" }, "name": { "type": "string" } }, "required": ["id", "name"] }, "EventLandingOrganizerContactLinkDto": { "type": "object", "properties": { "channel": { "type": "string" }, "label": { "type": "string" }, "url": { "type": "string" } }, "required": ["channel", "label", "url"] }, "EventLandingOrganizerDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "avatarUrl": { "type": "string", "nullable": true }, "gender": { "type": "string", "enum": ["male", "female", "other"], "nullable": true }, "bio": { "type": "string", "nullable": true }, "cityName": { "type": "string", "nullable": true }, "isVerified": { "type": "boolean" }, "memberSince": { "format": "date-time", "type": "string" }, "followersCount": { "type": "number" }, "pastEventsCount": { "type": "number" }, "totalGuestsCount": { "type": "number" }, "contactLinks": { "type": "array", "items": { "$ref": "#/components/schemas/EventLandingOrganizerContactLinkDto" } } }, "required": [ "id", "firstName", "lastName", "avatarUrl", "gender", "bio", "cityName", "isVerified", "memberSince", "followersCount", "pastEventsCount", "totalGuestsCount", "contactLinks" ] }, "ReviewStatus": { "type": "string", "enum": ["published", "hidden", "deleted"], "description": "Publication status." }, "ReviewUserSummaryDto": { "type": "object", "properties": { "id": { "type": "string", "description": "User identifier.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "firstName": { "type": "string", "description": "User first name.", "example": "Ali", "nullable": true }, "lastName": { "type": "string", "description": "User last name.", "example": "Rezaei", "nullable": true }, "avatarUrl": { "type": "string", "description": "Public profile avatar URL when the user has uploaded one.", "example": "https://cdn.example.com/avatars/u1.jpg", "nullable": true } }, "required": ["id", "firstName", "lastName", "avatarUrl"] }, "ReviewResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Review identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "eventId": { "type": "string", "description": "Reviewed event identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "userId": { "type": "string", "description": "Guest who wrote the review.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "bookingId": { "type": "string", "description": "Booking that earned the right to review.", "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "format": "uuid" }, "rating": { "type": "number", "description": "Star rating from 1 to 5.", "example": 5, "minimum": 1, "maximum": 5 }, "body": { "type": "string", "description": "Optional review text; null when the guest rated without a comment.", "example": "Great event — well organized and welcoming.", "nullable": true }, "hostReplyBody": { "type": "string", "description": "Organizer reply text, when present.", "example": "Thank you for attending!", "nullable": true }, "hostReplyAt": { "type": "string", "description": "Timestamp when the organizer first replied.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time", "nullable": true }, "status": { "description": "Publication status.", "example": "published", "allOf": [ { "$ref": "#/components/schemas/ReviewStatus" } ] }, "createdAt": { "type": "string", "description": "Review creation timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" }, "updatedAt": { "type": "string", "description": "Last update timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "user": { "description": "Guest who wrote the review (public name and optional avatar).", "allOf": [ { "$ref": "#/components/schemas/ReviewUserSummaryDto" } ] } }, "required": ["id", "eventId", "userId", "bookingId", "rating", "body", "status", "createdAt", "updatedAt", "user"] }, "EventLandingBootstrapResponseDto": { "type": "object", "properties": { "event": { "$ref": "#/components/schemas/EventResponseDto" }, "category": { "$ref": "#/components/schemas/EventLandingCategoryDto" }, "city": { "$ref": "#/components/schemas/EventLandingCityDto" }, "media": { "type": "array", "items": { "$ref": "#/components/schemas/EventMediaResponseDto" } }, "faqs": { "type": "array", "items": { "$ref": "#/components/schemas/EventFaqResponseDto" } }, "organizer": { "$ref": "#/components/schemas/EventLandingOrganizerDto" }, "reviews": { "type": "array", "items": { "$ref": "#/components/schemas/ReviewResponseDto" } }, "reviewsTotal": { "type": "number" } }, "required": ["event", "category", "city", "media", "faqs", "organizer", "reviews", "reviewsTotal"] }, "EventViewerBookingDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "status": { "type": "string", "enum": ["pending_payment", "confirmed", "cancelled", "expired", "refunded", "no_show"] } }, "required": ["id", "status"] }, "EventViewerWaitlistDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "status": { "type": "string", "enum": ["waiting", "notified", "accepted", "expired", "cancelled", "converted"] } }, "required": ["id", "status"] }, "EventViewerLocationDto": { "type": "object", "properties": { "address": { "type": "string" }, "lat": { "type": "number" }, "lng": { "type": "number" } }, "required": ["address", "lat", "lng"] }, "EventViewerStateResponseDto": { "type": "object", "properties": { "isOwner": { "type": "boolean" }, "isBookmarked": { "type": "boolean" }, "isFollowingOrganizer": { "type": "boolean" }, "activeBooking": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/EventViewerBookingDto" } ] }, "waitlistEntry": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/EventViewerWaitlistDto" } ] }, "exactLocation": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/EventViewerLocationDto" } ] }, "canJoinAudience": { "type": "boolean", "description": "Whether the logged-in viewer matches the event gender, age, and city eligibility rules." }, "audienceBlockReasons": { "type": "array", "description": "Empty when canJoinAudience is true.", "items": { "type": "string", "enum": ["gender", "age", "city"] } } }, "required": [ "isOwner", "isBookmarked", "isFollowingOrganizer", "activeBooking", "waitlistEntry", "exactLocation", "canJoinAudience", "audienceBlockReasons" ] }, "RejectEventRevisionDto": { "type": "object", "properties": { "rejectionReason": { "type": "string", "minLength": 3, "maxLength": 2000 } }, "required": ["rejectionReason"] }, "AdminEventRelationSummaryDto": { "type": "object", "properties": { "id": { "type": "number" }, "name": { "type": "string" } }, "required": ["id", "name"] }, "AdminEventOrganizerSummaryDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "firstName": { "type": "object", "nullable": true }, "lastName": { "type": "object", "nullable": true }, "mobile": { "type": "object", "nullable": true } }, "required": ["id"] }, "AdminEventResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string" }, "startsAt": { "format": "date-time", "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "isFree": { "type": "boolean" }, "price": { "type": "number" }, "category": { "$ref": "#/components/schemas/AdminEventRelationSummaryDto" }, "province": { "$ref": "#/components/schemas/AdminEventRelationSummaryDto" }, "city": { "$ref": "#/components/schemas/AdminEventRelationSummaryDto" }, "organizer": { "$ref": "#/components/schemas/AdminEventOrganizerSummaryDto" } }, "required": ["id", "title", "startsAt", "status", "isFree", "price", "category", "province", "city", "organizer"] }, "AdminEventDetailResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "organizerId": { "type": "string", "format": "uuid" }, "categoryId": { "type": "number" }, "clonedFromEventId": { "type": "string", "format": "uuid", "nullable": true }, "title": { "type": "string" }, "slug": { "type": "string" }, "shortDescription": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "startsAt": { "format": "date-time", "type": "string" }, "endsAt": { "format": "date-time", "type": "string" }, "provinceId": { "type": "number" }, "cityId": { "type": "number" }, "categoryName": { "type": "string", "description": "Display name of the event category (avoids a second taxonomy round-trip)." }, "cityName": { "type": "string", "description": "Display name of the event city (avoids a second geography round-trip)." }, "address": { "type": "string", "nullable": true, "description": "Exact street address. null when settings.addressVisibility is 'attendees_only' and the current viewer is not the organizer/admin and has no confirmed booking — see generalArea in settings." }, "lat": { "type": "number", "nullable": true, "description": "Same visibility rule as address." }, "lng": { "type": "number", "nullable": true, "description": "Same visibility rule as address." }, "isFree": { "type": "boolean" }, "price": { "type": "number" }, "capacity": { "type": "number" }, "bookedCount": { "type": "number" }, "reservedCapacity": { "type": "number", "description": "Off-platform / pre-held seats (regular guests, Instagram). Occupies capacity with bookedCount." }, "genderRestriction": { "allOf": [ { "$ref": "#/components/schemas/EventGenderRestriction" } ] }, "ageRestriction": { "allOf": [ { "$ref": "#/components/schemas/EventAgeRestriction" } ] }, "cancellationFeePercent": { "type": "number", "description": "Last-minute fee for 0 < remaining time <= 12 hours." }, "cancellationFeePercent12To24Hours": { "type": "number", "description": "Fee for 12 < remaining time <= 24 hours." }, "cancellationFeePercentMoreThan24Hours": { "type": "number", "description": "Early fee for remaining time > 24 hours." }, "commissionPercent": { "type": "number", "nullable": true, "description": "Admin override of platform commission for this event. null means the event follows the platform default." }, "effectiveCommissionPercent": { "type": "number", "description": "Resolved commission percent actually applied — commissionPercent if set, else the platform default." }, "status": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "isFeatured": { "type": "boolean" }, "settings": { "$ref": "#/components/schemas/EventSettingsResponseDto" }, "discoveryPlacements": { "type": "array", "items": { "$ref": "#/components/schemas/EventDiscoveryPlacementDto" } }, "publishedAt": { "type": "string", "format": "date-time", "nullable": true }, "adminApprovedAt": { "type": "string", "format": "date-time", "nullable": true }, "rejectionReason": { "type": "string", "nullable": true }, "avgRating": { "type": "number", "nullable": true, "description": "Mean published guest rating (1–5), or null when none." }, "reviewsCount": { "type": "number", "description": "Count of published guest reviews for this event.", "minimum": 0 }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "squarePosterUrl": { "type": "string", "format": "uri", "nullable": true, "description": "1:1 square card poster URL." }, "bookmarkCount": { "type": "number", "minimum": 0, "description": "Number of users who bookmarked this event." }, "organizer": { "$ref": "#/components/schemas/AdminEventOrganizerSummaryDto" } }, "required": [ "id", "organizerId", "categoryId", "clonedFromEventId", "title", "slug", "shortDescription", "description", "startsAt", "endsAt", "provinceId", "cityId", "categoryName", "cityName", "address", "lat", "lng", "isFree", "price", "capacity", "bookedCount", "reservedCapacity", "genderRestriction", "ageRestriction", "cancellationFeePercent", "cancellationFeePercent12To24Hours", "cancellationFeePercentMoreThan24Hours", "commissionPercent", "effectiveCommissionPercent", "status", "isFeatured", "settings", "discoveryPlacements", "publishedAt", "adminApprovedAt", "rejectionReason", "reviewsCount", "createdAt", "updatedAt", "posterUrl", "squarePosterUrl", "bookmarkCount", "organizer" ] }, "UpdateEventCommissionDto": { "type": "object", "properties": { "commissionPercent": { "type": "number", "nullable": true, "minimum": 0, "maximum": 100, "example": 10, "description": "Per-event platform commission override (0-100). null resets the event to the platform default." } } }, "RejectEventDto": { "type": "object", "properties": { "rejectionReason": { "type": "string", "description": "Reason shown to the organizer when the review is rejected.", "minLength": 3, "maxLength": 2000 } }, "required": ["rejectionReason"] }, "DiscoveryCategorySummaryDto": { "type": "object", "properties": { "id": { "type": "number", "example": 1 }, "name": { "type": "string", "example": "موسیقی" }, "slug": { "type": "string", "example": "music" }, "parentId": { "type": "number", "nullable": true, "example": null }, "sortOrder": { "type": "number", "example": 0 }, "isActive": { "type": "boolean", "example": true } }, "required": ["id", "name", "slug", "parentId", "sortOrder", "isActive"] }, "HomeFeedOrganizerDto": { "type": "object", "properties": { "firstName": { "type": "string", "nullable": true }, "lastName": { "type": "string", "nullable": true } } }, "HomePopularEventDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string" }, "slug": { "type": "string" }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "squarePosterUrl": { "type": "string", "format": "uri", "nullable": true }, "organizer": { "$ref": "#/components/schemas/HomeFeedOrganizerDto" } }, "required": ["id", "title", "slug", "posterUrl", "squarePosterUrl", "organizer"] }, "HomeCategoryPreviewEventDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string" }, "slug": { "type": "string" }, "startsAt": { "format": "date-time", "type": "string" }, "endsAt": { "format": "date-time", "type": "string" }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "squarePosterUrl": { "type": "string", "format": "uri", "nullable": true } }, "required": ["id", "title", "slug", "startsAt", "endsAt", "posterUrl", "squarePosterUrl"] }, "HomeFeedCategoryPreviewGroupDto": { "type": "object", "properties": { "categoryId": { "type": "number", "example": 1 }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/HomeCategoryPreviewEventDto" } } }, "required": ["categoryId", "items"] }, "HomeCityPreviewEventDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string" }, "slug": { "type": "string" }, "startsAt": { "format": "date-time", "type": "string" }, "endsAt": { "format": "date-time", "type": "string" }, "categoryId": { "type": "number" }, "cityId": { "type": "number" }, "address": { "type": "string", "nullable": true }, "isFree": { "type": "boolean" }, "price": { "type": "number" }, "capacity": { "type": "number" }, "bookedCount": { "type": "number" }, "reservedCapacity": { "type": "number" }, "status": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "posterUrl": { "type": "string", "format": "uri", "nullable": true }, "squarePosterUrl": { "type": "string", "format": "uri", "nullable": true }, "organizer": { "$ref": "#/components/schemas/HomeFeedOrganizerDto" } }, "required": [ "id", "title", "slug", "startsAt", "endsAt", "categoryId", "cityId", "address", "isFree", "price", "capacity", "bookedCount", "reservedCapacity", "status", "posterUrl", "squarePosterUrl", "organizer" ] }, "HomeFeedCityPreviewGroupDto": { "type": "object", "properties": { "cityId": { "type": "number", "example": 1 }, "items": { "maxItems": 4, "type": "array", "items": { "$ref": "#/components/schemas/HomeCityPreviewEventDto" } } }, "required": ["cityId", "items"] }, "HomeFeedResponseDto": { "type": "object", "properties": { "popular": { "type": "array", "items": { "$ref": "#/components/schemas/HomePopularEventDto" } }, "categoryPreviews": { "type": "array", "items": { "$ref": "#/components/schemas/HomeFeedCategoryPreviewGroupDto" } }, "cityPreviews": { "type": "array", "items": { "$ref": "#/components/schemas/HomeFeedCityPreviewGroupDto" } } }, "required": ["popular", "categoryPreviews", "cityPreviews"] }, "DiscoveryBootstrapResponseDto": { "type": "object", "properties": { "categories": { "type": "array", "items": { "$ref": "#/components/schemas/EventCategoryResponseDto" } }, "cities": { "type": "array", "items": { "$ref": "#/components/schemas/CityResponseDto" } } }, "required": ["categories", "cities"] }, "FollowUserSummaryDto": { "type": "object", "properties": { "firstName": { "type": "string", "description": "User first name.", "example": "Ali", "nullable": true }, "lastName": { "type": "string", "description": "User last name.", "example": "SaZa", "nullable": true }, "avatarUrl": { "type": "string", "description": "Profile avatar URL.", "example": "https://storage.ghabilee.com/avatars/user-123.jpg", "format": "uri", "nullable": true }, "gender": { "description": "Gender for placeholder avatars when no photo is set.", "nullable": true, "allOf": [ { "$ref": "#/components/schemas/Gender" } ] } }, "required": ["firstName", "lastName"] }, "FollowResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Follow record identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "followerId": { "type": "string", "description": "Follower user identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "organizerId": { "type": "string", "description": "Organizer user identifier.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "status": { "type": "string", "description": "Follow status.", "enum": ["active", "muted"], "example": "active" }, "notifyNewEvents": { "type": "boolean", "description": "Whether new-event notifications are enabled for this follow.", "example": true }, "createdAt": { "type": "string", "description": "When the follow was created.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" }, "user": { "description": "Minimal profile of the related user (follower or organizer).", "allOf": [ { "$ref": "#/components/schemas/FollowUserSummaryDto" } ] } }, "required": ["id", "followerId", "organizerId", "status", "notifyNewEvents", "createdAt"] }, "UpdateFollowSettingsDto": { "type": "object", "properties": { "notifyNewEvents": { "type": "boolean", "description": "Whether to receive notifications when this organizer publishes events.", "example": true } } }, "UpdateEventMediaDto": { "type": "object", "properties": { "mediaType": { "type": "string", "enum": ["image", "video"] }, "url": { "type": "string" }, "thumbnailUrl": { "type": "string" }, "sortOrder": { "type": "number", "minimum": 0 }, "isPoster": { "type": "boolean" }, "isSquarePoster": { "type": "boolean" } } }, "UpdateEventFaqDto": { "type": "object", "properties": { "question": { "type": "string", "maxLength": 500 }, "answer": { "type": "string", "maxLength": 5000 }, "sortOrder": { "type": "number", "minimum": 0 } } }, "PreviousAttendeeResponseDto": { "type": "object", "properties": { "userId": { "type": "string", "format": "uuid" }, "firstName": { "type": "string", "nullable": true }, "lastName": { "type": "string", "nullable": true } }, "required": ["userId", "firstName", "lastName"] }, "OrganizerGuestListResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "organizerId": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "description": { "type": "string", "nullable": true }, "kind": { "type": "string", "enum": ["manual", "previous_attendees"] }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": ["id", "organizerId", "name", "description", "kind", "createdAt", "updatedAt"] }, "CreateOrganizerGuestListDto": { "type": "object", "properties": { "name": { "type": "string", "example": "مشتریان ثابت", "maxLength": 150 }, "description": { "type": "string", "example": "مهمانان رویدادهای قبلی و مخاطبین میزبان" } }, "required": ["name"] }, "GuestListItemResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "listId": { "type": "string", "format": "uuid" }, "mobile": { "type": "string", "description": "Present for host-entered manual contacts. Omitted for previous_attendees (users.mobile must not leak to hosts)." }, "firstName": { "type": "string", "nullable": true }, "lastName": { "type": "string", "nullable": true }, "userId": { "type": "string", "format": "uuid", "nullable": true }, "note": { "type": "string", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "listId", "firstName", "lastName", "userId", "note", "createdAt"] }, "AddGuestListItemDto": { "type": "object", "properties": { "mobile": { "type": "string", "example": "09121234567" }, "firstName": { "type": "string", "maxLength": 100 }, "lastName": { "type": "string", "maxLength": 100 }, "userId": { "type": "string", "description": "Linked platform user, if known" }, "note": { "type": "string" } }, "required": ["mobile"] }, "SetEventInvitedGuestsDto": { "type": "object", "properties": { "userIds": { "description": "Full replacement set of platform user ids invited to this event from the host guest list. An empty array clears the invite set.", "example": ["b3f1c2a0-1111-4111-8111-b3f1c2a01111"], "type": "array", "items": { "type": "string" } } }, "required": ["userIds"] }, "InvitedGuestResponseDto": { "type": "object", "properties": { "userId": { "type": "string", "format": "uuid", "nullable": true }, "mobile": { "type": "string", "description": "Admin-only. Host endpoints omit mobile so users.mobile never leaks to organizers." }, "firstName": { "type": "string", "nullable": true }, "lastName": { "type": "string", "nullable": true } }, "required": ["userId", "firstName", "lastName"] }, "GuestListNotifyResultDto": { "type": "object", "properties": { "sent": { "type": "number", "description": "How many invitation notifications were dispatched" }, "skipped": { "type": "number", "description": "Contacts skipped because no platform user could be resolved from user_id/mobile" }, "notifiedAt": { "format": "date-time", "type": "string" } }, "required": ["sent", "skipped", "notifiedAt"] }, "GuestListLinkResponseDto": { "type": "object", "properties": { "eventId": { "type": "string", "format": "uuid" }, "listId": { "type": "string", "format": "uuid" }, "listName": { "type": "string" }, "notifiedAt": { "type": "string", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["eventId", "listId", "notifiedAt", "createdAt"] }, "LinkGuestListToEventDto": { "type": "object", "properties": { "listId": { "type": "string", "description": "Guest list to attach to the event" } }, "required": ["listId"] }, "CreatePaymentDto": { "type": "object", "properties": { "walletAmount": { "type": "number", "description": "Wallet amount in Toman to use for this payment attempt", "minimum": 0, "example": 250000 }, "discountCode": { "type": "string", "description": "Optional discount code to apply to this booking", "example": "SAVE20AB" } } }, "PaymentMethod": { "type": "string", "enum": ["gateway", "wallet", "mixed"] }, "PaymentStatus": { "type": "string", "enum": ["pending", "processing", "succeeded", "failed", "cancelled"] }, "PaymentCheckoutResponseDto": { "type": "object", "properties": { "trackingId": { "type": "string" }, "paymentUrl": { "type": "string", "format": "uri" } }, "required": ["trackingId", "paymentUrl"] }, "CreatePaymentResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "bookingId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "paymentCode": { "type": "string" }, "method": { "allOf": [ { "$ref": "#/components/schemas/PaymentMethod" } ] }, "status": { "allOf": [ { "$ref": "#/components/schemas/PaymentStatus" } ] }, "totalAmount": { "type": "number" }, "walletAmount": { "type": "number" }, "gatewayAmount": { "type": "number" }, "gatewayProvider": { "type": "string", "nullable": true }, "gatewayTrackingId": { "type": "string", "nullable": true }, "gatewayRefId": { "type": "string", "nullable": true }, "paidAt": { "type": "string", "format": "date-time", "nullable": true }, "failedAt": { "type": "string", "format": "date-time", "nullable": true }, "failureReason": { "type": "string", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "checkout": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/PaymentCheckoutResponseDto" } ] }, "isSimulated": { "type": "boolean", "description": "True when gateway payment was automatically completed by the local simulator" }, "depositAmount": { "type": "number", "description": "Amount charged via gateway deposit for this checkout (Toman). Zero when paid fully from existing wallet balance." }, "walletBalanceUsed": { "type": "number", "description": "Portion of the booking total covered by wallet balance before any gateway deposit (Toman)." }, "depositId": { "type": "string", "format": "uuid", "nullable": true, "description": "Wallet deposit session id when checkout requires a gateway top-up." } }, "required": [ "id", "bookingId", "userId", "eventId", "paymentCode", "method", "status", "totalAmount", "walletAmount", "gatewayAmount", "gatewayProvider", "gatewayTrackingId", "gatewayRefId", "paidAt", "failedAt", "failureReason", "createdAt", "updatedAt", "checkout", "isSimulated", "depositAmount", "walletBalanceUsed", "depositId" ] }, "PreviewDiscountDto": { "type": "object", "properties": { "code": { "type": "string", "description": "Discount code to preview", "example": "SAVE20AB" } }, "required": ["code"] }, "DiscountPreviewResponseDto": { "type": "object", "properties": { "code": { "type": "string", "example": "SAVE20AB" }, "type": { "type": "string", "enum": ["percent", "fixed"] }, "value": { "type": "number", "example": 20 }, "listAmount": { "type": "number", "example": 100000 }, "discountAmount": { "type": "number", "example": 20000 }, "payableAmount": { "type": "number", "example": 80000 } }, "required": ["code", "type", "value", "listAmount", "discountAmount", "payableAmount"] }, "PaymentResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "bookingId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "paymentCode": { "type": "string" }, "method": { "allOf": [ { "$ref": "#/components/schemas/PaymentMethod" } ] }, "status": { "allOf": [ { "$ref": "#/components/schemas/PaymentStatus" } ] }, "totalAmount": { "type": "number" }, "walletAmount": { "type": "number" }, "gatewayAmount": { "type": "number" }, "gatewayProvider": { "type": "string", "nullable": true }, "gatewayTrackingId": { "type": "string", "nullable": true }, "gatewayRefId": { "type": "string", "nullable": true }, "paidAt": { "type": "string", "format": "date-time", "nullable": true }, "failedAt": { "type": "string", "format": "date-time", "nullable": true }, "failureReason": { "type": "string", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": [ "id", "bookingId", "userId", "eventId", "paymentCode", "method", "status", "totalAmount", "walletAmount", "gatewayAmount", "gatewayProvider", "gatewayTrackingId", "gatewayRefId", "paidAt", "failedAt", "failureReason", "createdAt", "updatedAt" ] }, "PaymentReceiptResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "paymentId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "bookingId": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "receiptCode": { "type": "string" }, "snapshot": { "type": "object", "additionalProperties": true }, "issuedAt": { "format": "date-time", "type": "string" } }, "required": ["id", "paymentId", "userId", "bookingId", "eventId", "receiptCode", "snapshot", "issuedAt"] }, "FinancialUserSummaryDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "mobile": { "type": "string" }, "firstName": { "type": "object", "nullable": true }, "lastName": { "type": "object", "nullable": true } }, "required": ["id", "mobile"] }, "AdminPaymentResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "bookingId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "paymentCode": { "type": "string" }, "method": { "allOf": [ { "$ref": "#/components/schemas/PaymentMethod" } ] }, "status": { "allOf": [ { "$ref": "#/components/schemas/PaymentStatus" } ] }, "totalAmount": { "type": "number" }, "walletAmount": { "type": "number" }, "gatewayAmount": { "type": "number" }, "gatewayProvider": { "type": "string", "nullable": true }, "gatewayTrackingId": { "type": "string", "nullable": true }, "gatewayRefId": { "type": "string", "nullable": true }, "paidAt": { "type": "string", "format": "date-time", "nullable": true }, "failedAt": { "type": "string", "format": "date-time", "nullable": true }, "failureReason": { "type": "string", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "bookingCode": { "type": "string" }, "eventTitle": { "type": "string" }, "user": { "$ref": "#/components/schemas/FinancialUserSummaryDto" } }, "required": [ "id", "bookingId", "userId", "eventId", "paymentCode", "method", "status", "totalAmount", "walletAmount", "gatewayAmount", "gatewayProvider", "gatewayTrackingId", "gatewayRefId", "paidAt", "failedAt", "failureReason", "createdAt", "updatedAt", "bookingCode", "eventTitle", "user" ] }, "AdminWalletDepositResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "walletId": { "type": "string", "format": "uuid" }, "depositCode": { "type": "string" }, "purpose": { "type": "string", "enum": ["top_up", "booking_checkout"] }, "amount": { "type": "number", "description": "Amount in Toman" }, "bookingId": { "type": "string", "format": "uuid", "nullable": true }, "paymentId": { "type": "string", "format": "uuid", "nullable": true }, "status": { "type": "string", "enum": ["pending", "processing", "succeeded", "failed", "cancelled"] }, "gatewayProvider": { "type": "string", "nullable": true }, "gatewayTrackingId": { "type": "string", "nullable": true }, "gatewayRefId": { "type": "string", "nullable": true }, "failureReason": { "type": "string", "nullable": true }, "completedAt": { "type": "string", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "user": { "$ref": "#/components/schemas/FinancialUserSummaryDto" } }, "required": [ "id", "userId", "walletId", "depositCode", "purpose", "amount", "bookingId", "paymentId", "status", "gatewayProvider", "gatewayTrackingId", "gatewayRefId", "failureReason", "completedAt", "createdAt", "updatedAt", "user" ] }, "WalletResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "balance": { "type": "number", "description": "Balance in Toman" }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": ["id", "userId", "balance", "createdAt", "updatedAt"] }, "WalletTransactionType": { "type": "string", "enum": ["booking_payment", "refund_guest_cancel", "refund_event_cancel", "organizer_earning", "withdrawal", "deposit"] }, "WalletTransactionResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "walletId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "transactionCode": { "type": "string" }, "type": { "allOf": [ { "$ref": "#/components/schemas/WalletTransactionType" } ] }, "amount": { "type": "number", "description": "Signed amount in Toman" }, "balanceAfter": { "type": "number", "description": "Balance after this transaction (Toman)" }, "referenceType": { "type": "object", "nullable": true }, "referenceId": { "type": "object", "format": "uuid", "nullable": true }, "description": { "type": "object", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "walletId", "userId", "transactionCode", "type", "amount", "balanceAfter", "createdAt"] }, "CreateWalletDepositDto": { "type": "object", "properties": { "amount": { "type": "number", "description": "Amount to deposit in Toman", "minimum": 10000 } }, "required": ["amount"] }, "CreateWalletDepositResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "walletId": { "type": "string", "format": "uuid" }, "depositCode": { "type": "string" }, "purpose": { "type": "string", "enum": ["top_up", "booking_checkout"] }, "amount": { "type": "number", "description": "Amount in Toman" }, "bookingId": { "type": "string", "format": "uuid", "nullable": true }, "paymentId": { "type": "string", "format": "uuid", "nullable": true }, "status": { "type": "string", "enum": ["pending", "processing", "succeeded", "failed", "cancelled"] }, "gatewayProvider": { "type": "string", "nullable": true }, "gatewayTrackingId": { "type": "string", "nullable": true }, "gatewayRefId": { "type": "string", "nullable": true }, "failureReason": { "type": "string", "nullable": true }, "completedAt": { "type": "string", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "checkout": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/PaymentCheckoutResponseDto" } ] }, "isSimulated": { "type": "boolean", "description": "True when the deposit was automatically completed by the local simulator" } }, "required": [ "id", "userId", "walletId", "depositCode", "purpose", "amount", "bookingId", "paymentId", "status", "gatewayProvider", "gatewayTrackingId", "gatewayRefId", "failureReason", "completedAt", "createdAt", "updatedAt", "checkout", "isSimulated" ] }, "WalletDepositResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "walletId": { "type": "string", "format": "uuid" }, "depositCode": { "type": "string" }, "purpose": { "type": "string", "enum": ["top_up", "booking_checkout"] }, "amount": { "type": "number", "description": "Amount in Toman" }, "bookingId": { "type": "string", "format": "uuid", "nullable": true }, "paymentId": { "type": "string", "format": "uuid", "nullable": true }, "status": { "type": "string", "enum": ["pending", "processing", "succeeded", "failed", "cancelled"] }, "gatewayProvider": { "type": "string", "nullable": true }, "gatewayTrackingId": { "type": "string", "nullable": true }, "gatewayRefId": { "type": "string", "nullable": true }, "failureReason": { "type": "string", "nullable": true }, "completedAt": { "type": "string", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": [ "id", "userId", "walletId", "depositCode", "purpose", "amount", "bookingId", "paymentId", "status", "gatewayProvider", "gatewayTrackingId", "gatewayRefId", "failureReason", "completedAt", "createdAt", "updatedAt" ] }, "GatewayCallbackDto": { "type": "object", "properties": { "status": { "type": "string", "enum": ["succeeded", "failed"] }, "signature": { "type": "string" }, "timestamp": { "type": "number", "description": "Unix timestamp (seconds) included in the HMAC payload." }, "nonce": { "type": "string", "description": "One-time nonce included in the HMAC payload to stop replays." }, "gatewayTrackingId": { "type": "string", "description": "Gateway authority/tracking id" }, "gatewayRefId": { "type": "string", "description": "Gateway final reference id" }, "failureReason": { "type": "string" } }, "required": ["status", "signature", "timestamp", "nonce"] }, "CreateBankAccountDto": { "type": "object", "properties": { "iban": { "type": "string", "example": "IR123456789012345678901234" }, "nationalCode": { "type": "string", "description": "Required unless the user has a verified host identity (national code is then taken from identity).", "example": "0012345678" } }, "required": ["iban"] }, "BankAccountOwnerType": { "type": "string", "enum": ["guest", "organizer"] }, "BankAccountVerificationStatus": { "type": "string", "enum": ["pending_review", "approved", "rejected"] }, "BankAccountResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "ownerType": { "allOf": [ { "$ref": "#/components/schemas/BankAccountOwnerType" } ] }, "iban": { "type": "string" }, "isDefault": { "type": "boolean" }, "nationalCode": { "type": "string", "nullable": true }, "verificationStatus": { "allOf": [ { "$ref": "#/components/schemas/BankAccountVerificationStatus" } ] }, "rejectionReason": { "type": "string", "nullable": true }, "reviewedAt": { "type": "string", "format": "date-time", "nullable": true }, "jibitMatched": { "type": "boolean", "nullable": true }, "jibitInquiredAt": { "type": "string", "format": "date-time", "nullable": true }, "jibitErrorCode": { "type": "string", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "user": { "type": "object", "nullable": true } }, "required": [ "id", "userId", "ownerType", "iban", "isDefault", "nationalCode", "verificationStatus", "rejectionReason", "reviewedAt", "jibitMatched", "jibitInquiredAt", "jibitErrorCode", "createdAt", "updatedAt" ] }, "RejectBankAccountDto": { "type": "object", "properties": { "reason": { "type": "string", "minLength": 3 } }, "required": ["reason"] }, "CreateWithdrawalRequestDto": { "type": "object", "properties": { "bankAccountId": { "type": "string" }, "amount": { "type": "number", "minimum": 1, "description": "Amount in Toman" } }, "required": ["bankAccountId", "amount"] }, "WithdrawalStatus": { "type": "string", "enum": ["pending", "processing", "completed", "rejected"] }, "WithdrawalUserSummaryDto": { "type": "object", "properties": { "mobile": { "type": "string" }, "firstName": { "type": "object", "nullable": true }, "lastName": { "type": "object", "nullable": true } }, "required": ["mobile"] }, "WithdrawalBankAccountSummaryDto": { "type": "object", "properties": { "iban": { "type": "string" } }, "required": ["iban"] }, "WithdrawalResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "walletId": { "type": "string", "format": "uuid" }, "bankAccountId": { "type": "string", "format": "uuid" }, "withdrawalCode": { "type": "string" }, "amount": { "type": "number" }, "status": { "allOf": [ { "$ref": "#/components/schemas/WithdrawalStatus" } ] }, "rejectionReason": { "type": "string", "nullable": true }, "processedAt": { "type": "string", "format": "date-time", "nullable": true }, "manualTrackingCode": { "type": "string", "nullable": true }, "manualReceiptUrl": { "type": "string", "format": "uri", "nullable": true }, "manualNote": { "type": "string", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "user": { "$ref": "#/components/schemas/WithdrawalUserSummaryDto" }, "bankAccount": { "$ref": "#/components/schemas/WithdrawalBankAccountSummaryDto" }, "previousStatus": { "allOf": [ { "$ref": "#/components/schemas/WithdrawalStatus" } ] } }, "required": [ "id", "userId", "walletId", "bankAccountId", "withdrawalCode", "amount", "status", "rejectionReason", "processedAt", "createdAt", "updatedAt" ] }, "CompleteManualPayoutDto": { "type": "object", "properties": { "trackingCode": { "type": "string", "description": "Bank tracking/reference code" }, "receiptUrl": { "type": "string", "description": "Receipt URL returned by POST /uploads" }, "note": { "type": "string", "description": "Optional admin note shown to the recipient" } }, "required": ["trackingCode", "receiptUrl"] }, "RejectWithdrawalRequestDto": { "type": "object", "properties": { "reason": { "type": "string" } }, "required": ["reason"] }, "OrganizerEarningStatus": { "type": "string", "enum": ["held", "available", "settled", "cancelled"] }, "OrganizerEarningResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "organizerId": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "bookingId": { "type": "string", "format": "uuid" }, "paymentId": { "type": "string", "format": "uuid" }, "grossAmount": { "type": "number" }, "commissionAmount": { "type": "number" }, "netAmount": { "type": "number" }, "cancellationFeeAmount": { "type": "number" }, "guestRefundAmount": { "type": "number" }, "status": { "allOf": [ { "$ref": "#/components/schemas/OrganizerEarningStatus" } ] }, "availableAt": { "format": "date-time", "type": "string" }, "settlementItemId": { "type": "object", "format": "uuid", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": [ "id", "organizerId", "eventId", "bookingId", "paymentId", "grossAmount", "commissionAmount", "netAmount", "cancellationFeeAmount", "guestRefundAmount", "status", "availableAt", "createdAt", "updatedAt" ] }, "SettlementBatchStatus": { "type": "string", "enum": ["pending", "processing", "completed", "failed"] }, "SettlementItemResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "amount": { "type": "number" }, "organizerEarningId": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "eventTitle": { "type": "string" } }, "required": ["id", "amount", "organizerEarningId", "eventId", "eventTitle"] }, "SettlementResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "settlementCode": { "type": "string" }, "organizerId": { "type": "string", "format": "uuid" }, "bankAccountId": { "type": "string", "format": "uuid" }, "periodStart": { "format": "date", "type": "string" }, "periodEnd": { "format": "date", "type": "string" }, "totalAmount": { "type": "number" }, "status": { "allOf": [ { "$ref": "#/components/schemas/SettlementBatchStatus" } ] }, "processedAt": { "type": "object", "format": "date-time", "nullable": true }, "failureReason": { "type": "string", "nullable": true }, "manualTrackingCode": { "type": "string", "nullable": true }, "manualReceiptUrl": { "type": "string", "format": "uri", "nullable": true }, "manualNote": { "type": "string", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/SettlementItemResponseDto" } } }, "required": [ "id", "settlementCode", "organizerId", "bankAccountId", "periodStart", "periodEnd", "totalAmount", "status", "createdAt", "updatedAt" ] }, "CreateSettlementDto": { "type": "object", "properties": { "organizerId": { "type": "string" }, "bankAccountId": { "type": "string" }, "periodStart": { "type": "string", "example": "2026-07-01" }, "periodEnd": { "type": "string", "example": "2026-07-07" } }, "required": ["organizerId", "bankAccountId", "periodStart", "periodEnd"] }, "FailSettlementDto": { "type": "object", "properties": { "reason": { "type": "string" } }, "required": ["reason"] }, "CreateDiscountCodesDto": { "type": "object", "properties": { "type": { "type": "string", "enum": ["percent", "fixed"], "example": "percent" }, "value": { "type": "number", "description": "Percent (1..99) when type=percent, or a fixed Toman amount when type=fixed", "example": 20, "minimum": 1 }, "quantity": { "type": "number", "description": "How many unique codes to generate", "example": 10, "minimum": 1, "maximum": 200 }, "maxUses": { "type": "number", "description": "Max total redemptions per code (unlimited when omitted)", "minimum": 1, "example": 1 }, "maxUsesPerUser": { "type": "number", "description": "Max redemptions per user per code (unlimited when omitted)", "minimum": 1, "example": 1 }, "validFrom": { "type": "string", "description": "Start of validity window (immediately when omitted)", "example": "2026-08-01T00:00:00.000Z" }, "validUntil": { "type": "string", "description": "End of validity window (event end time when omitted)", "example": "2026-08-10T00:00:00.000Z" }, "bearer": { "type": "string", "enum": ["organizer", "platform"], "description": "Who absorbs the discount. Admin-only; host requests are always forced to \"organizer\".", "example": "organizer" } }, "required": ["type", "value", "quantity"] }, "DiscountCodeResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "code": { "type": "string", "example": "SAVE20AB" }, "type": { "type": "string", "enum": ["percent", "fixed"] }, "value": { "type": "number", "example": 20 }, "bearer": { "type": "string", "enum": ["organizer", "platform"] }, "maxUses": { "type": "number", "nullable": true, "example": 1 }, "maxUsesPerUser": { "type": "number", "nullable": true, "example": 1 }, "validFrom": { "type": "string", "format": "date-time", "nullable": true }, "validUntil": { "type": "string", "format": "date-time", "nullable": true }, "generationBatchId": { "type": "string", "format": "uuid" }, "isActive": { "type": "boolean" }, "redeemedCount": { "type": "number", "description": "Total confirmed redemptions", "example": 3 }, "totalDiscountAmount": { "type": "number", "description": "Sum of discount granted across all redemptions (Toman)", "example": 60000 }, "createdAt": { "format": "date-time", "type": "string" } }, "required": [ "id", "eventId", "code", "type", "value", "bearer", "generationBatchId", "isActive", "redeemedCount", "totalDiscountAmount", "createdAt" ] }, "BulkCreateDiscountCodesResponseDto": { "type": "object", "properties": { "batchId": { "type": "string", "format": "uuid" }, "count": { "type": "number", "example": 10 }, "codes": { "description": "The generated codes, ready to share", "example": ["SAVE20AB", "SAVE20CD"], "type": "array", "items": { "type": "string" } }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/DiscountCodeResponseDto" } } }, "required": ["batchId", "count", "codes", "items"] }, "DiscountCodePageDto": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/DiscountCodeResponseDto" } } }, "required": ["items"] }, "DiscountReportSummaryDto": { "type": "object", "properties": { "totalCodes": { "type": "number", "example": 12 }, "totalRedemptions": { "type": "number", "example": 5 }, "totalDiscountAmount": { "type": "number", "description": "Total discount granted across the event (Toman)", "example": 100000 }, "totalPlatformAbsorbed": { "type": "number", "description": "Discount absorbed by the platform (Toman)", "example": 30000 }, "totalOrganizerAbsorbed": { "type": "number", "description": "Discount absorbed by the organizer (Toman)", "example": 70000 } }, "required": ["totalCodes", "totalRedemptions", "totalDiscountAmount", "totalPlatformAbsorbed", "totalOrganizerAbsorbed"] }, "DiscountRedemptionResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "discountCodeId": { "type": "string", "format": "uuid" }, "code": { "type": "string", "example": "SAVE20AB" }, "bookingId": { "type": "string", "format": "uuid" }, "paymentId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "userFirstName": { "type": "string", "nullable": true }, "userLastName": { "type": "string", "nullable": true }, "listAmount": { "type": "number", "example": 100000 }, "discountAmount": { "type": "number", "example": 20000 }, "payableAmount": { "type": "number", "example": 80000 }, "bearer": { "type": "string", "enum": ["organizer", "platform"] }, "platformAbsorbedAmount": { "type": "number", "example": 0 }, "organizerAbsorbedAmount": { "type": "number", "example": 20000 }, "createdAt": { "format": "date-time", "type": "string" } }, "required": [ "id", "discountCodeId", "code", "bookingId", "paymentId", "userId", "listAmount", "discountAmount", "payableAmount", "bearer", "platformAbsorbedAmount", "organizerAbsorbedAmount", "createdAt" ] }, "DiscountRedemptionPageDto": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/DiscountRedemptionResponseDto" } } }, "required": ["items"] }, "DiscountManagementBootstrapResponseDto": { "type": "object", "properties": { "codes": { "$ref": "#/components/schemas/DiscountCodePageDto" }, "report": { "$ref": "#/components/schemas/DiscountReportSummaryDto" }, "redemptions": { "$ref": "#/components/schemas/DiscountRedemptionPageDto" } }, "required": ["codes", "report", "redemptions"] }, "UpdateDiscountCodeDto": { "type": "object", "properties": { "isActive": { "type": "boolean", "description": "Enable or disable the code without deleting it", "example": false } }, "required": ["isActive"] }, "TrafficSource": { "type": "string", "enum": ["instagram", "telegram"], "description": "First-touch attribution from a tagged share link. Ignored if invalid." }, "CreateBookingDto": { "type": "object", "properties": { "attributionSource": { "description": "First-touch attribution from a tagged share link. Ignored if invalid.", "allOf": [ { "$ref": "#/components/schemas/TrafficSource" } ] } } }, "BookingResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "bookingCode": { "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/BookingStatus" } ] }, "expiresAt": { "type": "object", "format": "date-time", "nullable": true }, "confirmedAt": { "type": "object", "format": "date-time", "nullable": true }, "cancelledAt": { "type": "object", "format": "date-time", "nullable": true }, "cancellationReason": { "type": "string", "nullable": true }, "checkedInAt": { "type": "object", "format": "date-time", "nullable": true }, "noShowAt": { "type": "object", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "eventTitle": { "type": "string" }, "eventStartsAt": { "format": "date-time", "type": "string" }, "eventEndsAt": { "format": "date-time", "type": "string" }, "eventStatus": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "eventIsFree": { "type": "boolean" }, "eventPrice": { "type": "number" }, "eventCancellationFeePercent": { "type": "number" }, "eventCancellationFeePercent12To24Hours": { "type": "number" }, "eventCancellationFeePercentMoreThan24Hours": { "type": "number" }, "eventEffectiveCommissionPercent": { "type": "number" }, "discountCode": { "type": "string", "nullable": true }, "discountAmount": { "type": "number" }, "listAmount": { "type": "number", "nullable": true }, "payableAmount": { "type": "number", "nullable": true } }, "required": [ "id", "eventId", "userId", "bookingCode", "status", "cancellationReason", "createdAt", "updatedAt", "eventTitle", "eventStartsAt", "eventEndsAt", "eventStatus", "eventIsFree", "eventPrice", "eventCancellationFeePercent", "eventCancellationFeePercent12To24Hours", "eventCancellationFeePercentMoreThan24Hours", "eventEffectiveCommissionPercent", "discountCode", "discountAmount", "listAmount", "payableAmount" ] }, "CancelBookingDto": { "type": "object", "properties": { "cancellationReason": { "type": "string", "description": "Cancellation reason — mandatory (booking-cancellation-refund.md: required from the product’s point of view, previously only optional in this DTO)." } }, "required": ["cancellationReason"] }, "JoinWaitlistDto": { "type": "object", "properties": { "attributionSource": { "description": "First-touch attribution from a tagged share link. Copied to the booking on convert.", "allOf": [ { "$ref": "#/components/schemas/TrafficSource" } ] } } }, "WaitlistStatus": { "type": "string", "enum": ["waiting", "notified", "accepted", "expired", "cancelled", "converted"] }, "WaitlistResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "status": { "allOf": [ { "$ref": "#/components/schemas/WaitlistStatus" } ] }, "notifiedAt": { "type": "object", "format": "date-time", "nullable": true }, "offerExpiresAt": { "type": "object", "format": "date-time", "nullable": true }, "convertedAt": { "type": "object", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": ["id", "eventId", "userId", "status", "createdAt", "updatedAt"] }, "WaitlistAcceptedBookingDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "bookingCode": { "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/BookingStatus" } ] }, "expiresAt": { "type": "object", "format": "date-time", "nullable": true }, "confirmedAt": { "type": "object", "format": "date-time", "nullable": true }, "cancelledAt": { "type": "object", "format": "date-time", "nullable": true }, "cancellationReason": { "type": "string", "nullable": true }, "checkedInAt": { "type": "object", "format": "date-time", "nullable": true }, "noShowAt": { "type": "object", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": ["id", "eventId", "userId", "bookingCode", "status", "cancellationReason", "createdAt", "updatedAt"] }, "WaitlistAcceptResponseDto": { "type": "object", "properties": { "waitlistEntryId": { "type": "string", "format": "uuid" }, "booking": { "$ref": "#/components/schemas/WaitlistAcceptedBookingDto" } }, "required": ["waitlistEntryId", "booking"] }, "AdminBookingEventDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string" }, "startsAt": { "format": "date-time", "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "organizerId": { "type": "string", "format": "uuid" } }, "required": ["id", "title", "startsAt", "status", "organizerId"] }, "AdminBookingUserDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "mobile": { "type": "string" }, "firstName": { "type": "object", "nullable": true }, "lastName": { "type": "object", "nullable": true } }, "required": ["id", "mobile"] }, "AdminBookingResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "eventId": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "bookingCode": { "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/BookingStatus" } ] }, "expiresAt": { "type": "object", "format": "date-time", "nullable": true }, "confirmedAt": { "type": "object", "format": "date-time", "nullable": true }, "cancelledAt": { "type": "object", "format": "date-time", "nullable": true }, "cancellationReason": { "type": "string", "nullable": true }, "checkedInAt": { "type": "object", "format": "date-time", "nullable": true }, "noShowAt": { "type": "object", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "eventTitle": { "type": "string" }, "eventStartsAt": { "format": "date-time", "type": "string" }, "eventEndsAt": { "format": "date-time", "type": "string" }, "eventStatus": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "eventIsFree": { "type": "boolean" }, "eventPrice": { "type": "number" }, "eventCancellationFeePercent": { "type": "number" }, "eventCancellationFeePercent12To24Hours": { "type": "number" }, "eventCancellationFeePercentMoreThan24Hours": { "type": "number" }, "eventEffectiveCommissionPercent": { "type": "number" }, "discountCode": { "type": "string", "nullable": true }, "discountAmount": { "type": "number" }, "listAmount": { "type": "number", "nullable": true }, "payableAmount": { "type": "number", "nullable": true }, "event": { "$ref": "#/components/schemas/AdminBookingEventDto" }, "user": { "$ref": "#/components/schemas/AdminBookingUserDto" } }, "required": [ "id", "eventId", "userId", "bookingCode", "status", "cancellationReason", "createdAt", "updatedAt", "eventTitle", "eventStartsAt", "eventEndsAt", "eventStatus", "eventIsFree", "eventPrice", "eventCancellationFeePercent", "eventCancellationFeePercent12To24Hours", "eventCancellationFeePercentMoreThan24Hours", "eventEffectiveCommissionPercent", "discountCode", "discountAmount", "listAmount", "payableAmount", "event", "user" ] }, "OrganizerReviewResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Review identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "eventId": { "type": "string", "description": "Reviewed event identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "userId": { "type": "string", "description": "Guest who wrote the review.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "bookingId": { "type": "string", "description": "Booking that earned the right to review.", "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "format": "uuid" }, "rating": { "type": "number", "description": "Star rating from 1 to 5.", "example": 5, "minimum": 1, "maximum": 5 }, "body": { "type": "string", "description": "Optional review text; null when the guest rated without a comment.", "example": "Great event — well organized and welcoming.", "nullable": true }, "hostReplyBody": { "type": "string", "description": "Organizer reply text, when present.", "example": "Thank you for attending!", "nullable": true }, "hostReplyAt": { "type": "string", "description": "Timestamp when the organizer first replied.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time", "nullable": true }, "status": { "description": "Publication status.", "example": "published", "allOf": [ { "$ref": "#/components/schemas/ReviewStatus" } ] }, "createdAt": { "type": "string", "description": "Review creation timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" }, "updatedAt": { "type": "string", "description": "Last update timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "user": { "description": "Guest who wrote the review (public name and optional avatar).", "allOf": [ { "$ref": "#/components/schemas/ReviewUserSummaryDto" } ] }, "eventTitle": { "type": "string", "description": "Event title for organizer-wide review lists." }, "eventSlug": { "type": "string", "description": "Event slug for linking to the public event page." } }, "required": [ "id", "eventId", "userId", "bookingId", "rating", "body", "status", "createdAt", "updatedAt", "user", "eventTitle", "eventSlug" ] }, "CreateReviewDto": { "type": "object", "properties": { "bookingId": { "type": "string", "description": "The confirmed booking that earns the right to review this event.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "rating": { "type": "number", "description": "Star rating from 1 to 5.", "example": 5, "minimum": 1, "maximum": 5 }, "body": { "type": "string", "description": "Optional review text. Omit or leave empty for a rating-only review.", "example": "Great event — well organized and welcoming.", "nullable": true, "maxLength": 5000 } }, "required": ["bookingId", "rating"] }, "UpdateReviewDto": { "type": "object", "properties": { "rating": { "type": "number", "description": "Updated star rating from 1 to 5.", "example": 4, "minimum": 1, "maximum": 5 }, "body": { "type": "string", "description": "Updated review text. Pass null or empty string to clear the body.", "example": "Updated thoughts after reflecting on the experience.", "nullable": true, "maxLength": 5000 } } }, "HostReplyDto": { "type": "object", "properties": { "hostReplyBody": { "type": "string", "description": "Organizer reply to the review. Pass null to remove an existing reply. Cannot be an empty string.", "example": "Thank you for attending — glad you enjoyed it!", "nullable": true, "maxLength": 5000 } } }, "ReviewEventSummaryDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Event identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "title": { "type": "string", "description": "Event title.", "example": "Tehran hiking meetup" } }, "required": ["id", "title"] }, "AdminReviewResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Review identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "eventId": { "type": "string", "description": "Reviewed event identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "userId": { "type": "string", "description": "Guest who wrote the review.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "bookingId": { "type": "string", "description": "Booking that earned the right to review.", "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "format": "uuid" }, "rating": { "type": "number", "description": "Star rating from 1 to 5.", "example": 5, "minimum": 1, "maximum": 5 }, "body": { "type": "string", "description": "Optional review text; null when the guest rated without a comment.", "example": "Great event — well organized and welcoming.", "nullable": true }, "hostReplyBody": { "type": "string", "description": "Organizer reply text, when present.", "example": "Thank you for attending!", "nullable": true }, "hostReplyAt": { "type": "string", "description": "Timestamp when the organizer first replied.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time", "nullable": true }, "status": { "description": "Publication status.", "example": "published", "allOf": [ { "$ref": "#/components/schemas/ReviewStatus" } ] }, "createdAt": { "type": "string", "description": "Review creation timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" }, "updatedAt": { "type": "string", "description": "Last update timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "user": { "description": "Guest who wrote the review (public name and optional avatar).", "allOf": [ { "$ref": "#/components/schemas/ReviewUserSummaryDto" } ] }, "event": { "description": "Reviewed event summary for admin moderation screens.", "allOf": [ { "$ref": "#/components/schemas/ReviewEventSummaryDto" } ] } }, "required": ["id", "eventId", "userId", "bookingId", "rating", "body", "status", "createdAt", "updatedAt", "user", "event"] }, "UnreadCountResponseDto": { "type": "object", "properties": { "totalUnread": { "type": "number", "description": "Total unread messages across all conversations for the current user.", "example": 3 } }, "required": ["totalUnread"] }, "ConversationType": { "type": "string", "enum": ["event_group", "direct"], "description": "Conversation type." }, "ConversationResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Conversation identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "type": { "description": "Conversation type.", "example": "direct", "allOf": [ { "$ref": "#/components/schemas/ConversationType" } ] }, "eventId": { "type": "string", "description": "Linked event for event group chats.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid", "nullable": true }, "eventTitle": { "type": "string", "description": "Event title for event group chats.", "example": "Tehran hiking meetup", "nullable": true }, "otherUserId": { "type": "string", "description": "Other participant for direct chats.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid", "nullable": true }, "otherUserFirstName": { "type": "string", "description": "Other participant first name for direct chats.", "example": "علی", "nullable": true }, "otherUserLastName": { "type": "string", "description": "Other participant last name for direct chats.", "example": "رضایی", "nullable": true }, "preview": { "type": "string", "description": "Preview of the latest message in the conversation.", "example": "سلام، ایونت عالی بود!", "nullable": true }, "unreadCount": { "type": "number", "description": "Unread message count for the current user in this conversation.", "example": 2 }, "memberCount": { "type": "number", "description": "Number of participants in the conversation.", "example": 12 }, "messagingBlocked": { "type": "boolean", "description": "Whether this direct conversation currently prevents sending messages because either participant has blocked the other. This does not reveal who initiated the block.", "example": false }, "closesAt": { "type": "string", "description": "When an event group chat stops accepting new messages.", "example": "2026-08-16T09:00:00.000Z", "format": "date-time", "nullable": true }, "lastMessageAt": { "type": "string", "description": "Timestamp of the most recent message.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time", "nullable": true }, "createdAt": { "type": "string", "description": "Conversation creation timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" } }, "required": ["id", "type", "unreadCount", "memberCount", "messagingBlocked", "createdAt"] }, "SendMessageDto": { "type": "object", "properties": { "body": { "type": "string", "description": "Message text.", "example": "سلام، ایونت عالی بود!", "maxLength": 5000 }, "imageUrl": { "type": "string", "description": "Attached image URL.", "example": "https://storage.ghabilee.com/chat/photo-123.jpg", "format": "uri", "maxLength": 500 }, "replyToMessageId": { "type": "string", "description": "Optional parent message id in the same conversation to reply to. Soft-deleted or foreign parents are rejected.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" } } }, "MessageReplyToDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Parent message id (reply target).", "format": "uuid" }, "senderId": { "type": "string", "description": "Snapshot of the parent sender id at reply send time.", "format": "uuid" }, "senderDisplayName": { "type": "string", "description": "Snapshot display name of the parent sender at reply send time.", "example": "علی رضایی" }, "bodyPreview": { "type": "string", "description": "Truncated parent body; null when the parent was image-only.", "example": "سلام، ایونت عالی بود!", "nullable": true }, "hasImage": { "type": "boolean", "description": "Whether the parent message had an image attachment." }, "isUnavailable": { "type": "boolean", "description": "True when the parent cannot be jumped to (soft-deleted, missing, or wrong conversation)." } }, "required": ["id", "senderId", "senderDisplayName", "hasImage", "isUnavailable"] }, "MessageResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Message identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "conversationId": { "type": "string", "description": "Parent conversation identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "senderId": { "type": "string", "description": "Sending user identifier.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "senderFirstName": { "type": "string", "description": "Sender first name.", "example": "علی", "nullable": true }, "senderLastName": { "type": "string", "description": "Sender last name.", "example": "رضایی", "nullable": true }, "senderAvatarUrl": { "type": "string", "description": "Sender profile avatar URL.", "example": "https://storage.ghabilee.com/avatars/user-123.jpg", "format": "uri", "nullable": true }, "senderGender": { "description": "Sender gender for placeholder avatars when no photo is set.", "example": "male", "nullable": true, "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "body": { "type": "string", "description": "Message text, when present.", "example": "سلام، ایونت عالی بود!", "nullable": true }, "imageUrl": { "type": "string", "description": "Attached image URL, when present.", "example": "https://storage.ghabilee.com/chat/photo-123.jpg", "format": "uri", "nullable": true }, "replyTo": { "description": "Reply card snapshot when this message replies to another; null when it is not a reply.", "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/MessageReplyToDto" } ] }, "createdAt": { "type": "string", "description": "Message send timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" } }, "required": ["id", "conversationId", "senderId", "createdAt"] }, "ConversationParticipantResponseDto": { "type": "object", "properties": { "userId": { "type": "string", "description": "Participant user identifier.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "firstName": { "type": "string", "description": "First name.", "example": "علی", "nullable": true }, "lastName": { "type": "string", "description": "Last name.", "example": "رضایی", "nullable": true }, "avatarUrl": { "type": "string", "description": "Profile avatar URL.", "example": "https://storage.ghabilee.com/avatars/user-123.jpg", "format": "uri", "nullable": true }, "gender": { "description": "Gender for placeholder avatars when no photo is set.", "example": "male", "nullable": true, "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "joinedAt": { "type": "string", "description": "When the user joined this conversation.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" } }, "required": ["userId", "joinedAt"] }, "MarkConversationReadDto": { "type": "object", "properties": { "lastSeenMessageId": { "type": "string", "description": "Last message that was actually rendered to the user. Read state advances only through this message.", "format": "uuid" } }, "required": ["lastSeenMessageId"] }, "AdminChatUserDto": { "type": "object", "properties": { "userId": { "type": "string", "format": "uuid" }, "mobile": { "type": "string", "example": "09121234567" }, "firstName": { "type": "object", "nullable": true, "example": "علی" }, "lastName": { "type": "object", "nullable": true, "example": "رضایی" }, "avatarUrl": { "type": "object", "nullable": true, "format": "uri" }, "gender": { "nullable": true, "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "status": { "allOf": [ { "$ref": "#/components/schemas/UserStatus" } ] }, "joinedAt": { "type": "string", "format": "date-time" } }, "required": ["userId", "mobile", "status", "joinedAt"] }, "AdminConversationResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "type": { "allOf": [ { "$ref": "#/components/schemas/ConversationType" } ] }, "eventId": { "type": "string", "format": "uuid", "nullable": true }, "eventTitle": { "type": "object", "nullable": true }, "participantPreview": { "type": "array", "items": { "$ref": "#/components/schemas/AdminChatUserDto" } }, "memberCount": { "type": "number", "minimum": 0 }, "preview": { "type": "object", "nullable": true }, "closesAt": { "type": "string", "format": "date-time", "nullable": true }, "lastMessageAt": { "type": "string", "format": "date-time", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" } }, "required": ["id", "type", "participantPreview", "memberCount", "createdAt"] }, "AdminMessageResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "conversationId": { "type": "string", "format": "uuid" }, "senderId": { "type": "string", "format": "uuid" }, "senderMobile": { "type": "string", "example": "09121234567" }, "senderFirstName": { "type": "object", "nullable": true }, "senderLastName": { "type": "object", "nullable": true }, "senderAvatarUrl": { "type": "object", "nullable": true, "format": "uri" }, "senderGender": { "nullable": true, "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "body": { "type": "object", "nullable": true }, "imageUrl": { "type": "object", "nullable": true, "format": "uri" }, "replyTo": { "description": "Reply card snapshot when this message replies to another; null when it is not a reply.", "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/MessageReplyToDto" } ] }, "createdAt": { "type": "string", "format": "date-time" } }, "required": ["id", "conversationId", "senderId", "senderMobile", "createdAt"] }, "CreateContactMessageDto": { "type": "object", "properties": { "fullName": { "type": "string", "description": "Sender full name as typed on /contact.", "example": "علی رضایی", "minLength": 2, "maxLength": 100 }, "mobile": { "type": "string", "description": "Iranian mobile number. Stored as 98XXXXXXXXXX.", "example": "09123456789", "pattern": "^(\\+?98|0)?9\\d{9}$" }, "subject": { "type": "string", "description": "Short topic line.", "example": "پیگیری رزرو", "minLength": 2, "maxLength": 200 }, "message": { "type": "string", "description": "Free-text message body.", "example": "لطفاً وضعیت رزرو من را بررسی کنید.", "minLength": 10, "maxLength": 4000 }, "arcaptchaToken": { "type": "string", "description": "Arcaptcha challenge token from the widget (`arcaptcha-token`).", "example": "arcaptcha-challenge-token", "maxLength": 4096 } }, "required": ["fullName", "mobile", "subject", "message", "arcaptchaToken"] }, "ContactMessageResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Contact message identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "fullName": { "type": "string", "description": "Sender full name.", "example": "علی رضایی" }, "mobile": { "type": "string", "description": "Sender mobile in 98XXXXXXXXXX format.", "example": "989123456789" }, "subject": { "type": "string", "description": "Short topic line.", "example": "پیگیری رزرو" }, "message": { "type": "string", "description": "Message body.", "example": "لطفاً وضعیت رزرو من را بررسی کنید." }, "readAt": { "type": "string", "description": "When staff marked the message read. Null means unread.", "example": "2026-08-17T20:00:00.000Z", "format": "date-time", "nullable": true }, "readBy": { "type": "string", "description": "Admin who marked the message read.", "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "format": "uuid", "nullable": true }, "createdAt": { "type": "string", "description": "Submission timestamp.", "example": "2026-08-17T19:00:00.000Z", "format": "date-time" } }, "required": ["id", "fullName", "mobile", "subject", "message", "createdAt"] }, "CreateTrafficVisitDto": { "type": "object", "properties": { "pageKind": { "type": "string", "enum": ["home", "event"] }, "eventId": { "type": "string", "format": "uuid", "description": "Required when pageKind is event; omit for home." }, "source": { "allOf": [ { "$ref": "#/components/schemas/TrafficSource" } ] }, "visitorKey": { "type": "string", "format": "uuid", "description": "Durable anonymous visitor id from the browser localStorage." } }, "required": ["pageKind", "source", "visitorKey"] }, "CreateUserBlockDto": { "type": "object", "properties": { "blockedId": { "type": "string", "description": "User identifier to block.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" } }, "required": ["blockedId"] }, "UserBlockUserSummaryDto": { "type": "object", "properties": { "firstName": { "type": "string", "description": "Blocked user first name.", "example": "Ali", "nullable": true }, "lastName": { "type": "string", "description": "Blocked user last name.", "example": "SaZa", "nullable": true } }, "required": ["firstName", "lastName"] }, "UserBlockResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Block record identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "blockerId": { "type": "string", "description": "User who initiated the block.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "blockedId": { "type": "string", "description": "Blocked user identifier.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "createdAt": { "type": "string", "description": "When the block was created.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" }, "blockedUser": { "description": "Minimal profile of the blocked user.", "allOf": [ { "$ref": "#/components/schemas/UserBlockUserSummaryDto" } ] } }, "required": ["id", "blockerId", "blockedId", "createdAt", "blockedUser"] }, "ReportReasonResponseDto": { "type": "object", "properties": { "id": { "type": "number", "description": "Report reason identifier.", "example": 1, "minimum": 1 }, "code": { "type": "string", "description": "Stable report reason code for UI handling.", "example": "spam", "minLength": 1, "maxLength": 50 }, "label": { "type": "string", "description": "Human-readable report reason label.", "example": "اسپم یا تبلیغات ناخواسته", "minLength": 1, "maxLength": 200 }, "sortOrder": { "type": "number", "description": "Display order for report reasons.", "example": 1, "minimum": 0 } }, "required": ["id", "code", "label", "sortOrder"] }, "CreateUserReportDto": { "type": "object", "properties": { "reportedId": { "type": "string", "description": "User identifier being reported.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "reasonId": { "type": "number", "description": "Structured reason id from GET /report-reasons.", "example": 1, "minimum": 1 }, "description": { "type": "string", "description": "Free-text report detail. Required when reasonId is omitted.", "example": "This user sent repeated unsolicited messages.", "maxLength": 2000 }, "messageId": { "type": "string", "description": "Identifier of the chat message being reported. When supplied, the server verifies that the reporter participates in the message conversation and that reportedId is the message sender.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" } }, "required": ["reportedId"] }, "ReportStatus": { "type": "string", "enum": ["pending", "reviewed", "dismissed"], "description": "Report review status." }, "UserReportResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Report identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "reporterId": { "type": "string", "description": "Reporting user identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "reportedId": { "type": "string", "description": "Reported user identifier.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "reasonId": { "type": "number", "description": "Structured report reason identifier.", "example": 1, "minimum": 1 }, "description": { "type": "string", "description": "Free-text report detail.", "example": "This user sent repeated unsolicited messages.", "maxLength": 2000 }, "messageId": { "type": "string", "description": "Reported chat message identifier when this report was filed from a chat message.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "conversationId": { "type": "string", "description": "Conversation containing the reported message, persisted by the server for moderation context.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "messageBody": { "type": "string", "description": "Server-captured text snapshot of the reported message. Available only when messageId is present.", "example": "Please send money to this account." }, "messageImageUrl": { "type": "string", "description": "Server-captured image URL snapshot of the reported message. Available only when messageId is present.", "example": "https://storage.ghabilee.com/chat/photo-123.jpg", "format": "uri" }, "messageCreatedAt": { "type": "string", "description": "Original creation time of the reported message, captured by the server.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "status": { "description": "Report review status.", "example": "pending", "allOf": [ { "$ref": "#/components/schemas/ReportStatus" } ] }, "reviewedAt": { "type": "string", "description": "Admin review timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "reviewedBy": { "type": "string", "description": "Admin user identifier who reviewed the report.", "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "format": "uuid" }, "createdAt": { "type": "string", "description": "Report creation timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" } }, "required": ["id", "reporterId", "reportedId", "status", "createdAt"] }, "UserReportUserSummaryDto": { "type": "object", "properties": { "mobile": { "type": "string", "description": "User mobile number.", "example": "989123456789" }, "firstName": { "type": "string", "description": "User first name.", "example": "Ali", "nullable": true }, "lastName": { "type": "string", "description": "User last name.", "example": "SaZa", "nullable": true } }, "required": ["mobile", "firstName", "lastName"] }, "AdminUserReportResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Report identifier.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "reporterId": { "type": "string", "description": "Reporting user identifier.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "reportedId": { "type": "string", "description": "Reported user identifier.", "example": "c3cc67fb-8615-432c-9a57-a5249c4a6b1c", "format": "uuid" }, "reasonId": { "type": "number", "description": "Structured report reason identifier.", "example": 1, "minimum": 1 }, "description": { "type": "string", "description": "Free-text report detail.", "example": "This user sent repeated unsolicited messages.", "maxLength": 2000 }, "messageId": { "type": "string", "description": "Reported chat message identifier when this report was filed from a chat message.", "example": "16e33f70-d3cc-492c-a8fa-e2317fbfb37b", "format": "uuid" }, "conversationId": { "type": "string", "description": "Conversation containing the reported message, persisted by the server for moderation context.", "example": "6f6d7d3a-90f2-4ad7-8d52-994a9676c5e1", "format": "uuid" }, "messageBody": { "type": "string", "description": "Server-captured text snapshot of the reported message. Available only when messageId is present.", "example": "Please send money to this account." }, "messageImageUrl": { "type": "string", "description": "Server-captured image URL snapshot of the reported message. Available only when messageId is present.", "example": "https://storage.ghabilee.com/chat/photo-123.jpg", "format": "uri" }, "messageCreatedAt": { "type": "string", "description": "Original creation time of the reported message, captured by the server.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "status": { "description": "Report review status.", "example": "pending", "allOf": [ { "$ref": "#/components/schemas/ReportStatus" } ] }, "reviewedAt": { "type": "string", "description": "Admin review timestamp.", "example": "2026-08-15T09:00:00.000Z", "format": "date-time" }, "reviewedBy": { "type": "string", "description": "Admin user identifier who reviewed the report.", "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "format": "uuid" }, "createdAt": { "type": "string", "description": "Report creation timestamp.", "example": "2026-08-14T09:00:00.000Z", "format": "date-time" }, "reason": { "description": "Structured report reason detail joined for admin review screens.", "allOf": [ { "$ref": "#/components/schemas/ReportReasonResponseDto" } ] }, "reporter": { "description": "Minimal reporter profile shown to staff reviewers.", "allOf": [ { "$ref": "#/components/schemas/UserReportUserSummaryDto" } ] }, "reported": { "description": "Minimal reported-user profile shown to staff reviewers.", "allOf": [ { "$ref": "#/components/schemas/UserReportUserSummaryDto" } ] } }, "required": ["id", "reporterId", "reportedId", "status", "createdAt", "reporter", "reported"] }, "AdminUserReportBlockResponseDto": { "type": "object", "properties": { "report": { "description": "The report, now transitioned to `reviewed`.", "allOf": [ { "$ref": "#/components/schemas/UserReportResponseDto" } ] }, "block": { "description": "The reporter -> reported block row, created by this call or, if it already existed, returned unchanged (idempotent).", "allOf": [ { "$ref": "#/components/schemas/UserBlockResponseDto" } ] } }, "required": ["report", "block"] }, "CreateSupportTicketDto": { "type": "object", "properties": { "subject": { "type": "string", "example": "مشکل در پرداخت رزرو" }, "category": { "type": "string", "enum": ["general", "account", "event", "booking", "payment", "technical"], "example": "payment" }, "priority": { "type": "string", "enum": ["low", "normal", "high", "urgent"], "example": "normal" }, "message": { "type": "string", "example": "پرداخت انجام شد اما رزرو ثبت نشده است." } }, "required": ["subject", "category", "priority", "message"] }, "SupportTicketUserResponseDto": { "type": "object", "properties": { "mobile": { "type": "string", "example": "989121234567" }, "displayName": { "type": "object", "nullable": true, "example": "علی رضایی" } }, "required": ["mobile"] }, "SupportTicketMessageResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "senderId": { "type": "string", "format": "uuid" }, "body": { "type": "string" }, "isAdmin": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" } }, "required": ["id", "senderId", "body", "isAdmin", "createdAt"] }, "SupportTicketResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "subject": { "type": "string" }, "category": { "type": "string", "enum": ["general", "account", "event", "booking", "payment", "technical"] }, "priority": { "type": "string", "enum": ["low", "normal", "high", "urgent"] }, "status": { "type": "string", "enum": ["open", "waiting_admin", "waiting_user", "closed"] }, "lastMessageAt": { "type": "string", "format": "date-time" }, "closedAt": { "type": "string", "format": "date-time", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" }, "user": { "$ref": "#/components/schemas/SupportTicketUserResponseDto" }, "messages": { "type": "array", "items": { "$ref": "#/components/schemas/SupportTicketMessageResponseDto" } } }, "required": ["id", "userId", "subject", "category", "priority", "status", "lastMessageAt", "createdAt", "user", "messages"] }, "ReplySupportTicketDto": { "type": "object", "properties": { "message": { "type": "string", "example": "درخواست شما در حال بررسی است." } }, "required": ["message"] }, "UpdateSupportTicketStatusDto": { "type": "object", "properties": { "status": { "type": "string", "enum": ["open", "waiting_admin", "waiting_user", "closed"] } }, "required": ["status"] }, "UserCountsDto": { "type": "object", "properties": { "total": { "type": "number", "example": 1204 }, "active": { "type": "number", "example": 980 }, "pending": { "type": "number", "example": 45 }, "suspended": { "type": "number", "example": 12 } }, "required": ["total", "active", "pending", "suspended"] }, "EventStatusCountsDto": { "type": "object", "properties": { "draft": { "type": "number", "example": 30 }, "pending_review": { "type": "number", "example": 7 }, "published": { "type": "number", "example": 58 }, "full": { "type": "number", "example": 4 }, "rejected": { "type": "number", "example": 3 }, "cancelled": { "type": "number", "example": 9 }, "completed": { "type": "number", "example": 210 } }, "required": ["draft", "pending_review", "published", "full", "rejected", "cancelled", "completed"] }, "EventCountsDto": { "type": "object", "properties": { "total": { "type": "number", "example": 326 }, "active": { "type": "number", "example": 62, "description": "Events currently live/bookable — status IN ('published', 'full')." }, "totalHeld": { "type": "number", "example": 210, "description": "Same as byStatus.completed — events that have actually been held." }, "byStatus": { "$ref": "#/components/schemas/EventStatusCountsDto" } }, "required": ["total", "active", "totalHeld", "byStatus"] }, "BookingStatusCountsDto": { "type": "object", "properties": { "pending_payment": { "type": "number", "example": 40 }, "confirmed": { "type": "number", "example": 1500 }, "cancelled": { "type": "number", "example": 120 }, "expired": { "type": "number", "example": 30 }, "refunded": { "type": "number", "example": 18 }, "no_show": { "type": "number", "example": 25 } }, "required": ["pending_payment", "confirmed", "cancelled", "expired", "refunded", "no_show"] }, "BookingCountsDto": { "type": "object", "properties": { "total": { "type": "number", "example": 1733 }, "byStatus": { "$ref": "#/components/schemas/BookingStatusCountsDto" } }, "required": ["total", "byStatus"] }, "IdentityVerificationCountsDto": { "type": "object", "properties": { "none": { "type": "number", "example": 600 }, "pending": { "type": "number", "example": 25 }, "verified": { "type": "number", "example": 550 }, "rejected": { "type": "number", "example": 29 } }, "required": ["none", "pending", "verified", "rejected"] }, "FinancialTotalsDto": { "type": "object", "properties": { "totalCommissionCollected": { "type": "string", "example": "48500000", "description": "Sum of organizer_earnings.commission_amount where status <> 'cancelled' (Toman, as a string to avoid precision loss)." }, "totalWalletBalance": { "type": "string", "example": "312000000", "description": "Sum of wallets.balance across all wallets (Toman, as a string)." } }, "required": ["totalCommissionCollected", "totalWalletBalance"] }, "AdminReportsOverviewDto": { "type": "object", "properties": { "users": { "$ref": "#/components/schemas/UserCountsDto" }, "events": { "$ref": "#/components/schemas/EventCountsDto" }, "bookings": { "$ref": "#/components/schemas/BookingCountsDto" }, "identityVerifications": { "$ref": "#/components/schemas/IdentityVerificationCountsDto" }, "financial": { "$ref": "#/components/schemas/FinancialTotalsDto" } }, "required": ["users", "events", "bookings", "identityVerifications", "financial"] }, "AdminUserContactLinkDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "channel": { "allOf": [ { "$ref": "#/components/schemas/ContactChannel" } ] }, "label": { "type": "string" }, "value": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "isPublic": { "type": "boolean" }, "displayOrder": { "type": "number" } }, "required": ["id", "channel", "label", "value", "url", "isPublic", "displayOrder"] }, "HostPlan": { "type": "string", "enum": ["free", "unlimited"] }, "AdminUserSummaryCountsDto": { "type": "object", "properties": { "hostedEventsCount": { "type": "number" }, "bookingsCount": { "type": "number" }, "paymentsCount": { "type": "number" }, "reviewsWrittenCount": { "type": "number" }, "reviewsReceivedCount": { "type": "number" }, "followingCount": { "type": "number" }, "followersCount": { "type": "number" }, "reportsFiledCount": { "type": "number" }, "reportsReceivedCount": { "type": "number" }, "blockingCount": { "type": "number" }, "blockedByCount": { "type": "number" } }, "required": [ "hostedEventsCount", "bookingsCount", "paymentsCount", "reviewsWrittenCount", "reviewsReceivedCount", "followingCount", "followersCount", "reportsFiledCount", "reportsReceivedCount", "blockingCount", "blockedByCount" ] }, "AdminUserDetailResponseDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "mobile": { "type": "string" }, "firstName": { "type": "string", "nullable": true }, "lastName": { "type": "string", "nullable": true }, "gender": { "nullable": true, "allOf": [ { "$ref": "#/components/schemas/Gender" } ] }, "cityId": { "type": "number", "nullable": true }, "cityName": { "type": "string", "nullable": true }, "dateOfBirth": { "type": "string", "format": "date", "nullable": true }, "avatarUrl": { "type": "string", "format": "uri", "nullable": true }, "bio": { "type": "string", "nullable": true }, "defaultAddress": { "type": "string", "nullable": true }, "contactLinks": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUserContactLinkDto" } }, "role": { "allOf": [ { "$ref": "#/components/schemas/UserRole" } ] }, "hostPlan": { "allOf": [ { "$ref": "#/components/schemas/HostPlan" } ] }, "status": { "allOf": [ { "$ref": "#/components/schemas/UserStatus" } ] }, "identityStatus": { "type": "string", "enum": ["none", "pending", "verified", "rejected"] }, "identityVerifiedAt": { "type": "string", "format": "date-time", "nullable": true }, "mobileVerifiedAt": { "type": "string", "format": "date-time", "nullable": true }, "walletBalance": { "type": "number" }, "lastLoginAt": { "type": "string", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "summary": { "$ref": "#/components/schemas/AdminUserSummaryCountsDto" } }, "required": [ "id", "mobile", "firstName", "lastName", "gender", "cityId", "cityName", "dateOfBirth", "avatarUrl", "bio", "defaultAddress", "contactLinks", "role", "hostPlan", "status", "identityStatus", "identityVerifiedAt", "mobileVerifiedAt", "walletBalance", "lastLoginAt", "createdAt", "updatedAt", "summary" ] }, "AdminCreditWalletDto": { "type": "object", "properties": { "amount": { "type": "number", "minimum": 1, "description": "Credit amount in Toman (integer)", "example": 100000 }, "description": { "type": "string", "minLength": 3, "maxLength": 500, "description": "Optional admin note (Toman credit reason)" } }, "required": ["amount"] }, "AdminCreditWalletResponseDto": { "type": "object", "properties": { "walletBalance": { "type": "number", "description": "Wallet balance after credit (Toman)" }, "transaction": { "$ref": "#/components/schemas/WalletTransactionResponseDto" } }, "required": ["walletBalance", "transaction"] }, "AdminUserHostedEventDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "title": { "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/EventStatus" } ] }, "startsAt": { "format": "date-time", "type": "string" }, "endsAt": { "format": "date-time", "type": "string" }, "capacity": { "type": "number" }, "bookedCount": { "type": "number" }, "isFree": { "type": "boolean" }, "price": { "type": "number" }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "title", "status", "startsAt", "endsAt", "capacity", "bookedCount", "isFree", "price", "createdAt"] }, "AdminUserBookingDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "bookingCode": { "type": "string" }, "status": { "allOf": [ { "$ref": "#/components/schemas/BookingStatus" } ] }, "eventId": { "type": "string", "format": "uuid" }, "eventTitle": { "type": "string" }, "confirmedAt": { "type": "object", "format": "date-time", "nullable": true }, "cancelledAt": { "type": "object", "format": "date-time", "nullable": true }, "checkedInAt": { "type": "object", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "bookingCode", "status", "eventId", "eventTitle", "createdAt"] }, "AdminUserPaymentDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "paymentCode": { "type": "string" }, "method": { "allOf": [ { "$ref": "#/components/schemas/PaymentMethod" } ] }, "status": { "allOf": [ { "$ref": "#/components/schemas/PaymentStatus" } ] }, "totalAmount": { "type": "number" }, "walletAmount": { "type": "number" }, "gatewayAmount": { "type": "number" }, "eventId": { "type": "string", "format": "uuid" }, "eventTitle": { "type": "string" }, "paidAt": { "type": "object", "format": "date-time", "nullable": true }, "failedAt": { "type": "object", "format": "date-time", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": [ "id", "paymentCode", "method", "status", "totalAmount", "walletAmount", "gatewayAmount", "eventId", "eventTitle", "createdAt" ] }, "AdminUserReviewDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "rating": { "type": "number", "minimum": 1, "maximum": 5 }, "body": { "type": "object", "nullable": true }, "hostReplyBody": { "type": "object", "nullable": true }, "status": { "allOf": [ { "$ref": "#/components/schemas/ReviewStatus" } ] }, "eventId": { "type": "string", "format": "uuid" }, "eventTitle": { "type": "string" }, "authorId": { "type": "string", "format": "uuid" }, "authorName": { "type": "object", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "rating", "status", "eventId", "eventTitle", "authorId", "createdAt"] }, "AdminUserFollowDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "userId": { "type": "string", "format": "uuid" }, "mobile": { "type": "string" }, "name": { "type": "object", "nullable": true }, "notifyNewEvents": { "type": "boolean" }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "userId", "mobile", "notifyNewEvents", "createdAt"] }, "AdminUserReportDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "status": { "allOf": [ { "$ref": "#/components/schemas/ReportStatus" } ] }, "description": { "type": "object", "nullable": true }, "reasonLabel": { "type": "object", "nullable": true }, "otherUserId": { "type": "string", "format": "uuid" }, "otherUserMobile": { "type": "string" }, "otherUserName": { "type": "object", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "status", "otherUserId", "otherUserMobile", "createdAt"] }, "AdminUserBlockDto": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "otherUserId": { "type": "string", "format": "uuid" }, "otherUserMobile": { "type": "string" }, "otherUserName": { "type": "object", "nullable": true }, "createdAt": { "format": "date-time", "type": "string" } }, "required": ["id", "otherUserId", "otherUserMobile", "createdAt"] }, "AdminAuditLogResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Audit log entry identifier.", "example": "0c60c25d-831b-4d43-a5fd-6e8127d81134", "format": "uuid" }, "adminUserId": { "type": "string", "description": "Admin user id who performed the action (null if deleted).", "example": "0c60c25d-831b-4d43-a5fd-6e8127d81134", "format": "uuid" }, "adminMobile": { "type": "string", "description": "Admin's mobile number, for display (null if deleted).", "example": "09121234567" }, "method": { "type": "string", "description": "HTTP method of the audited action.", "example": "PATCH" }, "path": { "type": "string", "description": "Route path (pattern, not resolved URL).", "example": "/api/v1/admin/identity/verifications/:id/approve" }, "params": { "type": "object", "description": "Route params captured for the action (e.g. resource id).", "example": { "id": "0c60c25d-831b-4d43-a5fd-6e8127d81134" } }, "statusCode": { "type": "number", "description": "HTTP status code the action resulted in.", "example": 200 }, "requestId": { "type": "string", "description": "Correlation id shared with request logging.", "example": "3fbb1e0a-9e2d-4a3b-8b1a-7e6c2f0b1a2c" }, "createdAt": { "type": "string", "description": "When the action occurred.", "example": "2026-07-20T10:00:00.000Z", "format": "date-time" } }, "required": ["id", "method", "path", "params", "statusCode", "createdAt"] }, "UploadFileResponseDto": { "type": "object", "properties": { "id": { "type": "string", "description": "Relative storage path used as the file id", "example": "files/550e8400-e29b-41d4-a716-446655440000.jpg" }, "url": { "type": "string", "description": "Public absolute URL for the uploaded file", "example": "http://localhost:3000/uploads/files/550e8400-e29b-41d4-a716-446655440000.jpg" } }, "required": ["id", "url"] }, "BlogArticleCitySummaryDto": { "type": "object", "properties": { "id": { "type": "number", "example": 1 }, "slug": { "type": "string", "example": "ahvaz" }, "name": { "type": "string", "example": "اهواز" } }, "required": ["id", "slug", "name"] }, "BlogArticleEventCategorySummaryDto": { "type": "object", "properties": { "id": { "type": "number", "example": 1 }, "slug": { "type": "string", "example": "photography" }, "name": { "type": "string", "example": "عکاسی" } }, "required": ["id", "slug", "name"] }, "BlogArticleSummaryResponseDto": { "type": "object", "properties": { "id": { "type": "number", "example": 1 }, "slug": { "type": "string", "example": "ahvaz-events-guide" }, "title": { "type": "string", "example": "راهنمای پیدا کردن رویداد و دورهمی در اهواز" }, "excerpt": { "type": "string" }, "categorySlug": { "type": "string", "example": "city-guides" }, "categoryName": { "type": "string", "example": "راهنمای شهرها" }, "city": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/BlogArticleCitySummaryDto" } ] }, "eventCategory": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/BlogArticleEventCategorySummaryDto" } ] }, "featuredImageUrl": { "type": "string", "nullable": true, "format": "uri" }, "readingMinutes": { "type": "number", "example": 6 }, "isFeatured": { "type": "boolean", "example": false }, "publishedAt": { "type": "string", "nullable": true, "format": "date-time" }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" } }, "required": [ "id", "slug", "title", "excerpt", "categorySlug", "categoryName", "city", "eventCategory", "featuredImageUrl", "readingMinutes", "isFeatured", "publishedAt", "createdAt", "updatedAt" ] }, "BlogArticleResponseDto": { "type": "object", "properties": { "id": { "type": "number", "example": 1 }, "slug": { "type": "string", "example": "ahvaz-events-guide" }, "title": { "type": "string", "example": "راهنمای پیدا کردن رویداد و دورهمی در اهواز" }, "excerpt": { "type": "string" }, "categorySlug": { "type": "string", "example": "city-guides" }, "categoryName": { "type": "string", "example": "راهنمای شهرها" }, "city": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/BlogArticleCitySummaryDto" } ] }, "eventCategory": { "nullable": true, "type": "object", "allOf": [ { "$ref": "#/components/schemas/BlogArticleEventCategorySummaryDto" } ] }, "featuredImageUrl": { "type": "string", "nullable": true, "format": "uri" }, "readingMinutes": { "type": "number", "example": 6 }, "isFeatured": { "type": "boolean", "example": false }, "publishedAt": { "type": "string", "nullable": true, "format": "date-time" }, "createdAt": { "format": "date-time", "type": "string" }, "updatedAt": { "format": "date-time", "type": "string" }, "metaTitle": { "type": "string", "nullable": true }, "metaDescription": { "type": "string", "nullable": true }, "bodyHtml": { "type": "string" }, "isPublished": { "type": "boolean", "example": true }, "scheduledAt": { "type": "string", "nullable": true, "format": "date-time" } }, "required": [ "id", "slug", "title", "excerpt", "categorySlug", "categoryName", "city", "eventCategory", "featuredImageUrl", "readingMinutes", "isFeatured", "publishedAt", "createdAt", "updatedAt", "metaTitle", "metaDescription", "bodyHtml", "isPublished", "scheduledAt" ] }, "CreateBlogArticleDto": { "type": "object", "properties": { "slug": { "type": "string", "example": "ahvaz-events-guide" }, "title": { "type": "string", "example": "راهنمای پیدا کردن رویداد و دورهمی در اهواز", "description": "Kept short (<=52 chars) so \"title | قبیله\" stays under the 60-char SEO title budget." }, "excerpt": { "type": "string", "example": "با توجه به گرمای هوا، ساعت‌های مناسب و پراکندگی مناطق شهر، چطور رویداد یا کارگاه بهتری در اهواز انتخاب کنیم؟", "description": "Also used as the meta description fallback — kept within the 70-160 char SEO range." }, "metaTitle": { "type": "string", "nullable": true }, "metaDescription": { "type": "string", "nullable": true }, "categorySlug": { "type": "string", "enum": ["attendee-guide", "hosting", "city-guides", "event-selection"], "example": "city-guides" }, "categoryName": { "type": "string", "example": "راهنمای شهرها" }, "cityId": { "type": "number", "nullable": true }, "eventCategoryId": { "type": "number", "nullable": true }, "bodyHtml": { "type": "string", "description": "Rich-text HTML body from the admin editor." }, "featuredImageUrl": { "type": "string", "nullable": true, "format": "uri" }, "isFeatured": { "type": "boolean", "example": false }, "isPublished": { "type": "boolean", "example": false }, "scheduledAt": { "type": "string", "nullable": true, "format": "date-time" } }, "required": ["slug", "title", "excerpt", "categorySlug", "categoryName", "bodyHtml"] }, "UpdateBlogArticleDto": { "type": "object", "properties": { "slug": { "type": "string", "example": "ahvaz-events-guide" }, "title": { "type": "string", "example": "راهنمای پیدا کردن رویداد و دورهمی در اهواز", "description": "Kept short (<=52 chars) so \"title | قبیله\" stays under the 60-char SEO title budget." }, "excerpt": { "type": "string", "example": "با توجه به گرمای هوا، ساعت‌های مناسب و پراکندگی مناطق شهر، چطور رویداد یا کارگاه بهتری در اهواز انتخاب کنیم؟", "description": "Also used as the meta description fallback — kept within the 70-160 char SEO range." }, "metaTitle": { "type": "string", "nullable": true }, "metaDescription": { "type": "string", "nullable": true }, "categorySlug": { "type": "string", "enum": ["attendee-guide", "hosting", "city-guides", "event-selection"], "example": "city-guides" }, "categoryName": { "type": "string", "example": "راهنمای شهرها" }, "cityId": { "type": "number", "nullable": true }, "eventCategoryId": { "type": "number", "nullable": true }, "bodyHtml": { "type": "string", "description": "Rich-text HTML body from the admin editor." }, "featuredImageUrl": { "type": "string", "nullable": true, "format": "uri" }, "isFeatured": { "type": "boolean", "example": false }, "isPublished": { "type": "boolean", "example": false }, "scheduledAt": { "type": "string", "nullable": true, "format": "date-time" } } }, "HealthResponseDto": { "type": "object", "properties": { "status": { "type": "string", "description": "Current service health status.", "example": "ok" } }, "required": ["status"] } } } }