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

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

from . import core
from .core.api_error import ApiError
from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
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.bad_request_error import BadRequestError
from .errors.conflict_error import ConflictError
from .errors.forbidden_error import ForbiddenError
from .errors.not_found_error import NotFoundError
from .errors.unprocessable_entity_error import UnprocessableEntityError
from .types.artifact import Artifact
from .types.artifact_type import ArtifactType
from .types.billing_state_response import BillingStateResponse
from .types.browser_profile import BrowserProfile
from .types.browser_session_response import BrowserSessionResponse
from .types.change_tier_response import ChangeTierResponse
from .types.checkout_session_response import CheckoutSessionResponse
from .types.clear_cache_response import ClearCacheResponse
from .types.create_credential_request_credential import CreateCredentialRequestCredential
from .types.create_script_response import CreateScriptResponse
from .types.credential_response import CredentialResponse
from .types.extensions import Extensions
from .types.get_run_response import GetRunResponse
from .types.otp_type import OtpType
from .types.persistent_browser_type import PersistentBrowserType
from .types.plan_tier import PlanTier
from .types.portal_session_response import PortalSessionResponse
from .types.proxy_location import ProxyLocation
from .types.retry_run_webhook_request import RetryRunWebhookRequest
from .types.run_engine import RunEngine
from .types.run_sdk_action_request_action import RunSdkActionRequestAction
from .types.run_sdk_action_response import RunSdkActionResponse
from .types.script import Script
from .types.script_file_create import ScriptFileCreate
from .types.skyvern_forge_sdk_schemas_credentials_credential_type import SkyvernForgeSdkSchemasCredentialsCredentialType
from .types.skyvern_schemas_run_blocks_credential_type import SkyvernSchemasRunBlocksCredentialType
from .types.task_run_request_data_extraction_schema import TaskRunRequestDataExtractionSchema
from .types.task_run_request_proxy_location import TaskRunRequestProxyLocation
from .types.task_run_response import TaskRunResponse
from .types.totp_code import TotpCode
from .types.upload_file_response import UploadFileResponse
from .types.workflow import Workflow
from .types.workflow_create_yaml_request import WorkflowCreateYamlRequest
from .types.workflow_run import WorkflowRun
from .types.workflow_run_request_proxy_location import WorkflowRunRequestProxyLocation
from .types.workflow_run_response import WorkflowRunResponse
from .types.workflow_run_status import WorkflowRunStatus
from .types.workflow_run_timeline import WorkflowRunTimeline
from .types.workflow_status import WorkflowStatus

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


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

    def run_task(
        self,
        *,
        prompt: str,
        user_agent: typing.Optional[str] = None,
        url: typing.Optional[str] = OMIT,
        engine: typing.Optional[RunEngine] = OMIT,
        title: typing.Optional[str] = OMIT,
        proxy_location: typing.Optional[TaskRunRequestProxyLocation] = OMIT,
        data_extraction_schema: typing.Optional[TaskRunRequestDataExtractionSchema] = OMIT,
        error_code_mapping: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        max_steps: typing.Optional[int] = OMIT,
        webhook_url: typing.Optional[str] = OMIT,
        totp_identifier: typing.Optional[str] = OMIT,
        totp_url: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        model: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT,
        extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        publish_workflow: typing.Optional[bool] = OMIT,
        include_action_history_in_verification: typing.Optional[bool] = OMIT,
        max_screenshot_scrolls: typing.Optional[int] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        run_with: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[TaskRunResponse]:
        """
        Run a task

        Parameters
        ----------
        prompt : str

            The goal or task description for Skyvern to accomplish

        user_agent : typing.Optional[str]

        url : typing.Optional[str]

            The starting URL for the task. If not provided, Skyvern will attempt to determine an appropriate URL

        engine : typing.Optional[RunEngine]

            The engine that powers the agent task. The default value is `skyvern-2.0`, the latest Skyvern agent that performs pretty well with complex and multi-step tasks. `skyvern-1.0` is good for simple tasks like filling a form, or searching for information on Google. The `openai-cua` engine uses OpenAI's CUA model. The `anthropic-cua` uses Anthropic's Claude Sonnet 3.7 model with the computer use tool.

        title : typing.Optional[str]
            The title for the task

        proxy_location : typing.Optional[TaskRunRequestProxyLocation]

            Geographic Proxy location to route the browser traffic through. This is only available in Skyvern Cloud.

            Available geotargeting options:
            - RESIDENTIAL: the default value. Skyvern Cloud uses a random US residential proxy.
            - RESIDENTIAL_ES: Spain
            - RESIDENTIAL_IE: Ireland
            - RESIDENTIAL_GB: United Kingdom
            - RESIDENTIAL_IN: India
            - RESIDENTIAL_JP: Japan
            - RESIDENTIAL_FR: France
            - RESIDENTIAL_DE: Germany
            - RESIDENTIAL_NZ: New Zealand
            - RESIDENTIAL_PH: Philippines
            - RESIDENTIAL_ZA: South Africa
            - RESIDENTIAL_AR: Argentina
            - RESIDENTIAL_AU: Australia
            - RESIDENTIAL_ISP: ISP proxy
            - US-CA: California (deprecated, routes through RESIDENTIAL_ISP)
            - US-NY: New York (deprecated, routes through RESIDENTIAL_ISP)
            - US-TX: Texas (deprecated, routes through RESIDENTIAL_ISP)
            - US-FL: Florida (deprecated, routes through RESIDENTIAL_ISP)
            - US-WA: Washington (deprecated, routes through RESIDENTIAL_ISP)
            - NONE: No proxy
             Can also be a GeoTarget object for granular city/state targeting: {"country": "US", "subdivision": "CA", "city": "San Francisco"}

        data_extraction_schema : typing.Optional[TaskRunRequestDataExtractionSchema]

            The schema for data to be extracted from the webpage. If you're looking for consistent data schema being returned by the agent, it's highly recommended to use https://json-schema.org/.

        error_code_mapping : typing.Optional[typing.Dict[str, typing.Optional[str]]]

            Custom mapping of error codes to error messages if Skyvern encounters an error.

        max_steps : typing.Optional[int]

            Maximum number of steps the task can take. Task will fail if it exceeds this number. Cautions: you are charged per step so please set this number to a reasonable value. Contact sales@skyvern.com for custom pricing.

        webhook_url : typing.Optional[str]

            After a run is finished, send an update to this URL. Refer to https://www.skyvern.com/docs/running-tasks/webhooks-faq for more details.

        totp_identifier : typing.Optional[str]

            Identifier for the TOTP/2FA/MFA code when the code is pushed to Skyvern. Refer to https://www.skyvern.com/docs/credentials/totp#option-3-push-code-to-skyvern for more details.

        totp_url : typing.Optional[str]

            URL that serves TOTP/2FA/MFA codes for Skyvern to use during the workflow run. Refer to https://www.skyvern.com/docs/credentials/totp#option-2-get-code-from-your-endpoint for more details.

        browser_session_id : typing.Optional[str]

            Run the task or workflow in the specific Skyvern browser session. Having a browser session can persist the real-time state of the browser, so that the next run can continue from where the previous run left off.

        model : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]

            Optional model configuration.

        extra_http_headers : typing.Optional[typing.Dict[str, typing.Optional[str]]]
            The extra HTTP headers for the requests in browser.

        publish_workflow : typing.Optional[bool]
            Whether to publish this task as a reusable workflow. Only available for skyvern-2.0.

        include_action_history_in_verification : typing.Optional[bool]
            Whether to include action history when verifying that the task is complete

        max_screenshot_scrolls : typing.Optional[int]
            The maximum number of scrolls for the post action screenshot. When it's None or 0, it takes the current viewpoint screenshot.

        browser_address : typing.Optional[str]
            The CDP address for the task.

        run_with : typing.Optional[str]
            Whether to run the task with agent or code.

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

        Returns
        -------
        HttpResponse[TaskRunResponse]
            Successfully run task
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/run/tasks",
            method="POST",
            json={
                "prompt": prompt,
                "url": url,
                "engine": engine,
                "title": title,
                "proxy_location": convert_and_respect_annotation_metadata(
                    object_=proxy_location, annotation=TaskRunRequestProxyLocation, direction="write"
                ),
                "data_extraction_schema": convert_and_respect_annotation_metadata(
                    object_=data_extraction_schema, annotation=TaskRunRequestDataExtractionSchema, direction="write"
                ),
                "error_code_mapping": error_code_mapping,
                "max_steps": max_steps,
                "webhook_url": webhook_url,
                "totp_identifier": totp_identifier,
                "totp_url": totp_url,
                "browser_session_id": browser_session_id,
                "model": model,
                "extra_http_headers": extra_http_headers,
                "publish_workflow": publish_workflow,
                "include_action_history_in_verification": include_action_history_in_verification,
                "max_screenshot_scrolls": max_screenshot_scrolls,
                "browser_address": browser_address,
                "run_with": run_with,
            },
            headers={
                "content-type": "application/json",
                "x-user-agent": str(user_agent) if user_agent is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TaskRunResponse,
                    parse_obj_as(
                        type_=TaskRunResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 run_workflow(
        self,
        *,
        workflow_id: str,
        template: typing.Optional[bool] = None,
        max_steps_override: typing.Optional[int] = None,
        user_agent: typing.Optional[str] = None,
        parameters: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT,
        title: typing.Optional[str] = OMIT,
        proxy_location: typing.Optional[WorkflowRunRequestProxyLocation] = OMIT,
        webhook_url: typing.Optional[str] = OMIT,
        totp_url: typing.Optional[str] = OMIT,
        totp_identifier: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        browser_profile_id: typing.Optional[str] = OMIT,
        max_screenshot_scrolls: typing.Optional[int] = OMIT,
        extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        ai_fallback: typing.Optional[bool] = OMIT,
        run_with: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[WorkflowRunResponse]:
        """
        Run a workflow

        Parameters
        ----------
        workflow_id : str
            ID of the workflow to run. Workflow ID starts with `wpid_`.

        template : typing.Optional[bool]

        max_steps_override : typing.Optional[int]

        user_agent : typing.Optional[str]

        parameters : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]
            Parameters to pass to the workflow

        title : typing.Optional[str]
            The title for this workflow run

        proxy_location : typing.Optional[WorkflowRunRequestProxyLocation]

            Geographic Proxy location to route the browser traffic through. This is only available in Skyvern Cloud.

            Available geotargeting options:
            - RESIDENTIAL: the default value. Skyvern Cloud uses a random US residential proxy.
            - RESIDENTIAL_ES: Spain
            - RESIDENTIAL_IE: Ireland
            - RESIDENTIAL_GB: United Kingdom
            - RESIDENTIAL_IN: India
            - RESIDENTIAL_JP: Japan
            - RESIDENTIAL_FR: France
            - RESIDENTIAL_DE: Germany
            - RESIDENTIAL_NZ: New Zealand
            - RESIDENTIAL_PH: Philippines
            - RESIDENTIAL_ZA: South Africa
            - RESIDENTIAL_AR: Argentina
            - RESIDENTIAL_AU: Australia
            - RESIDENTIAL_ISP: ISP proxy
            - US-CA: California (deprecated, routes through RESIDENTIAL_ISP)
            - US-NY: New York (deprecated, routes through RESIDENTIAL_ISP)
            - US-TX: Texas (deprecated, routes through RESIDENTIAL_ISP)
            - US-FL: Florida (deprecated, routes through RESIDENTIAL_ISP)
            - US-WA: Washington (deprecated, routes through RESIDENTIAL_ISP)
            - NONE: No proxy
             Can also be a GeoTarget object for granular city/state targeting: {"country": "US", "subdivision": "CA", "city": "San Francisco"}

        webhook_url : typing.Optional[str]
            URL to send workflow status updates to after a run is finished. Refer to https://www.skyvern.com/docs/running-tasks/webhooks-faq for webhook questions.

        totp_url : typing.Optional[str]

            URL that serves TOTP/2FA/MFA codes for Skyvern to use during the workflow run. Refer to https://www.skyvern.com/docs/credentials/totp#option-2-get-code-from-your-endpoint for more details.

        totp_identifier : typing.Optional[str]

            Identifier for the TOTP/2FA/MFA code when the code is pushed to Skyvern. Refer to https://www.skyvern.com/docs/credentials/totp#option-3-push-code-to-skyvern for more details.

        browser_session_id : typing.Optional[str]
            ID of a Skyvern browser session to reuse, having it continue from the current screen state

        browser_profile_id : typing.Optional[str]
            ID of a browser profile to reuse for this workflow run

        max_screenshot_scrolls : typing.Optional[int]
            The maximum number of scrolls for the post action screenshot. When it's None or 0, it takes the current viewpoint screenshot.

        extra_http_headers : typing.Optional[typing.Dict[str, typing.Optional[str]]]
            The extra HTTP headers for the requests in browser.

        browser_address : typing.Optional[str]
            The CDP address for the workflow run.

        ai_fallback : typing.Optional[bool]
            Whether to fallback to AI if the workflow run fails.

        run_with : typing.Optional[str]
            Whether to run the workflow with agent or code.

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

        Returns
        -------
        HttpResponse[WorkflowRunResponse]
            Successfully run workflow
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/run/workflows",
            method="POST",
            params={
                "template": template,
            },
            json={
                "workflow_id": workflow_id,
                "parameters": parameters,
                "title": title,
                "proxy_location": convert_and_respect_annotation_metadata(
                    object_=proxy_location, annotation=WorkflowRunRequestProxyLocation, direction="write"
                ),
                "webhook_url": webhook_url,
                "totp_url": totp_url,
                "totp_identifier": totp_identifier,
                "browser_session_id": browser_session_id,
                "browser_profile_id": browser_profile_id,
                "max_screenshot_scrolls": max_screenshot_scrolls,
                "extra_http_headers": extra_http_headers,
                "browser_address": browser_address,
                "ai_fallback": ai_fallback,
                "run_with": run_with,
            },
            headers={
                "content-type": "application/json",
                "x-max-steps-override": str(max_steps_override) if max_steps_override is not None else None,
                "x-user-agent": str(user_agent) if user_agent is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    WorkflowRunResponse,
                    parse_obj_as(
                        type_=WorkflowRunResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_run(
        self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[GetRunResponse]:
        """
        Get run information (task run, workflow run)

        Parameters
        ----------
        run_id : str
            The id of the task run or the workflow run.

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

        Returns
        -------
        HttpResponse[GetRunResponse]
            Successfully got run
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    GetRunResponse,
                    parse_obj_as(
                        type_=GetRunResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 cancel_run(
        self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Optional[typing.Any]]:
        """
        Cancel a run (task or workflow)

        Parameters
        ----------
        run_id : str
            The id of the task run or the workflow run to cancel.

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

        Returns
        -------
        HttpResponse[typing.Optional[typing.Any]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}/cancel",
            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.Optional[typing.Any],
                    parse_obj_as(
                        type_=typing.Optional[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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflows(
        self,
        *,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        only_saved_tasks: typing.Optional[bool] = None,
        only_workflows: typing.Optional[bool] = None,
        only_templates: typing.Optional[bool] = None,
        search_key: typing.Optional[str] = None,
        title: typing.Optional[str] = None,
        folder_id: typing.Optional[str] = None,
        status: typing.Optional[typing.Union[WorkflowStatus, typing.Sequence[WorkflowStatus]]] = None,
        template: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.List[Workflow]]:
        """
        Get all workflows with the latest version for the organization.

        Search semantics:
        - If `search_key` is provided, its value is used as a unified search term for
          `workflows.title`, `folders.title`, and workflow parameter metadata (key, description, and default_value for
          `WorkflowParameterModel`).
        - Falls back to deprecated `title` (title-only search) if `search_key` is not provided.
        - Parameter metadata search excludes soft-deleted parameter rows across all parameter tables.

        Parameters
        ----------
        page : typing.Optional[int]

        page_size : typing.Optional[int]

        only_saved_tasks : typing.Optional[bool]

        only_workflows : typing.Optional[bool]

        only_templates : typing.Optional[bool]

        search_key : typing.Optional[str]
            Case-insensitive substring search across: workflow title, folder name, and parameter metadata (key, description, default_value). A workflow is returned if any of these fields match. Soft-deleted parameter definitions are excluded. Takes precedence over the deprecated `title` parameter.

        title : typing.Optional[str]
            Deprecated: use search_key instead. Falls back to title-only search if search_key is not provided.

        folder_id : typing.Optional[str]
            Filter workflows by folder ID

        status : typing.Optional[typing.Union[WorkflowStatus, typing.Sequence[WorkflowStatus]]]

        template : typing.Optional[bool]

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

        Returns
        -------
        HttpResponse[typing.List[Workflow]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/workflows",
            method="GET",
            params={
                "page": page,
                "page_size": page_size,
                "only_saved_tasks": only_saved_tasks,
                "only_workflows": only_workflows,
                "only_templates": only_templates,
                "search_key": search_key,
                "title": title,
                "folder_id": folder_id,
                "status": status,
                "template": template,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[Workflow],
                    parse_obj_as(
                        type_=typing.List[Workflow],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_workflow(
        self,
        *,
        folder_id: typing.Optional[str] = None,
        json_definition: typing.Optional[WorkflowCreateYamlRequest] = OMIT,
        yaml_definition: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[Workflow]:
        """
        Create a new workflow

        Parameters
        ----------
        folder_id : typing.Optional[str]
            Optional folder ID to assign the workflow to

        json_definition : typing.Optional[WorkflowCreateYamlRequest]
            Workflow definition in JSON format

        yaml_definition : typing.Optional[str]
            Workflow definition in YAML format

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

        Returns
        -------
        HttpResponse[Workflow]
            Successfully created workflow
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/workflows",
            method="POST",
            params={
                "folder_id": folder_id,
            },
            json={
                "json_definition": convert_and_respect_annotation_metadata(
                    object_=json_definition, annotation=WorkflowCreateYamlRequest, direction="write"
                ),
                "yaml_definition": yaml_definition,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Workflow,
                    parse_obj_as(
                        type_=Workflow,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow(
        self,
        workflow_id: str,
        *,
        json_definition: typing.Optional[WorkflowCreateYamlRequest] = OMIT,
        yaml_definition: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[Workflow]:
        """
        Update a workflow

        Parameters
        ----------
        workflow_id : str
            The ID of the workflow to update. Workflow ID starts with `wpid_`.

        json_definition : typing.Optional[WorkflowCreateYamlRequest]
            Workflow definition in JSON format

        yaml_definition : typing.Optional[str]
            Workflow definition in YAML format

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

        Returns
        -------
        HttpResponse[Workflow]
            Successfully updated workflow
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/workflows/{jsonable_encoder(workflow_id)}",
            method="POST",
            json={
                "json_definition": convert_and_respect_annotation_metadata(
                    object_=json_definition, annotation=WorkflowCreateYamlRequest, direction="write"
                ),
                "yaml_definition": yaml_definition,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Workflow,
                    parse_obj_as(
                        type_=Workflow,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow(
        self, workflow_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Optional[typing.Any]]:
        """
        Delete a workflow

        Parameters
        ----------
        workflow_id : str
            The ID of the workflow to delete. Workflow ID starts with `wpid_`.

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

        Returns
        -------
        HttpResponse[typing.Optional[typing.Any]]
            Successfully deleted workflow
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/workflows/{jsonable_encoder(workflow_id)}/delete",
            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.Optional[typing.Any],
                    parse_obj_as(
                        type_=typing.Optional[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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_artifact(
        self, artifact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[Artifact]:
        """
        Get an artifact

        Parameters
        ----------
        artifact_id : str

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

        Returns
        -------
        HttpResponse[Artifact]
            Successfully retrieved artifact
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/artifacts/{jsonable_encoder(artifact_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Artifact,
                    parse_obj_as(
                        type_=Artifact,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_run_artifacts(
        self,
        run_id: str,
        *,
        artifact_type: typing.Optional[typing.Union[ArtifactType, typing.Sequence[ArtifactType]]] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.List[Artifact]]:
        """
        Get artifacts for a run

        Parameters
        ----------
        run_id : str
            The id of the task run or the workflow run.

        artifact_type : typing.Optional[typing.Union[ArtifactType, typing.Sequence[ArtifactType]]]

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

        Returns
        -------
        HttpResponse[typing.List[Artifact]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}/artifacts",
            method="GET",
            params={
                "artifact_type": artifact_type,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[Artifact],
                    parse_obj_as(
                        type_=typing.List[Artifact],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 retry_run_webhook(
        self,
        run_id: str,
        *,
        request: typing.Optional[RetryRunWebhookRequest] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.Optional[typing.Any]]:
        """
        Retry sending the webhook for a run

        Parameters
        ----------
        run_id : str
            The id of the task run or the workflow run.

        request : typing.Optional[RetryRunWebhookRequest]

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

        Returns
        -------
        HttpResponse[typing.Optional[typing.Any]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}/retry_webhook",
            method="POST",
            json=convert_and_respect_annotation_metadata(
                object_=request, annotation=RetryRunWebhookRequest, direction="write"
            ),
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        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.Optional[typing.Any],
                    parse_obj_as(
                        type_=typing.Optional[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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_run_timeline(
        self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.List[WorkflowRunTimeline]]:
        """
        Get timeline for a run (workflow run or task_v2 run)

        Parameters
        ----------
        run_id : str
            The id of the workflow run or task_v2 run.

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

        Returns
        -------
        HttpResponse[typing.List[WorkflowRunTimeline]]
            Successfully retrieved run timeline
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}/timeline",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[WorkflowRunTimeline],
                    parse_obj_as(
                        type_=typing.List[WorkflowRunTimeline],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow_runs(
        self,
        *,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        status: typing.Optional[typing.Union[WorkflowRunStatus, typing.Sequence[WorkflowRunStatus]]] = None,
        search_key: typing.Optional[str] = None,
        error_code: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.List[WorkflowRun]]:
        """
        List workflow runs across all workflows for the current organization.

        Results are paginated and can be filtered by **status**, **search_key**, and **error_code**. All filters are combined with **AND** logic — a run must match every supplied filter to be returned.

        ### search_key

        A case-insensitive substring search that matches against **any** of the following fields:

        | Searched field | Description |
        |---|---|
        | `workflow_run_id` | The unique run identifier (e.g. `wr_123…`) |
        | Parameter **key** | The `key` of any workflow parameter definition associated with the run |
        | Parameter **description** | The `description` of any workflow parameter definition |
        | Run parameter **value** | The actual value supplied for any parameter when the run was created |
        | `extra_http_headers` | Extra HTTP headers attached to the run (searched as raw JSON text) |

        Soft-deleted parameter definitions are excluded from key/description matching. A run is returned if **any** of the fields above contain the search term.

        ### error_code

        An **exact-match** filter against the `error_code` field inside each task's `errors` JSON array. A run matches if **any** of its tasks contains an error object with a matching `error_code` value. Error codes are user-defined strings set during workflow execution (e.g. `INVALID_CREDENTIALS`, `LOGIN_FAILED`, `CAPTCHA_DETECTED`).

        ### Combining filters

        All query parameters use AND logic:
        - `?status=failed` — only failed runs
        - `?status=failed&error_code=LOGIN_FAILED` — failed runs **and** have a LOGIN_FAILED error
        - `?status=failed&error_code=LOGIN_FAILED&search_key=prod_credential` — all three conditions must match

        Parameters
        ----------
        page : typing.Optional[int]
            Page number for pagination.

        page_size : typing.Optional[int]
            Number of runs to return per page.

        status : typing.Optional[typing.Union[WorkflowRunStatus, typing.Sequence[WorkflowRunStatus]]]
            Filter by one or more run statuses.

        search_key : typing.Optional[str]
            Case-insensitive substring search across: workflow run ID, parameter key, parameter description, run parameter value, and extra HTTP headers. A run is returned if any of these fields match. Soft-deleted parameter definitions are excluded from key/description matching.

        error_code : typing.Optional[str]
            Exact-match filter on the error_code field inside each task's errors JSON array. A run matches if any of its tasks contains an error with a matching error_code. Error codes are user-defined strings set during workflow execution.

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

        Returns
        -------
        HttpResponse[typing.List[WorkflowRun]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/workflows/runs",
            method="GET",
            params={
                "page": page,
                "page_size": page_size,
                "status": status,
                "search_key": search_key,
                "error_code": error_code,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[WorkflowRun],
                    parse_obj_as(
                        type_=typing.List[WorkflowRun],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow(
        self,
        workflow_permanent_id: str,
        *,
        version: typing.Optional[int] = None,
        template: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[Workflow]:
        """
        Parameters
        ----------
        workflow_permanent_id : str

        version : typing.Optional[int]

        template : typing.Optional[bool]

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

        Returns
        -------
        HttpResponse[Workflow]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/workflows/{jsonable_encoder(workflow_permanent_id)}",
            method="GET",
            params={
                "version": version,
                "template": template,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Workflow,
                    parse_obj_as(
                        type_=Workflow,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow_versions(
        self,
        workflow_permanent_id: str,
        *,
        template: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.List[Workflow]]:
        """
        Get all versions of a workflow by its permanent ID.

        Parameters
        ----------
        workflow_permanent_id : str

        template : typing.Optional[bool]

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

        Returns
        -------
        HttpResponse[typing.List[Workflow]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/workflows/{jsonable_encoder(workflow_permanent_id)}/versions",
            method="GET",
            params={
                "template": template,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[Workflow],
                    parse_obj_as(
                        type_=typing.List[Workflow],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 upload_file(
        self, *, file: core.File, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[UploadFileResponse]:
        """
        Parameters
        ----------
        file : core.File
            See core.File for more documentation

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

        Returns
        -------
        HttpResponse[UploadFileResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/upload_file",
            method="POST",
            data={},
            files={
                "file": file,
            },
            request_options=request_options,
            omit=OMIT,
            force_multipart=True,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    UploadFileResponse,
                    parse_obj_as(
                        type_=UploadFileResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_profiles(
        self, *, include_deleted: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.List[BrowserProfile]]:
        """
        Get all browser profiles for the organization

        Parameters
        ----------
        include_deleted : typing.Optional[bool]
            Include deleted browser profiles

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

        Returns
        -------
        HttpResponse[typing.List[BrowserProfile]]
            Successfully retrieved browser profiles
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/browser_profiles",
            method="GET",
            params={
                "include_deleted": include_deleted,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[BrowserProfile],
                    parse_obj_as(
                        type_=typing.List[BrowserProfile],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_browser_profile(
        self,
        *,
        name: str,
        description: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        workflow_run_id: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[BrowserProfile]:
        """
        Create a browser profile from a persistent browser session or workflow run.

        Parameters
        ----------
        name : str
            Name for the browser profile

        description : typing.Optional[str]
            Optional profile description

        browser_session_id : typing.Optional[str]
            Persistent browser session to convert into a profile

        workflow_run_id : typing.Optional[str]
            Workflow run whose persisted session should be captured

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

        Returns
        -------
        HttpResponse[BrowserProfile]
            Successfully created browser profile
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/browser_profiles",
            method="POST",
            json={
                "name": name,
                "description": description,
                "browser_session_id": browser_session_id,
                "workflow_run_id": workflow_run_id,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BrowserProfile,
                    parse_obj_as(
                        type_=BrowserProfile,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 409:
                raise ConflictError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_profile(
        self, profile_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[BrowserProfile]:
        """
        Get a specific browser profile by ID

        Parameters
        ----------
        profile_id : str
            The ID of the browser profile. browser_profile_id starts with `bp_`

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

        Returns
        -------
        HttpResponse[BrowserProfile]
            Successfully retrieved browser profile
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/browser_profiles/{jsonable_encoder(profile_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BrowserProfile,
                    parse_obj_as(
                        type_=BrowserProfile,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_profile(
        self, profile_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[None]:
        """
        Delete a browser profile (soft delete)

        Parameters
        ----------
        profile_id : str
            The ID of the browser profile to delete. browser_profile_id starts with `bp_`

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

        Returns
        -------
        HttpResponse[None]
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/browser_profiles/{jsonable_encoder(profile_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return HttpResponse(response=_response, data=None)
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_sessions(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.List[BrowserSessionResponse]]:
        """
        Get all active browser sessions for the organization

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

        Returns
        -------
        HttpResponse[typing.List[BrowserSessionResponse]]
            Successfully retrieved all active browser sessions
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/browser_sessions",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[BrowserSessionResponse],
                    parse_obj_as(
                        type_=typing.List[BrowserSessionResponse],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_browser_session(
        self,
        *,
        timeout: typing.Optional[int] = OMIT,
        proxy_location: typing.Optional[ProxyLocation] = OMIT,
        extensions: typing.Optional[typing.Sequence[Extensions]] = OMIT,
        browser_type: typing.Optional[PersistentBrowserType] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[BrowserSessionResponse]:
        """
        Create a browser session that persists across multiple runs

        Parameters
        ----------
        timeout : typing.Optional[int]
            Timeout in minutes for the session. Timeout is applied after the session is started. Must be between 5 and 1440. Defaults to 60.

        proxy_location : typing.Optional[ProxyLocation]

            Geographic Proxy location to route the browser traffic through. This is only available in Skyvern Cloud.

            Available geotargeting options:
            - RESIDENTIAL: the default value. Skyvern Cloud uses a random US residential proxy.
            - RESIDENTIAL_ES: Spain
            - RESIDENTIAL_IE: Ireland
            - RESIDENTIAL_GB: United Kingdom
            - RESIDENTIAL_IN: India
            - RESIDENTIAL_JP: Japan
            - RESIDENTIAL_FR: France
            - RESIDENTIAL_DE: Germany
            - RESIDENTIAL_NZ: New Zealand
            - RESIDENTIAL_PH: Philippines
            - RESIDENTIAL_ZA: South Africa
            - RESIDENTIAL_AR: Argentina
            - RESIDENTIAL_AU: Australia
            - RESIDENTIAL_ISP: ISP proxy
            - US-CA: California (deprecated, routes through RESIDENTIAL_ISP)
            - US-NY: New York (deprecated, routes through RESIDENTIAL_ISP)
            - US-TX: Texas (deprecated, routes through RESIDENTIAL_ISP)
            - US-FL: Florida (deprecated, routes through RESIDENTIAL_ISP)
            - US-WA: Washington (deprecated, routes through RESIDENTIAL_ISP)
            - NONE: No proxy

        extensions : typing.Optional[typing.Sequence[Extensions]]
            A list of extensions to install in the browser session.

        browser_type : typing.Optional[PersistentBrowserType]
            The type of browser to use for the session.

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

        Returns
        -------
        HttpResponse[BrowserSessionResponse]
            Successfully created browser session
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/browser_sessions",
            method="POST",
            json={
                "timeout": timeout,
                "proxy_location": proxy_location,
                "extensions": extensions,
                "browser_type": browser_type,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BrowserSessionResponse,
                    parse_obj_as(
                        type_=BrowserSessionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 close_browser_session(
        self, browser_session_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Optional[typing.Any]]:
        """
        Close a session. Once closed, the session cannot be used again.

        Parameters
        ----------
        browser_session_id : str
            The ID of the browser session to close. completed_at will be set when the browser session is closed. browser_session_id starts with `pbs_`

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

        Returns
        -------
        HttpResponse[typing.Optional[typing.Any]]
            Successfully closed browser session
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/browser_sessions/{jsonable_encoder(browser_session_id)}/close",
            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.Optional[typing.Any],
                    parse_obj_as(
                        type_=typing.Optional[typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_session(
        self, browser_session_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[BrowserSessionResponse]:
        """
        Get details about a specific browser session, including the browser address for cdp connection.

        Parameters
        ----------
        browser_session_id : str
            The ID of the browser session. browser_session_id starts with `pbs_`

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

        Returns
        -------
        HttpResponse[BrowserSessionResponse]
            Successfully retrieved browser session details
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/browser_sessions/{jsonable_encoder(browser_session_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BrowserSessionResponse,
                    parse_obj_as(
                        type_=BrowserSessionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 send_totp_code(
        self,
        *,
        totp_identifier: str,
        content: str,
        task_id: typing.Optional[str] = OMIT,
        workflow_id: typing.Optional[str] = OMIT,
        workflow_run_id: typing.Optional[str] = OMIT,
        source: typing.Optional[str] = OMIT,
        expired_at: typing.Optional[dt.datetime] = OMIT,
        type: typing.Optional[OtpType] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[TotpCode]:
        """
        Forward a TOTP (2FA, MFA) email or sms message containing the code to Skyvern. This endpoint stores the code in database so that Skyvern can use it while running tasks/workflows.

        Parameters
        ----------
        totp_identifier : str
            The identifier of the TOTP code. It can be the email address, phone number, or the identifier of the user.

        content : str
            The content of the TOTP code. It can be the email content that contains the TOTP code, or the sms message that contains the TOTP code. Skyvern will automatically extract the TOTP code from the content.

        task_id : typing.Optional[str]
            The task_id the totp code is for. It can be the task_id of the task that the TOTP code is for.

        workflow_id : typing.Optional[str]
            The workflow ID the TOTP code is for. It can be the workflow ID of the workflow that the TOTP code is for.

        workflow_run_id : typing.Optional[str]
            The workflow run id that the TOTP code is for. It can be the workflow run id of the workflow run that the TOTP code is for.

        source : typing.Optional[str]
            An optional field. The source of the TOTP code. e.g. email, sms, etc.

        expired_at : typing.Optional[dt.datetime]
            The timestamp when the TOTP code expires

        type : typing.Optional[OtpType]
            Optional. If provided, forces extraction of this specific OTP type (totp or magic_link). Use this when the content contains multiple OTP types and you want to specify which one to extract.

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

        Returns
        -------
        HttpResponse[TotpCode]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/credentials/totp",
            method="POST",
            json={
                "totp_identifier": totp_identifier,
                "task_id": task_id,
                "workflow_id": workflow_id,
                "workflow_run_id": workflow_run_id,
                "source": source,
                "content": content,
                "expired_at": expired_at,
                "type": type,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TotpCode,
                    parse_obj_as(
                        type_=TotpCode,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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(
        self,
        *,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.List[CredentialResponse]]:
        """
        Retrieves a paginated list of credentials for the current organization

        Parameters
        ----------
        page : typing.Optional[int]
            Page number for pagination

        page_size : typing.Optional[int]
            Number of items per page

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

        Returns
        -------
        HttpResponse[typing.List[CredentialResponse]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/credentials",
            method="GET",
            params={
                "page": page,
                "page_size": page_size,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[CredentialResponse],
                    parse_obj_as(
                        type_=typing.List[CredentialResponse],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_credential(
        self,
        *,
        name: str,
        credential_type: SkyvernForgeSdkSchemasCredentialsCredentialType,
        credential: CreateCredentialRequestCredential,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[CredentialResponse]:
        """
        Creates a new credential for the current organization

        Parameters
        ----------
        name : str
            Name of the credential

        credential_type : SkyvernForgeSdkSchemasCredentialsCredentialType
            Type of credential to create

        credential : CreateCredentialRequestCredential
            The credential data to store

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

        Returns
        -------
        HttpResponse[CredentialResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/credentials",
            method="POST",
            json={
                "name": name,
                "credential_type": credential_type,
                "credential": convert_and_respect_annotation_metadata(
                    object_=credential, annotation=CreateCredentialRequestCredential, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialResponse,
                    parse_obj_as(
                        type_=CredentialResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_credential(
        self,
        credential_id: str,
        *,
        name: str,
        credential_type: SkyvernForgeSdkSchemasCredentialsCredentialType,
        credential: CreateCredentialRequestCredential,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[CredentialResponse]:
        """
        Overwrites the stored credential data (e.g. username/password) while keeping the same credential_id.

        Parameters
        ----------
        credential_id : str
            The unique identifier of the credential to update

        name : str
            Name of the credential

        credential_type : SkyvernForgeSdkSchemasCredentialsCredentialType
            Type of credential to create

        credential : CreateCredentialRequestCredential
            The credential data to store

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

        Returns
        -------
        HttpResponse[CredentialResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/credentials/{jsonable_encoder(credential_id)}/update",
            method="POST",
            json={
                "name": name,
                "credential_type": credential_type,
                "credential": convert_and_respect_annotation_metadata(
                    object_=credential, annotation=CreateCredentialRequestCredential, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialResponse,
                    parse_obj_as(
                        type_=CredentialResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_credential(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[None]:
        """
        Deletes a specific credential by its ID

        Parameters
        ----------
        credential_id : str
            The unique identifier of the credential to delete

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

        Returns
        -------
        HttpResponse[None]
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/credentials/{jsonable_encoder(credential_id)}/delete",
            method="POST",
            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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_credential(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[CredentialResponse]:
        """
        Retrieves a specific credential by its ID

        Parameters
        ----------
        credential_id : str
            The unique identifier of the credential

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

        Returns
        -------
        HttpResponse[CredentialResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/credentials/{jsonable_encoder(credential_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialResponse,
                    parse_obj_as(
                        type_=CredentialResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 login(
        self,
        *,
        credential_type: SkyvernSchemasRunBlocksCredentialType,
        url: typing.Optional[str] = OMIT,
        webhook_url: typing.Optional[str] = OMIT,
        proxy_location: typing.Optional[ProxyLocation] = OMIT,
        totp_identifier: typing.Optional[str] = OMIT,
        totp_url: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        browser_profile_id: typing.Optional[str] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        max_screenshot_scrolling_times: typing.Optional[int] = OMIT,
        prompt: typing.Optional[str] = OMIT,
        credential_id: typing.Optional[str] = OMIT,
        bitwarden_collection_id: typing.Optional[str] = OMIT,
        bitwarden_item_id: typing.Optional[str] = OMIT,
        onepassword_vault_id: typing.Optional[str] = OMIT,
        onepassword_item_id: typing.Optional[str] = OMIT,
        azure_vault_name: typing.Optional[str] = OMIT,
        azure_vault_username_key: typing.Optional[str] = OMIT,
        azure_vault_password_key: typing.Optional[str] = OMIT,
        azure_vault_totp_secret_key: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[WorkflowRunResponse]:
        """
        Log in to a website using either credential stored in Skyvern, Bitwarden, 1Password, or Azure Vault

        Parameters
        ----------
        credential_type : SkyvernSchemasRunBlocksCredentialType
            Where to get the credential from

        url : typing.Optional[str]
            Website URL

        webhook_url : typing.Optional[str]
            Webhook URL to send status updates

        proxy_location : typing.Optional[ProxyLocation]
            Proxy location to use

        totp_identifier : typing.Optional[str]
            Identifier for TOTP (Time-based One-Time Password) if required

        totp_url : typing.Optional[str]
            TOTP URL to fetch one-time passwords

        browser_session_id : typing.Optional[str]
            ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456`

        browser_profile_id : typing.Optional[str]
            ID of a browser profile to reuse for this run

        browser_address : typing.Optional[str]
            The CDP address for the task.

        extra_http_headers : typing.Optional[typing.Dict[str, typing.Optional[str]]]
            Additional HTTP headers to include in requests

        max_screenshot_scrolling_times : typing.Optional[int]
            Maximum number of times to scroll for screenshots

        prompt : typing.Optional[str]
            Login instructions. Skyvern has default prompt/instruction for login if this field is not provided.

        credential_id : typing.Optional[str]
            ID of the Skyvern credential to use for login.

        bitwarden_collection_id : typing.Optional[str]
            Bitwarden collection ID. You can find it in the Bitwarden collection URL. e.g. `https://vault.bitwarden.com/vaults/collection_id/items`

        bitwarden_item_id : typing.Optional[str]
            Bitwarden item ID

        onepassword_vault_id : typing.Optional[str]
            1Password vault ID

        onepassword_item_id : typing.Optional[str]
            1Password item ID

        azure_vault_name : typing.Optional[str]
            Azure Vault Name

        azure_vault_username_key : typing.Optional[str]
            Azure Vault username key

        azure_vault_password_key : typing.Optional[str]
            Azure Vault password key

        azure_vault_totp_secret_key : typing.Optional[str]
            Azure Vault TOTP secret key

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

        Returns
        -------
        HttpResponse[WorkflowRunResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/run/tasks/login",
            method="POST",
            json={
                "url": url,
                "webhook_url": webhook_url,
                "proxy_location": proxy_location,
                "totp_identifier": totp_identifier,
                "totp_url": totp_url,
                "browser_session_id": browser_session_id,
                "browser_profile_id": browser_profile_id,
                "browser_address": browser_address,
                "extra_http_headers": extra_http_headers,
                "max_screenshot_scrolling_times": max_screenshot_scrolling_times,
                "credential_type": credential_type,
                "prompt": prompt,
                "credential_id": credential_id,
                "bitwarden_collection_id": bitwarden_collection_id,
                "bitwarden_item_id": bitwarden_item_id,
                "onepassword_vault_id": onepassword_vault_id,
                "onepassword_item_id": onepassword_item_id,
                "azure_vault_name": azure_vault_name,
                "azure_vault_username_key": azure_vault_username_key,
                "azure_vault_password_key": azure_vault_password_key,
                "azure_vault_totp_secret_key": azure_vault_totp_secret_key,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    WorkflowRunResponse,
                    parse_obj_as(
                        type_=WorkflowRunResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 download_files(
        self,
        *,
        navigation_goal: str,
        url: typing.Optional[str] = OMIT,
        webhook_url: typing.Optional[str] = OMIT,
        proxy_location: typing.Optional[ProxyLocation] = OMIT,
        totp_identifier: typing.Optional[str] = OMIT,
        totp_url: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        browser_profile_id: typing.Optional[str] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        max_screenshot_scrolling_times: typing.Optional[int] = OMIT,
        download_suffix: typing.Optional[str] = OMIT,
        download_timeout: typing.Optional[float] = OMIT,
        max_steps_per_run: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[WorkflowRunResponse]:
        """
        Download a file from a website by navigating and clicking download buttons

        Parameters
        ----------
        navigation_goal : str
            Instructions for navigating to and downloading the file

        url : typing.Optional[str]
            Website URL

        webhook_url : typing.Optional[str]
            Webhook URL to send status updates

        proxy_location : typing.Optional[ProxyLocation]
            Proxy location to use

        totp_identifier : typing.Optional[str]
            Identifier for TOTP (Time-based One-Time Password) if required

        totp_url : typing.Optional[str]
            TOTP URL to fetch one-time passwords

        browser_session_id : typing.Optional[str]
            ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456`

        browser_profile_id : typing.Optional[str]
            ID of a browser profile to reuse for this run

        browser_address : typing.Optional[str]
            The CDP address for the task.

        extra_http_headers : typing.Optional[typing.Dict[str, typing.Optional[str]]]
            Additional HTTP headers to include in requests

        max_screenshot_scrolling_times : typing.Optional[int]
            Maximum number of times to scroll for screenshots

        download_suffix : typing.Optional[str]
            Suffix or complete filename for the downloaded file

        download_timeout : typing.Optional[float]
            Timeout in seconds for the download operation

        max_steps_per_run : typing.Optional[int]
            Maximum number of steps to execute

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

        Returns
        -------
        HttpResponse[WorkflowRunResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/run/tasks/download_files",
            method="POST",
            json={
                "url": url,
                "webhook_url": webhook_url,
                "proxy_location": proxy_location,
                "totp_identifier": totp_identifier,
                "totp_url": totp_url,
                "browser_session_id": browser_session_id,
                "browser_profile_id": browser_profile_id,
                "browser_address": browser_address,
                "extra_http_headers": extra_http_headers,
                "max_screenshot_scrolling_times": max_screenshot_scrolling_times,
                "navigation_goal": navigation_goal,
                "download_suffix": download_suffix,
                "download_timeout": download_timeout,
                "max_steps_per_run": max_steps_per_run,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    WorkflowRunResponse,
                    parse_obj_as(
                        type_=WorkflowRunResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_scripts(
        self,
        *,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[typing.List[Script]]:
        """
        Retrieves a paginated list of scripts for the current organization

        Parameters
        ----------
        page : typing.Optional[int]
            Page number for pagination

        page_size : typing.Optional[int]
            Number of items per page

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

        Returns
        -------
        HttpResponse[typing.List[Script]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/scripts",
            method="GET",
            params={
                "page": page,
                "page_size": page_size,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[Script],
                    parse_obj_as(
                        type_=typing.List[Script],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_script(
        self,
        *,
        workflow_id: typing.Optional[str] = OMIT,
        run_id: typing.Optional[str] = OMIT,
        files: typing.Optional[typing.Sequence[ScriptFileCreate]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[CreateScriptResponse]:
        """
        Create a new script with optional files and metadata

        Parameters
        ----------
        workflow_id : typing.Optional[str]
            Associated workflow ID

        run_id : typing.Optional[str]
            Associated run ID

        files : typing.Optional[typing.Sequence[ScriptFileCreate]]
            Array of files to include in the script

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

        Returns
        -------
        HttpResponse[CreateScriptResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/scripts",
            method="POST",
            json={
                "workflow_id": workflow_id,
                "run_id": run_id,
                "files": convert_and_respect_annotation_metadata(
                    object_=files, annotation=typing.Sequence[ScriptFileCreate], direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CreateScriptResponse,
                    parse_obj_as(
                        type_=CreateScriptResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_script(
        self, script_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[Script]:
        """
        Retrieves a specific script by its ID

        Parameters
        ----------
        script_id : str
            The unique identifier of the script

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

        Returns
        -------
        HttpResponse[Script]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/scripts/{jsonable_encoder(script_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Script,
                    parse_obj_as(
                        type_=Script,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 deploy_script(
        self,
        script_id: str,
        *,
        files: typing.Sequence[ScriptFileCreate],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[CreateScriptResponse]:
        """
        Deploy a script with updated files, creating a new version

        Parameters
        ----------
        script_id : str
            The unique identifier of the script

        files : typing.Sequence[ScriptFileCreate]
            Array of files to include in the script

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

        Returns
        -------
        HttpResponse[CreateScriptResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/scripts/{jsonable_encoder(script_id)}/deploy",
            method="POST",
            json={
                "files": convert_and_respect_annotation_metadata(
                    object_=files, annotation=typing.Sequence[ScriptFileCreate], direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CreateScriptResponse,
                    parse_obj_as(
                        type_=CreateScriptResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 clear_workflow_cache(
        self, workflow_permanent_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ClearCacheResponse]:
        """
        Clear all cached scripts for a specific workflow. This will trigger script regeneration on subsequent runs.

        Parameters
        ----------
        workflow_permanent_id : str
            The workflow permanent ID to clear cache for

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

        Returns
        -------
        HttpResponse[ClearCacheResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/scripts/{jsonable_encoder(workflow_permanent_id)}/cache",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ClearCacheResponse,
                    parse_obj_as(
                        type_=ClearCacheResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 run_sdk_action(
        self,
        *,
        url: str,
        action: RunSdkActionRequestAction,
        browser_session_id: typing.Optional[str] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        workflow_run_id: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[RunSdkActionResponse]:
        """
        Execute a single SDK action with the specified parameters

        Parameters
        ----------
        url : str
            The URL where the action should be executed

        action : RunSdkActionRequestAction
            The action to execute with its specific parameters

        browser_session_id : typing.Optional[str]
            The browser session ID

        browser_address : typing.Optional[str]
            The browser address

        workflow_run_id : typing.Optional[str]
            Optional workflow run ID to continue an existing workflow run

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

        Returns
        -------
        HttpResponse[RunSdkActionResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/sdk/run_action",
            method="POST",
            json={
                "url": url,
                "browser_session_id": browser_session_id,
                "browser_address": browser_address,
                "workflow_run_id": workflow_run_id,
                "action": convert_and_respect_annotation_metadata(
                    object_=action, annotation=RunSdkActionRequestAction, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    RunSdkActionResponse,
                    parse_obj_as(
                        type_=RunSdkActionResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_checkout_session_api_v1billing_checkout_post(
        self, *, tier: PlanTier, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[CheckoutSessionResponse]:
        """
        Create a Stripe Checkout Session for subscribing to a tier.

        Frontend should redirect the user to the returned URL.
        After successful checkout, Stripe will send a webhook that we handle
        to store the subscription and initialize billing state.

        Returns 400 if org already has an active subscription (use portal instead).

        Parameters
        ----------
        tier : PlanTier

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

        Returns
        -------
        HttpResponse[CheckoutSessionResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "api/v1/billing/checkout",
            method="POST",
            json={
                "tier": tier,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CheckoutSessionResponse,
                    parse_obj_as(
                        type_=CheckoutSessionResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_portal_session_api_v1billing_portal_post(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[PortalSessionResponse]:
        """
        Create a Stripe Customer Portal session for managing subscription.

        Frontend should redirect the user to the returned URL.
        The portal allows users to:
        - Update payment methods
        - Upgrade/downgrade plans
        - Cancel subscription
        - View invoices

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

        Returns
        -------
        HttpResponse[PortalSessionResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "api/v1/billing/portal",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    PortalSessionResponse,
                    parse_obj_as(
                        type_=PortalSessionResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_organization_billing_api_v1billing_state_get(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Optional[BillingStateResponse]]:
        """
        Parameters
        ----------
        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[typing.Optional[BillingStateResponse]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "api/v1/billing/state",
            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.Optional[BillingStateResponse],
                    parse_obj_as(
                        type_=typing.Optional[BillingStateResponse],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 change_tier_api_v1billing_change_tier_post(
        self, *, tier: PlanTier, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ChangeTierResponse]:
        """
        Redirect to Stripe Portal for tier changes.
        Portal handles proration based on configured settings:
        - Upgrades: Immediate proration charge
        - Downgrades: Apply at end of billing period

        Parameters
        ----------
        tier : PlanTier

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

        Returns
        -------
        HttpResponse[ChangeTierResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "api/v1/billing/change-tier",
            method="POST",
            json={
                "tier": tier,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ChangeTierResponse,
                    parse_obj_as(
                        type_=ChangeTierResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 AsyncRawSkyvern:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def run_task(
        self,
        *,
        prompt: str,
        user_agent: typing.Optional[str] = None,
        url: typing.Optional[str] = OMIT,
        engine: typing.Optional[RunEngine] = OMIT,
        title: typing.Optional[str] = OMIT,
        proxy_location: typing.Optional[TaskRunRequestProxyLocation] = OMIT,
        data_extraction_schema: typing.Optional[TaskRunRequestDataExtractionSchema] = OMIT,
        error_code_mapping: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        max_steps: typing.Optional[int] = OMIT,
        webhook_url: typing.Optional[str] = OMIT,
        totp_identifier: typing.Optional[str] = OMIT,
        totp_url: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        model: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT,
        extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        publish_workflow: typing.Optional[bool] = OMIT,
        include_action_history_in_verification: typing.Optional[bool] = OMIT,
        max_screenshot_scrolls: typing.Optional[int] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        run_with: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[TaskRunResponse]:
        """
        Run a task

        Parameters
        ----------
        prompt : str

            The goal or task description for Skyvern to accomplish

        user_agent : typing.Optional[str]

        url : typing.Optional[str]

            The starting URL for the task. If not provided, Skyvern will attempt to determine an appropriate URL

        engine : typing.Optional[RunEngine]

            The engine that powers the agent task. The default value is `skyvern-2.0`, the latest Skyvern agent that performs pretty well with complex and multi-step tasks. `skyvern-1.0` is good for simple tasks like filling a form, or searching for information on Google. The `openai-cua` engine uses OpenAI's CUA model. The `anthropic-cua` uses Anthropic's Claude Sonnet 3.7 model with the computer use tool.

        title : typing.Optional[str]
            The title for the task

        proxy_location : typing.Optional[TaskRunRequestProxyLocation]

            Geographic Proxy location to route the browser traffic through. This is only available in Skyvern Cloud.

            Available geotargeting options:
            - RESIDENTIAL: the default value. Skyvern Cloud uses a random US residential proxy.
            - RESIDENTIAL_ES: Spain
            - RESIDENTIAL_IE: Ireland
            - RESIDENTIAL_GB: United Kingdom
            - RESIDENTIAL_IN: India
            - RESIDENTIAL_JP: Japan
            - RESIDENTIAL_FR: France
            - RESIDENTIAL_DE: Germany
            - RESIDENTIAL_NZ: New Zealand
            - RESIDENTIAL_PH: Philippines
            - RESIDENTIAL_ZA: South Africa
            - RESIDENTIAL_AR: Argentina
            - RESIDENTIAL_AU: Australia
            - RESIDENTIAL_ISP: ISP proxy
            - US-CA: California (deprecated, routes through RESIDENTIAL_ISP)
            - US-NY: New York (deprecated, routes through RESIDENTIAL_ISP)
            - US-TX: Texas (deprecated, routes through RESIDENTIAL_ISP)
            - US-FL: Florida (deprecated, routes through RESIDENTIAL_ISP)
            - US-WA: Washington (deprecated, routes through RESIDENTIAL_ISP)
            - NONE: No proxy
             Can also be a GeoTarget object for granular city/state targeting: {"country": "US", "subdivision": "CA", "city": "San Francisco"}

        data_extraction_schema : typing.Optional[TaskRunRequestDataExtractionSchema]

            The schema for data to be extracted from the webpage. If you're looking for consistent data schema being returned by the agent, it's highly recommended to use https://json-schema.org/.

        error_code_mapping : typing.Optional[typing.Dict[str, typing.Optional[str]]]

            Custom mapping of error codes to error messages if Skyvern encounters an error.

        max_steps : typing.Optional[int]

            Maximum number of steps the task can take. Task will fail if it exceeds this number. Cautions: you are charged per step so please set this number to a reasonable value. Contact sales@skyvern.com for custom pricing.

        webhook_url : typing.Optional[str]

            After a run is finished, send an update to this URL. Refer to https://www.skyvern.com/docs/running-tasks/webhooks-faq for more details.

        totp_identifier : typing.Optional[str]

            Identifier for the TOTP/2FA/MFA code when the code is pushed to Skyvern. Refer to https://www.skyvern.com/docs/credentials/totp#option-3-push-code-to-skyvern for more details.

        totp_url : typing.Optional[str]

            URL that serves TOTP/2FA/MFA codes for Skyvern to use during the workflow run. Refer to https://www.skyvern.com/docs/credentials/totp#option-2-get-code-from-your-endpoint for more details.

        browser_session_id : typing.Optional[str]

            Run the task or workflow in the specific Skyvern browser session. Having a browser session can persist the real-time state of the browser, so that the next run can continue from where the previous run left off.

        model : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]

            Optional model configuration.

        extra_http_headers : typing.Optional[typing.Dict[str, typing.Optional[str]]]
            The extra HTTP headers for the requests in browser.

        publish_workflow : typing.Optional[bool]
            Whether to publish this task as a reusable workflow. Only available for skyvern-2.0.

        include_action_history_in_verification : typing.Optional[bool]
            Whether to include action history when verifying that the task is complete

        max_screenshot_scrolls : typing.Optional[int]
            The maximum number of scrolls for the post action screenshot. When it's None or 0, it takes the current viewpoint screenshot.

        browser_address : typing.Optional[str]
            The CDP address for the task.

        run_with : typing.Optional[str]
            Whether to run the task with agent or code.

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

        Returns
        -------
        AsyncHttpResponse[TaskRunResponse]
            Successfully run task
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/run/tasks",
            method="POST",
            json={
                "prompt": prompt,
                "url": url,
                "engine": engine,
                "title": title,
                "proxy_location": convert_and_respect_annotation_metadata(
                    object_=proxy_location, annotation=TaskRunRequestProxyLocation, direction="write"
                ),
                "data_extraction_schema": convert_and_respect_annotation_metadata(
                    object_=data_extraction_schema, annotation=TaskRunRequestDataExtractionSchema, direction="write"
                ),
                "error_code_mapping": error_code_mapping,
                "max_steps": max_steps,
                "webhook_url": webhook_url,
                "totp_identifier": totp_identifier,
                "totp_url": totp_url,
                "browser_session_id": browser_session_id,
                "model": model,
                "extra_http_headers": extra_http_headers,
                "publish_workflow": publish_workflow,
                "include_action_history_in_verification": include_action_history_in_verification,
                "max_screenshot_scrolls": max_screenshot_scrolls,
                "browser_address": browser_address,
                "run_with": run_with,
            },
            headers={
                "content-type": "application/json",
                "x-user-agent": str(user_agent) if user_agent is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TaskRunResponse,
                    parse_obj_as(
                        type_=TaskRunResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 run_workflow(
        self,
        *,
        workflow_id: str,
        template: typing.Optional[bool] = None,
        max_steps_override: typing.Optional[int] = None,
        user_agent: typing.Optional[str] = None,
        parameters: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT,
        title: typing.Optional[str] = OMIT,
        proxy_location: typing.Optional[WorkflowRunRequestProxyLocation] = OMIT,
        webhook_url: typing.Optional[str] = OMIT,
        totp_url: typing.Optional[str] = OMIT,
        totp_identifier: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        browser_profile_id: typing.Optional[str] = OMIT,
        max_screenshot_scrolls: typing.Optional[int] = OMIT,
        extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        ai_fallback: typing.Optional[bool] = OMIT,
        run_with: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[WorkflowRunResponse]:
        """
        Run a workflow

        Parameters
        ----------
        workflow_id : str
            ID of the workflow to run. Workflow ID starts with `wpid_`.

        template : typing.Optional[bool]

        max_steps_override : typing.Optional[int]

        user_agent : typing.Optional[str]

        parameters : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]
            Parameters to pass to the workflow

        title : typing.Optional[str]
            The title for this workflow run

        proxy_location : typing.Optional[WorkflowRunRequestProxyLocation]

            Geographic Proxy location to route the browser traffic through. This is only available in Skyvern Cloud.

            Available geotargeting options:
            - RESIDENTIAL: the default value. Skyvern Cloud uses a random US residential proxy.
            - RESIDENTIAL_ES: Spain
            - RESIDENTIAL_IE: Ireland
            - RESIDENTIAL_GB: United Kingdom
            - RESIDENTIAL_IN: India
            - RESIDENTIAL_JP: Japan
            - RESIDENTIAL_FR: France
            - RESIDENTIAL_DE: Germany
            - RESIDENTIAL_NZ: New Zealand
            - RESIDENTIAL_PH: Philippines
            - RESIDENTIAL_ZA: South Africa
            - RESIDENTIAL_AR: Argentina
            - RESIDENTIAL_AU: Australia
            - RESIDENTIAL_ISP: ISP proxy
            - US-CA: California (deprecated, routes through RESIDENTIAL_ISP)
            - US-NY: New York (deprecated, routes through RESIDENTIAL_ISP)
            - US-TX: Texas (deprecated, routes through RESIDENTIAL_ISP)
            - US-FL: Florida (deprecated, routes through RESIDENTIAL_ISP)
            - US-WA: Washington (deprecated, routes through RESIDENTIAL_ISP)
            - NONE: No proxy
             Can also be a GeoTarget object for granular city/state targeting: {"country": "US", "subdivision": "CA", "city": "San Francisco"}

        webhook_url : typing.Optional[str]
            URL to send workflow status updates to after a run is finished. Refer to https://www.skyvern.com/docs/running-tasks/webhooks-faq for webhook questions.

        totp_url : typing.Optional[str]

            URL that serves TOTP/2FA/MFA codes for Skyvern to use during the workflow run. Refer to https://www.skyvern.com/docs/credentials/totp#option-2-get-code-from-your-endpoint for more details.

        totp_identifier : typing.Optional[str]

            Identifier for the TOTP/2FA/MFA code when the code is pushed to Skyvern. Refer to https://www.skyvern.com/docs/credentials/totp#option-3-push-code-to-skyvern for more details.

        browser_session_id : typing.Optional[str]
            ID of a Skyvern browser session to reuse, having it continue from the current screen state

        browser_profile_id : typing.Optional[str]
            ID of a browser profile to reuse for this workflow run

        max_screenshot_scrolls : typing.Optional[int]
            The maximum number of scrolls for the post action screenshot. When it's None or 0, it takes the current viewpoint screenshot.

        extra_http_headers : typing.Optional[typing.Dict[str, typing.Optional[str]]]
            The extra HTTP headers for the requests in browser.

        browser_address : typing.Optional[str]
            The CDP address for the workflow run.

        ai_fallback : typing.Optional[bool]
            Whether to fallback to AI if the workflow run fails.

        run_with : typing.Optional[str]
            Whether to run the workflow with agent or code.

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

        Returns
        -------
        AsyncHttpResponse[WorkflowRunResponse]
            Successfully run workflow
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/run/workflows",
            method="POST",
            params={
                "template": template,
            },
            json={
                "workflow_id": workflow_id,
                "parameters": parameters,
                "title": title,
                "proxy_location": convert_and_respect_annotation_metadata(
                    object_=proxy_location, annotation=WorkflowRunRequestProxyLocation, direction="write"
                ),
                "webhook_url": webhook_url,
                "totp_url": totp_url,
                "totp_identifier": totp_identifier,
                "browser_session_id": browser_session_id,
                "browser_profile_id": browser_profile_id,
                "max_screenshot_scrolls": max_screenshot_scrolls,
                "extra_http_headers": extra_http_headers,
                "browser_address": browser_address,
                "ai_fallback": ai_fallback,
                "run_with": run_with,
            },
            headers={
                "content-type": "application/json",
                "x-max-steps-override": str(max_steps_override) if max_steps_override is not None else None,
                "x-user-agent": str(user_agent) if user_agent is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    WorkflowRunResponse,
                    parse_obj_as(
                        type_=WorkflowRunResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_run(
        self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[GetRunResponse]:
        """
        Get run information (task run, workflow run)

        Parameters
        ----------
        run_id : str
            The id of the task run or the workflow run.

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

        Returns
        -------
        AsyncHttpResponse[GetRunResponse]
            Successfully got run
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    GetRunResponse,
                    parse_obj_as(
                        type_=GetRunResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 cancel_run(
        self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Optional[typing.Any]]:
        """
        Cancel a run (task or workflow)

        Parameters
        ----------
        run_id : str
            The id of the task run or the workflow run to cancel.

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

        Returns
        -------
        AsyncHttpResponse[typing.Optional[typing.Any]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}/cancel",
            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.Optional[typing.Any],
                    parse_obj_as(
                        type_=typing.Optional[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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflows(
        self,
        *,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        only_saved_tasks: typing.Optional[bool] = None,
        only_workflows: typing.Optional[bool] = None,
        only_templates: typing.Optional[bool] = None,
        search_key: typing.Optional[str] = None,
        title: typing.Optional[str] = None,
        folder_id: typing.Optional[str] = None,
        status: typing.Optional[typing.Union[WorkflowStatus, typing.Sequence[WorkflowStatus]]] = None,
        template: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.List[Workflow]]:
        """
        Get all workflows with the latest version for the organization.

        Search semantics:
        - If `search_key` is provided, its value is used as a unified search term for
          `workflows.title`, `folders.title`, and workflow parameter metadata (key, description, and default_value for
          `WorkflowParameterModel`).
        - Falls back to deprecated `title` (title-only search) if `search_key` is not provided.
        - Parameter metadata search excludes soft-deleted parameter rows across all parameter tables.

        Parameters
        ----------
        page : typing.Optional[int]

        page_size : typing.Optional[int]

        only_saved_tasks : typing.Optional[bool]

        only_workflows : typing.Optional[bool]

        only_templates : typing.Optional[bool]

        search_key : typing.Optional[str]
            Case-insensitive substring search across: workflow title, folder name, and parameter metadata (key, description, default_value). A workflow is returned if any of these fields match. Soft-deleted parameter definitions are excluded. Takes precedence over the deprecated `title` parameter.

        title : typing.Optional[str]
            Deprecated: use search_key instead. Falls back to title-only search if search_key is not provided.

        folder_id : typing.Optional[str]
            Filter workflows by folder ID

        status : typing.Optional[typing.Union[WorkflowStatus, typing.Sequence[WorkflowStatus]]]

        template : typing.Optional[bool]

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

        Returns
        -------
        AsyncHttpResponse[typing.List[Workflow]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/workflows",
            method="GET",
            params={
                "page": page,
                "page_size": page_size,
                "only_saved_tasks": only_saved_tasks,
                "only_workflows": only_workflows,
                "only_templates": only_templates,
                "search_key": search_key,
                "title": title,
                "folder_id": folder_id,
                "status": status,
                "template": template,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[Workflow],
                    parse_obj_as(
                        type_=typing.List[Workflow],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_workflow(
        self,
        *,
        folder_id: typing.Optional[str] = None,
        json_definition: typing.Optional[WorkflowCreateYamlRequest] = OMIT,
        yaml_definition: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[Workflow]:
        """
        Create a new workflow

        Parameters
        ----------
        folder_id : typing.Optional[str]
            Optional folder ID to assign the workflow to

        json_definition : typing.Optional[WorkflowCreateYamlRequest]
            Workflow definition in JSON format

        yaml_definition : typing.Optional[str]
            Workflow definition in YAML format

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

        Returns
        -------
        AsyncHttpResponse[Workflow]
            Successfully created workflow
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/workflows",
            method="POST",
            params={
                "folder_id": folder_id,
            },
            json={
                "json_definition": convert_and_respect_annotation_metadata(
                    object_=json_definition, annotation=WorkflowCreateYamlRequest, direction="write"
                ),
                "yaml_definition": yaml_definition,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Workflow,
                    parse_obj_as(
                        type_=Workflow,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow(
        self,
        workflow_id: str,
        *,
        json_definition: typing.Optional[WorkflowCreateYamlRequest] = OMIT,
        yaml_definition: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[Workflow]:
        """
        Update a workflow

        Parameters
        ----------
        workflow_id : str
            The ID of the workflow to update. Workflow ID starts with `wpid_`.

        json_definition : typing.Optional[WorkflowCreateYamlRequest]
            Workflow definition in JSON format

        yaml_definition : typing.Optional[str]
            Workflow definition in YAML format

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

        Returns
        -------
        AsyncHttpResponse[Workflow]
            Successfully updated workflow
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/workflows/{jsonable_encoder(workflow_id)}",
            method="POST",
            json={
                "json_definition": convert_and_respect_annotation_metadata(
                    object_=json_definition, annotation=WorkflowCreateYamlRequest, direction="write"
                ),
                "yaml_definition": yaml_definition,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Workflow,
                    parse_obj_as(
                        type_=Workflow,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow(
        self, workflow_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Optional[typing.Any]]:
        """
        Delete a workflow

        Parameters
        ----------
        workflow_id : str
            The ID of the workflow to delete. Workflow ID starts with `wpid_`.

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

        Returns
        -------
        AsyncHttpResponse[typing.Optional[typing.Any]]
            Successfully deleted workflow
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/workflows/{jsonable_encoder(workflow_id)}/delete",
            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.Optional[typing.Any],
                    parse_obj_as(
                        type_=typing.Optional[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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_artifact(
        self, artifact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[Artifact]:
        """
        Get an artifact

        Parameters
        ----------
        artifact_id : str

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

        Returns
        -------
        AsyncHttpResponse[Artifact]
            Successfully retrieved artifact
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/artifacts/{jsonable_encoder(artifact_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Artifact,
                    parse_obj_as(
                        type_=Artifact,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_run_artifacts(
        self,
        run_id: str,
        *,
        artifact_type: typing.Optional[typing.Union[ArtifactType, typing.Sequence[ArtifactType]]] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.List[Artifact]]:
        """
        Get artifacts for a run

        Parameters
        ----------
        run_id : str
            The id of the task run or the workflow run.

        artifact_type : typing.Optional[typing.Union[ArtifactType, typing.Sequence[ArtifactType]]]

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

        Returns
        -------
        AsyncHttpResponse[typing.List[Artifact]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}/artifacts",
            method="GET",
            params={
                "artifact_type": artifact_type,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[Artifact],
                    parse_obj_as(
                        type_=typing.List[Artifact],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 retry_run_webhook(
        self,
        run_id: str,
        *,
        request: typing.Optional[RetryRunWebhookRequest] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.Optional[typing.Any]]:
        """
        Retry sending the webhook for a run

        Parameters
        ----------
        run_id : str
            The id of the task run or the workflow run.

        request : typing.Optional[RetryRunWebhookRequest]

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

        Returns
        -------
        AsyncHttpResponse[typing.Optional[typing.Any]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}/retry_webhook",
            method="POST",
            json=convert_and_respect_annotation_metadata(
                object_=request, annotation=RetryRunWebhookRequest, direction="write"
            ),
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        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.Optional[typing.Any],
                    parse_obj_as(
                        type_=typing.Optional[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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_run_timeline(
        self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.List[WorkflowRunTimeline]]:
        """
        Get timeline for a run (workflow run or task_v2 run)

        Parameters
        ----------
        run_id : str
            The id of the workflow run or task_v2 run.

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

        Returns
        -------
        AsyncHttpResponse[typing.List[WorkflowRunTimeline]]
            Successfully retrieved run timeline
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/runs/{jsonable_encoder(run_id)}/timeline",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[WorkflowRunTimeline],
                    parse_obj_as(
                        type_=typing.List[WorkflowRunTimeline],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow_runs(
        self,
        *,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        status: typing.Optional[typing.Union[WorkflowRunStatus, typing.Sequence[WorkflowRunStatus]]] = None,
        search_key: typing.Optional[str] = None,
        error_code: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.List[WorkflowRun]]:
        """
        List workflow runs across all workflows for the current organization.

        Results are paginated and can be filtered by **status**, **search_key**, and **error_code**. All filters are combined with **AND** logic — a run must match every supplied filter to be returned.

        ### search_key

        A case-insensitive substring search that matches against **any** of the following fields:

        | Searched field | Description |
        |---|---|
        | `workflow_run_id` | The unique run identifier (e.g. `wr_123…`) |
        | Parameter **key** | The `key` of any workflow parameter definition associated with the run |
        | Parameter **description** | The `description` of any workflow parameter definition |
        | Run parameter **value** | The actual value supplied for any parameter when the run was created |
        | `extra_http_headers` | Extra HTTP headers attached to the run (searched as raw JSON text) |

        Soft-deleted parameter definitions are excluded from key/description matching. A run is returned if **any** of the fields above contain the search term.

        ### error_code

        An **exact-match** filter against the `error_code` field inside each task's `errors` JSON array. A run matches if **any** of its tasks contains an error object with a matching `error_code` value. Error codes are user-defined strings set during workflow execution (e.g. `INVALID_CREDENTIALS`, `LOGIN_FAILED`, `CAPTCHA_DETECTED`).

        ### Combining filters

        All query parameters use AND logic:
        - `?status=failed` — only failed runs
        - `?status=failed&error_code=LOGIN_FAILED` — failed runs **and** have a LOGIN_FAILED error
        - `?status=failed&error_code=LOGIN_FAILED&search_key=prod_credential` — all three conditions must match

        Parameters
        ----------
        page : typing.Optional[int]
            Page number for pagination.

        page_size : typing.Optional[int]
            Number of runs to return per page.

        status : typing.Optional[typing.Union[WorkflowRunStatus, typing.Sequence[WorkflowRunStatus]]]
            Filter by one or more run statuses.

        search_key : typing.Optional[str]
            Case-insensitive substring search across: workflow run ID, parameter key, parameter description, run parameter value, and extra HTTP headers. A run is returned if any of these fields match. Soft-deleted parameter definitions are excluded from key/description matching.

        error_code : typing.Optional[str]
            Exact-match filter on the error_code field inside each task's errors JSON array. A run matches if any of its tasks contains an error with a matching error_code. Error codes are user-defined strings set during workflow execution.

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

        Returns
        -------
        AsyncHttpResponse[typing.List[WorkflowRun]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/workflows/runs",
            method="GET",
            params={
                "page": page,
                "page_size": page_size,
                "status": status,
                "search_key": search_key,
                "error_code": error_code,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[WorkflowRun],
                    parse_obj_as(
                        type_=typing.List[WorkflowRun],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow(
        self,
        workflow_permanent_id: str,
        *,
        version: typing.Optional[int] = None,
        template: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[Workflow]:
        """
        Parameters
        ----------
        workflow_permanent_id : str

        version : typing.Optional[int]

        template : typing.Optional[bool]

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

        Returns
        -------
        AsyncHttpResponse[Workflow]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/workflows/{jsonable_encoder(workflow_permanent_id)}",
            method="GET",
            params={
                "version": version,
                "template": template,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Workflow,
                    parse_obj_as(
                        type_=Workflow,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_workflow_versions(
        self,
        workflow_permanent_id: str,
        *,
        template: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.List[Workflow]]:
        """
        Get all versions of a workflow by its permanent ID.

        Parameters
        ----------
        workflow_permanent_id : str

        template : typing.Optional[bool]

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

        Returns
        -------
        AsyncHttpResponse[typing.List[Workflow]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/workflows/{jsonable_encoder(workflow_permanent_id)}/versions",
            method="GET",
            params={
                "template": template,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[Workflow],
                    parse_obj_as(
                        type_=typing.List[Workflow],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 upload_file(
        self, *, file: core.File, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[UploadFileResponse]:
        """
        Parameters
        ----------
        file : core.File
            See core.File for more documentation

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

        Returns
        -------
        AsyncHttpResponse[UploadFileResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/upload_file",
            method="POST",
            data={},
            files={
                "file": file,
            },
            request_options=request_options,
            omit=OMIT,
            force_multipart=True,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    UploadFileResponse,
                    parse_obj_as(
                        type_=UploadFileResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_profiles(
        self, *, include_deleted: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.List[BrowserProfile]]:
        """
        Get all browser profiles for the organization

        Parameters
        ----------
        include_deleted : typing.Optional[bool]
            Include deleted browser profiles

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

        Returns
        -------
        AsyncHttpResponse[typing.List[BrowserProfile]]
            Successfully retrieved browser profiles
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/browser_profiles",
            method="GET",
            params={
                "include_deleted": include_deleted,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[BrowserProfile],
                    parse_obj_as(
                        type_=typing.List[BrowserProfile],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_browser_profile(
        self,
        *,
        name: str,
        description: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        workflow_run_id: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[BrowserProfile]:
        """
        Create a browser profile from a persistent browser session or workflow run.

        Parameters
        ----------
        name : str
            Name for the browser profile

        description : typing.Optional[str]
            Optional profile description

        browser_session_id : typing.Optional[str]
            Persistent browser session to convert into a profile

        workflow_run_id : typing.Optional[str]
            Workflow run whose persisted session should be captured

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

        Returns
        -------
        AsyncHttpResponse[BrowserProfile]
            Successfully created browser profile
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/browser_profiles",
            method="POST",
            json={
                "name": name,
                "description": description,
                "browser_session_id": browser_session_id,
                "workflow_run_id": workflow_run_id,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BrowserProfile,
                    parse_obj_as(
                        type_=BrowserProfile,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 409:
                raise ConflictError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_profile(
        self, profile_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[BrowserProfile]:
        """
        Get a specific browser profile by ID

        Parameters
        ----------
        profile_id : str
            The ID of the browser profile. browser_profile_id starts with `bp_`

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

        Returns
        -------
        AsyncHttpResponse[BrowserProfile]
            Successfully retrieved browser profile
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/browser_profiles/{jsonable_encoder(profile_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BrowserProfile,
                    parse_obj_as(
                        type_=BrowserProfile,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_profile(
        self, profile_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[None]:
        """
        Delete a browser profile (soft delete)

        Parameters
        ----------
        profile_id : str
            The ID of the browser profile to delete. browser_profile_id starts with `bp_`

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

        Returns
        -------
        AsyncHttpResponse[None]
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/browser_profiles/{jsonable_encoder(profile_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return AsyncHttpResponse(response=_response, data=None)
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_sessions(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.List[BrowserSessionResponse]]:
        """
        Get all active browser sessions for the organization

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

        Returns
        -------
        AsyncHttpResponse[typing.List[BrowserSessionResponse]]
            Successfully retrieved all active browser sessions
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/browser_sessions",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[BrowserSessionResponse],
                    parse_obj_as(
                        type_=typing.List[BrowserSessionResponse],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_browser_session(
        self,
        *,
        timeout: typing.Optional[int] = OMIT,
        proxy_location: typing.Optional[ProxyLocation] = OMIT,
        extensions: typing.Optional[typing.Sequence[Extensions]] = OMIT,
        browser_type: typing.Optional[PersistentBrowserType] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[BrowserSessionResponse]:
        """
        Create a browser session that persists across multiple runs

        Parameters
        ----------
        timeout : typing.Optional[int]
            Timeout in minutes for the session. Timeout is applied after the session is started. Must be between 5 and 1440. Defaults to 60.

        proxy_location : typing.Optional[ProxyLocation]

            Geographic Proxy location to route the browser traffic through. This is only available in Skyvern Cloud.

            Available geotargeting options:
            - RESIDENTIAL: the default value. Skyvern Cloud uses a random US residential proxy.
            - RESIDENTIAL_ES: Spain
            - RESIDENTIAL_IE: Ireland
            - RESIDENTIAL_GB: United Kingdom
            - RESIDENTIAL_IN: India
            - RESIDENTIAL_JP: Japan
            - RESIDENTIAL_FR: France
            - RESIDENTIAL_DE: Germany
            - RESIDENTIAL_NZ: New Zealand
            - RESIDENTIAL_PH: Philippines
            - RESIDENTIAL_ZA: South Africa
            - RESIDENTIAL_AR: Argentina
            - RESIDENTIAL_AU: Australia
            - RESIDENTIAL_ISP: ISP proxy
            - US-CA: California (deprecated, routes through RESIDENTIAL_ISP)
            - US-NY: New York (deprecated, routes through RESIDENTIAL_ISP)
            - US-TX: Texas (deprecated, routes through RESIDENTIAL_ISP)
            - US-FL: Florida (deprecated, routes through RESIDENTIAL_ISP)
            - US-WA: Washington (deprecated, routes through RESIDENTIAL_ISP)
            - NONE: No proxy

        extensions : typing.Optional[typing.Sequence[Extensions]]
            A list of extensions to install in the browser session.

        browser_type : typing.Optional[PersistentBrowserType]
            The type of browser to use for the session.

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

        Returns
        -------
        AsyncHttpResponse[BrowserSessionResponse]
            Successfully created browser session
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/browser_sessions",
            method="POST",
            json={
                "timeout": timeout,
                "proxy_location": proxy_location,
                "extensions": extensions,
                "browser_type": browser_type,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BrowserSessionResponse,
                    parse_obj_as(
                        type_=BrowserSessionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 close_browser_session(
        self, browser_session_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Optional[typing.Any]]:
        """
        Close a session. Once closed, the session cannot be used again.

        Parameters
        ----------
        browser_session_id : str
            The ID of the browser session to close. completed_at will be set when the browser session is closed. browser_session_id starts with `pbs_`

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

        Returns
        -------
        AsyncHttpResponse[typing.Optional[typing.Any]]
            Successfully closed browser session
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/browser_sessions/{jsonable_encoder(browser_session_id)}/close",
            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.Optional[typing.Any],
                    parse_obj_as(
                        type_=typing.Optional[typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_browser_session(
        self, browser_session_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[BrowserSessionResponse]:
        """
        Get details about a specific browser session, including the browser address for cdp connection.

        Parameters
        ----------
        browser_session_id : str
            The ID of the browser session. browser_session_id starts with `pbs_`

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

        Returns
        -------
        AsyncHttpResponse[BrowserSessionResponse]
            Successfully retrieved browser session details
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/browser_sessions/{jsonable_encoder(browser_session_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BrowserSessionResponse,
                    parse_obj_as(
                        type_=BrowserSessionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 send_totp_code(
        self,
        *,
        totp_identifier: str,
        content: str,
        task_id: typing.Optional[str] = OMIT,
        workflow_id: typing.Optional[str] = OMIT,
        workflow_run_id: typing.Optional[str] = OMIT,
        source: typing.Optional[str] = OMIT,
        expired_at: typing.Optional[dt.datetime] = OMIT,
        type: typing.Optional[OtpType] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[TotpCode]:
        """
        Forward a TOTP (2FA, MFA) email or sms message containing the code to Skyvern. This endpoint stores the code in database so that Skyvern can use it while running tasks/workflows.

        Parameters
        ----------
        totp_identifier : str
            The identifier of the TOTP code. It can be the email address, phone number, or the identifier of the user.

        content : str
            The content of the TOTP code. It can be the email content that contains the TOTP code, or the sms message that contains the TOTP code. Skyvern will automatically extract the TOTP code from the content.

        task_id : typing.Optional[str]
            The task_id the totp code is for. It can be the task_id of the task that the TOTP code is for.

        workflow_id : typing.Optional[str]
            The workflow ID the TOTP code is for. It can be the workflow ID of the workflow that the TOTP code is for.

        workflow_run_id : typing.Optional[str]
            The workflow run id that the TOTP code is for. It can be the workflow run id of the workflow run that the TOTP code is for.

        source : typing.Optional[str]
            An optional field. The source of the TOTP code. e.g. email, sms, etc.

        expired_at : typing.Optional[dt.datetime]
            The timestamp when the TOTP code expires

        type : typing.Optional[OtpType]
            Optional. If provided, forces extraction of this specific OTP type (totp or magic_link). Use this when the content contains multiple OTP types and you want to specify which one to extract.

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

        Returns
        -------
        AsyncHttpResponse[TotpCode]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/credentials/totp",
            method="POST",
            json={
                "totp_identifier": totp_identifier,
                "task_id": task_id,
                "workflow_id": workflow_id,
                "workflow_run_id": workflow_run_id,
                "source": source,
                "content": content,
                "expired_at": expired_at,
                "type": type,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TotpCode,
                    parse_obj_as(
                        type_=TotpCode,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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(
        self,
        *,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.List[CredentialResponse]]:
        """
        Retrieves a paginated list of credentials for the current organization

        Parameters
        ----------
        page : typing.Optional[int]
            Page number for pagination

        page_size : typing.Optional[int]
            Number of items per page

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

        Returns
        -------
        AsyncHttpResponse[typing.List[CredentialResponse]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/credentials",
            method="GET",
            params={
                "page": page,
                "page_size": page_size,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[CredentialResponse],
                    parse_obj_as(
                        type_=typing.List[CredentialResponse],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_credential(
        self,
        *,
        name: str,
        credential_type: SkyvernForgeSdkSchemasCredentialsCredentialType,
        credential: CreateCredentialRequestCredential,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[CredentialResponse]:
        """
        Creates a new credential for the current organization

        Parameters
        ----------
        name : str
            Name of the credential

        credential_type : SkyvernForgeSdkSchemasCredentialsCredentialType
            Type of credential to create

        credential : CreateCredentialRequestCredential
            The credential data to store

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

        Returns
        -------
        AsyncHttpResponse[CredentialResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/credentials",
            method="POST",
            json={
                "name": name,
                "credential_type": credential_type,
                "credential": convert_and_respect_annotation_metadata(
                    object_=credential, annotation=CreateCredentialRequestCredential, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialResponse,
                    parse_obj_as(
                        type_=CredentialResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_credential(
        self,
        credential_id: str,
        *,
        name: str,
        credential_type: SkyvernForgeSdkSchemasCredentialsCredentialType,
        credential: CreateCredentialRequestCredential,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[CredentialResponse]:
        """
        Overwrites the stored credential data (e.g. username/password) while keeping the same credential_id.

        Parameters
        ----------
        credential_id : str
            The unique identifier of the credential to update

        name : str
            Name of the credential

        credential_type : SkyvernForgeSdkSchemasCredentialsCredentialType
            Type of credential to create

        credential : CreateCredentialRequestCredential
            The credential data to store

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

        Returns
        -------
        AsyncHttpResponse[CredentialResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/credentials/{jsonable_encoder(credential_id)}/update",
            method="POST",
            json={
                "name": name,
                "credential_type": credential_type,
                "credential": convert_and_respect_annotation_metadata(
                    object_=credential, annotation=CreateCredentialRequestCredential, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialResponse,
                    parse_obj_as(
                        type_=CredentialResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_credential(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[None]:
        """
        Deletes a specific credential by its ID

        Parameters
        ----------
        credential_id : str
            The unique identifier of the credential to delete

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

        Returns
        -------
        AsyncHttpResponse[None]
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/credentials/{jsonable_encoder(credential_id)}/delete",
            method="POST",
            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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_credential(
        self, credential_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[CredentialResponse]:
        """
        Retrieves a specific credential by its ID

        Parameters
        ----------
        credential_id : str
            The unique identifier of the credential

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

        Returns
        -------
        AsyncHttpResponse[CredentialResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/credentials/{jsonable_encoder(credential_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CredentialResponse,
                    parse_obj_as(
                        type_=CredentialResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 login(
        self,
        *,
        credential_type: SkyvernSchemasRunBlocksCredentialType,
        url: typing.Optional[str] = OMIT,
        webhook_url: typing.Optional[str] = OMIT,
        proxy_location: typing.Optional[ProxyLocation] = OMIT,
        totp_identifier: typing.Optional[str] = OMIT,
        totp_url: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        browser_profile_id: typing.Optional[str] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        max_screenshot_scrolling_times: typing.Optional[int] = OMIT,
        prompt: typing.Optional[str] = OMIT,
        credential_id: typing.Optional[str] = OMIT,
        bitwarden_collection_id: typing.Optional[str] = OMIT,
        bitwarden_item_id: typing.Optional[str] = OMIT,
        onepassword_vault_id: typing.Optional[str] = OMIT,
        onepassword_item_id: typing.Optional[str] = OMIT,
        azure_vault_name: typing.Optional[str] = OMIT,
        azure_vault_username_key: typing.Optional[str] = OMIT,
        azure_vault_password_key: typing.Optional[str] = OMIT,
        azure_vault_totp_secret_key: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[WorkflowRunResponse]:
        """
        Log in to a website using either credential stored in Skyvern, Bitwarden, 1Password, or Azure Vault

        Parameters
        ----------
        credential_type : SkyvernSchemasRunBlocksCredentialType
            Where to get the credential from

        url : typing.Optional[str]
            Website URL

        webhook_url : typing.Optional[str]
            Webhook URL to send status updates

        proxy_location : typing.Optional[ProxyLocation]
            Proxy location to use

        totp_identifier : typing.Optional[str]
            Identifier for TOTP (Time-based One-Time Password) if required

        totp_url : typing.Optional[str]
            TOTP URL to fetch one-time passwords

        browser_session_id : typing.Optional[str]
            ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456`

        browser_profile_id : typing.Optional[str]
            ID of a browser profile to reuse for this run

        browser_address : typing.Optional[str]
            The CDP address for the task.

        extra_http_headers : typing.Optional[typing.Dict[str, typing.Optional[str]]]
            Additional HTTP headers to include in requests

        max_screenshot_scrolling_times : typing.Optional[int]
            Maximum number of times to scroll for screenshots

        prompt : typing.Optional[str]
            Login instructions. Skyvern has default prompt/instruction for login if this field is not provided.

        credential_id : typing.Optional[str]
            ID of the Skyvern credential to use for login.

        bitwarden_collection_id : typing.Optional[str]
            Bitwarden collection ID. You can find it in the Bitwarden collection URL. e.g. `https://vault.bitwarden.com/vaults/collection_id/items`

        bitwarden_item_id : typing.Optional[str]
            Bitwarden item ID

        onepassword_vault_id : typing.Optional[str]
            1Password vault ID

        onepassword_item_id : typing.Optional[str]
            1Password item ID

        azure_vault_name : typing.Optional[str]
            Azure Vault Name

        azure_vault_username_key : typing.Optional[str]
            Azure Vault username key

        azure_vault_password_key : typing.Optional[str]
            Azure Vault password key

        azure_vault_totp_secret_key : typing.Optional[str]
            Azure Vault TOTP secret key

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

        Returns
        -------
        AsyncHttpResponse[WorkflowRunResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/run/tasks/login",
            method="POST",
            json={
                "url": url,
                "webhook_url": webhook_url,
                "proxy_location": proxy_location,
                "totp_identifier": totp_identifier,
                "totp_url": totp_url,
                "browser_session_id": browser_session_id,
                "browser_profile_id": browser_profile_id,
                "browser_address": browser_address,
                "extra_http_headers": extra_http_headers,
                "max_screenshot_scrolling_times": max_screenshot_scrolling_times,
                "credential_type": credential_type,
                "prompt": prompt,
                "credential_id": credential_id,
                "bitwarden_collection_id": bitwarden_collection_id,
                "bitwarden_item_id": bitwarden_item_id,
                "onepassword_vault_id": onepassword_vault_id,
                "onepassword_item_id": onepassword_item_id,
                "azure_vault_name": azure_vault_name,
                "azure_vault_username_key": azure_vault_username_key,
                "azure_vault_password_key": azure_vault_password_key,
                "azure_vault_totp_secret_key": azure_vault_totp_secret_key,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    WorkflowRunResponse,
                    parse_obj_as(
                        type_=WorkflowRunResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 download_files(
        self,
        *,
        navigation_goal: str,
        url: typing.Optional[str] = OMIT,
        webhook_url: typing.Optional[str] = OMIT,
        proxy_location: typing.Optional[ProxyLocation] = OMIT,
        totp_identifier: typing.Optional[str] = OMIT,
        totp_url: typing.Optional[str] = OMIT,
        browser_session_id: typing.Optional[str] = OMIT,
        browser_profile_id: typing.Optional[str] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
        max_screenshot_scrolling_times: typing.Optional[int] = OMIT,
        download_suffix: typing.Optional[str] = OMIT,
        download_timeout: typing.Optional[float] = OMIT,
        max_steps_per_run: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[WorkflowRunResponse]:
        """
        Download a file from a website by navigating and clicking download buttons

        Parameters
        ----------
        navigation_goal : str
            Instructions for navigating to and downloading the file

        url : typing.Optional[str]
            Website URL

        webhook_url : typing.Optional[str]
            Webhook URL to send status updates

        proxy_location : typing.Optional[ProxyLocation]
            Proxy location to use

        totp_identifier : typing.Optional[str]
            Identifier for TOTP (Time-based One-Time Password) if required

        totp_url : typing.Optional[str]
            TOTP URL to fetch one-time passwords

        browser_session_id : typing.Optional[str]
            ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456`

        browser_profile_id : typing.Optional[str]
            ID of a browser profile to reuse for this run

        browser_address : typing.Optional[str]
            The CDP address for the task.

        extra_http_headers : typing.Optional[typing.Dict[str, typing.Optional[str]]]
            Additional HTTP headers to include in requests

        max_screenshot_scrolling_times : typing.Optional[int]
            Maximum number of times to scroll for screenshots

        download_suffix : typing.Optional[str]
            Suffix or complete filename for the downloaded file

        download_timeout : typing.Optional[float]
            Timeout in seconds for the download operation

        max_steps_per_run : typing.Optional[int]
            Maximum number of steps to execute

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

        Returns
        -------
        AsyncHttpResponse[WorkflowRunResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/run/tasks/download_files",
            method="POST",
            json={
                "url": url,
                "webhook_url": webhook_url,
                "proxy_location": proxy_location,
                "totp_identifier": totp_identifier,
                "totp_url": totp_url,
                "browser_session_id": browser_session_id,
                "browser_profile_id": browser_profile_id,
                "browser_address": browser_address,
                "extra_http_headers": extra_http_headers,
                "max_screenshot_scrolling_times": max_screenshot_scrolling_times,
                "navigation_goal": navigation_goal,
                "download_suffix": download_suffix,
                "download_timeout": download_timeout,
                "max_steps_per_run": max_steps_per_run,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    WorkflowRunResponse,
                    parse_obj_as(
                        type_=WorkflowRunResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_scripts(
        self,
        *,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[typing.List[Script]]:
        """
        Retrieves a paginated list of scripts for the current organization

        Parameters
        ----------
        page : typing.Optional[int]
            Page number for pagination

        page_size : typing.Optional[int]
            Number of items per page

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

        Returns
        -------
        AsyncHttpResponse[typing.List[Script]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/scripts",
            method="GET",
            params={
                "page": page,
                "page_size": page_size,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.List[Script],
                    parse_obj_as(
                        type_=typing.List[Script],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_script(
        self,
        *,
        workflow_id: typing.Optional[str] = OMIT,
        run_id: typing.Optional[str] = OMIT,
        files: typing.Optional[typing.Sequence[ScriptFileCreate]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[CreateScriptResponse]:
        """
        Create a new script with optional files and metadata

        Parameters
        ----------
        workflow_id : typing.Optional[str]
            Associated workflow ID

        run_id : typing.Optional[str]
            Associated run ID

        files : typing.Optional[typing.Sequence[ScriptFileCreate]]
            Array of files to include in the script

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

        Returns
        -------
        AsyncHttpResponse[CreateScriptResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/scripts",
            method="POST",
            json={
                "workflow_id": workflow_id,
                "run_id": run_id,
                "files": convert_and_respect_annotation_metadata(
                    object_=files, annotation=typing.Sequence[ScriptFileCreate], direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CreateScriptResponse,
                    parse_obj_as(
                        type_=CreateScriptResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_script(
        self, script_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[Script]:
        """
        Retrieves a specific script by its ID

        Parameters
        ----------
        script_id : str
            The unique identifier of the script

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

        Returns
        -------
        AsyncHttpResponse[Script]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/scripts/{jsonable_encoder(script_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Script,
                    parse_obj_as(
                        type_=Script,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 deploy_script(
        self,
        script_id: str,
        *,
        files: typing.Sequence[ScriptFileCreate],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[CreateScriptResponse]:
        """
        Deploy a script with updated files, creating a new version

        Parameters
        ----------
        script_id : str
            The unique identifier of the script

        files : typing.Sequence[ScriptFileCreate]
            Array of files to include in the script

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

        Returns
        -------
        AsyncHttpResponse[CreateScriptResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/scripts/{jsonable_encoder(script_id)}/deploy",
            method="POST",
            json={
                "files": convert_and_respect_annotation_metadata(
                    object_=files, annotation=typing.Sequence[ScriptFileCreate], direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CreateScriptResponse,
                    parse_obj_as(
                        type_=CreateScriptResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 clear_workflow_cache(
        self, workflow_permanent_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ClearCacheResponse]:
        """
        Clear all cached scripts for a specific workflow. This will trigger script regeneration on subsequent runs.

        Parameters
        ----------
        workflow_permanent_id : str
            The workflow permanent ID to clear cache for

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

        Returns
        -------
        AsyncHttpResponse[ClearCacheResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/scripts/{jsonable_encoder(workflow_permanent_id)}/cache",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ClearCacheResponse,
                    parse_obj_as(
                        type_=ClearCacheResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 run_sdk_action(
        self,
        *,
        url: str,
        action: RunSdkActionRequestAction,
        browser_session_id: typing.Optional[str] = OMIT,
        browser_address: typing.Optional[str] = OMIT,
        workflow_run_id: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[RunSdkActionResponse]:
        """
        Execute a single SDK action with the specified parameters

        Parameters
        ----------
        url : str
            The URL where the action should be executed

        action : RunSdkActionRequestAction
            The action to execute with its specific parameters

        browser_session_id : typing.Optional[str]
            The browser session ID

        browser_address : typing.Optional[str]
            The browser address

        workflow_run_id : typing.Optional[str]
            Optional workflow run ID to continue an existing workflow run

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

        Returns
        -------
        AsyncHttpResponse[RunSdkActionResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/sdk/run_action",
            method="POST",
            json={
                "url": url,
                "browser_session_id": browser_session_id,
                "browser_address": browser_address,
                "workflow_run_id": workflow_run_id,
                "action": convert_and_respect_annotation_metadata(
                    object_=action, annotation=RunSdkActionRequestAction, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    RunSdkActionResponse,
                    parse_obj_as(
                        type_=RunSdkActionResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_checkout_session_api_v1billing_checkout_post(
        self, *, tier: PlanTier, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[CheckoutSessionResponse]:
        """
        Create a Stripe Checkout Session for subscribing to a tier.

        Frontend should redirect the user to the returned URL.
        After successful checkout, Stripe will send a webhook that we handle
        to store the subscription and initialize billing state.

        Returns 400 if org already has an active subscription (use portal instead).

        Parameters
        ----------
        tier : PlanTier

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

        Returns
        -------
        AsyncHttpResponse[CheckoutSessionResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "api/v1/billing/checkout",
            method="POST",
            json={
                "tier": tier,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CheckoutSessionResponse,
                    parse_obj_as(
                        type_=CheckoutSessionResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 create_portal_session_api_v1billing_portal_post(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[PortalSessionResponse]:
        """
        Create a Stripe Customer Portal session for managing subscription.

        Frontend should redirect the user to the returned URL.
        The portal allows users to:
        - Update payment methods
        - Upgrade/downgrade plans
        - Cancel subscription
        - View invoices

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

        Returns
        -------
        AsyncHttpResponse[PortalSessionResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "api/v1/billing/portal",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    PortalSessionResponse,
                    parse_obj_as(
                        type_=PortalSessionResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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_organization_billing_api_v1billing_state_get(
        self, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Optional[BillingStateResponse]]:
        """
        Parameters
        ----------
        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[typing.Optional[BillingStateResponse]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "api/v1/billing/state",
            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.Optional[BillingStateResponse],
                    parse_obj_as(
                        type_=typing.Optional[BillingStateResponse],  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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 change_tier_api_v1billing_change_tier_post(
        self, *, tier: PlanTier, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ChangeTierResponse]:
        """
        Redirect to Stripe Portal for tier changes.
        Portal handles proration based on configured settings:
        - Upgrades: Immediate proration charge
        - Downgrades: Apply at end of billing period

        Parameters
        ----------
        tier : PlanTier

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

        Returns
        -------
        AsyncHttpResponse[ChangeTierResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "api/v1/billing/change-tier",
            method="POST",
            json={
                "tier": tier,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ChangeTierResponse,
                    parse_obj_as(
                        type_=ChangeTierResponse,  # 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.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[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)
