# Auth paths are relative to the /api server mount.

client-signup:
  post:
    tags: [Client Auth]
    summary: Register a new client account
    description: |
      - Creates an inactive client account.
      - Sends an OTP by SMS and never returns it.
      - The next step is `PATCH /activate` with purpose `activation`.
      - Unknown fields are ignored; only validated fields are consumed.
      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9037-359&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10261-28061&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
    x-figma:
      mobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9037-359&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10261-28061&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
    operationId: clientSignup
    security:
      - SecretKeyAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '../openapi.yaml#/components/schemas/ClientSignupRequest'
          examples:
            client:
              summary: Example client registration with non-production credentials
              value:
                name: "Example Client"
                countryCode: "+966"
                phone: "0512345678"
                email: "client@example.com"
                password: "ExamplePass1!"
                confirmPassword: "ExamplePass1!"
    responses:
      '200':
        description: Account created; OTP activation is required.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ClientSignupResponse'
            example:
              key: needActive
              message: "تم انشاء الحساب بنجاح"
              status: 200
              data:
                id: "665f1c2a9b4e1d0012ab34cd"
                name: "محمد"
                avatar: "https://dashboard.kam-teswa.4hoste.com/assets/uploads/defaultUser/defaultUserImage.png"
                countryCode: "+966"
                phone: "0512345678"
                fullPhone: "+9660512345678"
                email: "user@example.com"
                userType: client
                status: active
                statusText: "نشط"
                notifyCount: 0
                isNotify: true
                active: false
                updatedPhone: ""
                updatedCountryCode: ""
                balance: 0
                address: ""
                location: null
                token: ""
      '400':
        description: Validation or duplicate identity error.
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
                - $ref: '../openapi.yaml#/components/schemas/ConflictErrorResponse'
            examples:
              missingName:
                value: { key: fail, message: "الاسم مطلوب يرجي ادخال البيانات", status: 400 }
              duplicatePhone:
                value: { key: fail, message: "رقم الجوال مسجل مسبقا", status: 400 }
              weakPassword:
                value: { key: fail, message: "كلمة المرور يجب أن تكون 8 أحرف على الأقل وتحتوي على حرف كبير، حرف صغير، رقم، ورمز خاص", status: 400 }
      '500':
        $ref: '#/shared-server-error'

provider-signup:
  post:
    tags: [Provider Auth]
    summary: Submit a provider account request
    description: |
      - Creates matching Provider and ProviderMeta records with the same ID, active false, and approvalStatus wait.
      - Sends the existing account-activation OTP without returning it.
      - Password hashing is handled by the existing User model save hook.
      - Optional files are validated by signature before storage.
      - Unknown body fields are rejected.
      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22050&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-85434&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
    x-figma:
      mobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22050&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-85434&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
    operationId: providerSignup
    security:
      - SecretKeyAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '../openapi.yaml#/components/schemas/ProviderSignupRequest'
          example:
            name: "Example Provider"
            countryCode: "+966"
            phone: "0551234567"
            email: "provider@example.com"
            password: "ExamplePass1!"
            confirmPassword: "ExamplePass1!"
            nationalId: "1000000000"
            city: "665f1c2a9b4e1d0012ab34cf"
            whatsappCountryCode: "+966"
            whatsappNumber: "0551234567"
    responses:
      '200':
        description: Provider account request received; OTP activation is required.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ProviderSignupResponse'
            example:
              key: needActive
              message: "تم إرسال طلب إنشاء حساب مقدم الخدمة بنجاح"
              status: 200
              data:
                id: "665f1c2a9b4e1d0012ab34ce"
                avatar: "https://dashboard.kam-teswa.4hoste.com/assets/uploads/defaultUser/defaultUserImage.png"
                name: "Example Provider"
                countryCode: "+966"
                phone: "0551234567"
                fullPhone: "+9660551234567"
                city:
                  id: "665f1c2a9b4e1d0012ab34cf"
                  name: ""
                userType: provider
                status: active
                statusText: "نشط"
                notifyCount: 0
                isNotify: true
                active: false
                updatedPhone: ""
                updatedCountryCode: ""
                nationalId: "1000000000"
                rating: 0
                isAvailable: true
                isAvailableText: "متاح"
                commercialRegisterImages: []
                approvalStatus: wait
                balance: 0
                location:
                  title: ""
                  description: ""
                  longitude: 0
                  latitude: 0
                premiumSubscription: ""
                whatsappNumber: "0551234567"
                whatsappCountryCode: "+966"
                token: ""
      '400':
        description: Validation failed, including duplicate phone/email, rejected fields, or a missing/unavailable city.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            examples:
              invalidCity:
                value: { key: fail, message: "معرف المدينة غير صالح", status: 400 }
              duplicatePhone:
                value: { key: fail, message: "رقم الجوال مسجل مسبقا", status: 400 }
              invalidCommercialRegisterFile:
                value: { key: fail, message: "صيغة ملف السجل التجاري غير مدعومة، الصيغ المسموحة: jpg, jpeg, png, pdf", status: 400 }
      '500':
        $ref: '#/shared-server-error'

signin:
  post:
    tags: [Shared Auth]
    summary: Sign in as client or provider
    description: |
      - Verifies `countryCode + phone + password`.
      - `userType` selects exactly `client` or `provider`; identities are not mixed.
      - For an AccountIdentity-backed profile, the single canonical
        `AccountIdentity.password` is verified for both Client and Provider modes.
        A stale legacy profile password never overrides it.
      - Legacy profiles without AccountIdentity keep their existing password behavior.
      - A successful JWT contains the stored `userType` claim.
      - Inactive accounts receive a new OTP and a `needActive` response without a token.
      - Blocked accounts and providers awaiting/rejected by admin are refused.
      - Password and OTP values are never returned or logged.
      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-21936&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-84532&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
    x-figma:
      mobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-21936&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-84532&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
    operationId: signIn
    x-account-token-policy: >-
      Tokens are opaque to clients. While AUTH_ACCOUNT_IDENTITY_TOKEN_ENABLED is
      false, legacy profile-subject tokens remain unchanged. When enabled, a
      linked active account receives an AccountIdentity-subject token whose
      current Client/Provider profile is resolved from DB activeMode.
    security:
      - SecretKeyAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '../openapi.yaml#/components/schemas/LoginRequest'
          examples:
            client:
              summary: Client login
              value:
                countryCode: "+966"
                phone: "0512345678"
                password: "123456789@aA"
                userType: client
                deviceId: "client-device-installation-id"
                deviceType: android
            provider:
              summary: Provider login
              value:
                countryCode: "+966"
                phone: "0551234567"
                password: "123456789@aA"
                userType: provider
                deviceId: "provider-device-installation-id"
                deviceType: ios
    responses:
      '200':
        description: Login succeeded, or the account requires OTP activation.
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '../openapi.yaml#/components/schemas/AuthSuccessResponse'
                - $ref: '../openapi.yaml#/components/schemas/NeedActivationResponse'
            examples:
              clientSuccess:
                summary: Client authenticated
                value:
                  key: success
                  message: "تم تسجيل الدخول بنجاح"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34cd"
                    name: "Muhammed Mustafa"
                    countryCode: "+966"
                    phone: "0512345678"
                    fullPhone: "+9660512345678"
                    userType: client
                    status: active
                    active: true
                    token: "<client-access-token>"
              providerSuccess:
                summary: Provider authenticated
                value:
                  key: success
                  message: "تم تسجيل الدخول بنجاح"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34ce"
                    name: "Provider"
                    countryCode: "+966"
                    phone: "0551234567"
                    fullPhone: "+9660551234567"
                    userType: provider
                    approvalStatus: accept
                    status: active
                    active: true
                    token: "<provider-access-token>"
              needsActivation:
                summary: No token and no OTP in the response
                value:
                  key: needActive
                  message: "تفعيل الحساب مطلوب"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34cd"
                    userType: client
                    status: active
                    active: false
      '400':
        description: Invalid input, unknown account, wrong password, or provider approval is pending/rejected.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            examples:
              wrongPassword:
                value: { key: fail, message: "كلمة المرور غير صحيحة", status: 400 }
              invalidUserType:
                value: { key: fail, message: "نوع المستخدم يجب أن يكون client أو provider", status: 400 }
              accountNotFound:
                value: { key: fail, message: "هذا الحساب غير موجود", status: 400 }
      '423':
        $ref: '#/shared-blocked'
      '500':
        $ref: '#/shared-server-error'

activate:
  patch:
    tags: [Shared Auth]
    summary: Verify OTP code
    description: |
      - Verifies the OTP, its expiry, the selected `userType`, and the current auth flow.
      - `purpose: activation` activates the account and may issue a normal JWT.
      - `purpose: forgot_password` does not change the password or activation state.
        For AccountIdentity-backed users, the OTP and reset-session hashes belong to
        AccountIdentity and are shared across Client/Provider roles.
      - `purpose: change_phone` requires an authenticated client/provider and updates the
        pending phone only after successful OTP verification. Send the new value as
        `phone`; the `changePhoneToken` was already validated by `PATCH /change-phone`
        and is not submitted again. The response returns the complete safe client or
        provider profile object according to the authenticated account type.
      - Forgot-password verification returns a 10-minute opaque reset token; only its hash is stored.
      - The OTP is invalidated on success and is never returned.
      - Three invalid attempts temporarily block further verification in the running process.
      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22212&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-85434&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
      <div class="figma-links"><span>Change phone:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9476-6942&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Client Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-128827&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Client Web</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27245&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Provider Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101837&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Provider Web</a></div>
    x-figma:
      mobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22212&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-85434&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      changePhoneClientMobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9476-6942&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      changePhoneClientMobileNodeId: "9476:6942"
      changePhoneClientWeb: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-128827&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      changePhoneClientWebNodeId: "10257:128827"
      changePhoneProviderMobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27245&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      changePhoneProviderMobileNodeId: "9967:27245"
      changePhoneProviderWeb: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101837&viewport=-948%2C0%2C0.12&t=c4yTtQLlUc9FQir5-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      changePhoneProviderWebNodeId: "10762:101837"
    operationId: verifyAuthOtp
    x-account-token-policy: >-
      Successful activation may issue the same feature-flagged AccountIdentity
      token as sign-in. Pending providers never receive operational provider
      access, and legacy token behavior remains available while the flag is off.
    security:
      - SecretKeyAuth: []
      - SecretKeyAuth: []
        ClientBearerAuth: []
      - SecretKeyAuth: []
        ProviderBearerAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '../openapi.yaml#/components/schemas/ActivateRequest'
          examples:
            changePhone:
              summary: Verify the OTP sent to the pending new phone
              value:
                purpose: change_phone
                countryCode: "+966"
                phone: "0500000000"
                code: "123456"
    responses:
      '200':
        description: Account activated, reset session created, or phone updated with the complete client/provider profile returned.
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '../openapi.yaml#/components/schemas/AuthSuccessResponse'
                - $ref: '../openapi.yaml#/components/schemas/ResetSessionResponse'
                - $ref: '../openapi.yaml#/components/schemas/ProfileSuccessResponse'
            examples:
              resetSession:
                value:
                  key: success
                  message: "كود التحقق صحيح"
                  status: 200
                  data:
                    userType: client
                    expiresIn: 600
                    purpose: forgot_password
              activation:
                value:
                  key: success
                  message: "تم تفعيل الحساب بنجاح"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34cd"
                    userType: client
                    status: active
                    active: true
                    token: "<client-access-token>"
              changePhoneClient:
                summary: Client phone updated; complete safe client object returned
                value:
                  key: success
                  message: "تم تحديث رقم الجوال بنجاح"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34cd"
                    name: "محمد أحمد"
                    avatar: "https://dashboard.example.com/assets/uploads/defaultUserImage/defaultUserImage.png"
                    countryCode: "+966"
                    phone: "0500000000"
                    fullPhone: "+9660500000000"
                    email: "client@example.com"
                    userType: client
                    status: active
                    statusText: "نشط"
                    notifyCount: 0
                    isNotify: true
                    active: true
                    updatedPhone: ""
                    updatedCountryCode: ""
                    balance: 0
                    location:
                      title: "الموقع الرئيسي"
                      description: "الرياض، المملكة العربية السعودية"
                      longitude: 46.6753
                      latitude: 24.7136
                    token: "<client-access-token>"
              changePhoneProvider:
                summary: Provider phone updated; complete safe provider object returned
                value:
                  key: success
                  message: "تم تحديث رقم الجوال بنجاح"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34ce"
                    avatar: "https://dashboard.example.com/assets/uploads/defaultUserImage/defaultUserImage.png"
                    name: "متجر الرياض"
                    countryCode: "+966"
                    phone: "0551234567"
                    fullPhone: "+9660551234567"
                    city:
                      id: "665f1c2a9b4e1d0012ab3401"
                      name: "الرياض"
                    userType: provider
                    status: active
                    statusText: "نشط"
                    notifyCount: 0
                    isNotify: true
                    active: true
                    updatedPhone: ""
                    updatedCountryCode: ""
                    nationalId: "1012345678"
                    rating: 4.8
                    isAvailable: true
                    isAvailableText: "متاح"
                    commercialRegisterImages: []
                    approvalStatus: accept
                    balance: 0
                    location:
                      title: "الموقع الرئيسي"
                      description: "الرياض، المملكة العربية السعودية"
                      longitude: 46.6753
                      latitude: 24.7136
                    premiumSubscription: ""
                    whatsappNumber: "0551234567"
                    whatsappCountryCode: "+966"
                    token: "<provider-access-token>"
      '400':
        description: Unknown account, invalid/expired OTP, wrong purpose, invalid device data, or attempt cooldown.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            examples:
              invalidCode:
                value: { key: fail, message: "كود التحقق غير صحيح", status: 400 }
              expiredCode:
                value: { key: fail, message: "انتهت صلاحيه الكود", status: 400 }
              wrongPurpose:
                value: { key: fail, message: "كود التحقق لا يطابق عملية المصادقة الحالية", status: 400 }
              accountNotFound:
                value: { key: fail, message: "هذا الحساب غير موجود", status: 400 }
      '419':
        description: A supplied optional bearer token is invalid, expired, or revoked.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
            example: { key: unauthorized, message: "يجب تسجيل الدخول", status: 419 }
      '423':
        $ref: '#/shared-blocked'
      '500':
        $ref: '#/shared-server-error'

location:
  patch:
    tags: [Shared Auth]
    summary: Save client or provider location
    description: |
      Save the selected coordinates as the account's GeoJSON location.

      - With a selected Client/Provider bearer token, `optionalAuth` identifies that account and `countryCode`/`phone` may be omitted.
      - Without a bearer token, the current fallback supports a provider awaiting approval and requires `countryCode` plus `phone`.
      - The mobile application may send `titleAr`, `titleEn`, `descriptionAr`, and `descriptionEn`.
      - The response returns `location.title` and `location.description` in the language selected by the `lang` header.
      - `userType` is not a request field; the verified bearer token or provider lookup determines the account type.
      - A client or approved provider receives an access token; a pending provider receives `wait_approval` without a usable access token.
      - `SecretKeyAuth` is the only API security requirement for this public onboarding step.
      - Swagger UI offers `No bearer token`, `Client token`, and `Provider token`; it sends only the selected credential.
      - Passwords and OTP values are never returned.

      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9064-3213&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Client App</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10227-32828&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Client Web</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22666&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Provider App</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-85434&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Provider Web</a></div>
    x-figma:
      client:
        app: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9064-3213&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
        web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10227-32828&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      provider:
        app: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22666&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
        web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-85434&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
    operationId: setAuthLocation
    x-account-token-policy: >-
      When this flow must issue a new token, it follows the same gated
      AccountIdentity/legacy policy as sign-in. An existing bearer remains
      opaque and is reused; pending providers receive no operational token.
    security:
      - SecretKeyAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '../openapi.yaml#/components/schemas/LocationRequest'
          examples:
            clientLocation:
              summary: Save a client location using the selected Client token
              value:
                longitude: 46.6753
                latitude: 24.7136
                titleAr: "الموقع الرئيسي"
                titleEn: "Main location"
                descriptionAr: "الرياض، المملكة العربية السعودية"
                descriptionEn: "Riyadh, Saudi Arabia"
            providerLocation:
              summary: Save a pending-provider location without a bearer token
              value:
                countryCode: "+966"
                phone: "0551234567"
                longitude: 46.6753
                latitude: 24.7136
                titleAr: "موقع المتجر"
                titleEn: "Store location"
                descriptionAr: "الرياض، المملكة العربية السعودية"
                descriptionEn: "Riyadh, Saudi Arabia"
    responses:
      '200':
        description: Location saved; the response varies according to account type and provider approval state.
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '../openapi.yaml#/components/schemas/LocationSuccessResponse'
                - $ref: '../openapi.yaml#/components/schemas/LocationPendingApprovalResponse'
            examples:
              clientSuccess:
                summary: Complete client DTO after saving the location
                value:
                  key: success
                  message: "تم حفظ الموقع بنجاح"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34cd"
                    name: "محمد أحمد"
                    avatar: "https://dashboard.example.com/assets/uploads/users/clients/665f1c2a9b4e1d0012ab34cd/avatar.png"
                    countryCode: "+966"
                    phone: "0512345678"
                    fullPhone: "+9660512345678"
                    email: "client@example.com"
                    userType: client
                    status: active
                    statusText: "نشط"
                    notifyCount: 0
                    isNotify: true
                    active: true
                    updatedPhone: ""
                    updatedCountryCode: ""
                    balance: 0
                    address: "الرياض"
                    location: { title: "الموقع الرئيسي", description: "الرياض، المملكة العربية السعودية", longitude: 46.6753, latitude: 24.7136 }
              approvedProviderSuccess:
                summary: Complete approved-provider DTO after saving the location
                value:
                  key: success
                  message: "تم حفظ الموقع بنجاح"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34ce"
                    avatar: "https://dashboard.example.com/assets/uploads/users/providers/665f1c2a9b4e1d0012ab34ce/avatar.png"
                    name: "متجر الرياض"
                    countryCode: "+966"
                    phone: "0551234567"
                    fullPhone: "+9660551234567"
                    city: { id: "665f1c2a9b4e1d0012ab3401", name: "الرياض" }
                    userType: provider
                    status: active
                    statusText: "نشط"
                    notifyCount: 0
                    isNotify: true
                    active: true
                    updatedPhone: ""
                    updatedCountryCode: ""
                    nationalId: "1012345678"
                    rating: 4.8
                    isAvailable: true
                    isAvailableText: "متاح"
                    commercialRegisterImages:
                      - "https://dashboard.example.com/assets/uploads/users/providers/665f1c2a9b4e1d0012ab34ce/register-1.pdf"
                    approvalStatus: accept
                    balance: 0
                    location: { title: "موقع المتجر", description: "الرياض، المملكة العربية السعودية", longitude: 46.6753, latitude: 24.7136 }
                    premiumSubscription: ""
                    whatsappNumber: "0551234567"
                    whatsappCountryCode: "+966"
              pendingProvider:
                summary: Complete pending-provider DTO after saving the location
                value:
                  key: wait_approval
                  message: "تم حفظ موقعك، حسابك قيد المراجعة من قبل الإدارة"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34cf"
                    avatar: "https://dashboard.example.com/assets/uploads/users/providersMeta/665f1c2a9b4e1d0012ab34cf/avatar.png"
                    name: "متجر جديد"
                    countryCode: "+966"
                    phone: "0557654321"
                    fullPhone: "+9660557654321"
                    city: { id: "665f1c2a9b4e1d0012ab3401", name: "الرياض" }
                    userType: provider
                    status: active
                    statusText: "نشط"
                    notifyCount: 0
                    isNotify: true
                    active: true
                    updatedPhone: ""
                    updatedCountryCode: ""
                    nationalId: "1098765432"
                    rating: 0
                    isAvailable: true
                    isAvailableText: "متاح"
                    commercialRegisterImages:
                      - "https://dashboard.example.com/assets/uploads/users/providers/665f1c2a9b4e1d0012ab34cf/register-1.pdf"
                    approvalStatus: wait
                    balance: 0
                    location: { title: "موقع المتجر", description: "الرياض، المملكة العربية السعودية", longitude: 46.6753, latitude: 24.7136 }
                    premiumSubscription: ""
                    whatsappNumber: "0557654321"
                    whatsappCountryCode: "+966"
      '400':
        description: Invalid phone/country code, unknown account, or coordinates outside their valid ranges.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            example: { key: fail, message: "بيانات الموقع غير صحيحة", status: 400 }
      '423':
        $ref: '#/shared-blocked'
      '500':
        $ref: '#/shared-server-error'

send-code:
  patch:
    tags: [Shared Auth]
    summary: Send OTP code
    description: |
      Send an OTP code to the user's phone number.
      This endpoint is used for both account activation and forgot-password flow.

      - `purpose: activation` starts or resends the activation-code flow. The current
        validator also accepts this purpose for an already active account.
      - `purpose: forgot_password` starts password recovery.
      - AccountIdentity-backed Client/Provider roles share one recovery state and one
        canonical password. Legacy profiles remain on the legacy recovery state.
      - The OTP expires after 1 minute and is never returned.
      - Repeated requests are throttled for 30 seconds in the running process.
      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22212&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-85434&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
    x-figma:
      mobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22212&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-85434&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
    operationId: sendPasswordResetCode
    security:
      - SecretKeyAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '../openapi.yaml#/components/schemas/SendCodeRequest'
          examples:
            client:
              value: { countryCode: "+966", phone: "0512345678", userType: client, purpose: forgot_password }
            provider:
              value: { countryCode: "+966", phone: "0551234567", userType: provider, purpose: activation }
    responses:
      '200':
        description: SMS provider accepted the OTP request. No OTP is returned.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/SendCodeSuccessResponse'
            example:
              key: needActive
              message: "تم ارسال كود التحقق بنجاح"
              status: 200
      '400':
        description: Validation error, unknown account, or cooldown still active.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            examples:
              invalidPurpose:
                value: { key: fail, message: "غرض التحقق يجب أن يكون activation أو forgot_password أو change_phone", status: 400 }
              invalidUserType:
                value: { key: fail, message: "نوع المستخدم يجب أن يكون client أو provider", status: 400 }
              cooldown:
                value: { key: fail, message: "يرجى الانتظار 45 ثانية قبل المحاولة مرة أخرى.", status: 400 }
              accountNotFound:
                value: { key: fail, message: "هذا الحساب غير موجود", status: 400 }
      '423':
        $ref: '#/shared-blocked'
      '500':
        $ref: '#/shared-server-error'

change-password:
  patch:
    tags: [Shared Auth]
    summary: Reset password after OTP verification
    description: |
      - Completes password recovery after `PATCH /activate` verifies the recovery code.
      - Requires the one-time `resetToken` field issued by that verification step.
      - Enforces the 8-128 character password policy and matching confirmation.
      - AccountIdentity-backed Client/Provider roles update only the canonical
        `AccountIdentity.password`; legacy profile password mirrors are not authoritative.
      - Legacy profiles without AccountIdentity retain the existing User-model behavior.
      - The reset token/OTP state is invalidated, `tokenVersion` and
        `passwordChangedAt` advance internally, and all Client/Provider UserToken
        sessions linked to the AccountIdentity are revoked.
      - No password, OTP, or reset token is returned.
      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22264&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-84669&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
    x-figma:
      mobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-22264&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10760-84669&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
    operationId: changePasswordAfterOtp
    security:
      - SecretKeyAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '../openapi.yaml#/components/schemas/ChangePasswordRequest'
          examples:
            client:
              value:
                countryCode: "+966"
                phone: "0512345678"
                userType: client
                resetToken: "<one-time-reset-token>"
                password: "ExampleNewPass1!"
                confirmPassword: "ExampleNewPass1!"
            provider:
              value:
                countryCode: "+966"
                phone: "0551234567"
                userType: provider
                resetToken: "<one-time-reset-token>"
                password: "ExampleNewPass1!"
                confirmPassword: "ExampleNewPass1!"
    responses:
      '200':
        description: Password changed; reset state consumed and previous sessions revoked.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/SuccessResponse'
            example:
              key: success
              message: "تم تغيير كلمة المرور بنجاح"
              status: 200
      '400':
        description: Validation error, unknown account, weak/mismatched password, or same password.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            examples:
              invalidUserType:
                value: { key: fail, message: "نوع المستخدم يجب أن يكون client أو provider", status: 400 }
              missingResetToken:
                value: { key: fail, message: "رمز إعادة تعيين كلمة المرور مطلوب يرجي ادخال البيانات", status: 400 }
              passwordMismatch:
                value: { key: fail, message: "كلمة المرور وتأكيد كلمة المرور غير متطابقين", status: 400 }
              accountNotFound:
                value: { key: fail, message: "هذا الحساب غير موجود", status: 400 }
      '419':
        description: Reset token is invalid, expired, or already consumed.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
            example:
              key: unauthorized
              message: "جلسة إعادة تعيين كلمة المرور غير صالحة أو منتهية"
              status: 419
      '423':
        $ref: '#/shared-blocked'
      '500':
        $ref: '#/shared-server-error'

signout:
  post:
    tags: [Shared Auth]
    summary: Sign out
    description: |
      - Signs out the authenticated client or provider from the current device.
      - Deletes the current bearer token from server-side token storage.
      - Deletes the device record matching `deviceId`.
      - After a successful response, Swagger UI removes only the bearer scheme used by this request from Authorize.
      - Does not return the token or any other sensitive value.
      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9449-7875&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Client App</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-112264&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Client Web</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-26780&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Provider App</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-100498&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Provider Web</a></div>
    x-figma:
      client:
        app: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9449-7875&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
        web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-112264&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      provider:
        app: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-26780&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
        web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-100498&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
    operationId: signOut
    security:
      - SecretKeyAuth: []
        ClientBearerAuth: []
      - SecretKeyAuth: []
        ProviderBearerAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            type: object
            required: [deviceId]
            properties:
              deviceId:
                type: string
                description: Identifier of the device session to remove.
                example: "current-device-id"
          example:
            deviceId: "current-device-id"
    responses:
      '200':
        description: Current token and device session were removed successfully.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/SuccessResponse'
            example:
              key: success
              message: "تم تسجيل الخروج بنجاح"
              status: 200
      '400':
        description: deviceId is missing or invalid.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            examples:
              missingDeviceId:
                value:
                  key: fail
                  message: "deviceId مطلوب يرجي ادخال البيانات"
                  status: 400
      '419':
        description: Bearer token is missing, invalid, expired, or revoked.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
            example: { key: unauthorized, message: "يجب تسجيل الدخول", status: 419 }
      '500':
        $ref: '#/shared-server-error'

delete-account:
  delete:
    tags: [Shared Auth]
    summary: Delete the authenticated account
    description: |
      Delete account is allowed only when the authenticated client/provider has no active financial, order, or auction obligations.

      The account identity and type are taken only from the selected bearer token. `userId` and `userType` are not accepted in the request.

      Deletion is blocked for a client when:
      - Wallet balance is greater than zero.
      - A paid order is not completed, including accepted, in-progress, shipping, delivery, or returning stages.
      - The client participates in a current or upcoming auction.
      - The client owns a current or upcoming auction where that product flow is supported.
      - The client won and paid for an auction but receipt is not confirmed.

      Deletion is blocked for a provider when:
      - The provider has an unsettled wallet balance or earnings.
      - A financial transaction or settlement has the actual `pending` status.
        Accepted or rejected financial records do not block deletion.
      - A paid order or business operation is still active.
      - The provider owns a current or upcoming auction.
      - A paid auction still has an unfinished receipt or delivery.

      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-26780&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">App</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-100498&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
    x-figma:
      app: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-26780&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      appNodeId: "9967:26780"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-100498&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      webNodeId: "10762:100498"
    operationId: deleteAccount
    security:
      - SecretKeyAuth: []
        ClientBearerAuth: []
      - SecretKeyAuth: []
        ProviderBearerAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    responses:
      '200':
        description: The authenticated account was deleted successfully.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/DeleteAccountSuccessResponse'
            example:
              key: success
              message: "تم حذف الحساب بنجاح"
              status: 200
              data:
                deleted: true
      '400':
        description: Account deletion is blocked by one or more active obligations.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/DeleteAccountBlockedResponse'
            examples:
              clientBlocked:
                summary: Client has wallet, order, and auction obligations
                value:
                  key: fail
                  message: "لا يمكن حذف الحساب لوجود عمليات نشطة أو مستحقات مالية"
                  status: 400
                  data:
                    canDelete: false
                    reasons:
                      - code: wallet_balance_exists
                        message: "لا يمكن حذف الحساب لوجود رصيد في المحفظة"
                      - code: active_paid_order_exists
                        message: "لا يمكن حذف الحساب لوجود طلبات مدفوعة لم تنتهِ بعد"
                      - code: auction_won_paid_not_received
                        message: "لا يمكن حذف الحساب لوجود مزاد مدفوع لم يتم استلامه بعد"
              providerBlocked:
                summary: Provider has unsettled financial obligations
                value:
                  key: fail
                  message: "لا يمكن حذف الحساب لوجود معاملات مالية أو عمليات نشطة"
                  status: 400
                  data:
                    canDelete: false
                    reasons:
                      - code: provider_balance_exists
                        message: "لا يمكن حذف الحساب لوجود رصيد مالي غير مصفى"
                      - code: provider_pending_financial_transaction_exists
                        message: "لا يمكن حذف الحساب لوجود معاملات مالية غير منتهية"
      '401':
        description: Standard unauthorized response when no valid client/provider bearer is supplied.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
            example:
              key: unauthorized
              message: "غير مصرح"
              status: 401
      '419':
        description: Legacy unauthorized status currently emitted by the project authentication middleware.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
            example:
              key: unauthorized
              message: "غير مصرح"
              status: 419
      '423':
        $ref: '#/shared-blocked'
      '500':
        $ref: '#/shared-server-error'

profile:
  get:
    tags: [Shared Profile]
    summary: Get current user profile
    description: |
      - Returns the authenticated client or provider profile.
      - Uses the bearer token actor type; client and provider credentials are never mixed.
      - The response contains no password or OTP.
      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9468-9077&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Client App</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-120591&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Client Web</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27377&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Provider App</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101365&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Provider Web</a></div>
    x-figma:
      client:
        app: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9468-9077&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
        web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-120591&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      provider:
        app: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27377&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
        web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101365&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
    operationId: getAuthProfile
    security:
      - SecretKeyAuth: []
        ClientBearerAuth: []
      - SecretKeyAuth: []
        ProviderBearerAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    responses:
      '200':
        description: Current authenticated profile.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ProfileSuccessResponse'
            examples:
              client:
                value:
                  key: success
                  message: "الملف الشخصي"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34cd"
                    name: "Example Client"
                    avatar: "https://example.com/client-avatar.png"
                    countryCode: "+966"
                    phone: "0512345678"
                    fullPhone: "+9660512345678"
                    userType: client
                    status: active
                    active: true
                    token: "<client-access-token>"
                    tokenType: access
              provider:
                value:
                  key: success
                  message: "الملف الشخصي"
                  status: 200
                  data:
                    id: "665f1c2a9b4e1d0012ab34ce"
                    name: "Example Provider"
                    avatar: "https://example.com/provider-avatar.png"
                    countryCode: "+966"
                    phone: "0551234567"
                    fullPhone: "+9660551234567"
                    userType: provider
                    status: active
                    active: true
                    token: "<provider-access-token>"
                    tokenType: access
      '419':
        description: Bearer token is missing, invalid, expired, or revoked.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
            example: { key: unauthorized, message: "يجب تسجيل الدخول", status: 419 }
      '500':
        $ref: '#/shared-server-error'

client-profile-update:
  patch:
    tags: [Client Profile]
    summary: Update client profile
    description: |
      - Updates only the fields that are sent; every field is optional.
      - The client is identified from the bearer token, never from the body.
      - Phone, password, status, and every other restricted field cannot be changed here; unknown fields are rejected.
      - The response never contains a password or OTP.

      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9462-8930&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-122763&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
    x-figma:
      mobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9462-8930&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      mobileNodeId: "9462:8930"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-122763&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      webNodeId: "10257:122763"
    operationId: updateClientProfile
    security:
      - SecretKeyAuth: []
        ClientBearerAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: false
      content:
        multipart/form-data:
          schema:
            type: object
            properties:
              name:
                type: string
                minLength: 2
                maxLength: 80
                example: "محمد"
              email:
                type: string
                format: email
                description: Lowercased and trimmed. Must not be used by another client account.
                example: "user@example.com"
              avatar:
                type: string
                format: binary
                description: "Optional image. Allowed types: jpg, jpeg, png, webp (validated by file signature)."
          example:
            name: "Example Client"
            email: "client@example.com"
    responses:
      '200':
        description: Client profile updated successfully; returns the safe profile DTO.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ClientProfileResponse'
            example:
              key: success
              message: "تم تعديل الملف الشخصي بنجاح"
              status: 200
              data:
                id: "665f1c2a9b4e1d0012ab34cd"
                name: "Example Client"
                avatar: "https://example.com/client-avatar.png"
                countryCode: "+966"
                phone: "0512345678"
                fullPhone: "+9660512345678"
                userType: client
                status: active
                active: true
                token: "<client-access-token>"
                tokenType: access
      '400':
        description: Validation failed (invalid name/email, duplicate email, unsupported avatar type, or unknown fields).
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            example: { key: fail, message: "البريد الإلكتروني مستخدم بالفعل، اختر بريدًا إلكترونيًا آخر", status: 400 }
      '419':
        description: Bearer token is missing, invalid, expired, revoked, or belongs to a provider.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
            example: { key: unauthorized, message: "يجب تسجيل الدخول", status: 419 }
      '423':
        $ref: '#/shared-blocked'
      '500':
        $ref: '#/shared-server-error'

provider-profile-update:
  patch:
    tags: [Provider Profile]
    summary: Update provider profile
    description: |
      - Updates only the fields that are sent; every field is optional.
      - The provider is identified from the bearer token, never from the body.
      - Commercial-register files: the FINAL total is limited to 3. Final = existing − removed + added.
        A provider with 3 files may remove one and add one in the same request (final = 3 ✅),
        but adding without removing when already at 3 is rejected (final = 4 ❌).
      - `removeCommercialRegisterImages` only accepts files that belong to the authenticated provider.
      - Phone, password, status, approvalStatus, and every other restricted field cannot be changed here; unknown fields are rejected.
      - The response never contains a password, OTP, or internal file paths.

      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27198&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101662&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Web</a></div>
    x-figma:
      mobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27198&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      mobileNodeId: "9967:27198"
      web: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101662&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      webNodeId: "10762:101662"
    operationId: updateProviderProfile
    security:
      - SecretKeyAuth: []
        ProviderBearerAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: false
      content:
        multipart/form-data:
          schema:
            type: object
            properties:
              name:
                type: string
                minLength: 2
                maxLength: 80
                example: "متجر الوفرة"
              email:
                type: string
                format: email
                description: Lowercased and trimmed. Must not be used by another provider account.
                example: "store@example.com"
              nationalId:
                type: string
                description: Identity number (free-text string, trimmed).
                example: "1000000000"
              city:
                type: string
                description: City id (Mongo ObjectId). Must reference an active, visible city.
                example: "665f1c2a9b4e1d0012ab34cf"
              avatar:
                type: string
                format: binary
                description: "Optional image. Allowed types: jpg, jpeg, png, webp (validated by file signature)."
              commercialRegisterImage:
                type: array
                maxItems: 3
                items:
                  type: string
                  format: binary
                description: |
                  New commercial-register files. Allowed types: jpg, jpeg, png, pdf (validated by file signature).
                  Maximum FINAL count is 3 files: existing − removed + added ≤ 3. A file can be
                  removed and replaced in the same request as long as the final count stays ≤ 3.
              removeCommercialRegisterImages:
                type: array
                items:
                  type: string
                description: |
                  Commercial-register files to remove, identified by the stored file name or the
                  URL returned in `commercialRegisterImages` of the profile DTO. Accepts repeated
                  form fields, a JSON-string array, or the comma-separated value emitted by Swagger
                  UI. Only files owned by the authenticated provider are accepted.
                example: ["commercialRegisterImage-1699999999999.png"]
          encoding:
            removeCommercialRegisterImages:
              style: form
              explode: true
          example:
            name: "Example Provider"
            email: "provider@example.com"
            nationalId: "1000000000"
            city: "665f1c2a9b4e1d0012ab34cf"
            removeCommercialRegisterImages: []
    responses:
      '200':
        description: Provider profile updated successfully; returns the safe profile DTO.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ProviderProfileResponse'
            example:
              key: success
              message: "تم تعديل الملف الشخصي بنجاح"
              status: 200
              data:
                id: "665f1c2a9b4e1d0012ab34ce"
                name: "Example Provider"
                avatar: "https://example.com/provider-avatar.png"
                countryCode: "+966"
                phone: "0551234567"
                fullPhone: "+9660551234567"
                userType: provider
                status: active
                active: true
                approvalStatus: accept
                commercialRegisterImages: []
                token: "<provider-access-token>"
                tokenType: access
      '400':
        description: Validation failed (invalid field, duplicate email, unknown city, unsupported file type, final commercial-register count above 3, removing a file that is not owned, or unknown fields).
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse'
            example: { key: fail, message: "الحد الأقصى لملفات السجل التجاري هو 3 ملفات", status: 400 }
      '419':
        description: Bearer token is missing, invalid, expired, revoked, or belongs to a client.
        content:
          application/json:
            schema:
              $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
            example: { key: unauthorized, message: "يجب تسجيل الدخول", status: 419 }
      '423':
        $ref: '#/shared-blocked'
      '500':
        $ref: '#/shared-server-error'

change-phone-verify-password:
  post:
    tags: [Shared Profile]
    summary: Verify password before changing phone
    description: |
      Verifies the authenticated client/provider password and returns a one-purpose,
      10-minute `changePhoneToken`. AccountIdentity-backed roles verify the one shared
      canonical AccountIdentity password; legacy profiles keep legacy verification.
      No password is returned or logged.

      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9476-6942&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Client Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-128827&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Client Web</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27245&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Provider Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101837&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Provider Web</a></div>
    x-figma: &change-phone-figma
      clientMobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9476-6942&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      clientMobileNodeId: "9476:6942"
      clientWeb: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-128827&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      clientWebNodeId: "10257:128827"
      providerMobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27245&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      providerMobileNodeId: "9967:27245"
      providerWeb: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101837&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      providerWebNodeId: "10762:101837"
    operationId: verifyChangePhonePassword
    security: &settings-security
      - SecretKeyAuth: []
        ClientBearerAuth: []
      - SecretKeyAuth: []
        ProviderBearerAuth: []
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            type: object
            required: [password]
            additionalProperties: false
            properties:
              password: { type: string, format: password, minLength: 8, maxLength: 128, writeOnly: true }
          example:
            password: "ExampleCurrentPass1!"
    responses:
      '200':
        description: Password verified; short-lived change-phone session created.
        content:
          application/json:
            schema:
              allOf:
                - $ref: '../openapi.yaml#/components/schemas/SuccessResponse'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        changePhoneToken:
                          type: string
                          description: Short-lived token returned once for the next change-phone steps.
                        tokenType: { type: string, enum: [change_phone] }
                        userType: { type: string, enum: [client, provider] }
                        expiresIn: { type: integer, example: 600 }
                        purpose: { type: string, enum: [change_phone] }
            example:
              key: success
              message: "تم التحقق من كلمة المرور بنجاح"
              status: 200
              data:
                changePhoneToken: "<one-time-change-phone-token>"
                tokenType: change_phone
                userType: client
                expiresIn: 600
                purpose: change_phone
      '400':
        description: Password is wrong or malformed.
        content:
          application/json:
            schema: { $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse' }
            example: { key: fail, message: "كلمة المرور غير صحيحة", status: 400 }
      '419': { $ref: '#/shared-unauthorized' }
      '423': { $ref: '#/shared-blocked' }
      '500': { $ref: '#/shared-server-error' }

change-phone:
  patch:
    tags: [Shared Profile]
    summary: Send OTP to a new phone
    description: |
      Requires the authenticated actor and the short-lived token from the password step.
      Saves the new phone as pending and sends OTP to it; the current phone is unchanged.

      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9476-6942&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Client Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-128827&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Client Web</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27245&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Provider Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101837&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Provider Web</a></div>
    x-figma: *change-phone-figma
    operationId: requestPhoneChange
    security: *settings-security
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            type: object
            required: [countryCode, updatedPhone, changePhoneToken]
            additionalProperties: false
            properties:
              countryCode: { type: string, example: "+966" }
              updatedPhone: { type: string, example: "0500000000" }
              changePhoneToken: { type: string, writeOnly: true }
          example:
            countryCode: "+966"
            updatedPhone: "0500000000"
            changePhoneToken: "<one-time-change-phone-token>"
    responses:
      '200':
        description: OTP sent to the pending phone; current phone remains unchanged.
        content:
          application/json:
            schema: { $ref: '../openapi.yaml#/components/schemas/SuccessResponse' }
            example:
              key: success
              message: "تم إرسال رمز التحقق إلى رقم الجوال الجديد"
              status: 200
              data: { purpose: change_phone, countryCode: "+966", updatedPhone: "0500000000" }
      '400':
        description: Invalid, duplicate, or unchanged phone number.
        content:
          application/json:
            schema: { $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse' }
            examples:
              duplicatePhone:
                value: { key: fail, message: "رقم الجوال مستخدم من قبل", status: 400 }
              invalidPhone:
                value: { key: fail, message: "رقم الجوال غير صحيح لكود الدولة", status: 400 }
              unchangedPhone:
                value: { key: fail, message: "رقم الجوال مطابق للرقم المسجل", status: 400 }
      '419': { $ref: '#/shared-unauthorized' }
      '423': { $ref: '#/shared-blocked' }
      '500': { $ref: '#/shared-server-error' }

update-password:
  patch:
    tags: [Shared Profile]
    summary: Change authenticated account password
    description: |
      Verifies `oldPassword`, enforces the strong-password policy for `newPassword`,
      and requires matching confirmation. This is separate from forgot-password reset.
      For AccountIdentity-backed users, this changes the single password shared by
      Client and Provider roles, increments the internal token version, records the
      password-change time, and revokes all linked UserToken sessions. Legacy profiles
      without AccountIdentity keep their existing behavior.

      <div class="figma-links"><span>Design:</span><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9476-7202&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Client Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-123459&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Client Web</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27260&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1" target="_blank" rel="noopener">Provider Mobile</a><a href="https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101782&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038" target="_blank" rel="noopener">Provider Web</a></div>
    x-figma:
      clientMobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9476-7202&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      clientMobileNodeId: "9476:7202"
      clientWeb: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10257-123459&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      clientWebNodeId: "10257:123459"
      providerMobile: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?node-id=9967-27260&viewport=176%2C570%2C0.02&t=c4yTtQLlUc9FQir5-1&scaling=scale-down&content-scaling=fixed&starting-point-node-id=9064%3A6087&page-id=0%3A1"
      providerMobileNodeId: "9967:27260"
      providerWeb: "https://www.figma.com/proto/mTRILiWaS8pzLcNnmAUr2k/%D9%83%D9%85-%D8%AA%D8%B3%D9%88%D9%8A?page-id=10227%3A31854&node-id=10762-101782&viewport=-948%2C0%2C0.12&t=PNIRCKFxGV24peHK-1&scaling=min-zoom&content-scaling=fixed&starting-point-node-id=10449%3A65038"
      providerWebNodeId: "10762:101782"
    operationId: updateAuthenticatedPassword
    security: *settings-security
    parameters:
      - $ref: '../openapi.yaml#/components/parameters/LangHeader'
    requestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            type: object
            required: [oldPassword, newPassword, confirmPassword]
            additionalProperties: false
            properties:
              oldPassword: { type: string, format: password, minLength: 8, maxLength: 128, writeOnly: true }
              newPassword: { type: string, format: password, minLength: 8, maxLength: 128, writeOnly: true }
              confirmPassword: { type: string, format: password, minLength: 8, maxLength: 128, writeOnly: true }
          example:
            oldPassword: "ExampleCurrentPass1!"
            newPassword: "ExampleNewPass2!"
            confirmPassword: "ExampleNewPass2!"
    responses:
      '200':
        description: Password changed; all linked sessions are revoked for AccountIdentity-backed users.
        content:
          application/json:
            schema: { $ref: '../openapi.yaml#/components/schemas/SuccessResponse' }
            example: { key: success, message: "تم تغيير كلمة المرور بنجاح", status: 200 }
      '400':
        description: Old password is wrong, new password is weak/same, or confirmation does not match.
        content:
          application/json:
            schema: { $ref: '../openapi.yaml#/components/schemas/ValidationErrorResponse' }
            examples:
              wrongOldPassword:
                value: { key: fail, message: "كلمة المرور غير صحيحة", status: 400 }
              confirmationMismatch:
                value: { key: fail, message: "كلمتا المرور الجديدتان غير متطابقتين", status: 400 }
      '419': { $ref: '#/shared-unauthorized' }
      '423': { $ref: '#/shared-blocked' }
      '500': { $ref: '#/shared-server-error' }

shared-blocked:
  description: The account is blocked.
  content:
    application/json:
      schema:
        $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
      example: { key: blocked, message: "حسابك موقوف حالياً", status: 423 }

shared-unauthorized:
  description: Client/provider access token or change-phone proof is missing, invalid, expired, or mismatched.
  content:
    application/json:
      schema:
        $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
      example: { key: unauthorized, message: "يجب تسجيل الدخول", status: 419 }

shared-server-error:
  description: Unexpected server error without a stack trace.
  content:
    application/json:
      schema:
        $ref: '../openapi.yaml#/components/schemas/ErrorResponse'
      example: { key: exception, message: "حدث خطأ، برجاء التواصل مع الدعم", status: 500 }
