# This file was auto-generated by Fern from our API Definition.

import datetime as dt
import typing
from json.decoder import JSONDecodeError

from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.datetime_utils import serialize_datetime
from ..core.http_response import AsyncHttpResponse, HttpResponse
from ..core.jsonable_encoder import jsonable_encoder
from ..core.pydantic_utilities import parse_obj_as
from ..core.request_options import RequestOptions
from ..core.serialization import convert_and_respect_annotation_metadata
from ..errors.unprocessable_entity_error import UnprocessableEntityError
from ..types.auth_schema_response import AuthSchemaResponse
from ..types.authorization_response import AuthorizationResponse
from ..types.available_connectors_response import AvailableConnectorsResponse
from ..types.complete_o_auth_response import CompleteOAuthResponse
from ..types.connections_response import ConnectionsResponse
from ..types.connector_info import ConnectorInfo
from ..types.connector_response import ConnectorResponse
from ..types.connector_tools_response import ConnectorToolsResponse
from ..types.credential_health_status import CredentialHealthStatus
from ..types.credential_test_request import CredentialTestRequest
from ..types.credential_test_result import CredentialTestResult
from ..types.delete_connection_response import DeleteConnectionResponse
from ..types.execution_logs_response import ExecutionLogsResponse
from ..types.imap_connection_response import ImapConnectionResponse
from ..types.imap_footer_response import ImapFooterResponse
from ..types.o_auth_credentials_response import OAuthCredentialsResponse
from ..types.rename_connection_response import RenameConnectionResponse
from ..types.toggle_connection_response import ToggleConnectionResponse

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class RawPersonalConnectorsClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    def list_available_connectors(
        self,
        *,
        organization_id: typing.Optional[str] = None,
        q: typing.Optional[str] = None,
        category: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[AvailableConnectorsResponse]:
        """
        List all available connector types that can be connected.

        Returns all available connector types from the synced catalog.

        When authenticated, shows per-user connection status (is_connected, connection_id).

        Parameters
        ----------
        organization_id : typing.Optional[str]
            Organization ID

        q : typing.Optional[str]
            Search by tool_key or display_name

        category : typing.Optional[str]
            Filter by category

        limit : typing.Optional[int]
            Max connectors returned

        offset : typing.Optional[int]
            Pagination offset

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[AvailableConnectorsResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/available",
            method="GET",
            params={
                "organization_id": organization_id,
                "q": q,
                "category": category,
                "limit": limit,
                "offset": offset,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    AvailableConnectorsResponse,
                    parse_obj_as(
                        type_=AvailableConnectorsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get_connector_details(
        self,
        tool_key: str,
        *,
        organization_id: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[ConnectorInfo]:
        """
        Get details of a specific connector by tool_key.

        Returns connector information including connection status if authenticated.

        Parameters
        ----------
        tool_key : str

        organization_id : typing.Optional[str]
            Organization ID

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ConnectorInfo]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/available/{jsonable_encoder(tool_key)}",
            method="GET",
            params={
                "organization_id": organization_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ConnectorInfo,
                    parse_obj_as(
                        type_=ConnectorInfo,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get_auth_schema(
        self, connector_type: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[AuthSchemaResponse]:
        """
        Get authentication schema for a connector type.

        Returns the auth type (oauth or credentials) and required fields.
        The UI uses this to determine whether to start an OAuth flow or
        render a credentials form.

        Parameters
        ----------
        connector_type : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[AuthSchemaResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/auth-schema/{jsonable_encoder(connector_type)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    AuthSchemaResponse,
                    parse_obj_as(
                        type_=AuthSchemaResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def initiate_oauth(
        self,
        *,
        organization_id: str,
        connector_type: str,
        integration_params: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[AuthorizationResponse]:
        """
        Initiate authorization flow for a connector.

        For OAuth connectors: starts standard OAuth flow.
        For credential connectors (e.g. IMAP): returns URL to a hosted credentials form.

        Parameters
        ----------
        organization_id : str
            Organization ID

        connector_type : str
            Connector type to authorize

        integration_params : typing.Optional[typing.Dict[str, typing.Any]]
            Optional integration-specific parameters (e.g., SharePoint site name)

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[AuthorizationResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/authorize",
            method="POST",
            params={
                "organization_id": organization_id,
            },
            json={
                "connector_type": connector_type,
                "integration_params": integration_params,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    AuthorizationResponse,
                    parse_obj_as(
                        type_=AuthorizationResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def list_connected_connectors(
        self, *, organization_id: str, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.List[ConnectorResponse]]:
        """
        List user's connected personal connectors.

        Parameters
        ----------
        organization_id : str
            Organization ID

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.List[ConnectorResponse]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors",
            method="GET",
            params={
                "organization_id": organization_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[ConnectorResponse],
                    parse_obj_as(
                        type_=typing.List[ConnectorResponse],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get_tool_connections(
        self,
        tool_key: str,
        *,
        organization_id: str,
        application_id: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[ConnectionsResponse]:
        """
        Get user's connected accounts for a specific tool.

        This endpoint is used by Flowplate connector components to populate
        the account dropdown with the user's connected accounts.

        Args:
            tool_key: The tool identifier (e.g., "clickup", "github")
            organization_id: The organization ID
            application_id: Application scope for filtering (default: "flowplate")
            auth: Authentication context
            db: Database session

        Returns:
            ConnectionsResponse with list of user's connections for the tool

        Parameters
        ----------
        tool_key : str

        organization_id : str
            Organization ID

        application_id : typing.Optional[str]
            Application scope

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ConnectionsResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/connections/{jsonable_encoder(tool_key)}",
            method="GET",
            params={
                "organization_id": organization_id,
                "application_id": application_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ConnectionsResponse,
                    parse_obj_as(
                        type_=ConnectionsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def complete_oauth(
        self, *, organization_id: str, state_token: str, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[CompleteOAuthResponse]:
        """
        Complete an OAuth flow by polling the connector service for connection status.

        This endpoint is used when the callback can't reach the Freddy backend
        (e.g., localhost development). The client polls this endpoint after the user
        completes OAuth in the browser. If the connector reports the connection as active,
        the UserToolCredential record is created.

        Parameters
        ----------
        organization_id : str
            Organization ID

        state_token : str
            The OAuth state token (ostate_xxx) returned from /authorize

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[CompleteOAuthResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/complete-oauth",
            method="POST",
            params={
                "organization_id": organization_id,
            },
            json={
                "state_token": state_token,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CompleteOAuthResponse,
                    parse_obj_as(
                        type_=CompleteOAuthResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def delete_connection(
        self, credential_id: str, *, organization_id: str, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[DeleteConnectionResponse]:
        """
        Delete a user's connection/credential.

        This removes the connected account from the user's profile.
        The user will need to re-authenticate to use this connector again.

        Args:
            credential_id: The credential ID to delete
            organization_id: The organization ID
            auth: Authentication context
            db: Database session

        Returns:
            DeleteConnectionResponse confirming deletion

        Raises:
            ResourceNotFoundException: If credential not found or not owned by user

        Parameters
        ----------
        credential_id : str

        organization_id : str
            Organization ID

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[DeleteConnectionResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/connections/{jsonable_encoder(credential_id)}",
            method="DELETE",
            params={
                "organization_id": organization_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    DeleteConnectionResponse,
                    parse_obj_as(
                        type_=DeleteConnectionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def rename_connection(
        self,
        credential_id: str,
        *,
        organization_id: str,
        credential_name: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[RenameConnectionResponse]:
        """
        Rename a user's connection/credential.

        This allows users to set a custom name for their connected account
        that will be displayed in dropdowns.

        Args:
            credential_id: The credential ID to rename
            request: The new credential name
            organization_id: The organization ID
            auth: Authentication context
            db: Database session

        Returns:
            RenameConnectionResponse with updated name

        Raises:
            ResourceNotFoundException: If credential not found or not owned by user

        Parameters
        ----------
        credential_id : str

        organization_id : str
            Organization ID

        credential_name : str
            New name for the credential

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[RenameConnectionResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/connections/{jsonable_encoder(credential_id)}",
            method="PATCH",
            params={
                "organization_id": organization_id,
            },
            json={
                "credential_name": credential_name,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    RenameConnectionResponse,
                    parse_obj_as(
                        type_=RenameConnectionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def toggle_connection_active(
        self,
        credential_id: str,
        *,
        organization_id: str,
        is_active: bool,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[ToggleConnectionResponse]:
        """
        Enable or disable a personal connector credential.

        When disabled, the connector's tools won't be loaded in chat.
        The connection is preserved and can be re-enabled at any time.

        Parameters
        ----------
        credential_id : str

        organization_id : str
            Organization ID

        is_active : bool
            Whether the connection should be active

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ToggleConnectionResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/connections/{jsonable_encoder(credential_id)}/active",
            method="PATCH",
            params={
                "organization_id": organization_id,
            },
            json={
                "is_active": is_active,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ToggleConnectionResponse,
                    parse_obj_as(
                        type_=ToggleConnectionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def connect_imap(
        self,
        *,
        organization_id: str,
        email_address: str,
        password: str,
        imap_host: str,
        smtp_host: str,
        imap_port: typing.Optional[int] = OMIT,
        smtp_port: typing.Optional[int] = OMIT,
        use_ssl: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[ImapConnectionResponse]:
        """
        Connect IMAP/SMTP email account with username/password authentication.

        Parameters
        ----------
        organization_id : str
            Organization ID

        email_address : str
            Email address

        password : str
            Email password

        imap_host : str
            IMAP server hostname

        smtp_host : str
            SMTP server hostname

        imap_port : typing.Optional[int]
            IMAP port (993 for SSL, 143 for STARTTLS)

        smtp_port : typing.Optional[int]
            SMTP port (465 for SSL, 587 for STARTTLS)

        use_ssl : typing.Optional[bool]
            Use SSL/TLS connection

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ImapConnectionResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/connect-imap",
            method="POST",
            params={
                "organization_id": organization_id,
            },
            json={
                "email_address": email_address,
                "password": password,
                "imap_host": imap_host,
                "imap_port": imap_port,
                "smtp_host": smtp_host,
                "smtp_port": smtp_port,
                "use_ssl": use_ssl,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ImapConnectionResponse,
                    parse_obj_as(
                        type_=ImapConnectionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def set_imap_footer(
        self,
        *,
        organization_id: str,
        footer_html: typing.Optional[str] = OMIT,
        footer_text: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[ImapFooterResponse]:
        """
        Set custom email footer for IMAP account.

        Parameters
        ----------
        organization_id : str
            Organization ID

        footer_html : typing.Optional[str]
            HTML footer content

        footer_text : typing.Optional[str]
            Plain text footer content

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ImapFooterResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/imap-footer",
            method="POST",
            params={
                "organization_id": organization_id,
            },
            json={
                "footer_html": footer_html,
                "footer_text": footer_text,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ImapFooterResponse,
                    parse_obj_as(
                        type_=ImapFooterResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def oauth_proxy_callback(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Any]:
        """
        Proxy endpoint for OAuth provider callbacks.

        OAuth providers send the auth code here (our domain) instead of directly
        to the integration service. We forward all query params server-side so
        the provider consent screen shows our domain, not a third party's.

        Parameters
        ----------
        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Any]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/oauth-proxy",
            method="GET",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return HttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def oauth_proxy_callback_v1personal_connectors_oauth_proxy_get(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Any]:
        """
        Proxy endpoint for OAuth provider callbacks.

        OAuth providers send the auth code here (our domain) instead of directly
        to the integration service. We forward all query params server-side so
        the provider consent screen shows our domain, not a third party's.

        Parameters
        ----------
        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Any]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/oauth-proxy",
            method="POST",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return HttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def oauth_callback(
        self,
        *,
        code: typing.Optional[str] = None,
        state: typing.Optional[str] = None,
        error: typing.Optional[str] = None,
        status: typing.Optional[str] = None,
        connected_account_id: typing.Optional[str] = None,
        app_name: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[None]:
        """
        Handle OAuth callback from third-party services.

        This endpoint handles OAuth callbacks and:
        1. Validates the state token (or finds most recent for ClickUp)
        2. Routes to the appropriate callback flow based on parameters
        3. Exchanges the authorization code for access/refresh tokens
        4. Stores the tokens securely in the database
        5. Creates MCP configuration and personal connector records

        Note: ClickUp doesn't return the state parameter, so we find the most recent
        unexpired OAuth state for ClickUp connectors.

        Parameters
        ----------
        code : typing.Optional[str]
            OAuth authorization code

        state : typing.Optional[str]
            OAuth state token (not provided by ClickUp)

        error : typing.Optional[str]
            OAuth error parameter

        status : typing.Optional[str]
            Connection status

        connected_account_id : typing.Optional[str]
            Connected account ID

        app_name : typing.Optional[str]
            Connected app name

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[None]
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/callback",
            method="GET",
            params={
                "code": code,
                "state": state,
                "error": error,
                "status": status,
                "connected_account_id": connected_account_id,
                "app_name": app_name,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return HttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def disconnect_connector(
        self, connector_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[None]:
        """
        Disconnect a personal connector (soft delete).

        This endpoint:
        1. Finds the personal connector by ID
        2. Soft deletes both the connector and its MCP configuration
        3. Returns success response

        Parameters
        ----------
        connector_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[None]
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return HttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def update_connector(
        self, connector_id: str, *, is_active: bool, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ConnectorResponse]:
        """
        Update a personal connector's status (enable/disable).

        This endpoint:
        1. Finds the personal connector by ID
        2. Updates the is_active flag on both connector and MCP configuration
        3. Returns updated connector information

        Parameters
        ----------
        connector_id : str

        is_active : bool
            Whether connector should be active

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ConnectorResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector_id)}",
            method="PATCH",
            json={
                "is_active": is_active,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ConnectorResponse,
                    parse_obj_as(
                        type_=ConnectorResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def list_connector_tools(
        self, connector_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ConnectorToolsResponse]:
        """
        List available tools for a personal connector.

        This endpoint:
        1. Finds the personal connector by ID
        2. Connects to the MCP server to fetch available tools
        3. Compares with tool_configuration to determine enabled status
        4. Returns list of tools with their status

        Parameters
        ----------
        connector_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ConnectorToolsResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector_id)}/tools",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ConnectorToolsResponse,
                    parse_obj_as(
                        type_=ConnectorToolsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def update_connector_tools(
        self,
        connector_id: str,
        *,
        enabled: bool,
        allowed_tools: typing.Optional[typing.Sequence[str]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.Dict[str, typing.Any]]:
        """
        Update tool configuration for a personal connector.

        This endpoint:
        1. Finds the personal connector by ID
        2. Updates the tool_configuration in MCP configuration
        3. Returns success response

        Parameters
        ----------
        connector_id : str

        enabled : bool
            Enable/disable all tools

        allowed_tools : typing.Optional[typing.Sequence[str]]
            List of allowed tool names (None = all allowed)

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector_id)}/tools",
            method="PATCH",
            json={
                "enabled": enabled,
                "allowed_tools": allowed_tools,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def disconnect_connector_credential(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[None]:
        """
        Disconnect a connector credential (soft delete).

        This endpoint:
        1. Finds the user tool credential by ID
        2. Revokes the connection if connection_id exists
        3. Marks the credential as inactive (soft delete)
        4. Returns success response

        Parameters
        ----------
        credential_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[None]
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/composio/{jsonable_encoder(credential_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return HttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def list_credential_actions(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Any]:
        """
        List available actions for a connected tool credential.

        This endpoint:
        1. Verifies the credential belongs to the requesting user
        2. Fetches the tool_key from the associated connector tool
        3. Retrieves available actions for the tool
        4. Returns list of actions with their metadata

        Args:
            credential_id: The user tool credential ID
            auth: Authentication context
            db: Database session

        Returns:
            ActionsListResponse with list of available actions

        Raises:
            ResourceNotFoundException: If credential not found or not owned by user
            InternalServerException: If connector API call fails

        Parameters
        ----------
        credential_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Any]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(credential_id)}/actions",
            method="GET",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return HttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def credentials_form_page(
        self, state_token: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Any]:
        """
        Render hosted credentials form for non-OAuth connectors.

        This is the fallback UI for connectors that require username/password
        instead of OAuth. The form tests and saves credentials via JS fetch.

        Parameters
        ----------
        state_token : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Any]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/credentials-form/{jsonable_encoder(state_token)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return HttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def credentials_form_submit(
        self,
        state_token: str,
        *,
        test_only: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.Dict[str, typing.Any]]:
        """
        Handle credential form submission (test or save).

        Called by the hosted credentials form via JS fetch.

        Parameters
        ----------
        state_token : str

        test_only : typing.Optional[bool]
            Only test, don't save

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/credentials-form/{jsonable_encoder(state_token)}",
            method="POST",
            params={
                "test_only": test_only,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def redirect_to_auth(
        self, connection_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Any]:
        """
        Redirect to the actual OAuth authorization URL.

        This endpoint provides an Aitronos-branded redirect URL.
        Supports both state_token (ostate_*) and connection_id lookups.

        Parameters
        ----------
        connection_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Any]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/auth/{jsonable_encoder(connection_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return HttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def list_execution_logs(
        self,
        *,
        organization_id: str,
        credential_id: typing.Optional[str] = None,
        status: typing.Optional[str] = None,
        start_date: typing.Optional[dt.datetime] = None,
        end_date: typing.Optional[dt.datetime] = None,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[ExecutionLogsResponse]:
        """
        List tool execution logs for the authenticated user.

        Returns sanitized execution logs with filtering by date, status, and credential.
        Sensitive data (parameters, results) is not returned in this endpoint.

        Parameters
        ----------
        organization_id : str
            Organization ID

        credential_id : typing.Optional[str]
            Filter by credential ID

        status : typing.Optional[str]
            Filter by status (success, error, blocked, timeout)

        start_date : typing.Optional[dt.datetime]
            Filter logs from this date

        end_date : typing.Optional[dt.datetime]
            Filter logs until this date

        limit : typing.Optional[int]
            Max logs to return

        offset : typing.Optional[int]
            Pagination offset

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ExecutionLogsResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/logs",
            method="GET",
            params={
                "organization_id": organization_id,
                "credential_id": credential_id,
                "status": status,
                "start_date": serialize_datetime(start_date) if start_date is not None else None,
                "end_date": serialize_datetime(end_date) if end_date is not None else None,
                "limit": limit,
                "offset": offset,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ExecutionLogsResponse,
                    parse_obj_as(
                        type_=ExecutionLogsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def check_credential_health(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[CredentialHealthStatus]:
        """
        Check the health status of a credential.

        Verifies:
        1. Credential exists and belongs to the user
        2. Credential is active in the database
        3. External connection is valid (if applicable)

        Returns healthy/unhealthy status with reason.

        Parameters
        ----------
        credential_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[CredentialHealthStatus]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(credential_id)}/health",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialHealthStatus,
                    parse_obj_as(
                        type_=CredentialHealthStatus,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def test_credential(
        self,
        credential_id: str,
        *,
        request: typing.Optional[CredentialTestRequest] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[CredentialTestResult]:
        """
        Test a credential by executing a controlled read-only action.

        This endpoint:
        1. Verifies the credential exists and belongs to the user
        2. Executes a safe read-only action (or specified action)
        3. Returns success/failure with execution details

        For IMAP credentials, tests the connection directly.
        For connector credentials, executes a default read action for the tool.

        Parameters
        ----------
        credential_id : str

        request : typing.Optional[CredentialTestRequest]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[CredentialTestResult]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(credential_id)}/test",
            method="POST",
            json=convert_and_respect_annotation_metadata(
                object_=request, annotation=typing.Optional[CredentialTestRequest], direction="write"
            ),
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialTestResult,
                    parse_obj_as(
                        type_=CredentialTestResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get_connector_credentials(
        self,
        credential_id: str,
        *,
        flowplate_key: typing.Optional[str] = None,
        aitronos_hub_key: typing.Optional[str] = None,
        api_key: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[OAuthCredentialsResponse]:
        """
        Retrieve OAuth credentials (access token, refresh token, etc.) for a connected
            account. This endpoint is restricted to admin services only.

            **Authentication Required:**
            - `X-Flowplate-Key`: Flowplate Master API Key, OR
            - `X-API-Key`: Pipeline Connector API Key

            **Security Notes:**
            - This endpoint returns sensitive OAuth tokens
            - Only use for service-to-service communication
            - Never expose these credentials to end users

        Parameters
        ----------
        credential_id : str

        flowplate_key : typing.Optional[str]
            Flowplate Master API Key

        aitronos_hub_key : typing.Optional[str]
            Aitronos Hub Master API Key

        api_key : typing.Optional[str]
            Pipeline Connector API Key

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[OAuthCredentialsResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/admin/credentials/{jsonable_encoder(credential_id)}",
            method="GET",
            headers={
                "X-Flowplate-Key": str(flowplate_key) if flowplate_key is not None else None,
                "X-Aitronos-Hub-Key": str(aitronos_hub_key) if aitronos_hub_key is not None else None,
                "X-API-Key": str(api_key) if api_key is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    OAuthCredentialsResponse,
                    parse_obj_as(
                        type_=OAuthCredentialsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get_credentials_by_connection_id(
        self,
        connection_id: str,
        *,
        flowplate_key: typing.Optional[str] = None,
        aitronos_hub_key: typing.Optional[str] = None,
        api_key: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[OAuthCredentialsResponse]:
        """
        Retrieve OAuth credentials directly using a connection ID.
            This is useful when you have the connection ID but not the credential ID.

            **Authentication Required:**
            - `X-Flowplate-Key`: Flowplate Master API Key, OR
            - `X-API-Key`: Pipeline Connector API Key

        Parameters
        ----------
        connection_id : str

        flowplate_key : typing.Optional[str]
            Flowplate Master API Key

        aitronos_hub_key : typing.Optional[str]
            Aitronos Hub Master API Key

        api_key : typing.Optional[str]
            Pipeline Connector API Key

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[OAuthCredentialsResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/admin/credentials/by-connection/{jsonable_encoder(connection_id)}",
            method="GET",
            headers={
                "X-Flowplate-Key": str(flowplate_key) if flowplate_key is not None else None,
                "X-Aitronos-Hub-Key": str(aitronos_hub_key) if aitronos_hub_key is not None else None,
                "X-API-Key": str(api_key) if api_key is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    OAuthCredentialsResponse,
                    parse_obj_as(
                        type_=OAuthCredentialsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get_connector_comments(
        self,
        connector: str,
        item_id: str,
        *,
        limit: typing.Optional[int] = None,
        owner: typing.Optional[str] = None,
        repo: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.Dict[str, typing.Any]]:
        """
        Fetch comments for a connector item (issue, task, ticket).

        The item_id is the prefixed card ID from the artifact (e.g., GH-42).

        For GitHub, the ``owner`` and ``repo`` query params are required since
        issue numbers are scoped to a repository.

        Parameters
        ----------
        connector : str

        item_id : str

        limit : typing.Optional[int]
            Max comments to return

        owner : typing.Optional[str]
            Repository owner (required for GitHub)

        repo : typing.Optional[str]
            Repository name (required for GitHub)

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector)}/items/{jsonable_encoder(item_id)}/comments",
            method="GET",
            params={
                "limit": limit,
                "owner": owner,
                "repo": repo,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def add_connector_comment(
        self,
        connector: str,
        item_id: str,
        *,
        text: str,
        owner: typing.Optional[str] = None,
        repo: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.Dict[str, typing.Any]]:
        """
        Add a comment/reply to a connector item (issue, task, ticket).

        The item_id is the prefixed card ID from the artifact (e.g., GH-42).
        The backend strips the prefix to get the native connector ID.

        For GitHub, the ``owner`` and ``repo`` query params are required since
        issue numbers are scoped to a repository.

        Parameters
        ----------
        connector : str

        item_id : str

        text : str
            Comment body text

        owner : typing.Optional[str]
            Repository owner (required for GitHub)

        repo : typing.Optional[str]
            Repository name (required for GitHub)

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector)}/items/{jsonable_encoder(item_id)}/comments",
            method="POST",
            params={
                "owner": owner,
                "repo": repo,
            },
            json={
                "text": text,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)


class AsyncRawPersonalConnectorsClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def list_available_connectors(
        self,
        *,
        organization_id: typing.Optional[str] = None,
        q: typing.Optional[str] = None,
        category: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[AvailableConnectorsResponse]:
        """
        List all available connector types that can be connected.

        Returns all available connector types from the synced catalog.

        When authenticated, shows per-user connection status (is_connected, connection_id).

        Parameters
        ----------
        organization_id : typing.Optional[str]
            Organization ID

        q : typing.Optional[str]
            Search by tool_key or display_name

        category : typing.Optional[str]
            Filter by category

        limit : typing.Optional[int]
            Max connectors returned

        offset : typing.Optional[int]
            Pagination offset

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[AvailableConnectorsResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/available",
            method="GET",
            params={
                "organization_id": organization_id,
                "q": q,
                "category": category,
                "limit": limit,
                "offset": offset,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    AvailableConnectorsResponse,
                    parse_obj_as(
                        type_=AvailableConnectorsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get_connector_details(
        self,
        tool_key: str,
        *,
        organization_id: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[ConnectorInfo]:
        """
        Get details of a specific connector by tool_key.

        Returns connector information including connection status if authenticated.

        Parameters
        ----------
        tool_key : str

        organization_id : typing.Optional[str]
            Organization ID

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ConnectorInfo]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/available/{jsonable_encoder(tool_key)}",
            method="GET",
            params={
                "organization_id": organization_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ConnectorInfo,
                    parse_obj_as(
                        type_=ConnectorInfo,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get_auth_schema(
        self, connector_type: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[AuthSchemaResponse]:
        """
        Get authentication schema for a connector type.

        Returns the auth type (oauth or credentials) and required fields.
        The UI uses this to determine whether to start an OAuth flow or
        render a credentials form.

        Parameters
        ----------
        connector_type : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[AuthSchemaResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/auth-schema/{jsonable_encoder(connector_type)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    AuthSchemaResponse,
                    parse_obj_as(
                        type_=AuthSchemaResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def initiate_oauth(
        self,
        *,
        organization_id: str,
        connector_type: str,
        integration_params: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[AuthorizationResponse]:
        """
        Initiate authorization flow for a connector.

        For OAuth connectors: starts standard OAuth flow.
        For credential connectors (e.g. IMAP): returns URL to a hosted credentials form.

        Parameters
        ----------
        organization_id : str
            Organization ID

        connector_type : str
            Connector type to authorize

        integration_params : typing.Optional[typing.Dict[str, typing.Any]]
            Optional integration-specific parameters (e.g., SharePoint site name)

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[AuthorizationResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/authorize",
            method="POST",
            params={
                "organization_id": organization_id,
            },
            json={
                "connector_type": connector_type,
                "integration_params": integration_params,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    AuthorizationResponse,
                    parse_obj_as(
                        type_=AuthorizationResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def list_connected_connectors(
        self, *, organization_id: str, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.List[ConnectorResponse]]:
        """
        List user's connected personal connectors.

        Parameters
        ----------
        organization_id : str
            Organization ID

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.List[ConnectorResponse]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors",
            method="GET",
            params={
                "organization_id": organization_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[ConnectorResponse],
                    parse_obj_as(
                        type_=typing.List[ConnectorResponse],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get_tool_connections(
        self,
        tool_key: str,
        *,
        organization_id: str,
        application_id: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[ConnectionsResponse]:
        """
        Get user's connected accounts for a specific tool.

        This endpoint is used by Flowplate connector components to populate
        the account dropdown with the user's connected accounts.

        Args:
            tool_key: The tool identifier (e.g., "clickup", "github")
            organization_id: The organization ID
            application_id: Application scope for filtering (default: "flowplate")
            auth: Authentication context
            db: Database session

        Returns:
            ConnectionsResponse with list of user's connections for the tool

        Parameters
        ----------
        tool_key : str

        organization_id : str
            Organization ID

        application_id : typing.Optional[str]
            Application scope

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ConnectionsResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/connections/{jsonable_encoder(tool_key)}",
            method="GET",
            params={
                "organization_id": organization_id,
                "application_id": application_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ConnectionsResponse,
                    parse_obj_as(
                        type_=ConnectionsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def complete_oauth(
        self, *, organization_id: str, state_token: str, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[CompleteOAuthResponse]:
        """
        Complete an OAuth flow by polling the connector service for connection status.

        This endpoint is used when the callback can't reach the Freddy backend
        (e.g., localhost development). The client polls this endpoint after the user
        completes OAuth in the browser. If the connector reports the connection as active,
        the UserToolCredential record is created.

        Parameters
        ----------
        organization_id : str
            Organization ID

        state_token : str
            The OAuth state token (ostate_xxx) returned from /authorize

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[CompleteOAuthResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/complete-oauth",
            method="POST",
            params={
                "organization_id": organization_id,
            },
            json={
                "state_token": state_token,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CompleteOAuthResponse,
                    parse_obj_as(
                        type_=CompleteOAuthResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def delete_connection(
        self, credential_id: str, *, organization_id: str, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[DeleteConnectionResponse]:
        """
        Delete a user's connection/credential.

        This removes the connected account from the user's profile.
        The user will need to re-authenticate to use this connector again.

        Args:
            credential_id: The credential ID to delete
            organization_id: The organization ID
            auth: Authentication context
            db: Database session

        Returns:
            DeleteConnectionResponse confirming deletion

        Raises:
            ResourceNotFoundException: If credential not found or not owned by user

        Parameters
        ----------
        credential_id : str

        organization_id : str
            Organization ID

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[DeleteConnectionResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/connections/{jsonable_encoder(credential_id)}",
            method="DELETE",
            params={
                "organization_id": organization_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    DeleteConnectionResponse,
                    parse_obj_as(
                        type_=DeleteConnectionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def rename_connection(
        self,
        credential_id: str,
        *,
        organization_id: str,
        credential_name: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[RenameConnectionResponse]:
        """
        Rename a user's connection/credential.

        This allows users to set a custom name for their connected account
        that will be displayed in dropdowns.

        Args:
            credential_id: The credential ID to rename
            request: The new credential name
            organization_id: The organization ID
            auth: Authentication context
            db: Database session

        Returns:
            RenameConnectionResponse with updated name

        Raises:
            ResourceNotFoundException: If credential not found or not owned by user

        Parameters
        ----------
        credential_id : str

        organization_id : str
            Organization ID

        credential_name : str
            New name for the credential

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[RenameConnectionResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/connections/{jsonable_encoder(credential_id)}",
            method="PATCH",
            params={
                "organization_id": organization_id,
            },
            json={
                "credential_name": credential_name,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    RenameConnectionResponse,
                    parse_obj_as(
                        type_=RenameConnectionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def toggle_connection_active(
        self,
        credential_id: str,
        *,
        organization_id: str,
        is_active: bool,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[ToggleConnectionResponse]:
        """
        Enable or disable a personal connector credential.

        When disabled, the connector's tools won't be loaded in chat.
        The connection is preserved and can be re-enabled at any time.

        Parameters
        ----------
        credential_id : str

        organization_id : str
            Organization ID

        is_active : bool
            Whether the connection should be active

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ToggleConnectionResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/connections/{jsonable_encoder(credential_id)}/active",
            method="PATCH",
            params={
                "organization_id": organization_id,
            },
            json={
                "is_active": is_active,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ToggleConnectionResponse,
                    parse_obj_as(
                        type_=ToggleConnectionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def connect_imap(
        self,
        *,
        organization_id: str,
        email_address: str,
        password: str,
        imap_host: str,
        smtp_host: str,
        imap_port: typing.Optional[int] = OMIT,
        smtp_port: typing.Optional[int] = OMIT,
        use_ssl: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[ImapConnectionResponse]:
        """
        Connect IMAP/SMTP email account with username/password authentication.

        Parameters
        ----------
        organization_id : str
            Organization ID

        email_address : str
            Email address

        password : str
            Email password

        imap_host : str
            IMAP server hostname

        smtp_host : str
            SMTP server hostname

        imap_port : typing.Optional[int]
            IMAP port (993 for SSL, 143 for STARTTLS)

        smtp_port : typing.Optional[int]
            SMTP port (465 for SSL, 587 for STARTTLS)

        use_ssl : typing.Optional[bool]
            Use SSL/TLS connection

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ImapConnectionResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/connect-imap",
            method="POST",
            params={
                "organization_id": organization_id,
            },
            json={
                "email_address": email_address,
                "password": password,
                "imap_host": imap_host,
                "imap_port": imap_port,
                "smtp_host": smtp_host,
                "smtp_port": smtp_port,
                "use_ssl": use_ssl,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ImapConnectionResponse,
                    parse_obj_as(
                        type_=ImapConnectionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def set_imap_footer(
        self,
        *,
        organization_id: str,
        footer_html: typing.Optional[str] = OMIT,
        footer_text: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[ImapFooterResponse]:
        """
        Set custom email footer for IMAP account.

        Parameters
        ----------
        organization_id : str
            Organization ID

        footer_html : typing.Optional[str]
            HTML footer content

        footer_text : typing.Optional[str]
            Plain text footer content

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ImapFooterResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/imap-footer",
            method="POST",
            params={
                "organization_id": organization_id,
            },
            json={
                "footer_html": footer_html,
                "footer_text": footer_text,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ImapFooterResponse,
                    parse_obj_as(
                        type_=ImapFooterResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def oauth_proxy_callback(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Any]:
        """
        Proxy endpoint for OAuth provider callbacks.

        OAuth providers send the auth code here (our domain) instead of directly
        to the integration service. We forward all query params server-side so
        the provider consent screen shows our domain, not a third party's.

        Parameters
        ----------
        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Any]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/oauth-proxy",
            method="GET",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return AsyncHttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def oauth_proxy_callback_v1personal_connectors_oauth_proxy_get(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Any]:
        """
        Proxy endpoint for OAuth provider callbacks.

        OAuth providers send the auth code here (our domain) instead of directly
        to the integration service. We forward all query params server-side so
        the provider consent screen shows our domain, not a third party's.

        Parameters
        ----------
        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Any]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/oauth-proxy",
            method="POST",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return AsyncHttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def oauth_callback(
        self,
        *,
        code: typing.Optional[str] = None,
        state: typing.Optional[str] = None,
        error: typing.Optional[str] = None,
        status: typing.Optional[str] = None,
        connected_account_id: typing.Optional[str] = None,
        app_name: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[None]:
        """
        Handle OAuth callback from third-party services.

        This endpoint handles OAuth callbacks and:
        1. Validates the state token (or finds most recent for ClickUp)
        2. Routes to the appropriate callback flow based on parameters
        3. Exchanges the authorization code for access/refresh tokens
        4. Stores the tokens securely in the database
        5. Creates MCP configuration and personal connector records

        Note: ClickUp doesn't return the state parameter, so we find the most recent
        unexpired OAuth state for ClickUp connectors.

        Parameters
        ----------
        code : typing.Optional[str]
            OAuth authorization code

        state : typing.Optional[str]
            OAuth state token (not provided by ClickUp)

        error : typing.Optional[str]
            OAuth error parameter

        status : typing.Optional[str]
            Connection status

        connected_account_id : typing.Optional[str]
            Connected account ID

        app_name : typing.Optional[str]
            Connected app name

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[None]
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/callback",
            method="GET",
            params={
                "code": code,
                "state": state,
                "error": error,
                "status": status,
                "connected_account_id": connected_account_id,
                "app_name": app_name,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return AsyncHttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def disconnect_connector(
        self, connector_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[None]:
        """
        Disconnect a personal connector (soft delete).

        This endpoint:
        1. Finds the personal connector by ID
        2. Soft deletes both the connector and its MCP configuration
        3. Returns success response

        Parameters
        ----------
        connector_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[None]
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return AsyncHttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def update_connector(
        self, connector_id: str, *, is_active: bool, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ConnectorResponse]:
        """
        Update a personal connector's status (enable/disable).

        This endpoint:
        1. Finds the personal connector by ID
        2. Updates the is_active flag on both connector and MCP configuration
        3. Returns updated connector information

        Parameters
        ----------
        connector_id : str

        is_active : bool
            Whether connector should be active

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ConnectorResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector_id)}",
            method="PATCH",
            json={
                "is_active": is_active,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ConnectorResponse,
                    parse_obj_as(
                        type_=ConnectorResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def list_connector_tools(
        self, connector_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ConnectorToolsResponse]:
        """
        List available tools for a personal connector.

        This endpoint:
        1. Finds the personal connector by ID
        2. Connects to the MCP server to fetch available tools
        3. Compares with tool_configuration to determine enabled status
        4. Returns list of tools with their status

        Parameters
        ----------
        connector_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ConnectorToolsResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector_id)}/tools",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ConnectorToolsResponse,
                    parse_obj_as(
                        type_=ConnectorToolsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def update_connector_tools(
        self,
        connector_id: str,
        *,
        enabled: bool,
        allowed_tools: typing.Optional[typing.Sequence[str]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.Dict[str, typing.Any]]:
        """
        Update tool configuration for a personal connector.

        This endpoint:
        1. Finds the personal connector by ID
        2. Updates the tool_configuration in MCP configuration
        3. Returns success response

        Parameters
        ----------
        connector_id : str

        enabled : bool
            Enable/disable all tools

        allowed_tools : typing.Optional[typing.Sequence[str]]
            List of allowed tool names (None = all allowed)

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector_id)}/tools",
            method="PATCH",
            json={
                "enabled": enabled,
                "allowed_tools": allowed_tools,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def disconnect_connector_credential(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[None]:
        """
        Disconnect a connector credential (soft delete).

        This endpoint:
        1. Finds the user tool credential by ID
        2. Revokes the connection if connection_id exists
        3. Marks the credential as inactive (soft delete)
        4. Returns success response

        Parameters
        ----------
        credential_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[None]
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/composio/{jsonable_encoder(credential_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return AsyncHttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def list_credential_actions(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Any]:
        """
        List available actions for a connected tool credential.

        This endpoint:
        1. Verifies the credential belongs to the requesting user
        2. Fetches the tool_key from the associated connector tool
        3. Retrieves available actions for the tool
        4. Returns list of actions with their metadata

        Args:
            credential_id: The user tool credential ID
            auth: Authentication context
            db: Database session

        Returns:
            ActionsListResponse with list of available actions

        Raises:
            ResourceNotFoundException: If credential not found or not owned by user
            InternalServerException: If connector API call fails

        Parameters
        ----------
        credential_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Any]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(credential_id)}/actions",
            method="GET",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return AsyncHttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def credentials_form_page(
        self, state_token: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Any]:
        """
        Render hosted credentials form for non-OAuth connectors.

        This is the fallback UI for connectors that require username/password
        instead of OAuth. The form tests and saves credentials via JS fetch.

        Parameters
        ----------
        state_token : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Any]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/credentials-form/{jsonable_encoder(state_token)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return AsyncHttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def credentials_form_submit(
        self,
        state_token: str,
        *,
        test_only: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.Dict[str, typing.Any]]:
        """
        Handle credential form submission (test or save).

        Called by the hosted credentials form via JS fetch.

        Parameters
        ----------
        state_token : str

        test_only : typing.Optional[bool]
            Only test, don't save

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/credentials-form/{jsonable_encoder(state_token)}",
            method="POST",
            params={
                "test_only": test_only,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def redirect_to_auth(
        self, connection_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Any]:
        """
        Redirect to the actual OAuth authorization URL.

        This endpoint provides an Aitronos-branded redirect URL.
        Supports both state_token (ostate_*) and connection_id lookups.

        Parameters
        ----------
        connection_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Any]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/auth/{jsonable_encoder(connection_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if _response is None or not _response.text.strip():
                return AsyncHttpResponse(response=_response, data=None)
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Any,
                    parse_obj_as(
                        type_=typing.Any,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def list_execution_logs(
        self,
        *,
        organization_id: str,
        credential_id: typing.Optional[str] = None,
        status: typing.Optional[str] = None,
        start_date: typing.Optional[dt.datetime] = None,
        end_date: typing.Optional[dt.datetime] = None,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[ExecutionLogsResponse]:
        """
        List tool execution logs for the authenticated user.

        Returns sanitized execution logs with filtering by date, status, and credential.
        Sensitive data (parameters, results) is not returned in this endpoint.

        Parameters
        ----------
        organization_id : str
            Organization ID

        credential_id : typing.Optional[str]
            Filter by credential ID

        status : typing.Optional[str]
            Filter by status (success, error, blocked, timeout)

        start_date : typing.Optional[dt.datetime]
            Filter logs from this date

        end_date : typing.Optional[dt.datetime]
            Filter logs until this date

        limit : typing.Optional[int]
            Max logs to return

        offset : typing.Optional[int]
            Pagination offset

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ExecutionLogsResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/personal-connectors/logs",
            method="GET",
            params={
                "organization_id": organization_id,
                "credential_id": credential_id,
                "status": status,
                "start_date": serialize_datetime(start_date) if start_date is not None else None,
                "end_date": serialize_datetime(end_date) if end_date is not None else None,
                "limit": limit,
                "offset": offset,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ExecutionLogsResponse,
                    parse_obj_as(
                        type_=ExecutionLogsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def check_credential_health(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[CredentialHealthStatus]:
        """
        Check the health status of a credential.

        Verifies:
        1. Credential exists and belongs to the user
        2. Credential is active in the database
        3. External connection is valid (if applicable)

        Returns healthy/unhealthy status with reason.

        Parameters
        ----------
        credential_id : str

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[CredentialHealthStatus]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(credential_id)}/health",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialHealthStatus,
                    parse_obj_as(
                        type_=CredentialHealthStatus,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def test_credential(
        self,
        credential_id: str,
        *,
        request: typing.Optional[CredentialTestRequest] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[CredentialTestResult]:
        """
        Test a credential by executing a controlled read-only action.

        This endpoint:
        1. Verifies the credential exists and belongs to the user
        2. Executes a safe read-only action (or specified action)
        3. Returns success/failure with execution details

        For IMAP credentials, tests the connection directly.
        For connector credentials, executes a default read action for the tool.

        Parameters
        ----------
        credential_id : str

        request : typing.Optional[CredentialTestRequest]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[CredentialTestResult]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(credential_id)}/test",
            method="POST",
            json=convert_and_respect_annotation_metadata(
                object_=request, annotation=typing.Optional[CredentialTestRequest], direction="write"
            ),
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialTestResult,
                    parse_obj_as(
                        type_=CredentialTestResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get_connector_credentials(
        self,
        credential_id: str,
        *,
        flowplate_key: typing.Optional[str] = None,
        aitronos_hub_key: typing.Optional[str] = None,
        api_key: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[OAuthCredentialsResponse]:
        """
        Retrieve OAuth credentials (access token, refresh token, etc.) for a connected
            account. This endpoint is restricted to admin services only.

            **Authentication Required:**
            - `X-Flowplate-Key`: Flowplate Master API Key, OR
            - `X-API-Key`: Pipeline Connector API Key

            **Security Notes:**
            - This endpoint returns sensitive OAuth tokens
            - Only use for service-to-service communication
            - Never expose these credentials to end users

        Parameters
        ----------
        credential_id : str

        flowplate_key : typing.Optional[str]
            Flowplate Master API Key

        aitronos_hub_key : typing.Optional[str]
            Aitronos Hub Master API Key

        api_key : typing.Optional[str]
            Pipeline Connector API Key

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[OAuthCredentialsResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/admin/credentials/{jsonable_encoder(credential_id)}",
            method="GET",
            headers={
                "X-Flowplate-Key": str(flowplate_key) if flowplate_key is not None else None,
                "X-Aitronos-Hub-Key": str(aitronos_hub_key) if aitronos_hub_key is not None else None,
                "X-API-Key": str(api_key) if api_key is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    OAuthCredentialsResponse,
                    parse_obj_as(
                        type_=OAuthCredentialsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get_credentials_by_connection_id(
        self,
        connection_id: str,
        *,
        flowplate_key: typing.Optional[str] = None,
        aitronos_hub_key: typing.Optional[str] = None,
        api_key: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[OAuthCredentialsResponse]:
        """
        Retrieve OAuth credentials directly using a connection ID.
            This is useful when you have the connection ID but not the credential ID.

            **Authentication Required:**
            - `X-Flowplate-Key`: Flowplate Master API Key, OR
            - `X-API-Key`: Pipeline Connector API Key

        Parameters
        ----------
        connection_id : str

        flowplate_key : typing.Optional[str]
            Flowplate Master API Key

        aitronos_hub_key : typing.Optional[str]
            Aitronos Hub Master API Key

        api_key : typing.Optional[str]
            Pipeline Connector API Key

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[OAuthCredentialsResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/admin/credentials/by-connection/{jsonable_encoder(connection_id)}",
            method="GET",
            headers={
                "X-Flowplate-Key": str(flowplate_key) if flowplate_key is not None else None,
                "X-Aitronos-Hub-Key": str(aitronos_hub_key) if aitronos_hub_key is not None else None,
                "X-API-Key": str(api_key) if api_key is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    OAuthCredentialsResponse,
                    parse_obj_as(
                        type_=OAuthCredentialsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get_connector_comments(
        self,
        connector: str,
        item_id: str,
        *,
        limit: typing.Optional[int] = None,
        owner: typing.Optional[str] = None,
        repo: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.Dict[str, typing.Any]]:
        """
        Fetch comments for a connector item (issue, task, ticket).

        The item_id is the prefixed card ID from the artifact (e.g., GH-42).

        For GitHub, the ``owner`` and ``repo`` query params are required since
        issue numbers are scoped to a repository.

        Parameters
        ----------
        connector : str

        item_id : str

        limit : typing.Optional[int]
            Max comments to return

        owner : typing.Optional[str]
            Repository owner (required for GitHub)

        repo : typing.Optional[str]
            Repository name (required for GitHub)

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector)}/items/{jsonable_encoder(item_id)}/comments",
            method="GET",
            params={
                "limit": limit,
                "owner": owner,
                "repo": repo,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def add_connector_comment(
        self,
        connector: str,
        item_id: str,
        *,
        text: str,
        owner: typing.Optional[str] = None,
        repo: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.Dict[str, typing.Any]]:
        """
        Add a comment/reply to a connector item (issue, task, ticket).

        The item_id is the prefixed card ID from the artifact (e.g., GH-42).
        The backend strips the prefix to get the native connector ID.

        For GitHub, the ``owner`` and ``repo`` query params are required since
        issue numbers are scoped to a repository.

        Parameters
        ----------
        connector : str

        item_id : str

        text : str
            Comment body text

        owner : typing.Optional[str]
            Repository owner (required for GitHub)

        repo : typing.Optional[str]
            Repository name (required for GitHub)

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/personal-connectors/{jsonable_encoder(connector)}/items/{jsonable_encoder(item_id)}/comments",
            method="POST",
            params={
                "owner": owner,
                "repo": repo,
            },
            json={
                "text": text,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
