This workflow follows the Gmail → Postgres recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Quantra - Daily Usage Monitoring",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8,
"triggerAtMinute": 0
}
]
}
},
"id": "trigger",
"name": "Daily 8AM Report",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
200
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT\n schemaname,\n tablename,\n pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) as total_size,\n pg_total_relation_size(schemaname || '.' || tablename) as size_bytes\nFROM pg_tables\nWHERE schemaname = 'public'\nORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC\nLIMIT 20;",
"options": {}
},
"id": "get-table-sizes",
"name": "Get Table Sizes",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
250,
0
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT\n 'time_entries' as table_name, COUNT(*) as row_count FROM time_entries\nUNION ALL\nSELECT 'notifications', COUNT(*) FROM notifications\nUNION ALL\nSELECT 'messages', COUNT(*) FROM messages\nUNION ALL\nSELECT 'shifts', COUNT(*) FROM shifts\nUNION ALL\nSELECT 'quantra_actions', COUNT(*) FROM quantra_actions\nUNION ALL\nSELECT 'employees', COUNT(*) FROM employees\nUNION ALL\nSELECT 'companies', COUNT(*) FROM companies\nUNION ALL\nSELECT 'workflow_executions', COUNT(*) FROM workflow_executions\nORDER BY row_count DESC;",
"options": {}
},
"id": "get-row-counts",
"name": "Get Row Counts",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
250,
150
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT\n pg_size_pretty(pg_database_size(current_database())) as database_size,\n pg_database_size(current_database()) as size_bytes,\n (SELECT count(*) FROM pg_stat_activity WHERE state = 'active') as active_connections,\n (SELECT count(*) FROM pg_stat_activity) as total_connections;",
"options": {}
},
"id": "get-database-stats",
"name": "Get Database Stats",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
250,
300
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT\n 'time_entries' as table_name, \n COUNT(*) as created_24h\nFROM time_entries WHERE created_at > NOW() - INTERVAL '24 hours'\nUNION ALL\nSELECT 'notifications', COUNT(*)\nFROM notifications WHERE created_at > NOW() - INTERVAL '24 hours'\nUNION ALL\nSELECT 'messages', COUNT(*)\nFROM messages WHERE created_at > NOW() - INTERVAL '24 hours'\nUNION ALL\nSELECT 'quantra_actions', COUNT(*)\nFROM quantra_actions WHERE created_at > NOW() - INTERVAL '24 hours';",
"options": {}
},
"id": "get-growth-metrics",
"name": "Get Growth Metrics",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
250,
450
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "append"
},
"id": "merge-results",
"name": "Wait For All",
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [
500,
200
]
},
{
"parameters": {
"jsCode": "// Get all items\nconst items = $input.all();\n\n// Separate data by type\nlet dbStats = null;\nconst tableSizes = [];\nconst rowCounts = [];\nconst growthMetrics = [];\n\nfor (const item of items) {\n const data = item.json;\n \n if (data.database_size) {\n dbStats = data;\n } else if (data.total_size && data.tablename) {\n tableSizes.push(data);\n } else if (data.row_count !== undefined) {\n rowCounts.push(data);\n } else if (data.created_24h !== undefined) {\n growthMetrics.push(data);\n }\n}\n\n// Calculate alerts\nconst alerts = [];\nconst sizeMB = dbStats ? dbStats.size_bytes / (1024 * 1024) : 0;\nconst sizePercent = (sizeMB / 500) * 100; // 500MB free tier\n\nif (sizeMB > 450) {\n alerts.push({ level: 'critical', message: `Database at ${sizePercent.toFixed(1)}% (${dbStats.database_size}) - above 90% limit!` });\n} else if (sizeMB > 375) {\n alerts.push({ level: 'warning', message: `Database at ${sizePercent.toFixed(1)}% (${dbStats.database_size}) - approaching limit` });\n}\n\nif (dbStats && dbStats.active_connections > 15) {\n alerts.push({ level: 'warning', message: `High active connections: ${dbStats.active_connections}` });\n}\n\nconst totalRows = rowCounts.reduce((sum, r) => sum + (parseInt(r.row_count) || 0), 0);\nconst total24h = growthMetrics.reduce((sum, g) => sum + (parseInt(g.created_24h) || 0), 0);\n\nreturn {\n json: {\n report_date: new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }),\n timestamp: new Date().toISOString(),\n database: {\n size: dbStats ? dbStats.database_size : 'Unknown',\n size_bytes: dbStats ? dbStats.size_bytes : 0,\n size_mb: sizeMB.toFixed(2),\n size_percent: sizePercent.toFixed(1),\n active_connections: dbStats ? dbStats.active_connections : 0,\n total_connections: dbStats ? dbStats.total_connections : 0\n },\n table_sizes: tableSizes.slice(0, 5),\n row_counts: rowCounts,\n growth_24h: growthMetrics,\n total_rows: totalRows,\n total_24h_inserts: total24h,\n alerts: alerts,\n has_alerts: alerts.length > 0,\n has_critical: alerts.some(a => a.level === 'critical')\n }\n};"
},
"id": "analyze-data",
"name": "Analyze Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
700,
200
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "has-alerts",
"leftValue": "={{ $json.has_alerts }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "check-alerts",
"name": "Has Alerts?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.1,
"position": [
900,
200
]
},
{
"parameters": {
"sendTo": "danavgeissler@quantrahq.com",
"subject": "=\u26a0\ufe0f ALERT: Quantra Database Alert - {{ $json.report_date }}",
"message": "=<h2 style=\"color: #dc2626;\">\u26a0\ufe0f Quantra Database Alert</h2>\n\n<p><strong>Date:</strong> {{ $json.report_date }}</p>\n\n<div style=\"padding: 15px; background: #fef2f2; border-left: 4px solid #dc2626; margin: 20px 0;\">\n<h3 style=\"margin-top: 0; color: #dc2626;\">Alerts Detected</h3>\n<ul>\n{{ $json.alerts.map(a => '<li style=\"color: ' + (a.level === 'critical' ? '#dc2626' : '#f59e0b') + ';\"><strong>' + a.level.toUpperCase() + ':</strong> ' + a.message + '</li>').join('') }}\n</ul>\n</div>\n\n<h3>Database Overview</h3>\n<table border=\"1\" cellpadding=\"10\" cellspacing=\"0\" style=\"border-collapse: collapse;\">\n <tr><td><strong>Database Size</strong></td><td>{{ $json.database.size }} ({{ $json.database.size_percent }}% of 500MB limit)</td></tr>\n <tr><td><strong>Total Rows</strong></td><td>{{ $json.total_rows.toLocaleString() }}</td></tr>\n <tr><td><strong>Connections</strong></td><td>{{ $json.database.active_connections }} active / {{ $json.database.total_connections }} total</td></tr>\n <tr><td><strong>24h New Records</strong></td><td>{{ $json.total_24h_inserts }}</td></tr>\n</table>\n\n<h3>Top Tables by Size</h3>\n<table border=\"1\" cellpadding=\"10\" cellspacing=\"0\" style=\"border-collapse: collapse; width: 100%;\">\n <tr style=\"background: #f3f4f6;\"><th>Table</th><th>Size</th></tr>\n {{ $json.table_sizes.map(t => '<tr><td>' + t.tablename + '</td><td>' + t.total_size + '</td></tr>').join('') }}\n</table>\n\n<p style=\"margin-top: 20px;\"><strong>Action Required:</strong> Please review and consider running cleanup or upgrading your plan.</p>\n\n<hr style=\"margin-top: 30px;\">\n<p style=\"color: #666; font-size: 12px;\">Quantra Autonomous Worker - Automated Monitoring</p>",
"options": {
"appendAttribution": false
}
},
"id": "gmail-alert",
"name": "Send Alert Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
1150,
100
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sendTo": "danavgeissler@quantrahq.com",
"subject": "=\ud83d\udcca Quantra Daily Report - {{ $json.report_date }}",
"message": "=<h2>\ud83d\udcca Daily Database Report</h2>\n\n<p><strong>Date:</strong> {{ $json.report_date }}</p>\n\n<h3>Overview</h3>\n<table border=\"1\" cellpadding=\"10\" cellspacing=\"0\" style=\"border-collapse: collapse;\">\n <tr><td><strong>Database Size</strong></td><td>{{ $json.database.size }} ({{ $json.database.size_percent }}% of limit)</td></tr>\n <tr><td><strong>Total Rows</strong></td><td>{{ $json.total_rows.toLocaleString() }}</td></tr>\n <tr><td><strong>Active Connections</strong></td><td>{{ $json.database.active_connections }}</td></tr>\n <tr><td><strong>24h New Records</strong></td><td>{{ $json.total_24h_inserts }}</td></tr>\n</table>\n\n<h3>Top 5 Tables by Size</h3>\n<table border=\"1\" cellpadding=\"10\" cellspacing=\"0\" style=\"border-collapse: collapse; width: 100%;\">\n <tr style=\"background: #f3f4f6;\"><th>Table</th><th>Size</th></tr>\n {{ $json.table_sizes.map(t => '<tr><td>' + t.tablename + '</td><td>' + t.total_size + '</td></tr>').join('') }}\n</table>\n\n<h3>24h Activity</h3>\n<table border=\"1\" cellpadding=\"10\" cellspacing=\"0\" style=\"border-collapse: collapse; width: 100%;\">\n <tr style=\"background: #f3f4f6;\"><th>Table</th><th>New Rows</th></tr>\n {{ $json.growth_24h.map(g => '<tr><td>' + g.table_name + '</td><td>+' + g.created_24h + '</td></tr>').join('') }}\n</table>\n\n<div style=\"margin-top: 20px; padding: 15px; background: #d1fae5; border-radius: 5px;\">\n \u2705 No alerts - all systems healthy\n</div>\n\n<hr style=\"margin-top: 30px;\">\n<p style=\"color: #666; font-size: 12px;\">Quantra Autonomous Worker - Automated Monitoring</p>",
"options": {
"appendAttribution": false
}
},
"id": "gmail-daily-report",
"name": "Send Daily Report",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
1150,
300
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Daily 8AM Report": {
"main": [
[
{
"node": "Get Table Sizes",
"type": "main",
"index": 0
},
{
"node": "Get Row Counts",
"type": "main",
"index": 0
},
{
"node": "Get Database Stats",
"type": "main",
"index": 0
},
{
"node": "Get Growth Metrics",
"type": "main",
"index": 0
}
]
]
},
"Get Table Sizes": {
"main": [
[
{
"node": "Wait For All",
"type": "main",
"index": 0
}
]
]
},
"Get Row Counts": {
"main": [
[
{
"node": "Wait For All",
"type": "main",
"index": 0
}
]
]
},
"Get Database Stats": {
"main": [
[
{
"node": "Wait For All",
"type": "main",
"index": 0
}
]
]
},
"Get Growth Metrics": {
"main": [
[
{
"node": "Wait For All",
"type": "main",
"index": 0
}
]
]
},
"Wait For All": {
"main": [
[
{
"node": "Analyze Data",
"type": "main",
"index": 0
}
]
]
},
"Analyze Data": {
"main": [
[
{
"node": "Has Alerts?",
"type": "main",
"index": 0
}
]
]
},
"Has Alerts?": {
"main": [
[
{
"node": "Send Alert Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Daily Report",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner"
},
"staticData": null,
"tags": [
{
"name": "monitoring"
},
{
"name": "scheduled"
},
{
"name": "cost-optimization"
},
{
"name": "gmail"
}
],
"versionId": "2"
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
gmailOAuth2postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Quantra - Daily Usage Monitoring. Uses postgres, gmail. Scheduled trigger; 10 nodes.
Source: https://github.com/Weath123/Quantra-Autonomous-Worker/blob/c2ca42d25b36ab2ba894ff9f62cde2be4073bc95/n8n-workflows/02-usage-monitoring.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow automates the complete end-to-end processing of daily revenue transactions for finance and accounting teams. It systematically retrieves, validates, and standardizes transaction data fro
Finance — Daily digest email. Uses postgres, gmail. Scheduled trigger; 8 nodes.
Monthly Energy Generation Report (Postgres → PDF → Email). Uses httpRequest, gmail, postgres. Scheduled trigger; 7 nodes.
This workflow runs daily at 9 AM to pull unpaid invoices from Microsoft SQL, send staged payment reminder emails via Gmail based on due date timing, update invoice status and an audit log in the datab
Relatórios aos Laboratórios. Uses httpRequest, gmail, formTrigger. Scheduled trigger; 54 nodes.