def do_POST_failover(self, action: str = 'failover') -> None:
        """Handle a ``POST`` request to ``/failover`` path.

        Handles manual failovers/switchovers, mainly from ``patronictl``.

        The request body should be a JSON dictionary, and it can contain the following keys:

            * ``leader``: name of the current leader in the cluster;
            * ``candidate``: name of the Patroni node to be promoted;
            * ``scheduled_at``: a string representing the timestamp when to execute the switchover/failover, e.g.
                ``2023-04-14T20:27:00+00:00``.

        Response HTTP status codes:

            * ``202``: if operation has been scheduled;
            * ``412``: if operation is not possible;
            * ``503``: if unable to register the operation to the DCS;
            * HTTP status returned by :func:`parse_schedule`, if any error was observed while parsing the schedule;
            * HTTP status returned by :func:`poll_failover_result` if the operation has been processed immediately;
            * ``400``: if none of the above applies.

        .. note::
            If unable to parse the request body, then the request is silently discarded.

        :param action: the action to be performed (``switchover`` or ``failover``).
        """
        request = self._read_json_content()
        (status_code, data) = (400, '')
        if not request:
            return

        leader = request.get('leader')
        candidate = request.get('candidate') or request.get('member')
        scheduled_at = request.get('scheduled_at')
        cluster = self.server.patroni.dcs.get_cluster()
        config = global_config.from_cluster(cluster)

        logger.info("received %s request with leader=%s candidate=%s scheduled_at=%s",
                    action, leader, candidate, scheduled_at)

        if action == 'failover' and not candidate:
            data = 'Failover could be performed only to a specific candidate'
        elif action == 'switchover' and not leader:
            data = 'Switchover could be performed only from a specific leader'

        if not data and scheduled_at:
            if action == 'failover':
                data = "Failover can't be scheduled"
            elif config.is_paused:
                data = "Can't schedule switchover in the paused state"
            else:
                (status_code, data, scheduled_at) = self.parse_schedule(scheduled_at, action)

        if not data and config.is_paused and not candidate:
            data = 'Switchover is possible only to a specific candidate in a paused state'

        if action == 'failover' and leader:
            logger.warning('received failover request with leader specified - performing switchover instead')
            action = 'switchover'

        if not data and leader == candidate:
            data = 'Switchover target and source are the same'

        if not data and not scheduled_at:
            data = self.is_failover_possible(cluster, leader, candidate, action)
            if data:
                status_code = 412

        if not data:
            if self.server.patroni.dcs.manual_failover(leader, candidate, scheduled_at=scheduled_at):
                self.server.patroni.ha.wakeup()
                if scheduled_at:
                    data = action.title() + ' scheduled'
                    status_code = 202
                else:
                    status_code, data = self.poll_failover_result(cluster.leader and cluster.leader.name,
                                                                  candidate, action)
            else:
                data = 'failed to write failover key into DCS'
                status_code = 503
        # pyright thinks ``status_code`` can be ``None`` because ``parse_schedule`` call may return ``None``. However,
        # if that's the case, ``status_code`` will be overwritten somewhere between ``parse_schedule`` and
        # ``write_response`` calls.
        if TYPE_CHECKING:  # pragma: no cover
            assert isinstance(status_code, int)
        self.write_response(status_code, data)
Handle a ``POST`` request to ``/failover`` path.

        Handles manual failovers/switchovers, mainly from ``patronictl``.

        The request body should be a JSON dictionary, and it can contain the following keys:

            * ``leader``: name of the current leader in the cluster;
            * ``candidate``: name of the Patroni node to be promoted;
            * ``scheduled_at``: a string representing the timestamp when to execute the switchover/failover, e.g.
                ``2023-04-14T20:27:00+00:00``.

        Response HTTP status codes:

            * ``202``: if operation has been scheduled;
            * ``412``: if operation is not possible;
            * ``503``: if unable to register the operation to the DCS;
            * HTTP status returned by :func:`parse_schedule`, if any error was observed while parsing the schedule;
            * HTTP status returned by :func:`poll_failover_result` if the operation has been processed immediately;
            * ``400``: if none of the above applies.

        .. note::
            If unable to parse the request body, then the request is silently discarded.

        :param action: the action to be performed (``switchover`` or ``failover``).
        
patroni/api.py