"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

from .basesdk import BaseSDK
from datetime import datetime
from jsonpath import JSONPath
from kombo import errors, models, utils
from kombo._hooks import HookContext
from kombo.types import Nullable, OptionalNullable, UNSET
from kombo.utils.unmarshal_json_response import unmarshal_json_response
from typing import Any, Awaitable, Dict, List, Mapping, Optional, Union


class Ats(BaseSDK):
    def get_applications(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        outcomes: Optional[List[str]] = None,
        job_ids: Optional[List[str]] = None,
        job_remote_ids: Optional[List[str]] = None,
        current_stage_ids: Optional[List[str]] = None,
        remote_created_after: Optional[datetime] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsApplicationsResponse]:
        r"""Get applications

        Retrieve all applications.

        Visit our in-depth guides to learn more about:

        - 💡 [Being aware of which applications are tracked](/ats/features/implementation-guide/tracking-created-applications#be-aware-of-which-applications-are-tracked)
        - 🚦 [Hiring signals](/ats/features/implementation-guide/tracking-created-applications#hiring-signals)
        - 📈 [Application stage changes](/ats/features/implementation-guide/tracking-created-applications#application-stage-changes)
        - ❓ [ATS-specific limitations](/ats/features/implementation-guide/tracking-created-applications#ats-specific-limitations)

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param outcomes: Filter by a comma-separated list of `PENDING`, `HIRED`, `DECLINED`
            * `PENDING`: The application is still being processed.
            * `HIRED`: The candidate was hired.
            * `DECLINED`: The candidate was declined.


            Leave this blank to get results matching all values.
        :param job_ids: Filter by a comma-separated list of job IDs. We will only return applications that are related to _any_ of the jobs.
        :param job_remote_ids: Filter by a comma-separated list of job remote IDs. We will only return applications that are related to _any_ of the jobs.
        :param current_stage_ids: Filter by a comma-separated list of application stage IDs. We will only return applications that are currently in _any_ of the stages.
        :param remote_created_after: Filter applications by the day they were created in the remote system. This allows you to get applications that were created on or after a certain day.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsApplicationsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            outcomes=outcomes,
            job_ids=job_ids,
            job_remote_ids=job_remote_ids,
            current_stage_ids=current_stage_ids,
            remote_created_after=remote_created_after,
        )

        req = self._build_request(
            method="GET",
            path="/ats/applications",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsApplicationsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsApplications",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsApplicationsResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_applications(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                outcomes=outcomes,
                job_ids=job_ids,
                job_remote_ids=job_remote_ids,
                current_stage_ids=current_stage_ids,
                remote_created_after=remote_created_after,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsApplicationsResponse(
                result=unmarshal_json_response(
                    models.GetAtsApplicationsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_applications_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        outcomes: Optional[List[str]] = None,
        job_ids: Optional[List[str]] = None,
        job_remote_ids: Optional[List[str]] = None,
        current_stage_ids: Optional[List[str]] = None,
        remote_created_after: Optional[datetime] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsApplicationsResponse]:
        r"""Get applications

        Retrieve all applications.

        Visit our in-depth guides to learn more about:

        - 💡 [Being aware of which applications are tracked](/ats/features/implementation-guide/tracking-created-applications#be-aware-of-which-applications-are-tracked)
        - 🚦 [Hiring signals](/ats/features/implementation-guide/tracking-created-applications#hiring-signals)
        - 📈 [Application stage changes](/ats/features/implementation-guide/tracking-created-applications#application-stage-changes)
        - ❓ [ATS-specific limitations](/ats/features/implementation-guide/tracking-created-applications#ats-specific-limitations)

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param outcomes: Filter by a comma-separated list of `PENDING`, `HIRED`, `DECLINED`
            * `PENDING`: The application is still being processed.
            * `HIRED`: The candidate was hired.
            * `DECLINED`: The candidate was declined.


            Leave this blank to get results matching all values.
        :param job_ids: Filter by a comma-separated list of job IDs. We will only return applications that are related to _any_ of the jobs.
        :param job_remote_ids: Filter by a comma-separated list of job remote IDs. We will only return applications that are related to _any_ of the jobs.
        :param current_stage_ids: Filter by a comma-separated list of application stage IDs. We will only return applications that are currently in _any_ of the stages.
        :param remote_created_after: Filter applications by the day they were created in the remote system. This allows you to get applications that were created on or after a certain day.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsApplicationsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            outcomes=outcomes,
            job_ids=job_ids,
            job_remote_ids=job_remote_ids,
            current_stage_ids=current_stage_ids,
            remote_created_after=remote_created_after,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/applications",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsApplicationsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsApplications",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsApplicationsResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_applications_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                outcomes=outcomes,
                job_ids=job_ids,
                job_remote_ids=job_remote_ids,
                current_stage_ids=current_stage_ids,
                remote_created_after=remote_created_after,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsApplicationsResponse(
                result=unmarshal_json_response(
                    models.GetAtsApplicationsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def move_application_to_stage(
        self,
        *,
        application_id: str,
        stage_id: str,
        remote_fields: Optional[
            Union[
                models.PutAtsApplicationsApplicationIDStageRequestBodyRemoteFields,
                models.PutAtsApplicationsApplicationIDStageRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PutAtsApplicationsApplicationIDStagePositiveResponse:
        r"""Move application to stage

        Moves an application to a specified stage. Use job-specific stages from GET /jobs, not the deprecated /application-stages endpoint.

        <Note>
        This endpoint requires the permission **Set application stage** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"stage_id\": \"3PJ8PZhZZa1eEdd2DtPNtVup\"
        }
        ```

        :param application_id: The Kombo ID of the application you want to move to a different stage.
        :param stage_id: The Kombo ID of the stage to move the application to. This stage must be allowed for the job that the application is connected to. Get available stages from the `stages` property on the job, not from the deprecated application-stages endpoint.
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PutAtsApplicationsApplicationIDStageRequest(
            application_id=application_id,
            body=models.PutAtsApplicationsApplicationIDStageRequestBody(
                stage_id=stage_id,
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PutAtsApplicationsApplicationIDStageRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="PUT",
            path="/ats/applications/{application_id}/stage",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PutAtsApplicationsApplicationIDStageGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PutAtsApplicationsApplicationIDStageRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PutAtsApplicationsApplicationIdStage",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PutAtsApplicationsApplicationIDStagePositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def move_application_to_stage_async(
        self,
        *,
        application_id: str,
        stage_id: str,
        remote_fields: Optional[
            Union[
                models.PutAtsApplicationsApplicationIDStageRequestBodyRemoteFields,
                models.PutAtsApplicationsApplicationIDStageRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PutAtsApplicationsApplicationIDStagePositiveResponse:
        r"""Move application to stage

        Moves an application to a specified stage. Use job-specific stages from GET /jobs, not the deprecated /application-stages endpoint.

        <Note>
        This endpoint requires the permission **Set application stage** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"stage_id\": \"3PJ8PZhZZa1eEdd2DtPNtVup\"
        }
        ```

        :param application_id: The Kombo ID of the application you want to move to a different stage.
        :param stage_id: The Kombo ID of the stage to move the application to. This stage must be allowed for the job that the application is connected to. Get available stages from the `stages` property on the job, not from the deprecated application-stages endpoint.
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PutAtsApplicationsApplicationIDStageRequest(
            application_id=application_id,
            body=models.PutAtsApplicationsApplicationIDStageRequestBody(
                stage_id=stage_id,
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PutAtsApplicationsApplicationIDStageRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="PUT",
            path="/ats/applications/{application_id}/stage",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PutAtsApplicationsApplicationIDStageGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PutAtsApplicationsApplicationIDStageRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PutAtsApplicationsApplicationIdStage",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PutAtsApplicationsApplicationIDStagePositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def add_application_result_link(
        self,
        *,
        application_id: str,
        label: str,
        url: str,
        details: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDResultLinksRequestBodyDetails,
                models.PostAtsApplicationsApplicationIDResultLinksRequestBodyDetailsTypedDict,
            ]
        ] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDResultLinksRequestBodyRemoteFields,
                models.PostAtsApplicationsApplicationIDResultLinksRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsApplicationsApplicationIDResultLinksPositiveResponse:
        r"""Add result link to application

        Add a result link to an application.

        This can, for example, be used to link a candidate back to a test result/assessment in your application. As not all ATS tools have a \"result link\" feature, we sometimes repurpose other fields to expose it.

        <Note>
        This endpoint requires the permission **Add result links** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"application_id\": \"8Xi6iZrwusZqJmDGXs49GBmJ\",
        \"label\": \"Assessment Result\",
        \"url\": \"https://example.com/test-results/5BtP1WC1UboS7CF3yxjKcvjG\",
        \"details\": {
        \"custom_field_name_prefix\": \"Acme:\",
        \"attributes\": [
        {
        \"key\": \"Score\",
        \"value\": \"100%\"
        },
        {
        \"key\": \"Time\",
        \"value\": \"2:30h\"
        }
        ]
        },
        \"remote_fields\": {}
        }
        ```

        :param application_id: The Kombo ID of the application you want to create the link for.
        :param label: If we can display a display name for the link, we will use this label.
        :param url: URL of the link.
        :param details: Additional details with attributes that will be added to the result. This can be percentages, scores, or any text.

            We generally recommend using short attribute keys and a short custom_field_name_prefix to avoid overflowing the ATS UI.
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsApplicationsApplicationIDResultLinksRequest(
            application_id=application_id,
            body=models.PostAtsApplicationsApplicationIDResultLinksRequestBody(
                label=label,
                url=url,
                details=utils.get_pydantic_model(
                    details,
                    Optional[
                        models.PostAtsApplicationsApplicationIDResultLinksRequestBodyDetails
                    ],
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsApplicationsApplicationIDResultLinksRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/applications/{application_id}/result-links",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsApplicationsApplicationIDResultLinksGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsApplicationsApplicationIDResultLinksRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsApplicationsApplicationIdResultLinks",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsApplicationsApplicationIDResultLinksPositiveResponse,
                http_res,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def add_application_result_link_async(
        self,
        *,
        application_id: str,
        label: str,
        url: str,
        details: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDResultLinksRequestBodyDetails,
                models.PostAtsApplicationsApplicationIDResultLinksRequestBodyDetailsTypedDict,
            ]
        ] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDResultLinksRequestBodyRemoteFields,
                models.PostAtsApplicationsApplicationIDResultLinksRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsApplicationsApplicationIDResultLinksPositiveResponse:
        r"""Add result link to application

        Add a result link to an application.

        This can, for example, be used to link a candidate back to a test result/assessment in your application. As not all ATS tools have a \"result link\" feature, we sometimes repurpose other fields to expose it.

        <Note>
        This endpoint requires the permission **Add result links** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"application_id\": \"8Xi6iZrwusZqJmDGXs49GBmJ\",
        \"label\": \"Assessment Result\",
        \"url\": \"https://example.com/test-results/5BtP1WC1UboS7CF3yxjKcvjG\",
        \"details\": {
        \"custom_field_name_prefix\": \"Acme:\",
        \"attributes\": [
        {
        \"key\": \"Score\",
        \"value\": \"100%\"
        },
        {
        \"key\": \"Time\",
        \"value\": \"2:30h\"
        }
        ]
        },
        \"remote_fields\": {}
        }
        ```

        :param application_id: The Kombo ID of the application you want to create the link for.
        :param label: If we can display a display name for the link, we will use this label.
        :param url: URL of the link.
        :param details: Additional details with attributes that will be added to the result. This can be percentages, scores, or any text.

            We generally recommend using short attribute keys and a short custom_field_name_prefix to avoid overflowing the ATS UI.
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsApplicationsApplicationIDResultLinksRequest(
            application_id=application_id,
            body=models.PostAtsApplicationsApplicationIDResultLinksRequestBody(
                label=label,
                url=url,
                details=utils.get_pydantic_model(
                    details,
                    Optional[
                        models.PostAtsApplicationsApplicationIDResultLinksRequestBodyDetails
                    ],
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsApplicationsApplicationIDResultLinksRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/applications/{application_id}/result-links",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsApplicationsApplicationIDResultLinksGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsApplicationsApplicationIDResultLinksRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsApplicationsApplicationIdResultLinks",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsApplicationsApplicationIDResultLinksPositiveResponse,
                http_res,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def add_application_note(
        self,
        *,
        application_id: str,
        content: str,
        content_type: models.ContentType,
        remote_fields: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDNotesRequestBodyRemoteFields,
                models.PostAtsApplicationsApplicationIDNotesRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsApplicationsApplicationIDNotesPositiveResponse:
        r"""Add note to application

        Add a note to an application.

        Add extra information to an application. This can be any extra text information you want to add to an application.

        <Note>
        This endpoint requires the permission **Add notes** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"content\": \"A new message from the candidate is available in YourChat!\",
        \"content_type\": \"PLAIN_TEXT\",
        \"remote_fields\": {}
        }
        ```

        :param application_id: The Kombo ID of the application you want to create the note for.
        :param content: UTF-8 content of the note.
        :param content_type: Content type of the note. Currently only `PLAIN_TEXT` is supported.
        :param remote_fields: Tool specific remote fields for the note.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsApplicationsApplicationIDNotesRequest(
            application_id=application_id,
            body=models.PostAtsApplicationsApplicationIDNotesRequestBody(
                content=content,
                content_type=content_type,
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsApplicationsApplicationIDNotesRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/applications/{application_id}/notes",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsApplicationsApplicationIDNotesGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsApplicationsApplicationIDNotesRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsApplicationsApplicationIdNotes",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsApplicationsApplicationIDNotesPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def add_application_note_async(
        self,
        *,
        application_id: str,
        content: str,
        content_type: models.ContentType,
        remote_fields: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDNotesRequestBodyRemoteFields,
                models.PostAtsApplicationsApplicationIDNotesRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsApplicationsApplicationIDNotesPositiveResponse:
        r"""Add note to application

        Add a note to an application.

        Add extra information to an application. This can be any extra text information you want to add to an application.

        <Note>
        This endpoint requires the permission **Add notes** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"content\": \"A new message from the candidate is available in YourChat!\",
        \"content_type\": \"PLAIN_TEXT\",
        \"remote_fields\": {}
        }
        ```

        :param application_id: The Kombo ID of the application you want to create the note for.
        :param content: UTF-8 content of the note.
        :param content_type: Content type of the note. Currently only `PLAIN_TEXT` is supported.
        :param remote_fields: Tool specific remote fields for the note.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsApplicationsApplicationIDNotesRequest(
            application_id=application_id,
            body=models.PostAtsApplicationsApplicationIDNotesRequestBody(
                content=content,
                content_type=content_type,
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsApplicationsApplicationIDNotesRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/applications/{application_id}/notes",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsApplicationsApplicationIDNotesGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsApplicationsApplicationIDNotesRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsApplicationsApplicationIdNotes",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsApplicationsApplicationIDNotesPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_application_attachments(
        self,
        *,
        application_id: str,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.GetAtsApplicationsApplicationIDAttachmentsPositiveResponse:
        r"""Get application attachments

        Get attachments from a candidate or application.

        Get attachments from an application. If the ATS stores the attachments on the candidate, it will get the attachments from the corresponding candidate instead.

        <Note>
        This endpoint requires the permission **Read document attachments** to be enabled in [your scope config](/scopes).
        </Note>

        :param application_id: The Kombo ID of the application you want to obtain attachments for.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsApplicationsApplicationIDAttachmentsRequest(
            application_id=application_id,
        )

        req = self._build_request(
            method="GET",
            path="/ats/applications/{application_id}/attachments",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsApplicationsApplicationIDAttachmentsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsApplicationsApplicationIdAttachments",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.GetAtsApplicationsApplicationIDAttachmentsPositiveResponse,
                http_res,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_application_attachments_async(
        self,
        *,
        application_id: str,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.GetAtsApplicationsApplicationIDAttachmentsPositiveResponse:
        r"""Get application attachments

        Get attachments from a candidate or application.

        Get attachments from an application. If the ATS stores the attachments on the candidate, it will get the attachments from the corresponding candidate instead.

        <Note>
        This endpoint requires the permission **Read document attachments** to be enabled in [your scope config](/scopes).
        </Note>

        :param application_id: The Kombo ID of the application you want to obtain attachments for.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsApplicationsApplicationIDAttachmentsRequest(
            application_id=application_id,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/applications/{application_id}/attachments",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsApplicationsApplicationIDAttachmentsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsApplicationsApplicationIdAttachments",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.GetAtsApplicationsApplicationIDAttachmentsPositiveResponse,
                http_res,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def add_application_attachment(
        self,
        *,
        application_id: str,
        attachment: Union[
            models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyAttachment,
            models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyAttachmentTypedDict,
        ],
        remote_fields: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyRemoteFields,
                models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsApplicationsApplicationIDAttachmentsPositiveResponse:
        r"""Add attachment to application

        Uploads an attachment file for the specified applicant.

        <Warning>
        If adding an attachment to an application is not supported by the integration, the attachment will be [added to the candidate](/ats/v1/post-candidates-candidate-id-attachments) instead.
        </Warning>

        <Note>
        This endpoint requires the permission **Add attachments** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"application_id\": \"GRKdd9dibYKKCrmGRSMJf3wu\",
        \"attachment\": {
        \"name\": \"Frank Doe CV.txt\",
        \"data\": \"SGkgdGhlcmUsIEtvbWJvIGlzIGN1cnJlbnRseSBoaXJpbmcgZW5naW5lZXJzIHRoYXQgbG92ZSB0byB3b3JrIG9uIGRldmVsb3BlciBwcm9kdWN0cy4=\",
        \"type\": \"CV\",
        \"content_type\": \"text/plain\"
        },
        \"remote_fields\": {}
        }
        ```

        :param application_id: POST /ats/applications/:application_id/attachments Parameter
        :param attachment:
        :param remote_fields:
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsApplicationsApplicationIDAttachmentsRequest(
            application_id=application_id,
            body=models.PostAtsApplicationsApplicationIDAttachmentsRequestBody(
                attachment=utils.get_pydantic_model(
                    attachment,
                    models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyAttachment,
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/applications/{application_id}/attachments",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsApplicationsApplicationIDAttachmentsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsApplicationsApplicationIDAttachmentsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsApplicationsApplicationIdAttachments",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsApplicationsApplicationIDAttachmentsPositiveResponse,
                http_res,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def add_application_attachment_async(
        self,
        *,
        application_id: str,
        attachment: Union[
            models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyAttachment,
            models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyAttachmentTypedDict,
        ],
        remote_fields: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyRemoteFields,
                models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsApplicationsApplicationIDAttachmentsPositiveResponse:
        r"""Add attachment to application

        Uploads an attachment file for the specified applicant.

        <Warning>
        If adding an attachment to an application is not supported by the integration, the attachment will be [added to the candidate](/ats/v1/post-candidates-candidate-id-attachments) instead.
        </Warning>

        <Note>
        This endpoint requires the permission **Add attachments** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"application_id\": \"GRKdd9dibYKKCrmGRSMJf3wu\",
        \"attachment\": {
        \"name\": \"Frank Doe CV.txt\",
        \"data\": \"SGkgdGhlcmUsIEtvbWJvIGlzIGN1cnJlbnRseSBoaXJpbmcgZW5naW5lZXJzIHRoYXQgbG92ZSB0byB3b3JrIG9uIGRldmVsb3BlciBwcm9kdWN0cy4=\",
        \"type\": \"CV\",
        \"content_type\": \"text/plain\"
        },
        \"remote_fields\": {}
        }
        ```

        :param application_id: POST /ats/applications/:application_id/attachments Parameter
        :param attachment:
        :param remote_fields:
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsApplicationsApplicationIDAttachmentsRequest(
            application_id=application_id,
            body=models.PostAtsApplicationsApplicationIDAttachmentsRequestBody(
                attachment=utils.get_pydantic_model(
                    attachment,
                    models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyAttachment,
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsApplicationsApplicationIDAttachmentsRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/applications/{application_id}/attachments",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsApplicationsApplicationIDAttachmentsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsApplicationsApplicationIDAttachmentsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsApplicationsApplicationIdAttachments",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsApplicationsApplicationIDAttachmentsPositiveResponse,
                http_res,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def reject_application(
        self,
        *,
        application_id: str,
        rejection_reason_id: str,
        note: Optional[str] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDRejectRequestBodyRemoteFields,
                models.PostAtsApplicationsApplicationIDRejectRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsApplicationsApplicationIDRejectPositiveResponse:
        r"""Reject application

        Rejects an application with a provided reason.

        Rejects an application with a provided reason. Optionally, you can provide a free text note. You can get the list of rejection reasons with our [Get rejection reasons endpoint](/ats/v1/get-rejection-reasons).

        <Note>
        This endpoint requires the permission **Reject applications** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"rejection_reason_id\": \"3PJ8PZhZZa1eEdd2DtPNtVup\",
        \"note\": \"Candidate was a great culture fit but didn't bring the hard skills we need.\",
        \"remote_fields\": {}
        }
        ```

        :param application_id: The Kombo ID of the application you want to reject.
        :param rejection_reason_id: The Kombo ID of the rejection reason.
        :param note: A optional free text rejection note. Passed through if possible.
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsApplicationsApplicationIDRejectRequest(
            application_id=application_id,
            body=models.PostAtsApplicationsApplicationIDRejectRequestBody(
                rejection_reason_id=rejection_reason_id,
                note=note,
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsApplicationsApplicationIDRejectRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/applications/{application_id}/reject",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsApplicationsApplicationIDRejectGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsApplicationsApplicationIDRejectRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsApplicationsApplicationIdReject",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsApplicationsApplicationIDRejectPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def reject_application_async(
        self,
        *,
        application_id: str,
        rejection_reason_id: str,
        note: Optional[str] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsApplicationsApplicationIDRejectRequestBodyRemoteFields,
                models.PostAtsApplicationsApplicationIDRejectRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsApplicationsApplicationIDRejectPositiveResponse:
        r"""Reject application

        Rejects an application with a provided reason.

        Rejects an application with a provided reason. Optionally, you can provide a free text note. You can get the list of rejection reasons with our [Get rejection reasons endpoint](/ats/v1/get-rejection-reasons).

        <Note>
        This endpoint requires the permission **Reject applications** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"rejection_reason_id\": \"3PJ8PZhZZa1eEdd2DtPNtVup\",
        \"note\": \"Candidate was a great culture fit but didn't bring the hard skills we need.\",
        \"remote_fields\": {}
        }
        ```

        :param application_id: The Kombo ID of the application you want to reject.
        :param rejection_reason_id: The Kombo ID of the rejection reason.
        :param note: A optional free text rejection note. Passed through if possible.
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsApplicationsApplicationIDRejectRequest(
            application_id=application_id,
            body=models.PostAtsApplicationsApplicationIDRejectRequestBody(
                rejection_reason_id=rejection_reason_id,
                note=note,
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsApplicationsApplicationIDRejectRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/applications/{application_id}/reject",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsApplicationsApplicationIDRejectGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsApplicationsApplicationIDRejectRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsApplicationsApplicationIdReject",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsApplicationsApplicationIDRejectPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_candidates(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        email: Optional[str] = None,
        job_ids: Optional[List[str]] = None,
        first_name: Optional[str] = None,
        last_name: Optional[str] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsCandidatesResponse]:
        r"""Get candidates

        Retrieve all candidates.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param email: Filter the candidates based on an email address. When set, returns only the candidates where the given `email` is in `email_addresses`. This filter is case-insensitive.
        :param job_ids: Filter by a comma-separated list of job IDs. We will only return candidates that have applied to _any_ of the jobs.
        :param first_name: Filter candidates by first name. This filter is case-insensitive and matches the exact first name. Fuzzy matching might be enabled in the future, so consider this for your implementation.
        :param last_name: Filter candidates by last name. This filter is case-insensitive and matches the exact last name. Fuzzy matching might be enabled in the future, so consider this for your implementation.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsCandidatesRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            email=email,
            job_ids=job_ids,
            first_name=first_name,
            last_name=last_name,
        )

        req = self._build_request(
            method="GET",
            path="/ats/candidates",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsCandidatesGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsCandidates",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsCandidatesResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_candidates(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                email=email,
                job_ids=job_ids,
                first_name=first_name,
                last_name=last_name,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsCandidatesResponse(
                result=unmarshal_json_response(
                    models.GetAtsCandidatesPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_candidates_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        email: Optional[str] = None,
        job_ids: Optional[List[str]] = None,
        first_name: Optional[str] = None,
        last_name: Optional[str] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsCandidatesResponse]:
        r"""Get candidates

        Retrieve all candidates.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param email: Filter the candidates based on an email address. When set, returns only the candidates where the given `email` is in `email_addresses`. This filter is case-insensitive.
        :param job_ids: Filter by a comma-separated list of job IDs. We will only return candidates that have applied to _any_ of the jobs.
        :param first_name: Filter candidates by first name. This filter is case-insensitive and matches the exact first name. Fuzzy matching might be enabled in the future, so consider this for your implementation.
        :param last_name: Filter candidates by last name. This filter is case-insensitive and matches the exact last name. Fuzzy matching might be enabled in the future, so consider this for your implementation.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsCandidatesRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            email=email,
            job_ids=job_ids,
            first_name=first_name,
            last_name=last_name,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/candidates",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsCandidatesGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsCandidates",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsCandidatesResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_candidates_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                email=email,
                job_ids=job_ids,
                first_name=first_name,
                last_name=last_name,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsCandidatesResponse(
                result=unmarshal_json_response(
                    models.GetAtsCandidatesPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def create_candidate(
        self,
        *,
        candidate: Union[
            models.PostAtsCandidatesRequestBodyCandidate,
            models.PostAtsCandidatesRequestBodyCandidateTypedDict,
        ],
        application: Union[
            models.PostAtsCandidatesRequestBodyApplication,
            models.PostAtsCandidatesRequestBodyApplicationTypedDict,
        ],
        screening_question_answers: Optional[
            Union[
                List[models.PostAtsCandidatesRequestBodyScreeningQuestionAnswer],
                List[
                    models.PostAtsCandidatesRequestBodyScreeningQuestionAnswerTypedDict
                ],
            ]
        ] = None,
        attachments: Optional[
            Union[
                List[models.PostAtsCandidatesRequestBodyAttachment],
                List[models.PostAtsCandidatesRequestBodyAttachmentTypedDict],
            ]
        ] = None,
        source: Optional[
            Union[
                models.PostAtsCandidatesRequestBodySource,
                models.PostAtsCandidatesRequestBodySourceTypedDict,
            ]
        ] = None,
        sourced_by: Optional[
            Union[
                models.PostAtsCandidatesRequestBodySourcedBy,
                models.PostAtsCandidatesRequestBodySourcedByTypedDict,
            ]
        ] = None,
        gdpr_consent: Optional[
            Union[
                models.PostAtsCandidatesRequestBodyGdprConsent,
                models.PostAtsCandidatesRequestBodyGdprConsentTypedDict,
            ]
        ] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsCandidatesRequestBodyRemoteFields,
                models.PostAtsCandidatesRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsCandidatesPositiveResponse:
        r"""Create candidate

        Create a new candidate and application for the specified job.

        <Warning>
        **We recommend using the [Create application](/ats/v1/post-jobs-job-id-applications) endpoint instead.**

        We realized that in practice it was always more about creating _applications_ instead of _candidates_, so we created a new, more aptly named one that you should use instead: [Create application](/ats/v1/post-jobs-job-id-applications)

        Using it also has the benefit that we return the newly created applicant at the root level, so you can easily store its ID.
        </Warning>

        <Note>
        This endpoint requires the permission **Create applications and candidates** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"candidate\": {
        \"first_name\": \"Frank\",
        \"last_name\": \"Doe\",
        \"company\": \"Acme Inc.\",
        \"title\": \"Head of Integrations\",
        \"email_address\": \"frank.doe@example.com\",
        \"phone_number\": \"+1-541-754-3010\",
        \"gender\": \"MALE\",
        \"salary_expectations\": {
        \"amount\": 100000,
        \"period\": \"YEAR\"
        },
        \"availability_date\": \"2021-01-01\",
        \"location\": {
        \"city\": \"New York\",
        \"country\": \"US\",
        \"state\": \"NY\"
        },
        \"social_links\": [
        {
        \"url\": \"https://www.linkedin.com/in/frank-doe-123456789/\"
        },
        {
        \"url\": \"https://twitter.com/frankdoe\"
        }
        ]
        },
        \"application\": {
        \"job_id\": \"BDpgnpZ148nrGh4mYHNxJBgx\",
        \"stage_id\": \"8x3YKRDcuRnwShdh96ShBNn1\"
        },
        \"attachments\": [
        {
        \"name\": \"Frank Doe CV.txt\",
        \"data\": \"SGkgdGhlcmUsIEtvbWJvIGlzIGN1cnJlbnRseSBoaXJpbmcgZW5naW5lZXJzIHRoYXQgbG92ZSB0byB3b3JrIG9uIGRldmVsb3BlciBwcm9kdWN0cy4=\",
        \"type\": \"CV\",
        \"content_type\": \"text/plain\"
        }
        ],
        \"screening_question_answers\": [
        {
        \"question_id\": \"3phFBNXRweGnDmsU9o2vdPuQ\",
        \"answer\": \"Yes\"
        },
        {
        \"question_id\": \"EYJjhMQT3LtVKXnTbnRT8s6U\",
        \"answer\": [
        \"GUzE666zfyjeoCJX6A8n7wh6\",
        \"5WPHzzKAv8cx97KtHRUV96U8\",
        \"7yZfKGzWigXxxRTygqAfHvyE\"
        ]
        }
        ],
        \"remote_fields\": {}
        }
        ```

        :param candidate:
        :param application: Currently, every candidate has one application. If you are interested in talent pools, please contact Kombo.
        :param screening_question_answers: Array of answers to screening questions. Currently, not all question types are supported, and unsupported ones will not be submitted.

            The available questions for a job can be retrieved from the get jobs endpoint. The answers will be validated based on the format of the questions. Make sure to follow this schema to avoid errors.
        :param attachments: An array of the attachments you would like upload.
        :param source: **(⚠️ Deprecated - Use [automatic source writing](/ats/features/application-attribution#automatic-attribution) instead)** Optional source information that will be attached to the candidate. If
            you're a job board or recruiting service, you can use this to make sure your
            customers can see which candidates came from you.

            This is deprecated because writing sources requires users to do some setup in most ATSs.
        :param sourced_by: Credit the recruiter or team member who sourced this candidate.

            While the `source` field tracks the channel/platform (e.g., \"Awesome Jobboard\"), the `sourced_by` field tracks the individual person responsible for finding the candidate.
        :param gdpr_consent: Optional GDPR consent information required in some jurisdictions (like the Czech Republic or Slovakia).
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsCandidatesRequestBody(
            candidate=utils.get_pydantic_model(
                candidate, models.PostAtsCandidatesRequestBodyCandidate
            ),
            application=utils.get_pydantic_model(
                application, models.PostAtsCandidatesRequestBodyApplication
            ),
            screening_question_answers=utils.get_pydantic_model(
                screening_question_answers,
                Optional[
                    List[models.PostAtsCandidatesRequestBodyScreeningQuestionAnswer]
                ],
            ),
            attachments=utils.get_pydantic_model(
                attachments,
                Optional[List[models.PostAtsCandidatesRequestBodyAttachment]],
            ),
            source=utils.get_pydantic_model(
                source, Optional[models.PostAtsCandidatesRequestBodySource]
            ),
            sourced_by=utils.get_pydantic_model(
                sourced_by, Optional[models.PostAtsCandidatesRequestBodySourcedBy]
            ),
            gdpr_consent=utils.get_pydantic_model(
                gdpr_consent, Optional[models.PostAtsCandidatesRequestBodyGdprConsent]
            ),
            remote_fields=utils.get_pydantic_model(
                remote_fields, Optional[models.PostAtsCandidatesRequestBodyRemoteFields]
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/candidates",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsCandidatesGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request, False, False, "json", models.PostAtsCandidatesRequestBody
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsCandidates",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsCandidatesPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def create_candidate_async(
        self,
        *,
        candidate: Union[
            models.PostAtsCandidatesRequestBodyCandidate,
            models.PostAtsCandidatesRequestBodyCandidateTypedDict,
        ],
        application: Union[
            models.PostAtsCandidatesRequestBodyApplication,
            models.PostAtsCandidatesRequestBodyApplicationTypedDict,
        ],
        screening_question_answers: Optional[
            Union[
                List[models.PostAtsCandidatesRequestBodyScreeningQuestionAnswer],
                List[
                    models.PostAtsCandidatesRequestBodyScreeningQuestionAnswerTypedDict
                ],
            ]
        ] = None,
        attachments: Optional[
            Union[
                List[models.PostAtsCandidatesRequestBodyAttachment],
                List[models.PostAtsCandidatesRequestBodyAttachmentTypedDict],
            ]
        ] = None,
        source: Optional[
            Union[
                models.PostAtsCandidatesRequestBodySource,
                models.PostAtsCandidatesRequestBodySourceTypedDict,
            ]
        ] = None,
        sourced_by: Optional[
            Union[
                models.PostAtsCandidatesRequestBodySourcedBy,
                models.PostAtsCandidatesRequestBodySourcedByTypedDict,
            ]
        ] = None,
        gdpr_consent: Optional[
            Union[
                models.PostAtsCandidatesRequestBodyGdprConsent,
                models.PostAtsCandidatesRequestBodyGdprConsentTypedDict,
            ]
        ] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsCandidatesRequestBodyRemoteFields,
                models.PostAtsCandidatesRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsCandidatesPositiveResponse:
        r"""Create candidate

        Create a new candidate and application for the specified job.

        <Warning>
        **We recommend using the [Create application](/ats/v1/post-jobs-job-id-applications) endpoint instead.**

        We realized that in practice it was always more about creating _applications_ instead of _candidates_, so we created a new, more aptly named one that you should use instead: [Create application](/ats/v1/post-jobs-job-id-applications)

        Using it also has the benefit that we return the newly created applicant at the root level, so you can easily store its ID.
        </Warning>

        <Note>
        This endpoint requires the permission **Create applications and candidates** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"candidate\": {
        \"first_name\": \"Frank\",
        \"last_name\": \"Doe\",
        \"company\": \"Acme Inc.\",
        \"title\": \"Head of Integrations\",
        \"email_address\": \"frank.doe@example.com\",
        \"phone_number\": \"+1-541-754-3010\",
        \"gender\": \"MALE\",
        \"salary_expectations\": {
        \"amount\": 100000,
        \"period\": \"YEAR\"
        },
        \"availability_date\": \"2021-01-01\",
        \"location\": {
        \"city\": \"New York\",
        \"country\": \"US\",
        \"state\": \"NY\"
        },
        \"social_links\": [
        {
        \"url\": \"https://www.linkedin.com/in/frank-doe-123456789/\"
        },
        {
        \"url\": \"https://twitter.com/frankdoe\"
        }
        ]
        },
        \"application\": {
        \"job_id\": \"BDpgnpZ148nrGh4mYHNxJBgx\",
        \"stage_id\": \"8x3YKRDcuRnwShdh96ShBNn1\"
        },
        \"attachments\": [
        {
        \"name\": \"Frank Doe CV.txt\",
        \"data\": \"SGkgdGhlcmUsIEtvbWJvIGlzIGN1cnJlbnRseSBoaXJpbmcgZW5naW5lZXJzIHRoYXQgbG92ZSB0byB3b3JrIG9uIGRldmVsb3BlciBwcm9kdWN0cy4=\",
        \"type\": \"CV\",
        \"content_type\": \"text/plain\"
        }
        ],
        \"screening_question_answers\": [
        {
        \"question_id\": \"3phFBNXRweGnDmsU9o2vdPuQ\",
        \"answer\": \"Yes\"
        },
        {
        \"question_id\": \"EYJjhMQT3LtVKXnTbnRT8s6U\",
        \"answer\": [
        \"GUzE666zfyjeoCJX6A8n7wh6\",
        \"5WPHzzKAv8cx97KtHRUV96U8\",
        \"7yZfKGzWigXxxRTygqAfHvyE\"
        ]
        }
        ],
        \"remote_fields\": {}
        }
        ```

        :param candidate:
        :param application: Currently, every candidate has one application. If you are interested in talent pools, please contact Kombo.
        :param screening_question_answers: Array of answers to screening questions. Currently, not all question types are supported, and unsupported ones will not be submitted.

            The available questions for a job can be retrieved from the get jobs endpoint. The answers will be validated based on the format of the questions. Make sure to follow this schema to avoid errors.
        :param attachments: An array of the attachments you would like upload.
        :param source: **(⚠️ Deprecated - Use [automatic source writing](/ats/features/application-attribution#automatic-attribution) instead)** Optional source information that will be attached to the candidate. If
            you're a job board or recruiting service, you can use this to make sure your
            customers can see which candidates came from you.

            This is deprecated because writing sources requires users to do some setup in most ATSs.
        :param sourced_by: Credit the recruiter or team member who sourced this candidate.

            While the `source` field tracks the channel/platform (e.g., \"Awesome Jobboard\"), the `sourced_by` field tracks the individual person responsible for finding the candidate.
        :param gdpr_consent: Optional GDPR consent information required in some jurisdictions (like the Czech Republic or Slovakia).
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsCandidatesRequestBody(
            candidate=utils.get_pydantic_model(
                candidate, models.PostAtsCandidatesRequestBodyCandidate
            ),
            application=utils.get_pydantic_model(
                application, models.PostAtsCandidatesRequestBodyApplication
            ),
            screening_question_answers=utils.get_pydantic_model(
                screening_question_answers,
                Optional[
                    List[models.PostAtsCandidatesRequestBodyScreeningQuestionAnswer]
                ],
            ),
            attachments=utils.get_pydantic_model(
                attachments,
                Optional[List[models.PostAtsCandidatesRequestBodyAttachment]],
            ),
            source=utils.get_pydantic_model(
                source, Optional[models.PostAtsCandidatesRequestBodySource]
            ),
            sourced_by=utils.get_pydantic_model(
                sourced_by, Optional[models.PostAtsCandidatesRequestBodySourcedBy]
            ),
            gdpr_consent=utils.get_pydantic_model(
                gdpr_consent, Optional[models.PostAtsCandidatesRequestBodyGdprConsent]
            ),
            remote_fields=utils.get_pydantic_model(
                remote_fields, Optional[models.PostAtsCandidatesRequestBodyRemoteFields]
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/candidates",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsCandidatesGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request, False, False, "json", models.PostAtsCandidatesRequestBody
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsCandidates",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsCandidatesPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_candidate_attachments(
        self,
        *,
        candidate_id: str,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.GetAtsCandidatesCandidateIDAttachmentsPositiveResponse:
        r"""Get candidate attachments

        Get attachments from a candidate, including all attachments of all of their applications.

        <Note>
        This endpoint requires the permission **Read document attachments** to be enabled in [your scope config](/scopes).
        </Note>

        :param candidate_id: The Kombo ID of the candidate you want to obtain attachments for.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsCandidatesCandidateIDAttachmentsRequest(
            candidate_id=candidate_id,
        )

        req = self._build_request(
            method="GET",
            path="/ats/candidates/{candidate_id}/attachments",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsCandidatesCandidateIDAttachmentsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsCandidatesCandidateIdAttachments",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.GetAtsCandidatesCandidateIDAttachmentsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_candidate_attachments_async(
        self,
        *,
        candidate_id: str,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.GetAtsCandidatesCandidateIDAttachmentsPositiveResponse:
        r"""Get candidate attachments

        Get attachments from a candidate, including all attachments of all of their applications.

        <Note>
        This endpoint requires the permission **Read document attachments** to be enabled in [your scope config](/scopes).
        </Note>

        :param candidate_id: The Kombo ID of the candidate you want to obtain attachments for.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsCandidatesCandidateIDAttachmentsRequest(
            candidate_id=candidate_id,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/candidates/{candidate_id}/attachments",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsCandidatesCandidateIDAttachmentsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsCandidatesCandidateIdAttachments",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.GetAtsCandidatesCandidateIDAttachmentsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def add_candidate_attachment(
        self,
        *,
        candidate_id: str,
        attachment: Union[
            models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyAttachment,
            models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyAttachmentTypedDict,
        ],
        remote_fields: Optional[
            Union[
                models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyRemoteFields,
                models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsCandidatesCandidateIDAttachmentsPositiveResponse:
        r"""Add attachment to candidate

        Uploads an attachment file for the specified candidate.

        <Warning>
        **We recommend using the [add attachment to application](/ats/v1/post-applications-application-id-attachments) endpoint instead.**

        We realized that in practice it was always more about adding attachments to _applications_ instead of _candidates_, so we created a new, more aptly named one that you should use instead: [add attachment to application](/ats/v1/post-applications-application-id-attachments)
        </Warning>

        <Note>
        This endpoint requires the permission **Add attachments** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"candidate_id\": \"GRKdd9dibYKKCrmGRSMJf3wu\",
        \"attachment\": {
        \"name\": \"Frank Doe CV.txt\",
        \"data\": \"SGkgdGhlcmUsIEtvbWJvIGlzIGN1cnJlbnRseSBoaXJpbmcgZW5naW5lZXJzIHRoYXQgbG92ZSB0byB3b3JrIG9uIGRldmVsb3BlciBwcm9kdWN0cy4=\",
        \"type\": \"CV\",
        \"content_type\": \"text/plain\"
        }
        }
        ```

        :param candidate_id: The Kombo ID of the candidate you want to add the attachment to.
        :param attachment:
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsCandidatesCandidateIDAttachmentsRequest(
            candidate_id=candidate_id,
            body=models.PostAtsCandidatesCandidateIDAttachmentsRequestBody(
                attachment=utils.get_pydantic_model(
                    attachment,
                    models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyAttachment,
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/candidates/{candidate_id}/attachments",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsCandidatesCandidateIDAttachmentsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsCandidatesCandidateIDAttachmentsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsCandidatesCandidateIdAttachments",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsCandidatesCandidateIDAttachmentsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def add_candidate_attachment_async(
        self,
        *,
        candidate_id: str,
        attachment: Union[
            models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyAttachment,
            models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyAttachmentTypedDict,
        ],
        remote_fields: Optional[
            Union[
                models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyRemoteFields,
                models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsCandidatesCandidateIDAttachmentsPositiveResponse:
        r"""Add attachment to candidate

        Uploads an attachment file for the specified candidate.

        <Warning>
        **We recommend using the [add attachment to application](/ats/v1/post-applications-application-id-attachments) endpoint instead.**

        We realized that in practice it was always more about adding attachments to _applications_ instead of _candidates_, so we created a new, more aptly named one that you should use instead: [add attachment to application](/ats/v1/post-applications-application-id-attachments)
        </Warning>

        <Note>
        This endpoint requires the permission **Add attachments** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"candidate_id\": \"GRKdd9dibYKKCrmGRSMJf3wu\",
        \"attachment\": {
        \"name\": \"Frank Doe CV.txt\",
        \"data\": \"SGkgdGhlcmUsIEtvbWJvIGlzIGN1cnJlbnRseSBoaXJpbmcgZW5naW5lZXJzIHRoYXQgbG92ZSB0byB3b3JrIG9uIGRldmVsb3BlciBwcm9kdWN0cy4=\",
        \"type\": \"CV\",
        \"content_type\": \"text/plain\"
        }
        }
        ```

        :param candidate_id: The Kombo ID of the candidate you want to add the attachment to.
        :param attachment:
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsCandidatesCandidateIDAttachmentsRequest(
            candidate_id=candidate_id,
            body=models.PostAtsCandidatesCandidateIDAttachmentsRequestBody(
                attachment=utils.get_pydantic_model(
                    attachment,
                    models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyAttachment,
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsCandidatesCandidateIDAttachmentsRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/candidates/{candidate_id}/attachments",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsCandidatesCandidateIDAttachmentsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsCandidatesCandidateIDAttachmentsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsCandidatesCandidateIdAttachments",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsCandidatesCandidateIDAttachmentsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def add_candidate_result_link(
        self,
        *,
        candidate_id: str,
        label: str,
        url: str,
        details: Optional[
            Union[
                models.PostAtsCandidatesCandidateIDResultLinksRequestBodyDetails,
                models.PostAtsCandidatesCandidateIDResultLinksRequestBodyDetailsTypedDict,
            ]
        ] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsCandidatesCandidateIDResultLinksRequestBodyRemoteFields,
                models.PostAtsCandidatesCandidateIDResultLinksRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsCandidatesCandidateIDResultLinksPositiveResponse:
        r"""Add result link to candidate

        Add a result link to a candidate.

        <Warning>
        **We recommend to use [add result link to application](/ats/v1/post-applications-application-id-result-links) instead.**

        This can, for example, be used to link a candidate back to a test result/assessment in your application. As not all ATS tools have a \"result link\" feature, we sometimes repurpose other fields to expose it.

        </Warning>


        <Note>
        This endpoint requires the permission **Add result links** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"label\": \"Assessment Result\",
        \"url\": \"https://example.com/test-results/5BtP1WC1UboS7CF3yxjKcvjG\",
        \"details\": {
        \"custom_field_name_prefix\": \"Acme:\",
        \"attributes\": [
        {
        \"key\": \"Score\",
        \"value\": \"100%\"
        },
        {
        \"key\": \"Time\",
        \"value\": \"2:30h\"
        }
        ]
        },
        \"remote_fields\": {}
        }
        ```

        :param candidate_id: The Kombo ID of the candidate you want to add the result link to.
        :param label: If the system allows us to display a display name for the link, we will use this label.
        :param url: URL of the link.
        :param details: Additional details with attributes that will be added to the result. This can be percentages, scores, or any text.

            We generally recommend using short attribute keys and a short custom_field_name_prefix to avoid overflowing the ATS UI.
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsCandidatesCandidateIDResultLinksRequest(
            candidate_id=candidate_id,
            body=models.PostAtsCandidatesCandidateIDResultLinksRequestBody(
                label=label,
                url=url,
                details=utils.get_pydantic_model(
                    details,
                    Optional[
                        models.PostAtsCandidatesCandidateIDResultLinksRequestBodyDetails
                    ],
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsCandidatesCandidateIDResultLinksRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/candidates/{candidate_id}/result-links",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsCandidatesCandidateIDResultLinksGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsCandidatesCandidateIDResultLinksRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsCandidatesCandidateIdResultLinks",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsCandidatesCandidateIDResultLinksPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def add_candidate_result_link_async(
        self,
        *,
        candidate_id: str,
        label: str,
        url: str,
        details: Optional[
            Union[
                models.PostAtsCandidatesCandidateIDResultLinksRequestBodyDetails,
                models.PostAtsCandidatesCandidateIDResultLinksRequestBodyDetailsTypedDict,
            ]
        ] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsCandidatesCandidateIDResultLinksRequestBodyRemoteFields,
                models.PostAtsCandidatesCandidateIDResultLinksRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsCandidatesCandidateIDResultLinksPositiveResponse:
        r"""Add result link to candidate

        Add a result link to a candidate.

        <Warning>
        **We recommend to use [add result link to application](/ats/v1/post-applications-application-id-result-links) instead.**

        This can, for example, be used to link a candidate back to a test result/assessment in your application. As not all ATS tools have a \"result link\" feature, we sometimes repurpose other fields to expose it.

        </Warning>


        <Note>
        This endpoint requires the permission **Add result links** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"label\": \"Assessment Result\",
        \"url\": \"https://example.com/test-results/5BtP1WC1UboS7CF3yxjKcvjG\",
        \"details\": {
        \"custom_field_name_prefix\": \"Acme:\",
        \"attributes\": [
        {
        \"key\": \"Score\",
        \"value\": \"100%\"
        },
        {
        \"key\": \"Time\",
        \"value\": \"2:30h\"
        }
        ]
        },
        \"remote_fields\": {}
        }
        ```

        :param candidate_id: The Kombo ID of the candidate you want to add the result link to.
        :param label: If the system allows us to display a display name for the link, we will use this label.
        :param url: URL of the link.
        :param details: Additional details with attributes that will be added to the result. This can be percentages, scores, or any text.

            We generally recommend using short attribute keys and a short custom_field_name_prefix to avoid overflowing the ATS UI.
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsCandidatesCandidateIDResultLinksRequest(
            candidate_id=candidate_id,
            body=models.PostAtsCandidatesCandidateIDResultLinksRequestBody(
                label=label,
                url=url,
                details=utils.get_pydantic_model(
                    details,
                    Optional[
                        models.PostAtsCandidatesCandidateIDResultLinksRequestBodyDetails
                    ],
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsCandidatesCandidateIDResultLinksRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/candidates/{candidate_id}/result-links",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsCandidatesCandidateIDResultLinksGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsCandidatesCandidateIDResultLinksRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsCandidatesCandidateIdResultLinks",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsCandidatesCandidateIDResultLinksPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def add_candidate_tag(
        self,
        *,
        candidate_id: str,
        tag: Union[
            models.PostAtsCandidatesCandidateIDTagsRequestBodyTag,
            models.PostAtsCandidatesCandidateIDTagsRequestBodyTagTypedDict,
        ],
        remote_fields: Optional[
            Union[
                models.PostAtsCandidatesCandidateIDTagsRequestBodyRemoteFields,
                models.PostAtsCandidatesCandidateIDTagsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsCandidatesCandidateIDTagsPositiveResponse:
        r"""Add tag to candidate

        Add a tag to a candidate.

        Kombo takes care of creating the tag if required, finding out the right ID, and appending it to the list of tags.

        <Note>
        This endpoint requires the permission **Manage tags** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"tag\": {
        \"name\": \"Excellent Fit\"
        }
        }
        ```

        :param candidate_id: The Kombo ID of the candidate you want to add the tag to.
        :param tag:
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsCandidatesCandidateIDTagsRequest(
            candidate_id=candidate_id,
            body=models.PostAtsCandidatesCandidateIDTagsRequestBody(
                tag=utils.get_pydantic_model(
                    tag, models.PostAtsCandidatesCandidateIDTagsRequestBodyTag
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsCandidatesCandidateIDTagsRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/candidates/{candidate_id}/tags",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsCandidatesCandidateIDTagsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsCandidatesCandidateIDTagsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsCandidatesCandidateIdTags",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsCandidatesCandidateIDTagsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def add_candidate_tag_async(
        self,
        *,
        candidate_id: str,
        tag: Union[
            models.PostAtsCandidatesCandidateIDTagsRequestBodyTag,
            models.PostAtsCandidatesCandidateIDTagsRequestBodyTagTypedDict,
        ],
        remote_fields: Optional[
            Union[
                models.PostAtsCandidatesCandidateIDTagsRequestBodyRemoteFields,
                models.PostAtsCandidatesCandidateIDTagsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsCandidatesCandidateIDTagsPositiveResponse:
        r"""Add tag to candidate

        Add a tag to a candidate.

        Kombo takes care of creating the tag if required, finding out the right ID, and appending it to the list of tags.

        <Note>
        This endpoint requires the permission **Manage tags** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"tag\": {
        \"name\": \"Excellent Fit\"
        }
        }
        ```

        :param candidate_id: The Kombo ID of the candidate you want to add the tag to.
        :param tag:
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsCandidatesCandidateIDTagsRequest(
            candidate_id=candidate_id,
            body=models.PostAtsCandidatesCandidateIDTagsRequestBody(
                tag=utils.get_pydantic_model(
                    tag, models.PostAtsCandidatesCandidateIDTagsRequestBodyTag
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsCandidatesCandidateIDTagsRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/candidates/{candidate_id}/tags",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsCandidatesCandidateIDTagsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsCandidatesCandidateIDTagsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsCandidatesCandidateIdTags",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsCandidatesCandidateIDTagsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def remove_candidate_tag(
        self,
        *,
        candidate_id: str,
        tag: Union[
            models.DeleteAtsCandidatesCandidateIDTagsRequestBodyTag,
            models.DeleteAtsCandidatesCandidateIDTagsRequestBodyTagTypedDict,
        ],
        remote_fields: Optional[
            Union[
                models.DeleteAtsCandidatesCandidateIDTagsRequestBodyRemoteFields,
                models.DeleteAtsCandidatesCandidateIDTagsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.DeleteAtsCandidatesCandidateIDTagsPositiveResponse:
        r"""Remove tag from candidate

        Remove a tag from a candidate based on its name.

        This will also succeed if the tag does not exist on the candidate.

        <Note>
        This endpoint requires the permission **Manage tags** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"tag\": {
        \"name\": \"Excellent Fit\"
        }
        }
        ```

        :param candidate_id: The Kombo ID of the candidate you want to remove the tag from.
        :param tag:
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.DeleteAtsCandidatesCandidateIDTagsRequest(
            candidate_id=candidate_id,
            body=models.DeleteAtsCandidatesCandidateIDTagsRequestBody(
                tag=utils.get_pydantic_model(
                    tag, models.DeleteAtsCandidatesCandidateIDTagsRequestBodyTag
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.DeleteAtsCandidatesCandidateIDTagsRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="DELETE",
            path="/ats/candidates/{candidate_id}/tags",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.DeleteAtsCandidatesCandidateIDTagsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.DeleteAtsCandidatesCandidateIDTagsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="DeleteAtsCandidatesCandidateIdTags",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.DeleteAtsCandidatesCandidateIDTagsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def remove_candidate_tag_async(
        self,
        *,
        candidate_id: str,
        tag: Union[
            models.DeleteAtsCandidatesCandidateIDTagsRequestBodyTag,
            models.DeleteAtsCandidatesCandidateIDTagsRequestBodyTagTypedDict,
        ],
        remote_fields: Optional[
            Union[
                models.DeleteAtsCandidatesCandidateIDTagsRequestBodyRemoteFields,
                models.DeleteAtsCandidatesCandidateIDTagsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.DeleteAtsCandidatesCandidateIDTagsPositiveResponse:
        r"""Remove tag from candidate

        Remove a tag from a candidate based on its name.

        This will also succeed if the tag does not exist on the candidate.

        <Note>
        This endpoint requires the permission **Manage tags** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"tag\": {
        \"name\": \"Excellent Fit\"
        }
        }
        ```

        :param candidate_id: The Kombo ID of the candidate you want to remove the tag from.
        :param tag:
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.DeleteAtsCandidatesCandidateIDTagsRequest(
            candidate_id=candidate_id,
            body=models.DeleteAtsCandidatesCandidateIDTagsRequestBody(
                tag=utils.get_pydantic_model(
                    tag, models.DeleteAtsCandidatesCandidateIDTagsRequestBodyTag
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.DeleteAtsCandidatesCandidateIDTagsRequestBodyRemoteFields
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="DELETE",
            path="/ats/candidates/{candidate_id}/tags",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.DeleteAtsCandidatesCandidateIDTagsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.DeleteAtsCandidatesCandidateIDTagsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="DeleteAtsCandidatesCandidateIdTags",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.DeleteAtsCandidatesCandidateIDTagsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_tags(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsTagsResponse]:
        r"""Get tags

        Retrieve all tags.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsTagsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
        )

        req = self._build_request(
            method="GET",
            path="/ats/tags",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsTagsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsTags",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsTagsResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_tags(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsTagsResponse(
                result=unmarshal_json_response(
                    models.GetAtsTagsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_tags_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsTagsResponse]:
        r"""Get tags

        Retrieve all tags.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsTagsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/tags",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsTagsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsTags",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsTagsResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_tags_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsTagsResponse(
                result=unmarshal_json_response(
                    models.GetAtsTagsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_application_stages(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsApplicationStagesResponse]:
        r"""Get application stages

        Get all application stages available in the ATS.

        <Warning>
        **This endpoint is deprecated!**

        Get all application stages available in the ATS. This is deprecated because most ATS systems have separate sets of stages for each job. We'd recommend using the `stages` property from the [GET Jobs endpoint](/ats/v1/get-jobs) instead.

        **Important**: Using global stages can cause \"Stage not found\" errors when moving applications, especially with systems like Workable that have job-specific stages.

        [Moving Applications Guide](/ats/implementation-guide/moving-and-rejecting-candidates).
        </Warning>

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsApplicationStagesRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
        )

        req = self._build_request(
            method="GET",
            path="/ats/application-stages",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsApplicationStagesGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsApplicationStages",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsApplicationStagesResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_application_stages(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsApplicationStagesResponse(
                result=unmarshal_json_response(
                    models.GetAtsApplicationStagesPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_application_stages_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsApplicationStagesResponse]:
        r"""Get application stages

        Get all application stages available in the ATS.

        <Warning>
        **This endpoint is deprecated!**

        Get all application stages available in the ATS. This is deprecated because most ATS systems have separate sets of stages for each job. We'd recommend using the `stages` property from the [GET Jobs endpoint](/ats/v1/get-jobs) instead.

        **Important**: Using global stages can cause \"Stage not found\" errors when moving applications, especially with systems like Workable that have job-specific stages.

        [Moving Applications Guide](/ats/implementation-guide/moving-and-rejecting-candidates).
        </Warning>

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsApplicationStagesRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/application-stages",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsApplicationStagesGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsApplicationStages",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsApplicationStagesResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_application_stages_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsApplicationStagesResponse(
                result=unmarshal_json_response(
                    models.GetAtsApplicationStagesPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_jobs(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        job_codes: Optional[List[str]] = None,
        post_url: Optional[str] = None,
        statuses: Optional[List[str]] = None,
        employment_types: Optional[List[str]] = None,
        visibilities: Optional[List[str]] = None,
        remote_created_after: Optional[datetime] = None,
        name_contains: Optional[str] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsJobsResponse]:
        r"""Get jobs

        Retrieve all jobs.

        Visit our in-depth guides to learn more about:

        - 🔄 [Getting updates of the data](/ats/features/implementation-guide/reading-jobs#getting-updates-of-the-data)
        - ❗ [Handling failing syncs](/ats/features/implementation-guide/reading-jobs#handling-failing-syncs)
        - 🔍 [Letting your customer choose which jobs to expose](/ats/features/implementation-guide/reading-jobs#let-your-customer-choose-which-jobs-to-expose-to-you)
        - 🔗 [Matching jobs in your database to ATS jobs](/ats/features/implementation-guide/reading-jobs#match-jobs-in-your-database-to-ats-jobs)
        - 🗑️ [Reacting to deleted/closed jobs](/ats/features/implementation-guide/reading-jobs#reacting-to-deleted-closed-jobs)

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param job_codes: Filter by a comma-separated list of job codes.
        :param post_url: Filter by the `post_url` field. Can be used to find a job based on its public posting URL.
        :param statuses: Filter by a comma-separated list of `OPEN`, `CLOSED`, `DRAFT`, `ARCHIVED`

            Leave this blank to get results matching all values.
        :param employment_types: Filter by a comma-separated list of `FULL_TIME`, `PART_TIME`, `CONTRACT`, `SEASONAL`, `INTERNSHIP`

            Leave this blank to get results matching all values.
        :param visibilities: Filter by a comma-separated list of `PUBLIC`, `INTERNAL`, `UNLISTED`, `CONFIDENTIAL`

            Leave this blank to get results matching all values.
        :param remote_created_after: Filter jobs by the day they were created in the remote system. This allows you to get jobs that were created on or after a certain day.
        :param name_contains: Filter by the `name` field. Can be used to find a job by keywords present in the job name.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsJobsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            job_codes=job_codes,
            post_url=post_url,
            statuses=statuses,
            employment_types=employment_types,
            visibilities=visibilities,
            remote_created_after=remote_created_after,
            name_contains=name_contains,
        )

        req = self._build_request(
            method="GET",
            path="/ats/jobs",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsJobsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsJobs",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsJobsResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_jobs(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                job_codes=job_codes,
                post_url=post_url,
                statuses=statuses,
                employment_types=employment_types,
                visibilities=visibilities,
                remote_created_after=remote_created_after,
                name_contains=name_contains,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsJobsResponse(
                result=unmarshal_json_response(
                    models.GetAtsJobsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_jobs_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        job_codes: Optional[List[str]] = None,
        post_url: Optional[str] = None,
        statuses: Optional[List[str]] = None,
        employment_types: Optional[List[str]] = None,
        visibilities: Optional[List[str]] = None,
        remote_created_after: Optional[datetime] = None,
        name_contains: Optional[str] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsJobsResponse]:
        r"""Get jobs

        Retrieve all jobs.

        Visit our in-depth guides to learn more about:

        - 🔄 [Getting updates of the data](/ats/features/implementation-guide/reading-jobs#getting-updates-of-the-data)
        - ❗ [Handling failing syncs](/ats/features/implementation-guide/reading-jobs#handling-failing-syncs)
        - 🔍 [Letting your customer choose which jobs to expose](/ats/features/implementation-guide/reading-jobs#let-your-customer-choose-which-jobs-to-expose-to-you)
        - 🔗 [Matching jobs in your database to ATS jobs](/ats/features/implementation-guide/reading-jobs#match-jobs-in-your-database-to-ats-jobs)
        - 🗑️ [Reacting to deleted/closed jobs](/ats/features/implementation-guide/reading-jobs#reacting-to-deleted-closed-jobs)

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param job_codes: Filter by a comma-separated list of job codes.
        :param post_url: Filter by the `post_url` field. Can be used to find a job based on its public posting URL.
        :param statuses: Filter by a comma-separated list of `OPEN`, `CLOSED`, `DRAFT`, `ARCHIVED`

            Leave this blank to get results matching all values.
        :param employment_types: Filter by a comma-separated list of `FULL_TIME`, `PART_TIME`, `CONTRACT`, `SEASONAL`, `INTERNSHIP`

            Leave this blank to get results matching all values.
        :param visibilities: Filter by a comma-separated list of `PUBLIC`, `INTERNAL`, `UNLISTED`, `CONFIDENTIAL`

            Leave this blank to get results matching all values.
        :param remote_created_after: Filter jobs by the day they were created in the remote system. This allows you to get jobs that were created on or after a certain day.
        :param name_contains: Filter by the `name` field. Can be used to find a job by keywords present in the job name.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsJobsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            job_codes=job_codes,
            post_url=post_url,
            statuses=statuses,
            employment_types=employment_types,
            visibilities=visibilities,
            remote_created_after=remote_created_after,
            name_contains=name_contains,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/jobs",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsJobsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsJobs",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsJobsResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_jobs_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                job_codes=job_codes,
                post_url=post_url,
                statuses=statuses,
                employment_types=employment_types,
                visibilities=visibilities,
                remote_created_after=remote_created_after,
                name_contains=name_contains,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsJobsResponse(
                result=unmarshal_json_response(
                    models.GetAtsJobsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def create_application(
        self,
        *,
        job_id: str,
        candidate: Union[
            models.PostAtsJobsJobIDApplicationsRequestBodyCandidate,
            models.PostAtsJobsJobIDApplicationsRequestBodyCandidateTypedDict,
        ],
        stage_id: Optional[str] = None,
        attachments: Optional[
            Union[
                List[models.PostAtsJobsJobIDApplicationsRequestBodyAttachment],
                List[models.PostAtsJobsJobIDApplicationsRequestBodyAttachmentTypedDict],
            ]
        ] = None,
        source: Optional[
            Union[
                models.PostAtsJobsJobIDApplicationsRequestBodySource,
                models.PostAtsJobsJobIDApplicationsRequestBodySourceTypedDict,
            ]
        ] = None,
        sourced_by: Optional[
            Union[
                models.PostAtsJobsJobIDApplicationsRequestBodySourcedBy,
                models.PostAtsJobsJobIDApplicationsRequestBodySourcedByTypedDict,
            ]
        ] = None,
        gdpr_consent: Optional[
            Union[
                models.PostAtsJobsJobIDApplicationsRequestBodyGdprConsent,
                models.PostAtsJobsJobIDApplicationsRequestBodyGdprConsentTypedDict,
            ]
        ] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsJobsJobIDApplicationsRequestBodyRemoteFields,
                models.PostAtsJobsJobIDApplicationsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        screening_question_answers: Optional[
            Union[
                List[
                    models.PostAtsJobsJobIDApplicationsRequestBodyScreeningQuestionAnswer
                ],
                List[
                    models.PostAtsJobsJobIDApplicationsRequestBodyScreeningQuestionAnswerTypedDict
                ],
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsJobsJobIDApplicationsPositiveResponse:
        r"""Create application

        Create a new application and candidate for the specified job.

        Visit our in-depth guides to learn more about:

        - 🌐 [Setting the source of the application](/ats/features/implementation-guide/creating-applications#set-the-source-of-the-application)
        - 📎 [Uploading attachments with the application](/ats/features/implementation-guide/creating-applications#upload-attachments-with-the-application)
        - ♻️ [Retry behaviour](/ats/features/implementation-guide/creating-applications#retry-behaviour)
        - ✏️ [Writing answers to screening questions](/ats/features/implementation-guide/creating-applications#write-answers-to-screening-questions)
        - ⚠️ [Handling ATS-specific limitations](/ats/features/implementation-guide/creating-applications#handle-ats-specific-limitations)

        <Note>
        This endpoint requires the permission **Create applications and candidates** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"candidate\": {
        \"first_name\": \"Frank\",
        \"last_name\": \"Doe\",
        \"company\": \"Acme Inc.\",
        \"title\": \"Head of Integrations\",
        \"email_address\": \"frank.doe@example.com\",
        \"phone_number\": \"+1-541-754-3010\",
        \"gender\": \"MALE\",
        \"salary_expectations\": {
        \"amount\": 100000,
        \"period\": \"YEAR\"
        },
        \"availability_date\": \"2021-01-01\",
        \"location\": {
        \"city\": \"New York\",
        \"country\": \"US\"
        }
        },
        \"stage_id\": \"8x3YKRDcuRnwShdh96ShBNn1\",
        \"attachments\": [
        {
        \"name\": \"Frank Doe CV.txt\",
        \"data\": \"SGkgdGhlcmUsIEtvbWJvIGlzIGN1cnJlbnRseSBoaXJpbmcgZW5naW5lZXJzIHRoYXQgbG92ZSB0byB3b3JrIG9uIGRldmVsb3BlciBwcm9kdWN0cy4=\",
        \"type\": \"CV\",
        \"content_type\": \"text/plain\"
        }
        ],
        \"screening_question_answers\": [
        {
        \"question_id\": \"3phFBNXRweGnDmsU9o2vdPuQ\",
        \"answer\": \"Yes\"
        },
        {
        \"question_id\": \"EYJjhMQT3LtVKXnTbnRT8s6U\",
        \"answer\": [
        \"GUzE666zfyjeoCJX6A8n7wh6\",
        \"5WPHzzKAv8cx97KtHRUV96U8\",
        \"7yZfKGzWigXxxRTygqAfHvyE\"
        ]
        }
        ],
        \"remote_fields\": {}
        }
        ```

        :param job_id: The Kombo ID or Remote ID of the Job this candidate should apply for. If you want to use the ID of the integrated system (remote_id) you need to prefix the id with \"remote:\". You can use the remote ID if you do not want to sync jobs.
        :param candidate:
        :param stage_id: Stage this candidate should be in. If left out, the default stage for this job will be used. You can obtain the possible `stage_id`s from the `get-jobs` endpoint.
        :param attachments: Array of the attachments you would like to upload. The first CV in the attachments will be treated as the resume of the candidate when the tool allows previewing a resume.
        :param source: **(⚠️ Deprecated - Use [automatic source writing](/ats/features/application-attribution#automatic-attribution) instead)** Optional source information that will be attached to the candidate. If
            you're a job board or recruiting service, you can use this to make sure your
            customers can see which candidates came from you.

            This is deprecated because writing sources requires users to do some setup in most ATSs.
        :param sourced_by: Credit the recruiter or team member who sourced this candidate.

            While the `source` field tracks the channel/platform (e.g., \"Awesome Jobboard\"), the `sourced_by` field tracks the individual person responsible for finding the candidate.
        :param gdpr_consent: Optional GDPR consent information required in some jurisdictions (like the Czech Republic or Slovakia).
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param screening_question_answers: Array of answers to screening questions. Currently, not all question types are supported, and unsupported ones will not be submitted.

            The available questions for a job can be retrieved from the get jobs endpoint. The answers will be validated based on the format of the questions. Make sure to follow this schema to avoid errors.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsJobsJobIDApplicationsRequest(
            job_id=job_id,
            body=models.PostAtsJobsJobIDApplicationsRequestBody(
                stage_id=stage_id,
                candidate=utils.get_pydantic_model(
                    candidate, models.PostAtsJobsJobIDApplicationsRequestBodyCandidate
                ),
                attachments=utils.get_pydantic_model(
                    attachments,
                    Optional[
                        List[models.PostAtsJobsJobIDApplicationsRequestBodyAttachment]
                    ],
                ),
                source=utils.get_pydantic_model(
                    source,
                    Optional[models.PostAtsJobsJobIDApplicationsRequestBodySource],
                ),
                sourced_by=utils.get_pydantic_model(
                    sourced_by,
                    Optional[models.PostAtsJobsJobIDApplicationsRequestBodySourcedBy],
                ),
                gdpr_consent=utils.get_pydantic_model(
                    gdpr_consent,
                    Optional[models.PostAtsJobsJobIDApplicationsRequestBodyGdprConsent],
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsJobsJobIDApplicationsRequestBodyRemoteFields
                    ],
                ),
                screening_question_answers=utils.get_pydantic_model(
                    screening_question_answers,
                    Optional[
                        List[
                            models.PostAtsJobsJobIDApplicationsRequestBodyScreeningQuestionAnswer
                        ]
                    ],
                ),
            ),
        )

        req = self._build_request(
            method="POST",
            path="/ats/jobs/{job_id}/applications",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsJobsJobIDApplicationsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsJobsJobIDApplicationsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsJobsJobIdApplications",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsJobsJobIDApplicationsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def create_application_async(
        self,
        *,
        job_id: str,
        candidate: Union[
            models.PostAtsJobsJobIDApplicationsRequestBodyCandidate,
            models.PostAtsJobsJobIDApplicationsRequestBodyCandidateTypedDict,
        ],
        stage_id: Optional[str] = None,
        attachments: Optional[
            Union[
                List[models.PostAtsJobsJobIDApplicationsRequestBodyAttachment],
                List[models.PostAtsJobsJobIDApplicationsRequestBodyAttachmentTypedDict],
            ]
        ] = None,
        source: Optional[
            Union[
                models.PostAtsJobsJobIDApplicationsRequestBodySource,
                models.PostAtsJobsJobIDApplicationsRequestBodySourceTypedDict,
            ]
        ] = None,
        sourced_by: Optional[
            Union[
                models.PostAtsJobsJobIDApplicationsRequestBodySourcedBy,
                models.PostAtsJobsJobIDApplicationsRequestBodySourcedByTypedDict,
            ]
        ] = None,
        gdpr_consent: Optional[
            Union[
                models.PostAtsJobsJobIDApplicationsRequestBodyGdprConsent,
                models.PostAtsJobsJobIDApplicationsRequestBodyGdprConsentTypedDict,
            ]
        ] = None,
        remote_fields: Optional[
            Union[
                models.PostAtsJobsJobIDApplicationsRequestBodyRemoteFields,
                models.PostAtsJobsJobIDApplicationsRequestBodyRemoteFieldsTypedDict,
            ]
        ] = None,
        screening_question_answers: Optional[
            Union[
                List[
                    models.PostAtsJobsJobIDApplicationsRequestBodyScreeningQuestionAnswer
                ],
                List[
                    models.PostAtsJobsJobIDApplicationsRequestBodyScreeningQuestionAnswerTypedDict
                ],
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsJobsJobIDApplicationsPositiveResponse:
        r"""Create application

        Create a new application and candidate for the specified job.

        Visit our in-depth guides to learn more about:

        - 🌐 [Setting the source of the application](/ats/features/implementation-guide/creating-applications#set-the-source-of-the-application)
        - 📎 [Uploading attachments with the application](/ats/features/implementation-guide/creating-applications#upload-attachments-with-the-application)
        - ♻️ [Retry behaviour](/ats/features/implementation-guide/creating-applications#retry-behaviour)
        - ✏️ [Writing answers to screening questions](/ats/features/implementation-guide/creating-applications#write-answers-to-screening-questions)
        - ⚠️ [Handling ATS-specific limitations](/ats/features/implementation-guide/creating-applications#handle-ats-specific-limitations)

        <Note>
        This endpoint requires the permission **Create applications and candidates** to be enabled in [your scope config](/scopes).
        </Note>

        ### Example Request Body

        ```json
        {
        \"candidate\": {
        \"first_name\": \"Frank\",
        \"last_name\": \"Doe\",
        \"company\": \"Acme Inc.\",
        \"title\": \"Head of Integrations\",
        \"email_address\": \"frank.doe@example.com\",
        \"phone_number\": \"+1-541-754-3010\",
        \"gender\": \"MALE\",
        \"salary_expectations\": {
        \"amount\": 100000,
        \"period\": \"YEAR\"
        },
        \"availability_date\": \"2021-01-01\",
        \"location\": {
        \"city\": \"New York\",
        \"country\": \"US\"
        }
        },
        \"stage_id\": \"8x3YKRDcuRnwShdh96ShBNn1\",
        \"attachments\": [
        {
        \"name\": \"Frank Doe CV.txt\",
        \"data\": \"SGkgdGhlcmUsIEtvbWJvIGlzIGN1cnJlbnRseSBoaXJpbmcgZW5naW5lZXJzIHRoYXQgbG92ZSB0byB3b3JrIG9uIGRldmVsb3BlciBwcm9kdWN0cy4=\",
        \"type\": \"CV\",
        \"content_type\": \"text/plain\"
        }
        ],
        \"screening_question_answers\": [
        {
        \"question_id\": \"3phFBNXRweGnDmsU9o2vdPuQ\",
        \"answer\": \"Yes\"
        },
        {
        \"question_id\": \"EYJjhMQT3LtVKXnTbnRT8s6U\",
        \"answer\": [
        \"GUzE666zfyjeoCJX6A8n7wh6\",
        \"5WPHzzKAv8cx97KtHRUV96U8\",
        \"7yZfKGzWigXxxRTygqAfHvyE\"
        ]
        }
        ],
        \"remote_fields\": {}
        }
        ```

        :param job_id: The Kombo ID or Remote ID of the Job this candidate should apply for. If you want to use the ID of the integrated system (remote_id) you need to prefix the id with \"remote:\". You can use the remote ID if you do not want to sync jobs.
        :param candidate:
        :param stage_id: Stage this candidate should be in. If left out, the default stage for this job will be used. You can obtain the possible `stage_id`s from the `get-jobs` endpoint.
        :param attachments: Array of the attachments you would like to upload. The first CV in the attachments will be treated as the resume of the candidate when the tool allows previewing a resume.
        :param source: **(⚠️ Deprecated - Use [automatic source writing](/ats/features/application-attribution#automatic-attribution) instead)** Optional source information that will be attached to the candidate. If
            you're a job board or recruiting service, you can use this to make sure your
            customers can see which candidates came from you.

            This is deprecated because writing sources requires users to do some setup in most ATSs.
        :param sourced_by: Credit the recruiter or team member who sourced this candidate.

            While the `source` field tracks the channel/platform (e.g., \"Awesome Jobboard\"), the `sourced_by` field tracks the individual person responsible for finding the candidate.
        :param gdpr_consent: Optional GDPR consent information required in some jurisdictions (like the Czech Republic or Slovakia).
        :param remote_fields: Additional fields that we will pass through to specific ATS systems.
        :param screening_question_answers: Array of answers to screening questions. Currently, not all question types are supported, and unsupported ones will not be submitted.

            The available questions for a job can be retrieved from the get jobs endpoint. The answers will be validated based on the format of the questions. Make sure to follow this schema to avoid errors.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsJobsJobIDApplicationsRequest(
            job_id=job_id,
            body=models.PostAtsJobsJobIDApplicationsRequestBody(
                stage_id=stage_id,
                candidate=utils.get_pydantic_model(
                    candidate, models.PostAtsJobsJobIDApplicationsRequestBodyCandidate
                ),
                attachments=utils.get_pydantic_model(
                    attachments,
                    Optional[
                        List[models.PostAtsJobsJobIDApplicationsRequestBodyAttachment]
                    ],
                ),
                source=utils.get_pydantic_model(
                    source,
                    Optional[models.PostAtsJobsJobIDApplicationsRequestBodySource],
                ),
                sourced_by=utils.get_pydantic_model(
                    sourced_by,
                    Optional[models.PostAtsJobsJobIDApplicationsRequestBodySourcedBy],
                ),
                gdpr_consent=utils.get_pydantic_model(
                    gdpr_consent,
                    Optional[models.PostAtsJobsJobIDApplicationsRequestBodyGdprConsent],
                ),
                remote_fields=utils.get_pydantic_model(
                    remote_fields,
                    Optional[
                        models.PostAtsJobsJobIDApplicationsRequestBodyRemoteFields
                    ],
                ),
                screening_question_answers=utils.get_pydantic_model(
                    screening_question_answers,
                    Optional[
                        List[
                            models.PostAtsJobsJobIDApplicationsRequestBodyScreeningQuestionAnswer
                        ]
                    ],
                ),
            ),
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/jobs/{job_id}/applications",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsJobsJobIDApplicationsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.body,
                False,
                False,
                "json",
                models.PostAtsJobsJobIDApplicationsRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsJobsJobIdApplications",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsJobsJobIDApplicationsPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_users(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        emails: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsUsersResponse]:
        r"""Get users

        Retrieve all users.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param emails: Filter by a comma-separated list of emails. We will only return users who have _any_ of the emails. The format of the emails is case-insensitive.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsUsersRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            emails=emails,
        )

        req = self._build_request(
            method="GET",
            path="/ats/users",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsUsersGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsUsers",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsUsersResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_users(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                emails=emails,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsUsersResponse(
                result=unmarshal_json_response(
                    models.GetAtsUsersPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_users_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        emails: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsUsersResponse]:
        r"""Get users

        Retrieve all users.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param emails: Filter by a comma-separated list of emails. We will only return users who have _any_ of the emails. The format of the emails is case-insensitive.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsUsersRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            emails=emails,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/users",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsUsersGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsUsers",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsUsersResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_users_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                emails=emails,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsUsersResponse(
                result=unmarshal_json_response(
                    models.GetAtsUsersPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_offers(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsOffersResponse]:
        r"""Get offers

        Retrieve all offers.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsOffersRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
        )

        req = self._build_request(
            method="GET",
            path="/ats/offers",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsOffersGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsOffers",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsOffersResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_offers(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsOffersResponse(
                result=unmarshal_json_response(
                    models.GetAtsOffersPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_offers_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsOffersResponse]:
        r"""Get offers

        Retrieve all offers.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsOffersRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/offers",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsOffersGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsOffers",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsOffersResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_offers_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsOffersResponse(
                result=unmarshal_json_response(
                    models.GetAtsOffersPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_rejection_reasons(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsRejectionReasonsResponse]:
        r"""Get rejection reasons

        Retrieve all rejection reasons.

        Get all rejection reasons available in the system. The Kombo ID is required in the associated [reject application action](/ats/v1/post-applications-application-id-reject).

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsRejectionReasonsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
        )

        req = self._build_request(
            method="GET",
            path="/ats/rejection-reasons",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsRejectionReasonsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsRejectionReasons",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsRejectionReasonsResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_rejection_reasons(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsRejectionReasonsResponse(
                result=unmarshal_json_response(
                    models.GetAtsRejectionReasonsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_rejection_reasons_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsRejectionReasonsResponse]:
        r"""Get rejection reasons

        Retrieve all rejection reasons.

        Get all rejection reasons available in the system. The Kombo ID is required in the associated [reject application action](/ats/v1/post-applications-application-id-reject).

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsRejectionReasonsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/rejection-reasons",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsRejectionReasonsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsRejectionReasons",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsRejectionReasonsResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_rejection_reasons_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsRejectionReasonsResponse(
                result=unmarshal_json_response(
                    models.GetAtsRejectionReasonsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def get_interviews(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        job_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsInterviewsResponse]:
        r"""Get interviews

        Retrieve all interviews.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param job_ids: Filter by a comma-separated list of job IDs. We will only return interviews for applications associated with any of these jobs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsInterviewsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            job_ids=job_ids,
        )

        req = self._build_request(
            method="GET",
            path="/ats/interviews",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsInterviewsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsInterviews",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Optional[models.GetAtsInterviewsResponse]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return None

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return None

            return self.get_interviews(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                job_ids=job_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsInterviewsResponse(
                result=unmarshal_json_response(
                    models.GetAtsInterviewsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def get_interviews_async(
        self,
        *,
        cursor: Optional[str] = None,
        page_size: Optional[int] = 100,
        updated_after: Optional[datetime] = None,
        include_deleted: Optional[bool] = False,
        ids: Optional[List[str]] = None,
        remote_ids: Optional[List[str]] = None,
        job_ids: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> Optional[models.GetAtsInterviewsResponse]:
        r"""Get interviews

        Retrieve all interviews.

        Top level filters use AND, while individual filters use OR if they accept multiple arguments. That means filters will be resolved like this: `(id IN ids) AND (remote_id IN remote_ids)`

        :param cursor: An optional cursor string used for pagination. This can be retrieved from the `next` property of the previous page response.
        :param page_size: The number of results to return per page. Maximum is 250.
        :param updated_after: Filter the entries based on the modification date in format `YYYY-MM-DDTHH:mm:ss.sssZ`. Returns records where either the record itself **OR** its nested data has been updated since this timestamp, even if the record's own `changed_at` field remains unchanged.

            If you want to track entry deletion, also set the `include_deleted=true` query parameter, because otherwise, deleted entries will be hidden.

            For more details, see [Understanding changed_at vs updated_after Behavior](https://docs.kombo.dev/ats/getting-started/fetching-data#understanding-changed_at-vs-updated_after-behavior).
        :param include_deleted: By default, deleted entries are not returned. Use the `include_deleted` query param to include deleted entries too.
        :param ids: Filter by a comma-separated list of IDs such as `222k7eCGyUdgt2JWZDNnkDs3,B5DVmypWENfU6eMe6gYDyJG3`.
        :param remote_ids: Filter by a comma-separated list of remote IDs.
        :param job_ids: Filter by a comma-separated list of job IDs. We will only return interviews for applications associated with any of these jobs.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.GetAtsInterviewsRequest(
            cursor=cursor,
            page_size=page_size,
            updated_after=updated_after,
            include_deleted=include_deleted,
            ids=ids,
            remote_ids=remote_ids,
            job_ids=job_ids,
        )

        req = self._build_request_async(
            method="GET",
            path="/ats/interviews",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.GetAtsInterviewsGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="GetAtsInterviews",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        def next_func() -> Awaitable[Optional[models.GetAtsInterviewsResponse]]:
            body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]])

            async def empty_result():
                return None

            next_cursor = JSONPath("$.next").parse(body)

            if len(next_cursor) == 0:
                return empty_result()

            next_cursor = next_cursor[0]
            if next_cursor is None or str(next_cursor).strip() == "":
                return empty_result()

            return self.get_interviews_async(
                cursor=next_cursor,
                page_size=page_size,
                updated_after=updated_after,
                include_deleted=include_deleted,
                ids=ids,
                remote_ids=remote_ids,
                job_ids=job_ids,
                retries=retries,
            )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return models.GetAtsInterviewsResponse(
                result=unmarshal_json_response(
                    models.GetAtsInterviewsPositiveResponse, http_res
                ),
                next=next_func,
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    def import_tracked_application(
        self,
        *,
        tracked_at: Nullable[datetime],
        erecruiter: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodyErecruiterUnion,
                models.PostAtsImportTrackedApplicationRequestBodyErecruiterUnionTypedDict,
            ]
        ] = None,
        successfactors: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodySuccessfactorsUnion,
                models.PostAtsImportTrackedApplicationRequestBodySuccessfactorsUnionTypedDict,
            ]
        ] = None,
        recruitee: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodyRecruiteeUnion,
                models.PostAtsImportTrackedApplicationRequestBodyRecruiteeUnionTypedDict,
            ]
        ] = None,
        greenhouse: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodyGreenhouseUnion,
                models.PostAtsImportTrackedApplicationRequestBodyGreenhouseUnionTypedDict,
            ]
        ] = None,
        onlyfy: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodyOnlyfyUnion,
                models.PostAtsImportTrackedApplicationRequestBodyOnlyfyUnionTypedDict,
            ]
        ] = None,
        smartrecruiters: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodySmartrecruitersUnion,
                models.PostAtsImportTrackedApplicationRequestBodySmartrecruitersUnionTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsImportTrackedApplicationPositiveResponse:
        r"""Import tracked application

        Import tracked application

        Retroactively import existing applications into Kombo's tracking system. This is particularly useful if you have enabled the 'sync only created applications' setting and want to start tracking applications that were created before using Kombo.

        To import an application, you'll need to provide specific identifiers based on the ATS. The available `id_type` values are defined by Kombo based on the tool's API capabilities. Please reach out to Kombo support if you require further types to be supported.

        Once imported, Kombo will automatically fetch and update the application's complete data during the next sync.

        ### Example Request Body

        ```json
        {
        \"tracked_at\": \"2024-04-12T14:33:47.000Z\",
        \"successfactors\": {
        \"id_type\": \"application_remote_id\",
        \"application_remote_id\": \"1224042\"
        }
        }
        ```

        :param tracked_at: YYYY-MM-DDTHH:mm:ss.sssZ
        :param erecruiter:
        :param successfactors:
        :param recruitee:
        :param greenhouse:
        :param onlyfy:
        :param smartrecruiters:
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsImportTrackedApplicationRequestBody(
            erecruiter=utils.get_pydantic_model(
                erecruiter,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodyErecruiterUnion
                ],
            ),
            successfactors=utils.get_pydantic_model(
                successfactors,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodySuccessfactorsUnion
                ],
            ),
            recruitee=utils.get_pydantic_model(
                recruitee,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodyRecruiteeUnion
                ],
            ),
            greenhouse=utils.get_pydantic_model(
                greenhouse,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodyGreenhouseUnion
                ],
            ),
            onlyfy=utils.get_pydantic_model(
                onlyfy,
                Optional[models.PostAtsImportTrackedApplicationRequestBodyOnlyfyUnion],
            ),
            smartrecruiters=utils.get_pydantic_model(
                smartrecruiters,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodySmartrecruitersUnion
                ],
            ),
            tracked_at=tracked_at,
        )

        req = self._build_request(
            method="POST",
            path="/ats/import-tracked-application",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsImportTrackedApplicationGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request,
                False,
                False,
                "json",
                models.PostAtsImportTrackedApplicationRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsImportTrackedApplication",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsImportTrackedApplicationPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)

    async def import_tracked_application_async(
        self,
        *,
        tracked_at: Nullable[datetime],
        erecruiter: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodyErecruiterUnion,
                models.PostAtsImportTrackedApplicationRequestBodyErecruiterUnionTypedDict,
            ]
        ] = None,
        successfactors: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodySuccessfactorsUnion,
                models.PostAtsImportTrackedApplicationRequestBodySuccessfactorsUnionTypedDict,
            ]
        ] = None,
        recruitee: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodyRecruiteeUnion,
                models.PostAtsImportTrackedApplicationRequestBodyRecruiteeUnionTypedDict,
            ]
        ] = None,
        greenhouse: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodyGreenhouseUnion,
                models.PostAtsImportTrackedApplicationRequestBodyGreenhouseUnionTypedDict,
            ]
        ] = None,
        onlyfy: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodyOnlyfyUnion,
                models.PostAtsImportTrackedApplicationRequestBodyOnlyfyUnionTypedDict,
            ]
        ] = None,
        smartrecruiters: Optional[
            Union[
                models.PostAtsImportTrackedApplicationRequestBodySmartrecruitersUnion,
                models.PostAtsImportTrackedApplicationRequestBodySmartrecruitersUnionTypedDict,
            ]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.PostAtsImportTrackedApplicationPositiveResponse:
        r"""Import tracked application

        Import tracked application

        Retroactively import existing applications into Kombo's tracking system. This is particularly useful if you have enabled the 'sync only created applications' setting and want to start tracking applications that were created before using Kombo.

        To import an application, you'll need to provide specific identifiers based on the ATS. The available `id_type` values are defined by Kombo based on the tool's API capabilities. Please reach out to Kombo support if you require further types to be supported.

        Once imported, Kombo will automatically fetch and update the application's complete data during the next sync.

        ### Example Request Body

        ```json
        {
        \"tracked_at\": \"2024-04-12T14:33:47.000Z\",
        \"successfactors\": {
        \"id_type\": \"application_remote_id\",
        \"application_remote_id\": \"1224042\"
        }
        }
        ```

        :param tracked_at: YYYY-MM-DDTHH:mm:ss.sssZ
        :param erecruiter:
        :param successfactors:
        :param recruitee:
        :param greenhouse:
        :param onlyfy:
        :param smartrecruiters:
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.PostAtsImportTrackedApplicationRequestBody(
            erecruiter=utils.get_pydantic_model(
                erecruiter,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodyErecruiterUnion
                ],
            ),
            successfactors=utils.get_pydantic_model(
                successfactors,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodySuccessfactorsUnion
                ],
            ),
            recruitee=utils.get_pydantic_model(
                recruitee,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodyRecruiteeUnion
                ],
            ),
            greenhouse=utils.get_pydantic_model(
                greenhouse,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodyGreenhouseUnion
                ],
            ),
            onlyfy=utils.get_pydantic_model(
                onlyfy,
                Optional[models.PostAtsImportTrackedApplicationRequestBodyOnlyfyUnion],
            ),
            smartrecruiters=utils.get_pydantic_model(
                smartrecruiters,
                Optional[
                    models.PostAtsImportTrackedApplicationRequestBodySmartrecruitersUnion
                ],
            ),
            tracked_at=tracked_at,
        )

        req = self._build_request_async(
            method="POST",
            path="/ats/import-tracked-application",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            _globals=models.PostAtsImportTrackedApplicationGlobals(
                integration_id=self.sdk_configuration.globals.integration_id,
            ),
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request,
                False,
                False,
                "json",
                models.PostAtsImportTrackedApplicationRequestBody,
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="PostAtsImportTrackedApplication",
                oauth2_scopes=None,
                security_source=self.sdk_configuration.security,
            ),
            request=req,
            error_status_codes=["default"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return unmarshal_json_response(
                models.PostAtsImportTrackedApplicationPositiveResponse, http_res
            )
        if utils.match_response(http_res, "default", "application/json"):
            response_data = unmarshal_json_response(errors.KomboAtsErrorData, http_res)
            raise errors.KomboAtsError(response_data, http_res)

        raise errors.SDKDefaultError("Unexpected response received", http_res)
