public function process()
    {
        // read the format requested for the output data
        $outputFormat = strtolower(Common::getRequestVar('format', 'xml', 'string', $this->request));

        $disablePostProcessing = $this->shouldDisablePostProcessing();

        // create the response
        $response = new ResponseBuilder($outputFormat, $this->request);
        if ($disablePostProcessing) {
            $response->disableDataTablePostProcessor();
        }

        $corsHandler = new CORSHandler();
        $corsHandler->handle();

        $tokenAuth = Common::getRequestVar('token_auth', '', 'string', $this->request);
        $shouldReloadAuth = false;

        try {
            ++self::$nestedApiInvocationCount;

            // IP check is needed here as we cannot listen to API.Request.authenticate as it would then not return proper API format response.
            // We can also not do it by listening to API.Request.dispatch as by then the user is already authenticated and we want to make sure
            // to not expose any information in case the IP is not whitelisted.
            $whitelist = new LoginWhitelist();
            if ($whitelist->shouldCheckWhitelist() && $whitelist->shouldWhitelistApplyToAPI()) {
                $ip = IP::getIpFromHeader();
                $whitelist->checkIsWhitelisted($ip);
            }

            // read parameters
            $moduleMethod = Common::getRequestVar('method', null, 'string', $this->request);

            list($module, $method) = $this->extractModuleAndMethod($moduleMethod);
            list($module, $method) = self::getRenamedModuleAndAction($module, $method);

            PluginManager::getInstance()->checkIsPluginActivated($module);

            $apiClassName = self::getClassNameAPI($module);

            if ($shouldReloadAuth = self::shouldReloadAuthUsingTokenAuth($this->request)) {
                $access = Access::getInstance();
                $tokenAuthToRestore = $access->getTokenAuth();
                $hadSuperUserAccess = $access->hasSuperUserAccess();
                self::forceReloadAuthUsingTokenAuth($tokenAuth);
            }

            // call the method
            $returnedValue = Proxy::getInstance()->call($apiClassName, $method, $this->request);

            // get the response with the request query parameters loaded, since DataTablePost processor will use the Report
            // class instance, which may inspect the query parameters. (eg, it may look for the idCustomReport parameters
            // which may only exist in $this->request, if the request was called programatically)
            $toReturn = Context::executeWithQueryParameters($this->request, function () use ($response, $returnedValue, $module, $method) {
                return $response->getResponse($returnedValue, $module, $method);
            });
        } catch (Exception $e) {
            StaticContainer::get(LoggerInterface::class)->error('Uncaught exception in API: {exception}', [
                'exception' => $e,
                'ignoreInScreenWriter' => true,
            ]);

            $toReturn = $response->getResponseException($e);
        } finally {
            --self::$nestedApiInvocationCount;
        }

        if ($shouldReloadAuth) {
            $this->restoreAuthUsingTokenAuth($tokenAuthToRestore, $hadSuperUserAccess);
        }

        return $toReturn;
    }