Tips > Reliability & Performance

Monitor Execution History with a Scheduled Health Check Workflow

Do not rely on manually checking the n8n execution log.

Do not rely on manually checking the n8n execution log. Build a scheduled workflow that queries the n8n API for recent failed executions and sends a daily or hourly summary. This catches failures in workflows that do not have an error workflow configured (or where the error workflow itself failed).

Real-world example: A team's error notification workflow had a bug that prevented it from sending Slack alerts for 3 days. A health check workflow would have caught the accumulating failures.

// Code node: "Query n8n API for Failed Executions"
// Uses n8n's internal API (available on self-hosted instances)

const baseUrl = $env.N8N_HOST || 'http://localhost:5678';
const apiKey = $env.N8N_API_KEY; // Set in n8n settings

const response = await this.helpers.httpRequest({
  method: 'GET',
  url: `${baseUrl}/api/v1/executions`,
  headers: {
    'X-N8N-API-KEY': apiKey
  },
  qs: {
    status: 'error',
    limit: 100,
    // Get executions from the last hour
    startedAfter: new Date(Date.now() - 60 * 60 * 1000).toISOString()
  }
});

const failedExecutions = response.data || [];

// Group by workflow
const byWorkflow = {};
for (const exec of failedExecutions) {
  const wfName = exec.workflowData?.name || 'Unknown';
  if (!byWorkflow[wfName]) {
    byWorkflow[wfName] = { count: 0, executions: [] };
  }
  byWorkflow[wfName].count++;
  byWorkflow[wfName].executions.push({
    id: exec.id,
    startedAt: exec.startedAt,
    stoppedAt: exec.stoppedAt,
    error: exec.data?.resultData?.error?.message || 'Unknown error'
  });
}

return [{
  json: {
    totalFailures: failedExecutions.length,
    byWorkflow,
    checkTime: new Date().toISOString(),
    period: 'last_hour'
  }
}];
```text
Format and send the report:

```javascript
// Code node: "Format Health Report"
const data = $input.first().json;

if (data.totalFailures === 0) {
  return [{
    json: {
      send: false,
      message: 'All clear -- no failures in the last hour.'
    }
  }];
}

const lines = [`*n8n Health Check: ${data.totalFailures} failures in the last hour*\n`];

for (const [workflow, info] of Object.entries(data.byWorkflow)) {
  lines.push(`*${workflow}*: ${info.count} failure(s)`);
  // Show the most recent error
  const latest = info.executions[0];
  lines.push(`  Latest error: ${latest.error}`);
  lines.push('');
}

lines.push(`_Check time: ${data.checkTime}_`);

return [{
  json: {
    send: true,
    message: lines.join('\n')
  }
}];
```text
```yaml

# Schedule Trigger: Run every hour

# IF node: Only send if $json.send === true

# Slack node: Post to #n8n-monitoring channel

Schedule: Every hour at :00
Channel: #n8n-monitoring
Alert threshold: Any failures (customizable)
```text
This is your safety net for the safety net. Even if individual error workflows fail, this health check catches the failures.

**Related:** [Use "Pin Data" to Freeze Node Output](../testing-and-debugging/01-use-pin-data-to-freeze-node-output.md) | [Break Large Workflows into Sub-Workflows](../workflow-architecture/01-break-large-workflows-into-sub-workflows.md)

Showcase builds

19 complete workflows from my own projects, each with its n8n workflow JSON to import. Showcase entries link the file at the end of the article.

See the showcase builds

Keep reading

191 entries grouped by topic, from first workflow to queue mode. Free, no signup.

Browse the encyclopedia

Need it built?

I design, build and run n8n systems for clients. Every engagement starts with a $1,500 diagnostic audit, credited toward the build.

Book a 20-minute call