openapi: 3.0.3
info:
  title: Protekt API
  version: 1.0.0
  description: >
    Protekt is a developer-friendly authentication platform. This specification
    covers two APIs:


    - **Account API**: Lets you manage your Protekt organization, including
    projects, API keys, team members, and usage metrics. Authenticated via a
    Management Token.

    - **Authentication API**: Handles all end-user identity operations such as
    signup, login, passwordless flows, SSO, MFA, and session management.
    Authenticated via a project API key.
servers:
  - url: https://api.protekt.io/v1
    description: Account API
  - url: https://auth.protekt.io/v1
    description: Authentication API
tags:
  - name: Projects
    description: Create and manage Protekt projects
  - name: Management Tokens
    description: Issue and revoke Management Tokens
  - name: API Keys
    description: Generate and manage project API keys
  - name: Members
    description: Manage organization team members
  - name: Usage
    description: Retrieve usage and MAU metrics
  - name: Auth
    description: Email/password signup, login, and logout
  - name: Tokens
    description: JWT refresh, verification, and revocation
  - name: Passwords
    description: Password reset, confirmation, and change
  - name: OTP
    description: One-time passcode (passwordless) flows
  - name: Magic Link
    description: Magic link (passwordless) flows
  - name: SSO
    description: Single Sign-On (OIDC / SAML) flows
  - name: MFA
    description: Multi-factor authentication enrollment and verification
  - name: Users
    description: User profile management
  - name: Sessions
    description: Session listing and revocation
components:
  securitySchemes:
    ManagementToken:
      type: http
      scheme: bearer
      description: >-
        Management Token obtained from POST /token. Required for all Account API
        endpoints.
    ApiKey:
      type: apiKey
      in: header
      name: X-Protekt-Key
      description: Project API key. Required for all Authentication API endpoints.
    UserToken:
      type: http
      scheme: bearer
      description: >-
        User JWT access token. Required for user-scoped Authentication API
        actions (for example, logout, session management).
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: invalid_credentials
            message:
              type: string
              example: The email or password is incorrect.
            details:
              type: object
              additionalProperties: true
    PaginationMeta:
      type: object
      properties:
        next_cursor:
          type: string
          nullable: true
          example: cur_abc123
        has_more:
          type: boolean
          example: true
    Project:
      type: object
      properties:
        id:
          type: string
          example: proj_01jk8abc
        login_id:
          type: string
          example: lp_7xqm9xyz
        name:
          type: string
          example: My App – Production
        description:
          type: string
          nullable: true
        redirect_url:
          type: string
          format: uri
          example: https://myapp.com/dashboard
        allowed_origins:
          type: array
          items:
            type: string
          example:
            - https://myapp.com
        token_expiry:
          type: integer
          description: JWT expiry in seconds
          example: 3600
        refresh_token_expiry:
          type: integer
          description: Refresh token expiry in seconds
          example: 2592000
        mfa_required:
          type: boolean
          example: false
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ProjectCreate:
      type: object
      required:
        - name
        - allowed_origins
        - redirect_url
      properties:
        name:
          type: string
          example: My App – Production
        allowed_origins:
          type: array
          items:
            type: string
          example:
            - https://myapp.com
        redirect_url:
          type: string
          format: uri
          example: https://myapp.com/dashboard
        description:
          type: string
        token_expiry:
          type: integer
          default: 3600
        refresh_token_expiry:
          type: integer
          default: 2592000
        mfa_required:
          type: boolean
          default: false
    ProjectUpdate:
      type: object
      properties:
        name:
          type: string
        allowed_origins:
          type: array
          items:
            type: string
        redirect_url:
          type: string
          format: uri
        token_expiry:
          type: integer
        mfa_required:
          type: boolean
    ManagementToken:
      type: object
      properties:
        management_token:
          type: string
          example: mgmt_eyJhbGciOiJIUz...
        expires_at:
          type: string
          format: date-time
    ApiKey:
      type: object
      properties:
        id:
          type: string
          example: key_xk901abc
        key:
          type: string
          description: Raw key value — only returned at creation time
          example: pk_live_AbCdEfGh...
        label:
          type: string
          example: Backend server – production
        project_id:
          type: string
          example: proj_01jk8abc
        scopes:
          type: array
          items:
            type: string
            enum:
              - auth:read
              - auth:write
              - users:read
              - users:write
        expires_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
    ApiKeyCreate:
      type: object
      required:
        - project_id
        - label
      properties:
        project_id:
          type: string
        label:
          type: string
        scopes:
          type: array
          items:
            type: string
            enum:
              - auth:read
              - auth:write
              - users:read
              - users:write
          default:
            - auth:read
            - auth:write
            - users:read
            - users:write
        expires_at:
          type: string
          format: date-time
          nullable: true
    ApiKeyMeta:
      type: object
      description: API key metadata — key value is never returned after creation
      properties:
        id:
          type: string
        label:
          type: string
        project_id:
          type: string
        scopes:
          type: array
          items:
            type: string
        expires_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
    Member:
      type: object
      properties:
        id:
          type: string
          example: mem_001
        email:
          type: string
          format: email
        role:
          type: string
          enum:
            - owner
            - admin
            - developer
        joined_at:
          type: string
          format: date-time
    UsageStats:
      type: object
      properties:
        period:
          type: object
          properties:
            from:
              type: string
              format: date
            to:
              type: string
              format: date
        mau:
          type: integer
          example: 1842
        total_logins:
          type: integer
          example: 9231
        tokens_issued:
          type: integer
          example: 11004
        passwordless_logins:
          type: integer
          example: 3120
        sso_logins:
          type: integer
          example: 894
    User:
      type: object
      properties:
        id:
          type: string
          example: usr_9klabc
        email:
          type: string
          format: email
        email_verified:
          type: boolean
        mfa_enabled:
          type: boolean
        mfa_methods:
          type: array
          items:
            type: string
            enum:
              - totp
              - sms
        metadata:
          type: object
          additionalProperties: true
        disabled:
          type: boolean
        created_at:
          type: string
          format: date-time
        last_login_at:
          type: string
          format: date-time
          nullable: true
    AuthTokenResponse:
      type: object
      properties:
        access_token:
          type: string
          example: eyJhbGci...
        refresh_token:
          type: string
          example: rt_xYz...
        expires_in:
          type: integer
          description: Seconds until the access token expires
          example: 3600
        token_type:
          type: string
          default: Bearer
        user:
          $ref: '#/components/schemas/User'
    MfaChallengeResponse:
      type: object
      description: Returned from POST /auth/login when MFA is required
      properties:
        mfa_required:
          type: boolean
          example: true
        mfa_token:
          type: string
          description: Temporary token to pass to POST /auth/mfa/verify
          example: mfa_tmp_abc...
        available_methods:
          type: array
          items:
            type: string
            enum:
              - totp
              - sms
    Session:
      type: object
      properties:
        id:
          type: string
          example: sess_kl8abc
        ip_address:
          type: string
          example: 197.210.xx.xx
        user_agent:
          type: string
        created_at:
          type: string
          format: date-time
        last_active_at:
          type: string
          format: date-time
        is_current:
          type: boolean
    TokenClaims:
      type: object
      properties:
        valid:
          type: boolean
        claims:
          type: object
          properties:
            sub:
              type: string
              example: usr_9klabc
            email:
              type: string
              format: email
            iat:
              type: integer
            exp:
              type: integer
            project_id:
              type: string
    TotpEnrollResponse:
      type: object
      properties:
        method:
          type: string
          example: totp
        totp_uri:
          type: string
          example: >-
            otpauth://totp/Protekt:user@example.com?secret=BASE32SECRET&issuer=Protekt
        qr_code:
          type: string
          description: Base64-encoded PNG QR code
          example: data:image/png;base64,...
  responses:
    Unauthorized:
      description: Missing, invalid, or expired token or API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: Validation error or malformed request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Too many requests
      headers:
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: Resource already exists
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  parameters:
    ProjectId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: The unique project ID (for example, proj_01jk8abc)
    UserId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: The unique user ID (for example, usr_9klabc)
    SessionId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: The unique session ID (for example, sess_kl8abc)
    ApiKeyId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: The unique API key ID (for example, key_xk901abc)
    MemberId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: The unique member ID (for example, mem_001)
    LimitParam:
      name: limit
      in: query
      schema:
        type: integer
        default: 20
        maximum: 100
      description: Max number of results per page
    CursorParam:
      name: cursor
      in: query
      schema:
        type: string
      description: Pagination cursor from the previous response
paths:
  /token:
    post:
      tags:
        - Management Tokens
      summary: Request a Management Token
      description: >-
        Authenticate with your Protekt account credentials to obtain a
        Management Token. This token is required for all Account API requests.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - password
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  format: password
      responses:
        '200':
          description: Management Token issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagementToken'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /token/revoke:
    post:
      tags:
        - Management Tokens
      summary: Revoke the current Management Token
      description: >-
        Immediately invalidate the current Management Token. Use this when
        rotating credentials or ending an admin session.
      security:
        - ManagementToken: []
      responses:
        '200':
          description: Token revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  revoked:
                    type: boolean
                    example: true
        '401':
          $ref: '#/components/responses/Unauthorized'
  /projects:
    get:
      tags:
        - Projects
      summary: List all projects
      description: >-
        Returns a paginated list of all projects belonging to the authenticated
        organization.
      security:
        - ManagementToken: []
      parameters:
        - $ref: '#/components/parameters/LimitParam'
        - $ref: '#/components/parameters/CursorParam'
      responses:
        '200':
          description: Paginated list of projects
          content:
            application/json:
              schema:
                allOf:
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Project'
                  - $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - Projects
      summary: Create a project
      description: >-
        Create a new Protekt project. A unique login_id is automatically
        generated and returned.
      security:
        - ManagementToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectCreate'
      responses:
        '201':
          description: Project created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /projects/{id}:
    get:
      tags:
        - Projects
      summary: Get a project
      description: >-
        Retrieve a single project by its ID, including current configuration and
        generated login_id.
      security:
        - ManagementToken: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Project details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Projects
      summary: Update a project
      description: >-
        Update one or more configuration fields on an existing project. Only the
        fields you provide will be changed.
      security:
        - ManagementToken: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectUpdate'
      responses:
        '200':
          description: Updated project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Projects
      summary: Delete a project
      description: >
        Permanently deletes a project and all associated configuration, users,
        and sessions.

        **This action is irreversible.** All active JWTs issued by this project
        will be immediately invalidated.
      security:
        - ManagementToken: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Project deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                    example: true
                  id:
                    type: string
                    example: proj_01jk8abc
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /api-keys:
    get:
      tags:
        - API Keys
      summary: List API keys
      description: >-
        List all API keys for the organization. Key values are never returned —
        only metadata.
      security:
        - ManagementToken: []
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
          description: Filter keys by a specific project
        - $ref: '#/components/parameters/LimitParam'
      responses:
        '200':
          description: List of API key metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApiKeyMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - API Keys
      summary: Create an API key
      description: >
        Generate a new API key scoped to a specific project. 

        **The raw key value is only returned once** at creation time — store it
        securely.
      security:
        - ManagementToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiKeyCreate'
      responses:
        '201':
          description: API key created (raw key included)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKey'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api-keys/{id}:
    delete:
      tags:
        - API Keys
      summary: Revoke an API key
      description: >-
        Permanently revoke an API key. Any service using this key will
        immediately lose access to the Authentication API.
      security:
        - ManagementToken: []
      parameters:
        - $ref: '#/components/parameters/ApiKeyId'
      responses:
        '200':
          description: API key revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  revoked:
                    type: boolean
                    example: true
                  id:
                    type: string
                    example: key_xk901abc
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /members:
    get:
      tags:
        - Members
      summary: List organization members
      description: List all members of the organization with their roles and access levels.
      security:
        - ManagementToken: []
      responses:
        '200':
          description: List of members
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Member'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /members/invite:
    post:
      tags:
        - Members
      summary: Invite a member
      description: Send an invitation email to add a new member to the organization.
      security:
        - ManagementToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - role
              properties:
                email:
                  type: string
                  format: email
                role:
                  type: string
                  enum:
                    - admin
                    - developer
      responses:
        '200':
          description: Invitation sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Invitation sent to dev@acme.com
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Conflict'
  /members/{id}:
    delete:
      tags:
        - Members
      summary: Remove a member
      description: >-
        Remove a member from the organization. They immediately lose all
        dashboard and API access.
      security:
        - ManagementToken: []
      parameters:
        - $ref: '#/components/parameters/MemberId'
      responses:
        '200':
          description: Member removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  removed:
                    type: boolean
                    example: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /usage:
    get:
      tags:
        - Usage
      summary: Get usage statistics
      description: >-
        Retrieve usage stats for the organization or a specific project,
        including Monthly Active Users (MAU) and login event counts.
      security:
        - ManagementToken: []
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
          description: Scope metrics to a specific project
        - name: from
          in: query
          schema:
            type: string
            format: date
          description: ISO 8601 start date for the reporting window
        - name: to
          in: query
          schema:
            type: string
            format: date
          description: ISO 8601 end date. Defaults to today.
      responses:
        '200':
          description: Usage statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsageStats'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/signup:
    post:
      tags:
        - Auth
      summary: Sign up a new user
      description: >-
        Register a new user with email and password. Protekt hashes and stores
        credentials internally. Optionally triggers an email verification flow.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - password
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  format: password
                  minLength: 8
                metadata:
                  type: object
                  additionalProperties: true
                  description: Arbitrary key-value pairs stored alongside the user profile
                send_verification:
                  type: boolean
                  description: >-
                    If true, sends an email verification link. Defaults to
                    project setting.
      responses:
        '201':
          description: User created and session issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthTokenResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '409':
          $ref: '#/components/responses/Conflict'
  /auth/login:
    post:
      tags:
        - Auth
      summary: Log in a user
      description: >
        Authenticate a user with email and password. Returns a signed JWT and
        refresh token on success.

        If MFA is required, returns a `mfa_required` challenge response instead
        of tokens.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - password
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  format: password
      responses:
        '200':
          description: Login successful or MFA challenge issued
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/AuthTokenResponse'
                  - $ref: '#/components/schemas/MfaChallengeResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
  /auth/logout:
    post:
      tags:
        - Auth
      summary: Log out a user
      description: >-
        End a user session by revoking the current access token and its
        associated refresh token.
      security:
        - ApiKey: []
        - UserToken: []
      responses:
        '200':
          description: Logged out successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  logged_out:
                    type: boolean
                    example: true
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/token/refresh:
    post:
      tags:
        - Tokens
      summary: Refresh an access token
      description: >-
        Obtain a new access token using a valid refresh token. The old refresh
        token is rotated and invalidated on each successful call.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - refresh_token
              properties:
                refresh_token:
                  type: string
                  example: rt_xYz...
      responses:
        '200':
          description: New tokens issued
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                  refresh_token:
                    type: string
                  expires_in:
                    type: integer
                    example: 3600
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/token/verify:
    post:
      tags:
        - Tokens
      summary: Verify an access token
      description: >-
        Validate an access token and return its decoded claims. Use this on your
        backend to confirm a user is authenticated before granting access to
        protected resources.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
                  description: The JWT access token to verify
      responses:
        '200':
          description: Token is valid — claims returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenClaims'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/token/revoke:
    post:
      tags:
        - Tokens
      summary: Revoke a token
      description: >-
        Immediately invalidate a specific access or refresh token without ending
        the entire session.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
                  description: The token to revoke
      responses:
        '200':
          description: Token revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  revoked:
                    type: boolean
                    example: true
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/password/reset:
    post:
      tags:
        - Passwords
      summary: Request a password reset
      description: >
        Trigger a password reset email. Protekt sends a one-time reset link to
        the user's registered email, expiring after 15 minutes.

        Always returns `200 OK` regardless of whether the email exists, to
        prevent user enumeration attacks.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
      responses:
        '200':
          description: Reset email dispatched (if email is registered)
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: If that email is registered, a reset link has been sent.
  /auth/password/confirm:
    post:
      tags:
        - Passwords
      summary: Confirm a password reset
      description: >-
        Complete the password reset flow by submitting the reset token (from the
        email link) and the new password.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - reset_token
                - new_password
              properties:
                reset_token:
                  type: string
                  description: One-time reset token from the email link
                new_password:
                  type: string
                  format: password
                  minLength: 8
      responses:
        '200':
          description: Password reset successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Password has been reset successfully.
        '400':
          $ref: '#/components/responses/BadRequest'
  /auth/password/change:
    post:
      tags:
        - Passwords
      summary: Change password (authenticated)
      description: >-
        Allow an authenticated user to change their own password by providing
        their current password and the desired new password.
      security:
        - ApiKey: []
        - UserToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - current_password
                - new_password
              properties:
                current_password:
                  type: string
                  format: password
                new_password:
                  type: string
                  format: password
                  minLength: 8
      responses:
        '200':
          description: Password changed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Password changed successfully.
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/otp/send:
    post:
      tags:
        - OTP
      summary: Send an OTP
      description: >-
        Send a one-time passcode to a user's email or phone for passwordless
        authentication or as a second factor. OTPs expire after 10 minutes.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  description: Required if phone is not provided
                phone:
                  type: string
                  description: E.164 format. Required if email is not provided
                  example: '+2348012345678'
                channel:
                  type: string
                  enum:
                    - email
                    - sms
                  default: email
      responses:
        '200':
          description: OTP sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: OTP sent
                  expires_in:
                    type: integer
                    example: 600
        '429':
          $ref: '#/components/responses/RateLimited'
  /auth/otp/verify:
    post:
      tags:
        - OTP
      summary: Verify an OTP
      description: >-
        Verify an OTP code sent via POST /auth/otp/send. Returns a JWT and
        refresh token on success.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - otp
              properties:
                email:
                  type: string
                  format: email
                  description: Required if OTP was sent to an email
                phone:
                  type: string
                  description: Required if OTP was sent to a phone number
                otp:
                  type: string
                  description: The 6-digit OTP
                  example: '482910'
      responses:
        '200':
          description: OTP verified — session issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthTokenResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
  /auth/magic-link/send:
    post:
      tags:
        - Magic Link
      summary: Send a magic link
      description: >-
        Send a magic link to the user's email. When clicked, the link
        authenticates the user without a password. The link expires after 15
        minutes and is single-use.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                redirect_url:
                  type: string
                  format: uri
                  description: Override the project's default redirect URL for this request
      responses:
        '200':
          description: Magic link sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Magic link sent
                  expires_in:
                    type: integer
                    example: 900
  /auth/magic-link/verify:
    post:
      tags:
        - Magic Link
      summary: Verify a magic link token
      description: >-
        Verify a magic link token extracted from the URL after the user clicks
        the emailed link. Returns a JWT and refresh token on success.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
                  description: The magic link token from the email URL parameter
      responses:
        '200':
          description: Magic link verified — session issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthTokenResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
  /auth/sso/authorize:
    get:
      tags:
        - SSO
      summary: Initiate an SSO login
      description: >-
        Initiate an SSO login flow. Redirects the user to their configured
        identity provider (IdP). Supports OIDC and SAML 2.0 providers configured
        in the Protekt Dashboard.
      security:
        - ApiKey: []
      parameters:
        - name: provider
          in: query
          required: true
          schema:
            type: string
            example: google
          description: The SSO provider slug (for example, google, okta, azure-ad)
        - name: redirect_url
          in: query
          schema:
            type: string
            format: uri
          description: Override the post-login redirect URL for this request
        - name: state
          in: query
          schema:
            type: string
          description: Opaque string passed back in the callback for CSRF protection
      responses:
        '302':
          description: Redirect to identity provider
        '400':
          $ref: '#/components/responses/BadRequest'
  /auth/sso/callback:
    get:
      tags:
        - SSO
      summary: SSO callback
      description: >
        The redirect target for the SSO provider after authentication. Protekt
        exchanges the authorization code for a session and redirects with
        tokens.

        Register `https://auth.protekt.io/v1/auth/sso/callback` as the callback
        URL in your IdP configuration.
      security:
        - ApiKey: []
      parameters:
        - name: code
          in: query
          required: true
          schema:
            type: string
          description: Authorization code from the IdP
        - name: state
          in: query
          schema:
            type: string
          description: The state value originally passed to /auth/sso/authorize
      responses:
        '302':
          description: Redirect to application with tokens
  /auth/mfa/enroll:
    post:
      tags:
        - MFA
      summary: Enroll in MFA
      description: >-
        Begin enrolling a user in MFA. Enrollment is not finalized until the
        user verifies a code via POST /auth/mfa/verify.
      security:
        - ApiKey: []
        - UserToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
              properties:
                method:
                  type: string
                  enum:
                    - totp
                    - sms
                phone:
                  type: string
                  description: E.164 phone number. Required when method is sms.
      responses:
        '200':
          description: MFA enrollment initiated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TotpEnrollResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/mfa/verify:
    post:
      tags:
        - MFA
      summary: Verify an MFA code
      description: >-
        Verify an MFA code. Used both to finalize MFA enrollment and to complete
        a login challenge. On a successful login challenge, returns a full JWT
        session.
      security:
        - ApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - code
              properties:
                mfa_token:
                  type: string
                  description: >-
                    Temporary MFA challenge token from POST /auth/login.
                    Required during login.
                  example: mfa_tmp_abc...
                code:
                  type: string
                  description: The 6-digit TOTP or SMS code
                  example: '482910'
      responses:
        '200':
          description: MFA verified — session issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthTokenResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
  /auth/mfa/unenroll:
    delete:
      tags:
        - MFA
      summary: Remove an MFA method
      description: >
        Remove an MFA method from a user's account. Requires a valid user access
        token.

        Returns 403 if the project enforces MFA (`mfa_required: true`).
      security:
        - ApiKey: []
        - UserToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
              properties:
                method:
                  type: string
                  enum:
                    - totp
                    - sms
      responses:
        '200':
          description: MFA method removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  unenrolled:
                    type: boolean
                    example: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: MFA is enforced by the project and cannot be removed
  /auth/users:
    get:
      tags:
        - Users
      summary: List users
      description: >-
        List all users registered within a project. Intended for admin/backend
        use. Requires an API key with users:read scope.
      security:
        - ApiKey: []
      parameters:
        - $ref: '#/components/parameters/LimitParam'
        - $ref: '#/components/parameters/CursorParam'
        - name: search
          in: query
          schema:
            type: string
          description: Filter by email (partial match)
      responses:
        '200':
          description: Paginated list of users
          content:
            application/json:
              schema:
                allOf:
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/User'
                  - $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/users/{id}:
    get:
      tags:
        - Users
      summary: Get a user
      description: >-
        Retrieve a user's full profile by ID, including metadata, MFA status,
        and account state.
      security:
        - ApiKey: []
      parameters:
        - $ref: '#/components/parameters/UserId'
      responses:
        '200':
          description: User profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Users
      summary: Update a user
      description: >-
        Update a user's profile or metadata. Admins can update any user; users
        can update their own profile via their access token.
      security:
        - ApiKey: []
        - UserToken: []
      parameters:
        - $ref: '#/components/parameters/UserId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  description: New email address. Triggers a re-verification email.
                metadata:
                  type: object
                  additionalProperties: true
                  description: Merged (not replaced) into existing metadata
                disabled:
                  type: boolean
                  description: Set to true to lock the account. Admin only.
      responses:
        '200':
          description: Updated user profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Users
      summary: Delete a user
      description: >-
        Permanently delete a user and all their sessions, tokens, and stored
        data from the project. This is irreversible.
      security:
        - ApiKey: []
      parameters:
        - $ref: '#/components/parameters/UserId'
      responses:
        '200':
          description: User deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                    example: true
                  id:
                    type: string
                    example: usr_9klabc
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /auth/sessions:
    get:
      tags:
        - Sessions
      summary: List active sessions
      description: >-
        List all active sessions for the currently authenticated user, including
        device, IP address, and last active time.
      security:
        - ApiKey: []
        - UserToken: []
      responses:
        '200':
          description: List of active sessions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Session'
        '401':
          $ref: '#/components/responses/Unauthorized'
    delete:
      tags:
        - Sessions
      summary: Revoke all sessions
      description: >-
        Revoke all active sessions for the current user — "sign out everywhere".
        The current session is also terminated unless keep_current is true.
      security:
        - ApiKey: []
        - UserToken: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                keep_current:
                  type: boolean
                  default: false
                  description: If true, the current session is preserved
      responses:
        '200':
          description: All sessions revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  revoked_count:
                    type: integer
                    example: 4
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/sessions/{id}:
    delete:
      tags:
        - Sessions
      summary: Revoke a session
      description: >-
        Terminate a specific session by ID. Useful for "sign out this device"
        functionality.
      security:
        - ApiKey: []
        - UserToken: []
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  revoked:
                    type: boolean
                    example: true
                  session_id:
                    type: string
                    example: sess_kl8abc
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
