Node.js package that retrieves data from an API, formats it and sends it out via email to a distribution list
When you have a lot of asynchronous requests to make, and you want to wait for all to finish, the proper method to use is to first promisify the requests (if they don't return a Promise already), and then to use Promise.all, which returns a Promise which resolves once all promises in the passed array have resolved.
Unfortunately, icingaServer looks to be callback-based, and you have to multiple methods from it. Luckily, there's a package called promisify that makes turning these callback APIs into Promises much easier.
You can reduce the nesting level by one by changing
clients.forEach((clientMap) =&gt; {
    Object.entries(clientMap).forEach(([client, hostnameWildcard]) =&gt; {

into a use of flatMap instead: clients.flatMap(Object.entries)
DRY You have two very similar callbacks in getServiceFiltered and getHostFiltered. All that differs is the function invoked and the parameter passed, so it'd be better to make another function to which you can pass the parts that change.
Parallel processing Rather than two calls which are made in serial, consider making them both at once, in parallel, if the API supports it - this'll let your script finish quicker.
Filtered array length While you can do arr.filter(callback).length, you might consider using reduce instead, since you don't care about the resulting array, you just care about the number of matching elements.
Unquoted keys In JS, there's no need to quote object keys unless the keys contain characters that are invalid for idenfifiers. Most prefer not to use quotes so as to keep the code free of unnecessary noise.
Refactored:
const { promisify } = require('util');
const icingaServer = new icingaApi(icingaConfig.url, icingaConfig.port, icingaConfig.username, icingaConfig.password);
const getServiceFiltered = promisify(icingaServer.getServiceFiltered).bind(icingaServer);
const getHostFiltered = promisify(icingaServer.getHostFiltered).bind(icingaServer);

const processClient = async ([client, hostnameWildcard]) =&gt; {
    const getClientData = async (method, filterKey) =&gt; {
        const result = await method({
            filter: 'match(host_name, host.name)',
            filter_vars: {
                [filterKey]: hostnameWildcard
            }
        });
        return {
            warnings: result.reduce((count, o) =&gt; count + (o.attrs.state === warning), 0),
            errors: result.reduce((count, o) =&gt; count + (o.attrs.state === warning), 0),
            totalCount: result.length,
        };
    };
    const [serviceData, hostData] = await Promise.all([
        getClientData(getServiceFiltered, 'service_name'),
        getClientData(getHostFiltered, 'host_name'),
    ]);
    return {
        name: `${client} (${hostData.totalCount}/${serviceData.totalCount})`,
        errors: serviceData.errors + hostData.errors,
        warnings: serviceData.warnings + hostData.warnings,
    };
};

Promise.all(
    clients.flatMap(Object.entries).map(processClient)
)
    .then(sendEmail)
    .catch((error) =&gt; {
        // handle errors
    });

where sendEmail now takes a parameter of the data to send.
Also note that // handle errors shouldn't just log an error if it occurs - ideally, you'd have a system in which such an error results in a developer being able to look at a dashboard or something to see at a glance what's failed recently, to see when an issue arises that needs looking into (for example, if the API changes and all requests start failing).