Over time, credentials accumulate and it becomes unclear which workflows use which credentials, whether any credentials are orphaned, and whether deprecated ...
Over time, credentials accumulate and it becomes unclear which workflows use which credentials, whether any credentials are orphaned, and whether deprecated API keys are still in use. Regular audits prevent stale credentials from becoming security liabilities.
Real-world example: An employee leaves the company. Their personal API keys are still stored as n8n credentials and used in 12 production workflows. Without an audit, the keys are never rotated and remain valid indefinitely.
Use the n8n API to audit credential usage programmatically:
// Code node: Audit credential usage
// Mode: Run Once for All Items
// Requires: n8n API access (set N8N_PUBLIC_API_ENABLED=true)
const n8nApiUrl = 'http://localhost:5678/api/v1';
const apiKey = $input.first().json.n8nApiKey; // n8n API key
// Fetch all credentials
const credentials = await this.helpers.httpRequest({
method: 'GET',
url: `${n8nApiUrl}/credentials`,
headers: { 'X-N8N-API-KEY': apiKey },
});
// Fetch all workflows
const workflows = await this.helpers.httpRequest({
method: 'GET',
url: `${n8nApiUrl}/workflows`,
headers: { 'X-N8N-API-KEY': apiKey },
});
// Build usage map
const credentialUsage = {};
for (const cred of credentials.data) {
credentialUsage[cred.id] = {
name: cred.name,
type: cred.type,
createdAt: cred.createdAt,
updatedAt: cred.updatedAt,
usedInWorkflows: [],
};
}
for (const wf of workflows.data) {
const nodes = wf.nodes || [];
for (const node of nodes) {
const creds = node.credentials || {};
for (const [type, credRef] of Object.entries(creds)) {
const credId = credRef.id;
if (credentialUsage[credId]) {
credentialUsage[credId].usedInWorkflows.push({
workflowId: wf.id,
workflowName: wf.name,
nodeName: node.name,
nodeType: node.type,
isActive: wf.active,
});
}
}
}
}
// Identify orphaned credentials (not used in any workflow)
const orphaned = Object.values(credentialUsage).filter(
c => c.usedInWorkflows.length === 0
);
return [
{ json: { type: 'summary', totalCredentials: credentials.data.length, orphanedCount: orphaned.length } },
...orphaned.map(c => ({ json: { type: 'orphaned', ...c } })),
];
```text
Schedule this audit workflow to run weekly and send the results to Slack or email. Delete or rotate orphaned credentials promptly.
**Related:** [Use Docker Compose with Health Checks for n8n and PostgreSQL](../self-hosting-operations/01-use-docker-compose-with-health-checks-for-n8n-and-postgresql.md) | [Use Path Parameters in Webhook URLs for Dynamic Routing](../webhook-mastery/01-use-path-parameters-in-webhook-urls-for-dynamic-routing.md)
I build production n8n and Cloudflare automation for teams — the same engineering behind HarperFlow. Fixed-price, escrow-protected, US-based.