# !!!
# WARNING: This file is autogenerated
# Only modify code within MANUAL() sections
# or your changes may be overwritten later!
# !!!

from __future__ import annotations

from typing import Any, Dict, List, Optional, Union

from stytch.b2b.api.organizations_members_connected_apps import ConnectedApps
from stytch.b2b.api.organizations_members_oauth_providers import OAuthProviders
from stytch.b2b.models.organizations import SearchQuery
from stytch.b2b.models.organizations_members import (
    CreateRequestOptions,
    CreateResponse,
    DeleteMFAPhoneNumberRequestOptions,
    DeleteMFAPhoneNumberResponse,
    DeletePasswordRequestOptions,
    DeletePasswordResponse,
    DeleteRequestOptions,
    DeleteResponse,
    DeleteTOTPRequestOptions,
    DeleteTOTPResponse,
    GetConnectedAppsRequestOptions,
    GetConnectedAppsResponse,
    GetResponse,
    OIDCProvidersResponse,
    ReactivateRequestOptions,
    ReactivateResponse,
    SearchRequestOptions,
    SearchResponse,
    StartEmailUpdateRequestLocale,
    StartEmailUpdateRequestOptions,
    StartEmailUpdateResponse,
    UnlinkRetiredEmailRequestOptions,
    UnlinkRetiredEmailResponse,
    UpdateRequestOptions,
    UpdateResponse,
)
from stytch.core.api_base import ApiBase
from stytch.core.http.client import AsyncClient, SyncClient


class Members:
    def __init__(
        self, api_base: ApiBase, sync_client: SyncClient, async_client: AsyncClient
    ) -> None:
        self.api_base = api_base
        self.sync_client = sync_client
        self.async_client = async_client
        self.oauth_providers = OAuthProviders(
            api_base=self.api_base,
            sync_client=self.sync_client,
            async_client=self.async_client,
        )
        self.connected_apps = ConnectedApps(
            api_base=self.api_base,
            sync_client=self.sync_client,
            async_client=self.async_client,
        )

    def update(
        self,
        organization_id: str,
        member_id: str,
        name: Optional[str] = None,
        trusted_metadata: Optional[Dict[str, Any]] = None,
        untrusted_metadata: Optional[Dict[str, Any]] = None,
        is_breakglass: Optional[bool] = None,
        mfa_phone_number: Optional[str] = None,
        mfa_enrolled: Optional[bool] = None,
        roles: Optional[List[str]] = None,
        preserve_existing_sessions: Optional[bool] = None,
        default_mfa_method: Optional[str] = None,
        email_address: Optional[str] = None,
        external_id: Optional[str] = None,
        unlink_email: Optional[bool] = None,
        method_options: Optional[UpdateRequestOptions] = None,
    ) -> UpdateResponse:
        """Updates a Member specified by `organization_id` and `member_id`.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - name: The name of the Member.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.info.name` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.info.name` action on the `stytch.self` Resource.
          - trusted_metadata: An arbitrary JSON object for storing application-specific data or identity-provider-specific data.
                  If a session header is passed into the request, this field may **not** be passed into the request. You cannot
                  update trusted metadata when acting as a Member.
          - untrusted_metadata: An arbitrary JSON object of application-specific data. These fields can be edited directly by the
          frontend SDK, and should not be used to store critical information. See the [Metadata resource](https://stytch.com/docs/b2b/api/metadata)
          for complete field behavior details.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.info.untrusted-metadata` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.info.untrusted-metadata` action on the `stytch.self` Resource.
          - is_breakglass: Identifies the Member as a break glass user - someone who has permissions to authenticate into an Organization by bypassing the Organization's settings. A break glass account is typically used for emergency purposes to gain access outside of normal authentication procedures. Refer to the [Organization object](organization-object) and its `auth_methods` and `allowed_auth_methods` fields for more details.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.is-breakglass` action on the `stytch.member` Resource.
          - mfa_phone_number: Sets the Member's phone number. Throws an error if the Member already has a phone number. To change the Member's phone number, use the [Delete member phone number endpoint](https://stytch.com/docs/b2b/api/delete-member-mfa-phone-number) to delete the Member's existing phone number first.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.info.mfa-phone` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.info.mfa-phone` action on the `stytch.self` Resource.
          - mfa_enrolled: Sets whether the Member is enrolled in MFA. If true, the Member must complete an MFA step whenever they wish to log in to their Organization. If false, the Member only needs to complete an MFA step if the Organization's MFA policy is set to `REQUIRED_FOR_ALL`.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.mfa-enrolled` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.settings.mfa-enrolled` action on the `stytch.self` Resource.
          - roles: Roles to explicitly assign to this Member.
         Will completely replace any existing explicitly assigned roles. See the
         [RBAC guide](https://stytch.com/docs/b2b/guides/rbac/role-assignment) for more information about role assignment.

           If a Role is removed from a Member, and the Member is also implicitly assigned this Role from an SSO connection
           or an SSO group, we will by default revoke any existing sessions for the Member that contain any SSO
           authentication factors with the affected connection ID. You can preserve these sessions by passing in the
           `preserve_existing_sessions` parameter with a value of `true`.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.roles` action on the `stytch.member` Resource.
          - preserve_existing_sessions: Whether to preserve existing sessions when explicit Roles that are revoked are also implicitly assigned
          by SSO connection or SSO group. Defaults to `false` - that is, existing Member Sessions that contain SSO
          authentication factors with the affected SSO connection IDs will be revoked.
          - default_mfa_method: The Member's default MFA method. This value is used to determine which secondary MFA method to use in the case of multiple methods registered for a Member. The current possible values are `sms_otp` and `totp`.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.default-mfa-method` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.settings.default-mfa-method` action on the `stytch.self` Resource.
          - email_address: Updates the Member's `email_address`, if provided.
                If a Member's email address is changed, other Members in the same Organization cannot use the old email address, although the Member may update back to their old email address.
                A Member's email address can only be useable again by other Members if the Member is deleted.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.info.email` action on the `stytch.member` Resource. Members cannot update their own email address.
          - external_id: An identifier that can be used in API calls wherever a member_id is expected. This is a string consisting of alphanumeric, `.`, `_`, `-`, or `|` characters with a maximum length of 128 characters. External IDs must be unique within an organization, but may be reused across different organizations in the same project.
          - unlink_email: If `unlink_email` is `true` and an `email_address` is provided, the Member's previous email will be deleted instead of retired. Defaults to `false`.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }
        if name is not None:
            data["name"] = name
        if trusted_metadata is not None:
            data["trusted_metadata"] = trusted_metadata
        if untrusted_metadata is not None:
            data["untrusted_metadata"] = untrusted_metadata
        if is_breakglass is not None:
            data["is_breakglass"] = is_breakglass
        if mfa_phone_number is not None:
            data["mfa_phone_number"] = mfa_phone_number
        if mfa_enrolled is not None:
            data["mfa_enrolled"] = mfa_enrolled
        if roles is not None:
            data["roles"] = roles
        if preserve_existing_sessions is not None:
            data["preserve_existing_sessions"] = preserve_existing_sessions
        if default_mfa_method is not None:
            data["default_mfa_method"] = default_mfa_method
        if email_address is not None:
            data["email_address"] = email_address
        if external_id is not None:
            data["external_id"] = external_id
        if unlink_email is not None:
            data["unlink_email"] = unlink_email

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}", data
        )
        res = self.sync_client.put(url, data, headers)
        return UpdateResponse.from_json(res.response.status_code, res.json)

    async def update_async(
        self,
        organization_id: str,
        member_id: str,
        name: Optional[str] = None,
        trusted_metadata: Optional[Dict[str, Any]] = None,
        untrusted_metadata: Optional[Dict[str, Any]] = None,
        is_breakglass: Optional[bool] = None,
        mfa_phone_number: Optional[str] = None,
        mfa_enrolled: Optional[bool] = None,
        roles: Optional[List[str]] = None,
        preserve_existing_sessions: Optional[bool] = None,
        default_mfa_method: Optional[str] = None,
        email_address: Optional[str] = None,
        external_id: Optional[str] = None,
        unlink_email: Optional[bool] = None,
        method_options: Optional[UpdateRequestOptions] = None,
    ) -> UpdateResponse:
        """Updates a Member specified by `organization_id` and `member_id`.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - name: The name of the Member.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.info.name` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.info.name` action on the `stytch.self` Resource.
          - trusted_metadata: An arbitrary JSON object for storing application-specific data or identity-provider-specific data.
                  If a session header is passed into the request, this field may **not** be passed into the request. You cannot
                  update trusted metadata when acting as a Member.
          - untrusted_metadata: An arbitrary JSON object of application-specific data. These fields can be edited directly by the
          frontend SDK, and should not be used to store critical information. See the [Metadata resource](https://stytch.com/docs/b2b/api/metadata)
          for complete field behavior details.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.info.untrusted-metadata` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.info.untrusted-metadata` action on the `stytch.self` Resource.
          - is_breakglass: Identifies the Member as a break glass user - someone who has permissions to authenticate into an Organization by bypassing the Organization's settings. A break glass account is typically used for emergency purposes to gain access outside of normal authentication procedures. Refer to the [Organization object](organization-object) and its `auth_methods` and `allowed_auth_methods` fields for more details.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.is-breakglass` action on the `stytch.member` Resource.
          - mfa_phone_number: Sets the Member's phone number. Throws an error if the Member already has a phone number. To change the Member's phone number, use the [Delete member phone number endpoint](https://stytch.com/docs/b2b/api/delete-member-mfa-phone-number) to delete the Member's existing phone number first.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.info.mfa-phone` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.info.mfa-phone` action on the `stytch.self` Resource.
          - mfa_enrolled: Sets whether the Member is enrolled in MFA. If true, the Member must complete an MFA step whenever they wish to log in to their Organization. If false, the Member only needs to complete an MFA step if the Organization's MFA policy is set to `REQUIRED_FOR_ALL`.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.mfa-enrolled` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.settings.mfa-enrolled` action on the `stytch.self` Resource.
          - roles: Roles to explicitly assign to this Member.
         Will completely replace any existing explicitly assigned roles. See the
         [RBAC guide](https://stytch.com/docs/b2b/guides/rbac/role-assignment) for more information about role assignment.

           If a Role is removed from a Member, and the Member is also implicitly assigned this Role from an SSO connection
           or an SSO group, we will by default revoke any existing sessions for the Member that contain any SSO
           authentication factors with the affected connection ID. You can preserve these sessions by passing in the
           `preserve_existing_sessions` parameter with a value of `true`.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.roles` action on the `stytch.member` Resource.
          - preserve_existing_sessions: Whether to preserve existing sessions when explicit Roles that are revoked are also implicitly assigned
          by SSO connection or SSO group. Defaults to `false` - that is, existing Member Sessions that contain SSO
          authentication factors with the affected SSO connection IDs will be revoked.
          - default_mfa_method: The Member's default MFA method. This value is used to determine which secondary MFA method to use in the case of multiple methods registered for a Member. The current possible values are `sms_otp` and `totp`.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.default-mfa-method` action on the `stytch.member` Resource. Alternatively, if the Member Session matches the Member associated with the `member_id` passed in the request, the authorization check will also allow a Member Session that has permission to perform the `update.settings.default-mfa-method` action on the `stytch.self` Resource.
          - email_address: Updates the Member's `email_address`, if provided.
                If a Member's email address is changed, other Members in the same Organization cannot use the old email address, although the Member may update back to their old email address.
                A Member's email address can only be useable again by other Members if the Member is deleted.

        If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.info.email` action on the `stytch.member` Resource. Members cannot update their own email address.
          - external_id: An identifier that can be used in API calls wherever a member_id is expected. This is a string consisting of alphanumeric, `.`, `_`, `-`, or `|` characters with a maximum length of 128 characters. External IDs must be unique within an organization, but may be reused across different organizations in the same project.
          - unlink_email: If `unlink_email` is `true` and an `email_address` is provided, the Member's previous email will be deleted instead of retired. Defaults to `false`.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }
        if name is not None:
            data["name"] = name
        if trusted_metadata is not None:
            data["trusted_metadata"] = trusted_metadata
        if untrusted_metadata is not None:
            data["untrusted_metadata"] = untrusted_metadata
        if is_breakglass is not None:
            data["is_breakglass"] = is_breakglass
        if mfa_phone_number is not None:
            data["mfa_phone_number"] = mfa_phone_number
        if mfa_enrolled is not None:
            data["mfa_enrolled"] = mfa_enrolled
        if roles is not None:
            data["roles"] = roles
        if preserve_existing_sessions is not None:
            data["preserve_existing_sessions"] = preserve_existing_sessions
        if default_mfa_method is not None:
            data["default_mfa_method"] = default_mfa_method
        if email_address is not None:
            data["email_address"] = email_address
        if external_id is not None:
            data["external_id"] = external_id
        if unlink_email is not None:
            data["unlink_email"] = unlink_email

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}", data
        )
        res = await self.async_client.put(url, data, headers)
        return UpdateResponse.from_json(res.response.status, res.json)

    def delete(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[DeleteRequestOptions] = None,
    ) -> DeleteResponse:
        """Deletes a Member specified by `organization_id` and `member_id`.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}", data
        )
        res = self.sync_client.delete(url, headers)
        return DeleteResponse.from_json(res.response.status_code, res.json)

    async def delete_async(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[DeleteRequestOptions] = None,
    ) -> DeleteResponse:
        """Deletes a Member specified by `organization_id` and `member_id`.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}", data
        )
        res = await self.async_client.delete(url, headers)
        return DeleteResponse.from_json(res.response.status, res.json)

    def reactivate(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[ReactivateRequestOptions] = None,
    ) -> ReactivateResponse:
        """Reactivates a deleted Member's status and its associated email status (if applicable) to active, specified by `organization_id` and `member_id`. This endpoint will only work for Members with at least one verified email where their `email_address_verified` is `true`.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/reactivate",
            data,
        )
        res = self.sync_client.put(url, data, headers)
        return ReactivateResponse.from_json(res.response.status_code, res.json)

    async def reactivate_async(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[ReactivateRequestOptions] = None,
    ) -> ReactivateResponse:
        """Reactivates a deleted Member's status and its associated email status (if applicable) to active, specified by `organization_id` and `member_id`. This endpoint will only work for Members with at least one verified email where their `email_address_verified` is `true`.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/reactivate",
            data,
        )
        res = await self.async_client.put(url, data, headers)
        return ReactivateResponse.from_json(res.response.status, res.json)

    def delete_mfa_phone_number(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[DeleteMFAPhoneNumberRequestOptions] = None,
    ) -> DeleteMFAPhoneNumberResponse:
        """Delete a Member's MFA phone number.

        To change a Member's phone number, you must first call this endpoint to delete the existing phone number.

        Existing Member Sessions that include a phone number authentication factor will not be revoked if the phone number is deleted, and MFA will not be enforced until the Member logs in again.
        If you wish to enforce MFA immediately after a phone number is deleted, you can do so by prompting the Member to enter a new phone number
        and calling the [OTP SMS send](https://stytch.com/docs/b2b/api/otp-sms-send) endpoint, then calling the [OTP SMS Authenticate](https://stytch.com/docs/b2b/api/authenticate-otp-sms) endpoint.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/mfa_phone_numbers/{member_id}",
            data,
        )
        res = self.sync_client.delete(url, headers)
        return DeleteMFAPhoneNumberResponse.from_json(
            res.response.status_code, res.json
        )

    async def delete_mfa_phone_number_async(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[DeleteMFAPhoneNumberRequestOptions] = None,
    ) -> DeleteMFAPhoneNumberResponse:
        """Delete a Member's MFA phone number.

        To change a Member's phone number, you must first call this endpoint to delete the existing phone number.

        Existing Member Sessions that include a phone number authentication factor will not be revoked if the phone number is deleted, and MFA will not be enforced until the Member logs in again.
        If you wish to enforce MFA immediately after a phone number is deleted, you can do so by prompting the Member to enter a new phone number
        and calling the [OTP SMS send](https://stytch.com/docs/b2b/api/otp-sms-send) endpoint, then calling the [OTP SMS Authenticate](https://stytch.com/docs/b2b/api/authenticate-otp-sms) endpoint.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/mfa_phone_numbers/{member_id}",
            data,
        )
        res = await self.async_client.delete(url, headers)
        return DeleteMFAPhoneNumberResponse.from_json(res.response.status, res.json)

    def delete_totp(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[DeleteTOTPRequestOptions] = None,
    ) -> DeleteTOTPResponse:
        """Delete a Member's MFA TOTP registration.

        To mint a new registration for a Member, you must first call this endpoint to delete the existing registration.

        Existing Member Sessions that include the TOTP authentication factor will not be revoked if the registration is deleted, and MFA will not be enforced until the Member logs in again.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/totp", data
        )
        res = self.sync_client.delete(url, headers)
        return DeleteTOTPResponse.from_json(res.response.status_code, res.json)

    async def delete_totp_async(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[DeleteTOTPRequestOptions] = None,
    ) -> DeleteTOTPResponse:
        """Delete a Member's MFA TOTP registration.

        To mint a new registration for a Member, you must first call this endpoint to delete the existing registration.

        Existing Member Sessions that include the TOTP authentication factor will not be revoked if the registration is deleted, and MFA will not be enforced until the Member logs in again.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/totp", data
        )
        res = await self.async_client.delete(url, headers)
        return DeleteTOTPResponse.from_json(res.response.status, res.json)

    def search(
        self,
        organization_ids: List[str],
        cursor: Optional[str] = None,
        limit: Optional[int] = None,
        query: Optional[Union[SearchQuery, Dict[str, Any]]] = None,
        method_options: Optional[SearchRequestOptions] = None,
    ) -> SearchResponse:
        """
        **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/second.

        Search for Members within specified Organizations. An array with at least one `organization_id` is required. Submitting an empty `query` returns all non-deleted Members within the specified Organizations.

        All fuzzy search filters require a minimum of three characters.

        Fields:
          - organization_ids: An array of organization_ids. At least one value is required.
          - cursor: The `cursor` field allows you to paginate through your results. Each result array is limited to 1000 results. If your query returns more than 1000 results, you will need to paginate the responses using the `cursor`. If you receive a response that includes a non-null `next_cursor` in the `results_metadata` object, repeat the search call with the `next_cursor` value set to the `cursor` field to retrieve the next page of results. Continue to make search calls until the `next_cursor` in the response is null.
          - limit: The number of search results to return per page. The default limit is 100. A maximum of 1000 results can be returned by a single search request. If the total size of your result set is greater than one page size, you must paginate the response. See the `cursor` field.
          - query: The optional query object contains the operator, i.e. `AND` or `OR`, and the operands that will filter your results. Only an operator is required. If you include no operands, no filtering will be applied. If you include no query object, it will return all Members with no filtering applied.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_ids": organization_ids,
        }
        if cursor is not None:
            data["cursor"] = cursor
        if limit is not None:
            data["limit"] = limit
        if query is not None:
            data["query"] = query if isinstance(query, dict) else query.dict()

        url = self.api_base.url_for("/v1/b2b/organizations/members/search", data)
        res = self.sync_client.post(url, data, headers)
        return SearchResponse.from_json(res.response.status_code, res.json)

    async def search_async(
        self,
        organization_ids: List[str],
        cursor: Optional[str] = None,
        limit: Optional[int] = None,
        query: Optional[SearchQuery] = None,
        method_options: Optional[SearchRequestOptions] = None,
    ) -> SearchResponse:
        """
        **Warning**: This endpoint is not recommended for use in login flows. Scaling issues may occur, as search performance may vary from ~150 milliseconds to 9 seconds depending on query complexity and rate limits are set to 100 requests/second.

        Search for Members within specified Organizations. An array with at least one `organization_id` is required. Submitting an empty `query` returns all non-deleted Members within the specified Organizations.

        All fuzzy search filters require a minimum of three characters.

        Fields:
          - organization_ids: An array of organization_ids. At least one value is required.
          - cursor: The `cursor` field allows you to paginate through your results. Each result array is limited to 1000 results. If your query returns more than 1000 results, you will need to paginate the responses using the `cursor`. If you receive a response that includes a non-null `next_cursor` in the `results_metadata` object, repeat the search call with the `next_cursor` value set to the `cursor` field to retrieve the next page of results. Continue to make search calls until the `next_cursor` in the response is null.
          - limit: The number of search results to return per page. The default limit is 100. A maximum of 1000 results can be returned by a single search request. If the total size of your result set is greater than one page size, you must paginate the response. See the `cursor` field.
          - query: The optional query object contains the operator, i.e. `AND` or `OR`, and the operands that will filter your results. Only an operator is required. If you include no operands, no filtering will be applied. If you include no query object, it will return all Members with no filtering applied.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_ids": organization_ids,
        }
        if cursor is not None:
            data["cursor"] = cursor
        if limit is not None:
            data["limit"] = limit
        if query is not None:
            data["query"] = query if isinstance(query, dict) else query.dict()

        url = self.api_base.url_for("/v1/b2b/organizations/members/search", data)
        res = await self.async_client.post(url, data, headers)
        return SearchResponse.from_json(res.response.status, res.json)

    def delete_password(
        self,
        organization_id: str,
        member_password_id: str,
        method_options: Optional[DeletePasswordRequestOptions] = None,
    ) -> DeletePasswordResponse:
        """Delete a Member's password.

        This endpoint only works for Organization-scoped passwords. For cross-org password Projects, use [Require Password Reset By Email](https://stytch.com/docs/b2b/api/passwords-require-reset-by-email) instead.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_password_id: Globally unique UUID that identifies a Member's password.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_password_id": member_password_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/passwords/{member_password_id}",
            data,
        )
        res = self.sync_client.delete(url, headers)
        return DeletePasswordResponse.from_json(res.response.status_code, res.json)

    async def delete_password_async(
        self,
        organization_id: str,
        member_password_id: str,
        method_options: Optional[DeletePasswordRequestOptions] = None,
    ) -> DeletePasswordResponse:
        """Delete a Member's password.

        This endpoint only works for Organization-scoped passwords. For cross-org password Projects, use [Require Password Reset By Email](https://stytch.com/docs/b2b/api/passwords-require-reset-by-email) instead.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_password_id: Globally unique UUID that identifies a Member's password.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_password_id": member_password_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/passwords/{member_password_id}",
            data,
        )
        res = await self.async_client.delete(url, headers)
        return DeletePasswordResponse.from_json(res.response.status, res.json)

    def dangerously_get(
        self,
        member_id: str,
        include_deleted: Optional[bool] = None,
    ) -> GetResponse:
        """Get a Member by `member_id`. This endpoint does not require an `organization_id`, enabling you to get members across organizations. This is a dangerous operation. Incorrect use may open you up to indirect object reference (IDOR) attacks. We recommend using the [Get Member](https://stytch.com/docs/b2b/api/get-member) API instead.

        Fields:
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
          - include_deleted: Whether to include deleted Members in the response. Defaults to false.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "member_id": member_id,
        }
        if include_deleted is not None:
            data["include_deleted"] = include_deleted

        url = self.api_base.url_for(
            "/v1/b2b/organizations/members/dangerously_get/{member_id}", data
        )
        res = self.sync_client.get(url, data, headers)
        return GetResponse.from_json(res.response.status_code, res.json)

    async def dangerously_get_async(
        self,
        member_id: str,
        include_deleted: Optional[bool] = None,
    ) -> GetResponse:
        """Get a Member by `member_id`. This endpoint does not require an `organization_id`, enabling you to get members across organizations. This is a dangerous operation. Incorrect use may open you up to indirect object reference (IDOR) attacks. We recommend using the [Get Member](https://stytch.com/docs/b2b/api/get-member) API instead.

        Fields:
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
          - include_deleted: Whether to include deleted Members in the response. Defaults to false.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "member_id": member_id,
        }
        if include_deleted is not None:
            data["include_deleted"] = include_deleted

        url = self.api_base.url_for(
            "/v1/b2b/organizations/members/dangerously_get/{member_id}", data
        )
        res = await self.async_client.get(url, data, headers)
        return GetResponse.from_json(res.response.status, res.json)

    def oidc_providers(
        self,
        organization_id: str,
        member_id: str,
        include_refresh_token: Optional[bool] = None,
    ) -> OIDCProvidersResponse:
        """Retrieve the saved OIDC access tokens and ID tokens for a member. After a successful OIDC login, Stytch will save the
        issued access token and ID token from the identity provider. If a refresh token has been issued, Stytch will refresh the
        access token automatically.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - include_refresh_token: Whether to return the refresh token Stytch has stored for the OAuth Provider. Defaults to false. **Important:** If your application exchanges the refresh token, Stytch may not be able to automatically refresh access tokens in the future.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }
        if include_refresh_token is not None:
            data["include_refresh_token"] = include_refresh_token

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/oidc_providers",
            data,
        )
        res = self.sync_client.get(url, data, headers)
        return OIDCProvidersResponse.from_json(res.response.status_code, res.json)

    async def oidc_providers_async(
        self,
        organization_id: str,
        member_id: str,
        include_refresh_token: Optional[bool] = None,
    ) -> OIDCProvidersResponse:
        """Retrieve the saved OIDC access tokens and ID tokens for a member. After a successful OIDC login, Stytch will save the
        issued access token and ID token from the identity provider. If a refresh token has been issued, Stytch will refresh the
        access token automatically.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - include_refresh_token: Whether to return the refresh token Stytch has stored for the OAuth Provider. Defaults to false. **Important:** If your application exchanges the refresh token, Stytch may not be able to automatically refresh access tokens in the future.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }
        if include_refresh_token is not None:
            data["include_refresh_token"] = include_refresh_token

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/oidc_providers",
            data,
        )
        res = await self.async_client.get(url, data, headers)
        return OIDCProvidersResponse.from_json(res.response.status, res.json)

    def unlink_retired_email(
        self,
        organization_id: str,
        member_id: str,
        email_id: Optional[str] = None,
        email_address: Optional[str] = None,
        method_options: Optional[UnlinkRetiredEmailRequestOptions] = None,
    ) -> UnlinkRetiredEmailResponse:
        """Unlinks a retired email address from a Member specified by their `organization_id` and `member_id`. The email address
        to be retired can be identified in the request body by either its `email_id`, its `email_address`, or both. If using
        both identifiers they must refer to the same email.

        A previously active email address can be marked as retired in one of two ways:

        - It's replaced with a new primary email address during an explicit Member update.
        - A new email address is surfaced by an OAuth, SAML or OIDC provider. In this case the new email address becomes the
          Member's primary email address and the old primary email address is retired.

        A retired email address cannot be used by other Members in the same Organization. However, unlinking retired email
        addresses allows them to be subsequently re-used by other Organization Members. Retired email addresses can be viewed
        on the [Member object](https://stytch.com/docs/b2b/api/member-object).

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - email_id: The globally unique UUID of a Member's email.
          - email_address: The email address of the Member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }
        if email_id is not None:
            data["email_id"] = email_id
        if email_address is not None:
            data["email_address"] = email_address

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/unlink_retired_email",
            data,
        )
        res = self.sync_client.post(url, data, headers)
        return UnlinkRetiredEmailResponse.from_json(res.response.status_code, res.json)

    async def unlink_retired_email_async(
        self,
        organization_id: str,
        member_id: str,
        email_id: Optional[str] = None,
        email_address: Optional[str] = None,
        method_options: Optional[UnlinkRetiredEmailRequestOptions] = None,
    ) -> UnlinkRetiredEmailResponse:
        """Unlinks a retired email address from a Member specified by their `organization_id` and `member_id`. The email address
        to be retired can be identified in the request body by either its `email_id`, its `email_address`, or both. If using
        both identifiers they must refer to the same email.

        A previously active email address can be marked as retired in one of two ways:

        - It's replaced with a new primary email address during an explicit Member update.
        - A new email address is surfaced by an OAuth, SAML or OIDC provider. In this case the new email address becomes the
          Member's primary email address and the old primary email address is retired.

        A retired email address cannot be used by other Members in the same Organization. However, unlinking retired email
        addresses allows them to be subsequently re-used by other Organization Members. Retired email addresses can be viewed
        on the [Member object](https://stytch.com/docs/b2b/api/member-object).

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - email_id: The globally unique UUID of a Member's email.
          - email_address: The email address of the Member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }
        if email_id is not None:
            data["email_id"] = email_id
        if email_address is not None:
            data["email_address"] = email_address

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/unlink_retired_email",
            data,
        )
        res = await self.async_client.post(url, data, headers)
        return UnlinkRetiredEmailResponse.from_json(res.response.status, res.json)

    def start_email_update(
        self,
        organization_id: str,
        member_id: str,
        email_address: str,
        login_redirect_url: Optional[str] = None,
        locale: Optional[Union[StartEmailUpdateRequestLocale, str]] = None,
        login_template_id: Optional[str] = None,
        method_options: Optional[StartEmailUpdateRequestOptions] = None,
    ) -> StartEmailUpdateResponse:
        """Starts a self-serve email update for a Member specified by their `organization_id` and `member_id`.
        To perform a self-serve update, members must be active and have an active, verified email address.

        The new email address must meet the following requirements:

        - Must not be in use by another member (retired emails count as used until they are [unlinked](https://stytch.com/docs/b2b/api/unlink-retired-member-email))
        - Must not be updating for another member (i.e. two members cannot attempt to update to the same email at once)

        The member will receive an Email Magic Link that expires in 5 minutes. If they do not verify their new email address in that timeframe, the email
        will be freed up for other members to use.

        The Magic Link will redirect to your `login_redirect_url` (or the configured default if one isn't provided), and you should invoke the [Authenticate Magic Link](https://stytch.com/docs/b2b/api/authenticate-magic-link) endpoint as normal to complete the flow.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - email_address: The new email address for the Member.
          - login_redirect_url: The URL that the Member clicks from the login Email Magic Link. This URL should be an endpoint in the backend server that
          verifies the request by querying Stytch's authenticate endpoint and finishes the login. If this value is not passed, the default login
          redirect URL that you set in your Dashboard is used. If you have not set a default login redirect URL, an error is returned.
          - locale: Used to determine which language to use when sending the user this delivery method. Parameter is a [IETF BCP 47 language tag](https://www.w3.org/International/articles/language-tags/), e.g. `"en"`.

        Currently supported languages are English (`"en"`), Spanish (`"es"`), French (`"fr"`) and Brazilian Portuguese (`"pt-br"`); if no value is provided, the copy defaults to English.

        Request support for additional languages [here](https://docs.google.com/forms/d/e/1FAIpQLScZSpAu_m2AmLXRT3F3kap-s_mcV6UTBitYn6CdyWP0-o7YjQ/viewform?usp=sf_link")!

          - login_template_id: Use a custom template for login emails. By default, it will use your default email template. The template must be from Stytch's
        built-in customizations or a custom HTML email for Magic Links - Login.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
            "email_address": email_address,
        }
        if login_redirect_url is not None:
            data["login_redirect_url"] = login_redirect_url
        if locale is not None:
            data["locale"] = locale
        if login_template_id is not None:
            data["login_template_id"] = login_template_id

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/start_email_update",
            data,
        )
        res = self.sync_client.post(url, data, headers)
        return StartEmailUpdateResponse.from_json(res.response.status_code, res.json)

    async def start_email_update_async(
        self,
        organization_id: str,
        member_id: str,
        email_address: str,
        login_redirect_url: Optional[str] = None,
        locale: Optional[StartEmailUpdateRequestLocale] = None,
        login_template_id: Optional[str] = None,
        method_options: Optional[StartEmailUpdateRequestOptions] = None,
    ) -> StartEmailUpdateResponse:
        """Starts a self-serve email update for a Member specified by their `organization_id` and `member_id`.
        To perform a self-serve update, members must be active and have an active, verified email address.

        The new email address must meet the following requirements:

        - Must not be in use by another member (retired emails count as used until they are [unlinked](https://stytch.com/docs/b2b/api/unlink-retired-member-email))
        - Must not be updating for another member (i.e. two members cannot attempt to update to the same email at once)

        The member will receive an Email Magic Link that expires in 5 minutes. If they do not verify their new email address in that timeframe, the email
        will be freed up for other members to use.

        The Magic Link will redirect to your `login_redirect_url` (or the configured default if one isn't provided), and you should invoke the [Authenticate Magic Link](https://stytch.com/docs/b2b/api/authenticate-magic-link) endpoint as normal to complete the flow.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - email_address: The new email address for the Member.
          - login_redirect_url: The URL that the Member clicks from the login Email Magic Link. This URL should be an endpoint in the backend server that
          verifies the request by querying Stytch's authenticate endpoint and finishes the login. If this value is not passed, the default login
          redirect URL that you set in your Dashboard is used. If you have not set a default login redirect URL, an error is returned.
          - locale: Used to determine which language to use when sending the user this delivery method. Parameter is a [IETF BCP 47 language tag](https://www.w3.org/International/articles/language-tags/), e.g. `"en"`.

        Currently supported languages are English (`"en"`), Spanish (`"es"`), French (`"fr"`) and Brazilian Portuguese (`"pt-br"`); if no value is provided, the copy defaults to English.

        Request support for additional languages [here](https://docs.google.com/forms/d/e/1FAIpQLScZSpAu_m2AmLXRT3F3kap-s_mcV6UTBitYn6CdyWP0-o7YjQ/viewform?usp=sf_link")!

          - login_template_id: Use a custom template for login emails. By default, it will use your default email template. The template must be from Stytch's
        built-in customizations or a custom HTML email for Magic Links - Login.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
            "email_address": email_address,
        }
        if login_redirect_url is not None:
            data["login_redirect_url"] = login_redirect_url
        if locale is not None:
            data["locale"] = locale
        if login_template_id is not None:
            data["login_template_id"] = login_template_id

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/start_email_update",
            data,
        )
        res = await self.async_client.post(url, data, headers)
        return StartEmailUpdateResponse.from_json(res.response.status, res.json)

    def get_connected_apps(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[GetConnectedAppsRequestOptions] = None,
    ) -> GetConnectedAppsResponse:
        """Member Get Connected Apps retrieves a list of Connected Apps with which the Member has successfully completed an
        authorization flow.
        If the Member revokes a Connected App's access (e.g. via the Revoke Connected App endpoint) then the Connected App will
        no longer be returned in the response. A Connected App's access may also be revoked if the Organization's allowed Connected
        App policy changes.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/connected_apps",
            data,
        )
        res = self.sync_client.get(url, data, headers)
        return GetConnectedAppsResponse.from_json(res.response.status_code, res.json)

    async def get_connected_apps_async(
        self,
        organization_id: str,
        member_id: str,
        method_options: Optional[GetConnectedAppsRequestOptions] = None,
    ) -> GetConnectedAppsResponse:
        """Member Get Connected Apps retrieves a list of Connected Apps with which the Member has successfully completed an
        authorization flow.
        If the Member revokes a Connected App's access (e.g. via the Revoke Connected App endpoint) then the Connected App will
        no longer be returned in the response. A Connected App's access may also be revoked if the Organization's allowed Connected
        App policy changes.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "member_id": member_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members/{member_id}/connected_apps",
            data,
        )
        res = await self.async_client.get(url, data, headers)
        return GetConnectedAppsResponse.from_json(res.response.status, res.json)

    def create(
        self,
        organization_id: str,
        email_address: str,
        name: Optional[str] = None,
        trusted_metadata: Optional[Dict[str, Any]] = None,
        untrusted_metadata: Optional[Dict[str, Any]] = None,
        create_member_as_pending: Optional[bool] = None,
        is_breakglass: Optional[bool] = None,
        mfa_phone_number: Optional[str] = None,
        mfa_enrolled: Optional[bool] = None,
        roles: Optional[List[str]] = None,
        external_id: Optional[str] = None,
        method_options: Optional[CreateRequestOptions] = None,
    ) -> CreateResponse:
        """Creates a Member. An `organization_id` and `email_address` are required.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - email_address: The email address of the Member.
          - name: The name of the Member.
          - trusted_metadata: An arbitrary JSON object for storing application-specific data or identity-provider-specific data.
          - untrusted_metadata: An arbitrary JSON object of application-specific data. These fields can be edited directly by the
          frontend SDK, and should not be used to store critical information. See the [Metadata resource](https://stytch.com/docs/b2b/api/metadata)
          for complete field behavior details.
          - create_member_as_pending: Flag for whether or not to save a Member as `pending` or `active` in Stytch. It defaults to false. If true, new Members will be created with status `pending` in Stytch's backend. Their status will remain `pending` and they will continue to receive signup email templates for every Email Magic Link until that Member authenticates and becomes `active`. If false, new Members will be created with status `active`.
          - is_breakglass: Identifies the Member as a break glass user - someone who has permissions to authenticate into an Organization by bypassing the Organization's settings. A break glass account is typically used for emergency purposes to gain access outside of normal authentication procedures. Refer to the [Organization object](organization-object) and its `auth_methods` and `allowed_auth_methods` fields for more details.
          - mfa_phone_number: The Member's phone number. A Member may only have one phone number. The phone number should be in E.164 format (i.e. +1XXXXXXXXXX).
          - mfa_enrolled: Sets whether the Member is enrolled in MFA. If true, the Member must complete an MFA step whenever they wish to log in to their Organization. If false, the Member only needs to complete an MFA step if the Organization's MFA policy is set to `REQUIRED_FOR_ALL`.
          - roles: Roles to explicitly assign to this Member. See the [RBAC guide](https://stytch.com/docs/b2b/guides/rbac/role-assignment)
           for more information about role assignment.
          - external_id: An identifier that can be used in API calls wherever a member_id is expected. This is a string consisting of alphanumeric, `.`, `_`, `-`, or `|` characters with a maximum length of 128 characters. External IDs must be unique within an organization, but may be reused across different organizations in the same project.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "email_address": email_address,
        }
        if name is not None:
            data["name"] = name
        if trusted_metadata is not None:
            data["trusted_metadata"] = trusted_metadata
        if untrusted_metadata is not None:
            data["untrusted_metadata"] = untrusted_metadata
        if create_member_as_pending is not None:
            data["create_member_as_pending"] = create_member_as_pending
        if is_breakglass is not None:
            data["is_breakglass"] = is_breakglass
        if mfa_phone_number is not None:
            data["mfa_phone_number"] = mfa_phone_number
        if mfa_enrolled is not None:
            data["mfa_enrolled"] = mfa_enrolled
        if roles is not None:
            data["roles"] = roles
        if external_id is not None:
            data["external_id"] = external_id

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members", data
        )
        res = self.sync_client.post(url, data, headers)
        return CreateResponse.from_json(res.response.status_code, res.json)

    async def create_async(
        self,
        organization_id: str,
        email_address: str,
        name: Optional[str] = None,
        trusted_metadata: Optional[Dict[str, Any]] = None,
        untrusted_metadata: Optional[Dict[str, Any]] = None,
        create_member_as_pending: Optional[bool] = None,
        is_breakglass: Optional[bool] = None,
        mfa_phone_number: Optional[str] = None,
        mfa_enrolled: Optional[bool] = None,
        roles: Optional[List[str]] = None,
        external_id: Optional[str] = None,
        method_options: Optional[CreateRequestOptions] = None,
    ) -> CreateResponse:
        """Creates a Member. An `organization_id` and `email_address` are required.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - email_address: The email address of the Member.
          - name: The name of the Member.
          - trusted_metadata: An arbitrary JSON object for storing application-specific data or identity-provider-specific data.
          - untrusted_metadata: An arbitrary JSON object of application-specific data. These fields can be edited directly by the
          frontend SDK, and should not be used to store critical information. See the [Metadata resource](https://stytch.com/docs/b2b/api/metadata)
          for complete field behavior details.
          - create_member_as_pending: Flag for whether or not to save a Member as `pending` or `active` in Stytch. It defaults to false. If true, new Members will be created with status `pending` in Stytch's backend. Their status will remain `pending` and they will continue to receive signup email templates for every Email Magic Link until that Member authenticates and becomes `active`. If false, new Members will be created with status `active`.
          - is_breakglass: Identifies the Member as a break glass user - someone who has permissions to authenticate into an Organization by bypassing the Organization's settings. A break glass account is typically used for emergency purposes to gain access outside of normal authentication procedures. Refer to the [Organization object](organization-object) and its `auth_methods` and `allowed_auth_methods` fields for more details.
          - mfa_phone_number: The Member's phone number. A Member may only have one phone number. The phone number should be in E.164 format (i.e. +1XXXXXXXXXX).
          - mfa_enrolled: Sets whether the Member is enrolled in MFA. If true, the Member must complete an MFA step whenever they wish to log in to their Organization. If false, the Member only needs to complete an MFA step if the Organization's MFA policy is set to `REQUIRED_FOR_ALL`.
          - roles: Roles to explicitly assign to this Member. See the [RBAC guide](https://stytch.com/docs/b2b/guides/rbac/role-assignment)
           for more information about role assignment.
          - external_id: An identifier that can be used in API calls wherever a member_id is expected. This is a string consisting of alphanumeric, `.`, `_`, `-`, or `|` characters with a maximum length of 128 characters. External IDs must be unique within an organization, but may be reused across different organizations in the same project.
        """  # noqa
        headers: Dict[str, str] = {}
        if method_options is not None:
            headers = method_options.add_headers(headers)
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "email_address": email_address,
        }
        if name is not None:
            data["name"] = name
        if trusted_metadata is not None:
            data["trusted_metadata"] = trusted_metadata
        if untrusted_metadata is not None:
            data["untrusted_metadata"] = untrusted_metadata
        if create_member_as_pending is not None:
            data["create_member_as_pending"] = create_member_as_pending
        if is_breakglass is not None:
            data["is_breakglass"] = is_breakglass
        if mfa_phone_number is not None:
            data["mfa_phone_number"] = mfa_phone_number
        if mfa_enrolled is not None:
            data["mfa_enrolled"] = mfa_enrolled
        if roles is not None:
            data["roles"] = roles
        if external_id is not None:
            data["external_id"] = external_id

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/members", data
        )
        res = await self.async_client.post(url, data, headers)
        return CreateResponse.from_json(res.response.status, res.json)

    def get(
        self,
        organization_id: str,
        member_id: Optional[str] = None,
        email_address: Optional[str] = None,
    ) -> GetResponse:
        """Get a Member by `member_id` or `email_address`.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - email_address: The email address of the Member.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "organization_id": organization_id,
        }
        if member_id is not None:
            data["member_id"] = member_id
        if email_address is not None:
            data["email_address"] = email_address

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/member", data
        )
        res = self.sync_client.get(url, data, headers)
        return GetResponse.from_json(res.response.status_code, res.json)

    async def get_async(
        self,
        organization_id: str,
        member_id: Optional[str] = None,
        email_address: Optional[str] = None,
    ) -> GetResponse:
        """Get a Member by `member_id` or `email_address`.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - member_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value. You may use an external_id here if one is set for the member.
          - email_address: The email address of the Member.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "organization_id": organization_id,
        }
        if member_id is not None:
            data["member_id"] = member_id
        if email_address is not None:
            data["email_address"] = email_address

        url = self.api_base.url_for(
            "/v1/b2b/organizations/{organization_id}/member", data
        )
        res = await self.async_client.get(url, data, headers)
        return GetResponse.from_json(res.response.status, res.json)
