> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowxi.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Login (email + password)

> Strict anti-enumeration: unknown email, wrong password, non-active account => INVALID_CREDENTIALS (401). If 2FA enabled: returns a challenge (no token). If 2FA disabled: issues a token (1 token per device_id).




## OpenAPI

````yaml api-reference/openapi.yaml post /api/v1/auth/login
openapi: 3.0.3
info:
  title: Flowxi API Documentation
  version: 1.0.0
servers:
  - url: https://api.flowxi.app
security:
  - bearerAuth: []
tags:
  - name: Health
    description: Service healthcheck.
  - name: Registration
    description: Registration flows (magic link + email OTP).
  - name: Auth
    description: Login and session management.
  - name: Two-factor authentication
    description: TOTP 2FA endpoints (login verification + manage 2FA).
  - name: Devices
    description: Session/device listing and revocation.
  - name: Me
    description: Profile of the authenticated user (Sanctum session).
paths:
  /api/v1/auth/login:
    post:
      tags:
        - Auth
      summary: Login (email + password)
      description: >
        Strict anti-enumeration: unknown email, wrong password, non-active
        account => INVALID_CREDENTIALS (401). If 2FA enabled: returns a
        challenge (no token). If 2FA disabled: issues a token (1 token per
        device_id).
      operationId: postApiV1AuthLogin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - password
                - device_type
                - device_name
                - device_id
              properties:
                email:
                  type: string
                  format: email
                  example: gbailey@example.net
                password:
                  type: string
                  example: +-0pBNvYgxwmi/#iw
                device_type:
                  type: string
                  example: web
                device_name:
                  type: string
                  example: Chrome (Mac)
                device_id:
                  type: string
                  example: device-uuid-123
                country:
                  type: string
                  nullable: true
                  example: BJ
      responses:
        '200':
          description: Login success (token) OR 2FA challenge returned.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/LoginSuccessResponse'
                  - $ref: '#/components/schemas/LoginMfaRequiredResponse'
              examples:
                login_success:
                  value:
                    code: LOGIN_SUCCESS
                    message: Login successful.
                    mfa_required: false
                    access_token: 125|XXXXXXXXXXXXXXXXXXXXXXXX
                    token_type: Bearer
                    account_status: active
                    user_id: 123
                mfa_required:
                  value:
                    code: MFA_REQUIRED
                    message: Two-factor authentication required.
                    mfa_required: true
                    challenge_id: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
                    otp_type: totp
                    expires_in: 300
        '401':
          description: Invalid credentials (anti-enumeration).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              example:
                code: INVALID_CREDENTIALS
                message: Invalid credentials.
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
      security: []
components:
  schemas:
    LoginSuccessResponse:
      allOf:
        - $ref: '#/components/schemas/ApiOkBase'
        - type: object
          required:
            - mfa_required
            - access_token
            - token_type
            - account_status
            - user_id
          properties:
            mfa_required:
              type: boolean
              example: false
            access_token:
              type: string
              example: 125|XXXXXXXXXXXXXXXXXXXXXXXX
            token_type:
              type: string
              example: Bearer
            account_status:
              type: string
              example: active
            user_id:
              type: integer
              example: 123
      example:
        code: LOGIN_SUCCESS
        message: Login successful.
        mfa_required: false
        access_token: 125|XXXXXXXXXXXXXXXXXXXXXXXX
        token_type: Bearer
        account_status: active
        user_id: 123
    LoginMfaRequiredResponse:
      allOf:
        - $ref: '#/components/schemas/ApiOkBase'
        - type: object
          required:
            - mfa_required
            - challenge_id
            - otp_type
            - expires_in
          properties:
            mfa_required:
              type: boolean
              example: true
            challenge_id:
              type: string
              format: uuid
              example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
            otp_type:
              type: string
              example: totp
            expires_in:
              type: integer
              example: 300
      example:
        code: MFA_REQUIRED
        message: Two-factor authentication required.
        mfa_required: true
        challenge_id: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
        otp_type: totp
        expires_in: 300
    ApiError:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Machine-readable error code.
          example: UNAUTHENTICATED
        message:
          type: string
          description: Localized, user-facing message.
          example: Unauthenticated.
    ApiOkBase:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          example: OK
        message:
          type: string
          example: OK
  responses:
    RateLimited:
      description: Rate limited.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            code: RATE_LIMITED
            message: Too many requests.
    ServerError:
      description: Server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            code: SERVER_ERROR
            message: Server error.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Sanctum

````