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.
YOUR_ID 4. Uses gmail, googleDrive, googleSheets, httpRequest. Scheduled trigger; 53 nodes.
14310 Send Overdue Invoice Payment Reminders With Ifirma Gmail Postgrid And Slack. Uses httpRequest, stopAndError, slack, gmail. Scheduled trigger; 53 nodes.