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

# Disable a single MFA channel

> Turns off ONE channel — `app`, `email`, or a legacy `sms` enrollment — and leaves the others in place.

The body carries a 6-digit `code` that must prove THE CHANNEL BEING REMOVED: for `app`, the current passcode from the member's authenticator (there is no challenge step); for `email`, the code mailed by `POST /v1/users/{id}/mfa/disable/challenge`. Neither channel accepts the other's code.

`sms` is accepted for CLEARING ONLY. The channel was retired: it cannot be enrolled, cannot be chosen as the preferred method, and is never offered at sign-in. An enrollment recorded before the retirement can still be removed here, which is also what unblocks `DELETE /v1/users/{id}/phone`. It is the ONE channel that needs no code and no body: SMS is refused at sign-in, and there is no SMS proof to demand. A member whose ONLY factor is a legacy `sms` enrollment cannot sign in to call this route themselves — an administrator clears it for them with `DELETE /v1/users/{id}/mfa/admin/sms`.

Removing the last enabled channel leaves the account with no second factor, the same end state as `DELETE /v1/users/{id}/mfa`, so check `GET /v1/users/{id}/mfa` first if that is not the intention.

On success the response has no body (204).

Failures:
- `400` — the channel name is not one this deployment serves.
- `400 IDE-0007` / `IDE-0001` / `IDE-0002` — `app` or `email` with the body missing or malformed.
- `400 IDE-0042` — the code is wrong (or, for `app`, already used).
- `400 IDE-0060` — `email` with no live mailed code: none was requested, it expired, or it was spent.
- `400 IDE-0061` — the mailed code was answered wrongly too many times; request a new one.
- `400 IDE-0063` — the authorization server locked authenticator verification; wait.
- `400 IDE-0045` — the MFA operation could not be completed.
- `404` — no such user.
- `503 IDE-0066` — the identity provider is unavailable (unreachable, too slow to answer, or failing). Nothing about the request was wrong; retry it later.



## OpenAPI

````yaml /en/openapi/v3-current/AM-identity.yaml delete /v1/users/{id}/mfa/{mfaType}
openapi: 3.1.0
info:
  contact:
    name: Lerian Studio
    url: https://lerian.studio
  description: OpenAPI 3.1 surface for the Access Manager identity component.
  license:
    name: Lerian Studio General License
  title: Plugin Access Manager — Identity API
  version: v1
servers:
  - url: https://identity.sandbox.lerian.net
security: []
tags:
  - description: >-
      M2M-only: each plugin declares its own permissions, roles and M2M
      contract, and the server reconciles them.
    name: Declarations
  - description: >-
      A tenant administrator's customers. Each partner holds what its
      credentials may DO (permissions, per product) and WHERE they may do it
      (scope, per product and dimension); an M2M application attached to one is
      confined to that intersection. Administrator-only: authorized on the
      "partners" resource, and every route resolves the owning organization from
      the caller's token.
    name: Partners
paths:
  /v1/users/{id}/mfa/{mfaType}:
    delete:
      tags:
        - MFA
      summary: Disable a single MFA channel
      description: >-
        Turns off ONE channel — `app`, `email`, or a legacy `sms` enrollment —
        and leaves the others in place.


        The body carries a 6-digit `code` that must prove THE CHANNEL BEING
        REMOVED: for `app`, the current passcode from the member's authenticator
        (there is no challenge step); for `email`, the code mailed by `POST
        /v1/users/{id}/mfa/disable/challenge`. Neither channel accepts the
        other's code.


        `sms` is accepted for CLEARING ONLY. The channel was retired: it cannot
        be enrolled, cannot be chosen as the preferred method, and is never
        offered at sign-in. An enrollment recorded before the retirement can
        still be removed here, which is also what unblocks `DELETE
        /v1/users/{id}/phone`. It is the ONE channel that needs no code and no
        body: SMS is refused at sign-in, and there is no SMS proof to demand. A
        member whose ONLY factor is a legacy `sms` enrollment cannot sign in to
        call this route themselves — an administrator clears it for them with
        `DELETE /v1/users/{id}/mfa/admin/sms`.


        Removing the last enabled channel leaves the account with no second
        factor, the same end state as `DELETE /v1/users/{id}/mfa`, so check `GET
        /v1/users/{id}/mfa` first if that is not the intention.


        On success the response has no body (204).


        Failures:

        - `400` — the channel name is not one this deployment serves.

        - `400 IDE-0007` / `IDE-0001` / `IDE-0002` — `app` or `email` with the
        body missing or malformed.

        - `400 IDE-0042` — the code is wrong (or, for `app`, already used).

        - `400 IDE-0060` — `email` with no live mailed code: none was requested,
        it expired, or it was spent.

        - `400 IDE-0061` — the mailed code was answered wrongly too many times;
        request a new one.

        - `400 IDE-0063` — the authorization server locked authenticator
        verification; wait.

        - `400 IDE-0045` — the MFA operation could not be completed.

        - `404` — no such user.

        - `503 IDE-0066` — the identity provider is unavailable (unreachable,
        too slow to answer, or failing). Nothing about the request was wrong;
        retry it later.
      operationId: disableMFAChannel
      parameters:
        - description: User ID.
          in: path
          name: id
          required: true
          schema:
            description: User ID.
            examples:
              - 00000000-0000-0000-0000-000000000000
            type: string
        - description: >-
            Which channel to disable: app, email, or sms (a legacy enrollment
            that can be cleared but never re-enrolled).
          in: path
          name: mfaType
          required: true
          schema:
            description: >-
              Which channel to disable: app, email, or sms (a legacy enrollment
              that can be cleared but never re-enrolled).
            examples:
              - app
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MFADisableInput'
              description: >-
                Confirmation code proving the channel being removed. Required
                for app and email; omitted for sms.
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPError'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPError'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPError'
          description: Not Found
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Unprocessable Entity
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPError'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPError'
          description: Service Unavailable
      security:
        - BearerAuth: []
components:
  schemas:
    MFADisableInput:
      additionalProperties: false
      properties:
        code:
          examples:
            - '123456'
          type: string
      required:
        - code
      type: object
    HTTPError:
      additionalProperties: false
      properties:
        code:
          type: string
        entityType:
          type: string
        err: {}
        message:
          type: string
        title:
          type: string
      type: object
    Detail:
      additionalProperties: true
      properties:
        code:
          description: >-
            Stable, machine-readable domain error code scoped to the emitting
            service (format: <SERVICE>-NNNN).
          type: string
        detail:
          description: >-
            A human-readable explanation specific to this occurrence of the
            problem.
          examples:
            - Property foo is required but is missing.
          type: string
        errors:
          description: Optional list of individual error details
          items:
            $ref: '#/components/schemas/ErrorDetail'
          type:
            - array
            - 'null'
        instance:
          description: >-
            A URI reference that identifies the specific occurrence of the
            problem.
          examples:
            - https://example.com/error-log/abc123
          format: uri
          type: string
        status:
          description: HTTP status code
          examples:
            - 400
          format: int64
          type: integer
        title:
          description: >-
            A short, human-readable summary of the problem type. This value
            should not change between occurrences of the error.
          examples:
            - Bad Request
          type: string
        type:
          default: about:blank
          description: A URI reference to human-readable documentation for the error.
          examples:
            - https://example.com/errors/example
          format: uri
          type: string
        upstream:
          $ref: '#/components/schemas/Upstream'
          description: >-
            RFC 9457 extension member: the error a proxied third-party provider
            reported. Absent unless the emitting service explicitly surfaced
            one.
      type: object
    ErrorDetail:
      additionalProperties: false
      properties:
        location:
          description: >-
            Where the error occurred, e.g. 'body.items[3].tags' or
            'path.thing-id'
          type: string
        message:
          description: Error message text
          type: string
        value:
          description: The value at the given location
      type: object
    Upstream:
      additionalProperties: false
      properties:
        code:
          description: The upstream provider's own error code, verbatim.
          examples:
            - E4001
          type: string
        message:
          description: >-
            The upstream provider's own error message, verbatim (bounded, never
            its raw response body).
          examples:
            - account not found at provider
          type: string
      type: object
  securitySchemes:
    BearerAuth:
      bearerFormat: JWT
      description: JWT bearer token issued by the identity provider.
      scheme: bearer
      type: http

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.