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

import typing
from contextlib import asynccontextmanager, contextmanager

import httpx
import websockets.exceptions
import websockets.sync.client as websockets_sync_client
from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.request_options import RequestOptions
from ..requests.outbound_call_config import OutboundCallConfigParams
from ..types.conversation_evaluation_result import ConversationEvaluationResult
from .raw_client import AsyncRawConversationsClient, RawConversationsClient
from .socket_client import AsyncConversationsSocketClient, ConversationsSocketClient
from .types.conversations_cancel_response import ConversationsCancelResponse
from .types.conversations_extract_data_response import ConversationsExtractDataResponse
from .types.conversations_get_analysis_response import ConversationsGetAnalysisResponse
from .types.conversations_get_response import ConversationsGetResponse
from .types.conversations_list_evaluations_response import ConversationsListEvaluationsResponse
from .types.conversations_list_extractions_response import ConversationsListExtractionsResponse
from .types.conversations_list_response import ConversationsListResponse
from .types.conversations_outbound_call_response import ConversationsOutboundCallResponse
from .types.conversations_sip_outbound_call_response import ConversationsSipOutboundCallResponse

try:
    from websockets.legacy.client import connect as websockets_client_connect  # type: ignore
except ImportError:
    from websockets import connect as websockets_client_connect  # type: ignore

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


class ConversationsClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._raw_client = RawConversationsClient(client_wrapper=client_wrapper)

    @property
    def with_raw_response(self) -> RawConversationsClient:
        """
        Retrieves a raw implementation of this client that returns raw responses.

        Returns
        -------
        RawConversationsClient
        """
        return self._raw_client

    def list(
        self,
        *,
        project: typing.Optional[str] = None,
        external_id: typing.Optional[str] = None,
        duration_min: typing.Optional[int] = None,
        duration_max: typing.Optional[int] = None,
        started_at_min: typing.Optional[str] = None,
        started_at_max: typing.Optional[str] = None,
        before: typing.Optional[str] = None,
        after: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationsListResponse:
        """
        Returns conversations with optional filtering.

        Parameters
        ----------
        project : typing.Optional[str]
            The name of the project to list conversations for.

        external_id : typing.Optional[str]
            Filter by external ID to get a specific conversation.

        duration_min : typing.Optional[int]
            Minimum duration in seconds.

        duration_max : typing.Optional[int]
            Maximum duration in seconds.

        started_at_min : typing.Optional[str]
            Minimum start date/time. Valid examples: `2025-04-17`, `2025-04-17T02:48:52.708Z`

        started_at_max : typing.Optional[str]
            Maximum start date/time. Valid examples: `2025-04-17`, `2025-04-17T02:48:52.708Z`

        before : typing.Optional[str]
            Cursor for pagination (before).

        after : typing.Optional[str]
            Cursor for pagination (after).

        limit : typing.Optional[int]
            Maximum number of conversations to return.

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

        Returns
        -------
        ConversationsListResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.list()
        """
        _response = self._raw_client.list(
            project=project,
            external_id=external_id,
            duration_min=duration_min,
            duration_max=duration_max,
            started_at_min=started_at_min,
            started_at_max=started_at_max,
            before=before,
            after=after,
            limit=limit,
            request_options=request_options,
        )
        return _response.data

    def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ConversationsGetResponse:
        """
        Returns a conversation by ID.

        Parameters
        ----------
        id : str
            The ID of the conversation to get.

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

        Returns
        -------
        ConversationsGetResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.get(
            id="id",
        )
        """
        _response = self._raw_client.get(id, request_options=request_options)
        return _response.data

    def cancel(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsCancelResponse:
        """
        Cancels an active conversation.

        Parameters
        ----------
        id : str
            The ID of the conversation to cancel.

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

        Returns
        -------
        ConversationsCancelResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.cancel(
            id="id",
        )
        """
        _response = self._raw_client.cancel(id, request_options=request_options)
        return _response.data

    def get_analysis(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsGetAnalysisResponse:
        """
        Returns an analysis of the specified conversation.

        Parameters
        ----------
        id : str
            The ID of the conversation to analyze.

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

        Returns
        -------
        ConversationsGetAnalysisResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.get_analysis(
            id="id",
        )
        """
        _response = self._raw_client.get_analysis(id, request_options=request_options)
        return _response.data

    def list_extractions(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsListExtractionsResponse:
        """
        Returns all extractions for a conversation.

        Parameters
        ----------
        id : str
            The ID of the conversation to get extractions for.

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

        Returns
        -------
        ConversationsListExtractionsResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.list_extractions(
            id="id",
        )
        """
        _response = self._raw_client.list_extractions(id, request_options=request_options)
        return _response.data

    def extract_data(
        self, id: str, *, schema_id: str, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsExtractDataResponse:
        """
        Extracts data from a conversation using a schema.

        Parameters
        ----------
        id : str
            The ID of the conversation to extract data from.

        schema_id : str
            ID of the extraction schema to use.

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

        Returns
        -------
        ConversationsExtractDataResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.extract_data(
            id="id",
            schema_id="conv_extract_schema_6458e4ac-533c-4bdf-8e6d-c2f06f87fd5c",
        )
        """
        _response = self._raw_client.extract_data(id, schema_id=schema_id, request_options=request_options)
        return _response.data

    def list_evaluations(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsListEvaluationsResponse:
        """
        Returns all evaluations for a conversation.

        Parameters
        ----------
        id : str
            The ID of the conversation to get evaluations for.

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

        Returns
        -------
        ConversationsListEvaluationsResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.list_evaluations(
            id="id",
        )
        """
        _response = self._raw_client.list_evaluations(id, request_options=request_options)
        return _response.data

    def evaluate(
        self, id: str, *, prompt_id: str, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationEvaluationResult:
        """
        Evaluates a conversation using an evaluation prompt.

        Parameters
        ----------
        id : str
            The ID of the conversation to evaluate.

        prompt_id : str
            ID of the evaluation prompt to use.

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

        Returns
        -------
        ConversationEvaluationResult
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.evaluate(
            id="id",
            prompt_id="conv_eval_prompt_d7cfe45d-35db-4ef6-a254-81ab1da76ce0",
        )
        """
        _response = self._raw_client.evaluate(id, prompt_id=prompt_id, request_options=request_options)
        return _response.data

    def outbound_call(
        self,
        *,
        to_phone_number: str,
        config: typing.Optional[OutboundCallConfigParams] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationsOutboundCallResponse:
        """
        Initiates a call to a given phone number using Phonic's Twilio account.

        Parameters
        ----------
        to_phone_number : str
            The phone number to call in E.164 format.

        config : typing.Optional[OutboundCallConfigParams]

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

        Returns
        -------
        ConversationsOutboundCallResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.outbound_call(
            to_phone_number="+19189397081",
            config={
                "agent": "support-agent",
                "welcome_message": "Hi {{customer_name}}. How can I help you today?",
                "system_prompt": "You are an expert in {{subject}}. Be friendly, helpful and concise.",
                "template_variables": {"customer_name": "David", "subject": "Chess"},
                "voice_id": "grant",
                "no_input_poke_sec": 30,
                "no_input_poke_text": "Are you still there?",
                "no_input_end_conversation_sec": 180,
                "languages": ["en", "es"],
                "boosted_keywords": ["Load ID", "dispatch"],
                "tools": [],
            },
        )
        """
        _response = self._raw_client.outbound_call(
            to_phone_number=to_phone_number, config=config, request_options=request_options
        )
        return _response.data

    def sip_outbound_call(
        self,
        *,
        sip_address: str,
        from_phone_number: str,
        to_phone_number: str,
        token: typing.Optional[str] = None,
        downstream_websocket_url: typing.Optional[str] = None,
        sip_auth_username: typing.Optional[str] = None,
        sip_auth_password: typing.Optional[str] = None,
        config: typing.Optional[OutboundCallConfigParams] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationsSipOutboundCallResponse:
        """
        Initiates a SIP outbound call using user-supplied SIP credentials in headers.

        Parameters
        ----------
        sip_address : str
            SIP address of the user's SIP trunk. Required.

        from_phone_number : str
            Caller ID phone number in E.164 format.

        to_phone_number : str
            Destination phone number in E.164 format.

        token : typing.Optional[str]
            Optional session token used for STS authorization.

        downstream_websocket_url : typing.Optional[str]
            Optional downstream STS WebSocket URL to override the default.

        sip_auth_username : typing.Optional[str]
            SIP auth username, if your provider requires it.

        sip_auth_password : typing.Optional[str]
            SIP auth password, if your provider requires it.

        config : typing.Optional[OutboundCallConfigParams]

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

        Returns
        -------
        ConversationsSipOutboundCallResponse
            Success response

        Examples
        --------
        from phonic import Phonic

        client = Phonic(
            api_key="YOUR_API_KEY",
        )
        client.conversations.sip_outbound_call(
            sip_address="X-Sip-Address",
            from_phone_number="from_phone_number",
            to_phone_number="to_phone_number",
        )
        """
        _response = self._raw_client.sip_outbound_call(
            sip_address=sip_address,
            from_phone_number=from_phone_number,
            to_phone_number=to_phone_number,
            token=token,
            downstream_websocket_url=downstream_websocket_url,
            sip_auth_username=sip_auth_username,
            sip_auth_password=sip_auth_password,
            config=config,
            request_options=request_options,
        )
        return _response.data

    @contextmanager
    def connect(
        self,
        *,
        downstream_websocket_url: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[ConversationsSocketClient]:
        """
        Main STS WebSocket channel for real-time voice conversations

        Parameters
        ----------
        downstream_websocket_url : typing.Optional[str]
            Custom downstream WebSocket URL

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

        Returns
        -------
        ConversationsSocketClient
        """
        ws_url = self._raw_client._client_wrapper.get_environment().production + "/v1/sts/ws"
        query_params = httpx.QueryParams()
        if downstream_websocket_url is not None:
            query_params = query_params.add("downstream_websocket_url", downstream_websocket_url)
        ws_url = ws_url + f"?{query_params}"
        headers = self._raw_client._client_wrapper.get_headers()
        if request_options and "additional_headers" in request_options:
            headers.update(request_options["additional_headers"])
        try:
            with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol:
                yield ConversationsSocketClient(websocket=protocol)
        except websockets.exceptions.InvalidStatusCode as exc:
            status_code: int = exc.status_code
            if status_code == 401:
                raise ApiError(
                    status_code=status_code,
                    headers=dict(headers),
                    body="Websocket initialized with invalid credentials.",
                )
            raise ApiError(
                status_code=status_code,
                headers=dict(headers),
                body="Unexpected error when initializing websocket connection.",
            )


class AsyncConversationsClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._raw_client = AsyncRawConversationsClient(client_wrapper=client_wrapper)

    @property
    def with_raw_response(self) -> AsyncRawConversationsClient:
        """
        Retrieves a raw implementation of this client that returns raw responses.

        Returns
        -------
        AsyncRawConversationsClient
        """
        return self._raw_client

    async def list(
        self,
        *,
        project: typing.Optional[str] = None,
        external_id: typing.Optional[str] = None,
        duration_min: typing.Optional[int] = None,
        duration_max: typing.Optional[int] = None,
        started_at_min: typing.Optional[str] = None,
        started_at_max: typing.Optional[str] = None,
        before: typing.Optional[str] = None,
        after: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationsListResponse:
        """
        Returns conversations with optional filtering.

        Parameters
        ----------
        project : typing.Optional[str]
            The name of the project to list conversations for.

        external_id : typing.Optional[str]
            Filter by external ID to get a specific conversation.

        duration_min : typing.Optional[int]
            Minimum duration in seconds.

        duration_max : typing.Optional[int]
            Maximum duration in seconds.

        started_at_min : typing.Optional[str]
            Minimum start date/time. Valid examples: `2025-04-17`, `2025-04-17T02:48:52.708Z`

        started_at_max : typing.Optional[str]
            Maximum start date/time. Valid examples: `2025-04-17`, `2025-04-17T02:48:52.708Z`

        before : typing.Optional[str]
            Cursor for pagination (before).

        after : typing.Optional[str]
            Cursor for pagination (after).

        limit : typing.Optional[int]
            Maximum number of conversations to return.

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

        Returns
        -------
        ConversationsListResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.list()


        asyncio.run(main())
        """
        _response = await self._raw_client.list(
            project=project,
            external_id=external_id,
            duration_min=duration_min,
            duration_max=duration_max,
            started_at_min=started_at_min,
            started_at_max=started_at_max,
            before=before,
            after=after,
            limit=limit,
            request_options=request_options,
        )
        return _response.data

    async def get(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsGetResponse:
        """
        Returns a conversation by ID.

        Parameters
        ----------
        id : str
            The ID of the conversation to get.

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

        Returns
        -------
        ConversationsGetResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.get(
                id="id",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.get(id, request_options=request_options)
        return _response.data

    async def cancel(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsCancelResponse:
        """
        Cancels an active conversation.

        Parameters
        ----------
        id : str
            The ID of the conversation to cancel.

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

        Returns
        -------
        ConversationsCancelResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.cancel(
                id="id",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.cancel(id, request_options=request_options)
        return _response.data

    async def get_analysis(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsGetAnalysisResponse:
        """
        Returns an analysis of the specified conversation.

        Parameters
        ----------
        id : str
            The ID of the conversation to analyze.

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

        Returns
        -------
        ConversationsGetAnalysisResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.get_analysis(
                id="id",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.get_analysis(id, request_options=request_options)
        return _response.data

    async def list_extractions(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsListExtractionsResponse:
        """
        Returns all extractions for a conversation.

        Parameters
        ----------
        id : str
            The ID of the conversation to get extractions for.

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

        Returns
        -------
        ConversationsListExtractionsResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.list_extractions(
                id="id",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.list_extractions(id, request_options=request_options)
        return _response.data

    async def extract_data(
        self, id: str, *, schema_id: str, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsExtractDataResponse:
        """
        Extracts data from a conversation using a schema.

        Parameters
        ----------
        id : str
            The ID of the conversation to extract data from.

        schema_id : str
            ID of the extraction schema to use.

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

        Returns
        -------
        ConversationsExtractDataResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.extract_data(
                id="id",
                schema_id="conv_extract_schema_6458e4ac-533c-4bdf-8e6d-c2f06f87fd5c",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.extract_data(id, schema_id=schema_id, request_options=request_options)
        return _response.data

    async def list_evaluations(
        self, id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationsListEvaluationsResponse:
        """
        Returns all evaluations for a conversation.

        Parameters
        ----------
        id : str
            The ID of the conversation to get evaluations for.

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

        Returns
        -------
        ConversationsListEvaluationsResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.list_evaluations(
                id="id",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.list_evaluations(id, request_options=request_options)
        return _response.data

    async def evaluate(
        self, id: str, *, prompt_id: str, request_options: typing.Optional[RequestOptions] = None
    ) -> ConversationEvaluationResult:
        """
        Evaluates a conversation using an evaluation prompt.

        Parameters
        ----------
        id : str
            The ID of the conversation to evaluate.

        prompt_id : str
            ID of the evaluation prompt to use.

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

        Returns
        -------
        ConversationEvaluationResult
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.evaluate(
                id="id",
                prompt_id="conv_eval_prompt_d7cfe45d-35db-4ef6-a254-81ab1da76ce0",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.evaluate(id, prompt_id=prompt_id, request_options=request_options)
        return _response.data

    async def outbound_call(
        self,
        *,
        to_phone_number: str,
        config: typing.Optional[OutboundCallConfigParams] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationsOutboundCallResponse:
        """
        Initiates a call to a given phone number using Phonic's Twilio account.

        Parameters
        ----------
        to_phone_number : str
            The phone number to call in E.164 format.

        config : typing.Optional[OutboundCallConfigParams]

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

        Returns
        -------
        ConversationsOutboundCallResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.outbound_call(
                to_phone_number="+19189397081",
                config={
                    "agent": "support-agent",
                    "welcome_message": "Hi {{customer_name}}. How can I help you today?",
                    "system_prompt": "You are an expert in {{subject}}. Be friendly, helpful and concise.",
                    "template_variables": {
                        "customer_name": "David",
                        "subject": "Chess",
                    },
                    "voice_id": "grant",
                    "no_input_poke_sec": 30,
                    "no_input_poke_text": "Are you still there?",
                    "no_input_end_conversation_sec": 180,
                    "languages": ["en", "es"],
                    "boosted_keywords": ["Load ID", "dispatch"],
                    "tools": [],
                },
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.outbound_call(
            to_phone_number=to_phone_number, config=config, request_options=request_options
        )
        return _response.data

    async def sip_outbound_call(
        self,
        *,
        sip_address: str,
        from_phone_number: str,
        to_phone_number: str,
        token: typing.Optional[str] = None,
        downstream_websocket_url: typing.Optional[str] = None,
        sip_auth_username: typing.Optional[str] = None,
        sip_auth_password: typing.Optional[str] = None,
        config: typing.Optional[OutboundCallConfigParams] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationsSipOutboundCallResponse:
        """
        Initiates a SIP outbound call using user-supplied SIP credentials in headers.

        Parameters
        ----------
        sip_address : str
            SIP address of the user's SIP trunk. Required.

        from_phone_number : str
            Caller ID phone number in E.164 format.

        to_phone_number : str
            Destination phone number in E.164 format.

        token : typing.Optional[str]
            Optional session token used for STS authorization.

        downstream_websocket_url : typing.Optional[str]
            Optional downstream STS WebSocket URL to override the default.

        sip_auth_username : typing.Optional[str]
            SIP auth username, if your provider requires it.

        sip_auth_password : typing.Optional[str]
            SIP auth password, if your provider requires it.

        config : typing.Optional[OutboundCallConfigParams]

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

        Returns
        -------
        ConversationsSipOutboundCallResponse
            Success response

        Examples
        --------
        import asyncio

        from phonic import AsyncPhonic

        client = AsyncPhonic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.conversations.sip_outbound_call(
                sip_address="X-Sip-Address",
                from_phone_number="from_phone_number",
                to_phone_number="to_phone_number",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.sip_outbound_call(
            sip_address=sip_address,
            from_phone_number=from_phone_number,
            to_phone_number=to_phone_number,
            token=token,
            downstream_websocket_url=downstream_websocket_url,
            sip_auth_username=sip_auth_username,
            sip_auth_password=sip_auth_password,
            config=config,
            request_options=request_options,
        )
        return _response.data

    @asynccontextmanager
    async def connect(
        self,
        *,
        downstream_websocket_url: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[AsyncConversationsSocketClient]:
        """
        Main STS WebSocket channel for real-time voice conversations

        Parameters
        ----------
        downstream_websocket_url : typing.Optional[str]
            Custom downstream WebSocket URL

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

        Returns
        -------
        AsyncConversationsSocketClient
        """
        ws_url = self._raw_client._client_wrapper.get_environment().production + "/v1/sts/ws"
        query_params = httpx.QueryParams()
        if downstream_websocket_url is not None:
            query_params = query_params.add("downstream_websocket_url", downstream_websocket_url)
        ws_url = ws_url + f"?{query_params}"
        headers = self._raw_client._client_wrapper.get_headers()
        if request_options and "additional_headers" in request_options:
            headers.update(request_options["additional_headers"])
        try:
            async with websockets_client_connect(ws_url, extra_headers=headers) as protocol:
                yield AsyncConversationsSocketClient(websocket=protocol)
        except websockets.exceptions.InvalidStatusCode as exc:
            status_code: int = exc.status_code
            if status_code == 401:
                raise ApiError(
                    status_code=status_code,
                    headers=dict(headers),
                    body="Websocket initialized with invalid credentials.",
                )
            raise ApiError(
                status_code=status_code,
                headers=dict(headers),
                body="Unexpected error when initializing websocket connection.",
            )
