diff --git a/app/(dashboard)/support-tickets/[id]/page.tsx b/app/(dashboard)/support-tickets/[id]/page.tsx index 7fd2e0d..46a1f67 100644 --- a/app/(dashboard)/support-tickets/[id]/page.tsx +++ b/app/(dashboard)/support-tickets/[id]/page.tsx @@ -82,7 +82,7 @@ const AdminSupportTicketDetail = () => { return } - const label = SUPPORT_ADMIN_STATUS_LABELS[nextStatus as keyof typeof SUPPORT_ADMIN_STATUS_LABELS] ?? nextStatus + const label = SUPPORT_ADMIN_STATUS_LABELS[nextStatus] ?? nextStatus showAlert(`وضعیت تیکت به «${label}» تغییر کند؟`, () => { void changeStatus(nextStatus) diff --git a/features/events/detail/AdminEventDetail.tsx b/features/events/detail/AdminEventDetail.tsx index 47d798a..9713d72 100644 --- a/features/events/detail/AdminEventDetail.tsx +++ b/features/events/detail/AdminEventDetail.tsx @@ -348,11 +348,11 @@ const AdminEventDetail = () => { void handleApprove()} + onApprove={handleApprove} onCancel={handleCancel} onComplete={handleComplete} onDelete={handleDelete} - onForcePublish={() => void handleForcePublish()} + onForcePublish={handleForcePublish} onOpenReject={() => { setHasOpenedRejectModal(true) setIsRejectModalOpen(true) diff --git a/features/events/detail/EventDiscountsPanel.tsx b/features/events/detail/EventDiscountsPanel.tsx index c0c6fee..c8c7452 100644 --- a/features/events/detail/EventDiscountsPanel.tsx +++ b/features/events/detail/EventDiscountsPanel.tsx @@ -475,7 +475,9 @@ const EventDiscountsPanel = ({ eventId, isFree, isEnded = false, isAdmin = false label="" name={`code-active-${code.id}`} value={code.isActive} - onValueChange={(next) => void handleToggleActive(code, Boolean(next))} + onValueChange={(next) => { + handleToggleActive(code, Boolean(next)) + }} /> {isEnded ? texts.events.eventEndedShort : code.isActive ? texts.events.availableToGuests : texts.events.deactivated} @@ -486,7 +488,9 @@ const EventDiscountsPanel = ({ eventId, isFree, isEnded = false, isAdmin = false disabled={!canMutateCodes || code.redeemedCount > 0 || pendingCodeId === code.id} size="sm" variant="flat" - onClick={() => void handleDelete(code)} + onClick={() => { + handleDelete(code) + }} > {texts.common.delete} diff --git a/features/events/detail/admin-event-detail/AdminEventBookingsTab.tsx b/features/events/detail/admin-event-detail/AdminEventBookingsTab.tsx index 8faed0e..2a91b81 100644 --- a/features/events/detail/admin-event-detail/AdminEventBookingsTab.tsx +++ b/features/events/detail/admin-event-detail/AdminEventBookingsTab.tsx @@ -151,7 +151,9 @@ const AdminEventBookingsTab = ({ eventId, eventStatus, onCheckedIn }: AdminEvent isLoading={pendingId === bookingId} size="sm" variant="flat" - onClick={() => void handleCheckIn(bookingId)} + onClick={() => { + handleCheckIn(bookingId) + }} > diff --git a/openapi.json b/openapi.json index 7f1fa7c..dbffebb 100644 --- a/openapi.json +++ b/openapi.json @@ -1823,806 +1823,6 @@ ] } }, - "/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.", @@ -4290,6 +3490,806 @@ ] } }, + "/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/event-categories": { "get": { "operationId": "EventCategoriesController_list", @@ -21321,7 +21321,7 @@ }, "hostPlan": { "type": "string", - "description": "Free hosts can create 10 events; unlimited removes the quota.", + "description": "Free hosts can create 10 events; unlimited removes the quota. Upgrading free → unlimited notifies the host (SMS + in-app) via host_plan_unlimited.", "enum": [ "free", "unlimited" @@ -21441,438 +21441,6 @@ "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": [ @@ -22808,6 +22376,438 @@ "createdAt" ] }, + "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 + } + } + }, "EventCategoryResponseDto": { "type": "object", "properties": {