This workflow corresponds to n8n.io template #17135 — we link there as the canonical source.
This workflow follows the Airtable → Google Sheets 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 →
{
"meta": {
"templateCredsSetupCompleted": false
},
"name": "AI Personal Admin Inbox Assistant",
"tags": [],
"nodes": [
{
"id": "ee1059a0-f837-4af6-bd02-b3597d055f03",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-608,
-256
],
"parameters": {
"color": 7,
"width": 480,
"height": 896,
"content": "## AI Personal Admin Inbox Assistant\n\n### How it works\n\nThis workflow monitors an admin Gmail inbox, normalizes and redacts incoming messages, checks task memory in Airtable and Google Sheets, and skips duplicates. New items are classified by OpenAI, converted into admin alerts, optionally sent to Slack and Microsoft Teams, and then logged to Sheets and/or Airtable before completion.\n\n### Setup steps\n\n- Connect Gmail credentials for the trigger account and configure the inbox/label or search criteria to monitor.\n- Open the Set Inbox Configuration code node and set memory/logging toggles, Slack and Teams alert preferences, destination IDs, and any redaction or priority rules.\n- Add OpenAI credentials and review the classifier prompt/model settings for the admin item categories you want extracted.\n- Connect Slack credentials and configure the target channel if Slack alerts are enabled.\n- Configure the Microsoft Teams incoming webhook URL used by the HTTP Request node if Teams alerts are enabled.\n- Connect Google Sheets credentials and set the spreadsheet, sheet names, and expected columns for task memory and task logging.\n- Connect Airtable credentials and set the base, table, and field mappings for task memory and task logging.\n- Verify the Sheets and Airtable schemas match the fields produced by the workflow, then test with a sample email before activating the Gmail trigger.\n\n### Customization\n\nYou can adjust which memory sources are used, the duplicate-detection rules, the AI classification categories, Slack/Teams alert conditions, and whether completed admin tasks are logged to Sheets, Airtable, or both."
},
"typeVersion": 1
},
{
"id": "6e0ce98d-1404-475b-9e96-7f8a69e6274a",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
160
],
"parameters": {
"color": 7,
"width": 240,
"height": 320,
"content": "## Receive admin email\n\nStarts the workflow whenever a matching admin email arrives in Gmail."
},
"typeVersion": 1
},
{
"id": "085dab67-3889-49b9-a9d0-bf9c852cdbd1",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
240,
160
],
"parameters": {
"color": 7,
"width": 448,
"height": 320,
"content": "## Configure and sanitize email\n\nLoads editable admin-inbox settings, then normalizes the message content and redacts sensitive details before downstream processing."
},
"typeVersion": 1
},
{
"id": "54aedfa3-24f5-4120-bb3b-a494f4165f38",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
736,
64
],
"parameters": {
"color": 7,
"width": 720,
"height": 512,
"content": "## Load Airtable memory\n\nChecks whether Airtable task memory should be used, reads it when enabled, compiles the results, or passes through an empty/bypassed memory state."
},
"typeVersion": 1
},
{
"id": "ecf99206-7c0d-4599-abf6-93f8ce516105",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1520,
48
],
"parameters": {
"color": 7,
"width": 720,
"height": 544,
"content": "## Load Sheets memory\n\nChecks whether Google Sheets task memory should be used, reads and combines existing task records, or transfers the prior memory state when Sheets memory is disabled."
},
"typeVersion": 1
},
{
"id": "91f3a302-ce1e-45f6-87cc-b11b63dba64c",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2272,
192
],
"parameters": {
"color": 7,
"width": 720,
"height": 496,
"content": "## Detect duplicate items\n\nCompares the incoming admin email against task memory, decides whether it is new, and ends processing early for duplicates."
},
"typeVersion": 1
},
{
"id": "06d66977-c776-425d-9aba-240b669b68f1",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
3024,
192
],
"parameters": {
"color": 7,
"width": 976,
"height": 496,
"content": "## Classify admin request\n\nUses OpenAI to classify and extract actionable admin details, examines the extraction, and routes invalid or skipped items to an early completion path."
},
"typeVersion": 1
},
{
"id": "04d03ef9-9dbd-4db3-9d06-bdf3f2cf3885",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
3568,
-256
],
"parameters": {
"color": 7,
"width": 448,
"height": 416,
"content": "## Prepare Slack alert\n\nFormats the extracted admin item into an alert payload and decides whether a Slack notification is required."
},
"typeVersion": 1
},
{
"id": "a178a888-98d0-4ba4-9fb9-95f1cd805933",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
4096,
-64
],
"parameters": {
"color": 7,
"width": 448,
"height": 512,
"content": "## Send Slack alert\n\nEither sends the formatted alert to Slack and validates delivery, or records that Slack notification was intentionally omitted."
},
"typeVersion": 1
},
{
"id": "cc07b086-b60b-417e-8625-4908e6ea2d7e",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
4592,
16
],
"parameters": {
"color": 7,
"width": 240,
"height": 368,
"content": "## Check Teams routing\n\nDetermines whether the workflow should also notify Microsoft Teams after the Slack step has been handled."
},
"typeVersion": 1
},
{
"id": "ff58c5ea-034e-46a8-832e-66b5caa0c200",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
4880,
-64
],
"parameters": {
"color": 7,
"width": 448,
"height": 512,
"content": "## Send Teams alert\n\nEither posts the admin alert to a Teams webhook and validates the result, or records that Teams notification was skipped."
},
"typeVersion": 1
},
{
"id": "1e7b4554-30e7-4750-813f-f3fa4a227906",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
5360,
48
],
"parameters": {
"color": 7,
"width": 240,
"height": 336,
"content": "## Create task log\n\nBuilds the consolidated admin task log object after alert delivery decisions have been completed."
},
"typeVersion": 1
},
{
"id": "2e969d2d-0cd0-4662-b6a5-008a43e57359",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
5648,
-32
],
"parameters": {
"color": 7,
"width": 720,
"height": 496,
"content": "## Log task to Sheets\n\nChecks whether Google Sheets logging is enabled, writes the task entry when required, verifies the entry, or bypasses Sheets logging."
},
"typeVersion": 1
},
{
"id": "bf4b3e17-8167-43bf-bde3-587df8a75db0",
"name": "Sticky Note13",
"type": "n8n-nodes-base.stickyNote",
"position": [
6400,
-32
],
"parameters": {
"color": 7,
"width": 720,
"height": 496,
"content": "## Log task to Airtable\n\nChecks whether Airtable logging is enabled, creates the task record when required, verifies the entry, or bypasses Airtable logging."
},
"typeVersion": 1
},
{
"id": "6df87a6e-dc91-4708-a3a5-c3228a67d3fb",
"name": "Sticky Note14",
"type": "n8n-nodes-base.stickyNote",
"position": [
7168,
0
],
"parameters": {
"color": 7,
"width": 240,
"height": 384,
"content": "## Complete admin processing\n\nFinalizes successful processing after optional alerts and logging have finished."
},
"typeVersion": 1
},
{
"id": "99999999-9999-9999-9999-000000000001",
"name": "Admin Email Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
0,
320
],
"parameters": {
"filters": {
"q": "label:Admin newer_than:7d"
},
"options": {
"downloadAttachments": false
},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "99999999-9999-9999-9999-000000000002",
"name": "Set Inbox Configuration",
"type": "n8n-nodes-base.code",
"position": [
288,
320
],
"parameters": {
"jsCode": "// ===== EDIT YOUR SETTINGS HERE =====\n// Defaults below match the Gumroad + n8n Creator listings.\n// Change only what you need; leave unused destinations as placeholders.\n\n// Where to log admin tasks:\n// 'sheets' -> Google Sheets only\n// 'airtable' -> Airtable only\n// 'both' -> Google Sheets and Airtable\nconst TASK_LOG = 'sheets';\n\nconst GOOGLE_SHEET_ID = 'your-google-sheet-id';\nconst SHEETS_TAB_NAME = 'Admin Tasks';\n\nconst AIRTABLE_BASE_ID = 'appXXXXXXXXXXXXXX';\nconst ADMIN_TASKS_TABLE_NAME = 'Admin Tasks';\n\n// Where to send alerts for urgent, due-soon, or review-needed admin items:\n// 'slack' -> Slack only\n// 'teams' -> Microsoft Teams only\n// 'both' -> Slack and Teams\n// 'none' -> no chat alerts\nconst NOTIFY_CHANNELS = 'slack';\nconst SLACK_CHANNEL = '#admin-tasks';\nconst TEAMS_WEBHOOK_URL = 'https://outlook.office.com/webhook/your-teams-incoming-webhook';\n\nconst URGENT_DUE_DAYS = 3;\nconst DUPLICATE_WINDOW_DAYS = 30;\nconst ALERT_REVIEW_ITEMS = true;\nconst REDACT_BEFORE_AI = true;\nconst DEFAULT_SOURCE = 'gmail_admin';\n\nconst payload = $input.first().json;\nconst taskLog = String(TASK_LOG || 'sheets').toLowerCase().trim();\nconst notifyChannels = String(NOTIFY_CHANNELS || 'slack').toLowerCase().trim();\n\nreturn [\n {\n json: {\n taskLog,\n useSheetsLog: taskLog === 'sheets' || taskLog === 'both',\n useAirtableLog: taskLog === 'airtable' || taskLog === 'both',\n googleSheetId: GOOGLE_SHEET_ID,\n sheetsTabName: SHEETS_TAB_NAME,\n airtableBaseId: AIRTABLE_BASE_ID,\n adminTasksTableName: ADMIN_TASKS_TABLE_NAME,\n notifyChannels,\n useSlack: notifyChannels === 'slack' || notifyChannels === 'both',\n useTeams: notifyChannels === 'teams' || notifyChannels === 'both',\n slackChannel: SLACK_CHANNEL,\n teamsWebhookUrl: TEAMS_WEBHOOK_URL,\n urgentDueDays: URGENT_DUE_DAYS,\n duplicateWindowDays: DUPLICATE_WINDOW_DAYS,\n alertReviewItems: ALERT_REVIEW_ITEMS,\n redactBeforeAi: REDACT_BEFORE_AI,\n defaultSource: DEFAULT_SOURCE,\n payload,\n },\n },\n];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000003",
"name": "Redact Email Content",
"type": "n8n-nodes-base.code",
"position": [
560,
320
],
"parameters": {
"jsCode": "const settings = $input.first().json;\nconst raw = settings.payload || {};\n\nconst htmlToText = (value) => String(value || '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n .replace(/<br\\s*\\/?>/gi, '\\n')\n .replace(/<\\/p>/gi, '\\n')\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\\s+\\n/g, '\\n')\n .replace(/[ \\t]+/g, ' ')\n .trim();\n\nconst redact = (value) => {\n let text = String(value || '');\n text = text.replace(/\\b\\d{3}-\\d{2}-\\d{4}\\b/g, '[REDACTED_SSN]');\n text = text.replace(/\\b(?:\\d[ -]*?){13,19}\\b/g, (match) => {\n const digits = match.replace(/\\D/g, '');\n return digits.length >= 13 ? `[REDACTED_NUMBER_ENDING_${digits.slice(-4)}]` : match;\n });\n text = text.replace(/\\b(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b/g, '[REDACTED_PHONE]');\n text = text.replace(/\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b/gi, '[REDACTED_EMAIL]');\n text = text.replace(/\\b\\d{1,6}\\s+[A-Za-z0-9.'-]+\\s+(?:Street|St|Avenue|Ave|Road|Rd|Drive|Dr|Lane|Ln|Boulevard|Blvd|Court|Ct|Way|Place|Pl)\\b/gi, '[REDACTED_ADDRESS]');\n text = text.replace(/\\b(?:account|acct|policy|member|case|claim|confirmation|reservation|invoice)\\s*(?:number|#|id)?\\s*[:#]?\\s*[A-Z0-9-]{4,}\\b/gi, (match) => {\n const id = match.match(/[A-Z0-9-]{4,}$/i)?.[0] || '';\n return `${match.split(':')[0].split('#')[0].trim()} [REDACTED_ID_ENDING_${id.slice(-4)}]`;\n });\n return text;\n};\n\nconst headers = raw.headers || raw.payload?.headers || {};\nconst subject = raw.subject || raw.Subject || headers.subject || raw.payload?.subject || '';\nconst fromEmail = raw.from || raw.From || headers.from || raw.payload?.from || '';\nconst messageId = raw.id || raw.messageId || raw.message_id || raw.payload?.id || `admin-${Date.now()}`;\nconst receivedAt = raw.date || raw.internalDate || raw.receivedAt || new Date().toISOString();\nconst textBody = raw.textPlain || raw.text || raw.snippet || raw.body?.text || '';\nconst htmlBody = raw.textHtml || raw.html || raw.body?.html || '';\nconst rawText = htmlToText(textBody || htmlBody || raw.snippet || JSON.stringify(raw).slice(0, 6000));\nconst redactedText = settings.redactBeforeAi ? redact(rawText) : rawText;\nconst compact = rawText.toLowerCase().replace(/\\s+/g, ' ').replace(/'/g, '').slice(0, 1000);\nconst adminHash = `${fromEmail}|${subject}|${compact}`.toLowerCase();\n\nreturn [\n {\n json: {\n ...settings,\n messageId: String(messageId),\n receivedAt,\n source: raw.source || settings.defaultSource || 'gmail_admin',\n originalSubject: String(subject),\n fromEmail: String(fromEmail),\n rawText: rawText.slice(0, 10000),\n redactedText: redactedText.slice(0, 10000),\n rawTextSnippet: rawText.slice(0, 1000),\n adminHash,\n redactionApplied: rawText !== redactedText,\n _mockExistingTasks: Array.isArray(raw._mockExistingTasks) ? raw._mockExistingTasks : [],\n _testScenario: raw._testScenario || raw.scenario || '',\n },\n },\n];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000040",
"name": "Evaluate Airtable Memory Usage",
"type": "n8n-nodes-base.if",
"position": [
780,
320
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "+1234567890",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.useAirtableLog }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000004",
"name": "Read Airtable Memory",
"type": "n8n-nodes-base.airtable",
"position": [
1040,
200
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $json.airtableBaseId }}"
},
"limit": 10,
"table": {
"__rl": true,
"mode": "name",
"value": "={{ $json.adminTasksTableName }}"
},
"options": {},
"operation": "search",
"returnAll": false,
"filterByFormula": "=OR({Message ID} = '{{ $json.messageId }}', {Admin Hash} = '{{ $json.adminHash }}')"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1,
"alwaysOutputData": true
},
{
"id": "99999999-9999-9999-9999-000000000041",
"name": "Compile Airtable Data",
"type": "n8n-nodes-base.code",
"position": [
1300,
200
],
"parameters": {
"jsCode": "const admin = $('Redact Email Content').first().json;\nconst rows = $input\n .all()\n .map((item) => item.json)\n .filter((row) => !row._mockEmpty)\n .map((row) => row.fields || row);\nreturn [{ json: { ...admin, _lookupRows: rows } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000042",
"name": "Skip Airtable Data Usage",
"type": "n8n-nodes-base.code",
"position": [
1040,
420
],
"parameters": {
"jsCode": "const admin = $('Redact Email Content').first().json;\nreturn [{ json: { ...admin, _lookupRows: [] } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000043",
"name": "Evaluate Sheets Memory Usage",
"type": "n8n-nodes-base.if",
"position": [
1560,
320
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "+1234567890",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.useSheetsLog }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000044",
"name": "Read Sheets Memory",
"type": "n8n-nodes-base.googleSheets",
"position": [
1820,
200
],
"parameters": {
"options": {},
"resource": "sheet",
"operation": "read",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $json.sheetsTabName }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.googleSheetId }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5,
"alwaysOutputData": true
},
{
"id": "99999999-9999-9999-9999-000000000045",
"name": "Combine Sheets Data",
"type": "n8n-nodes-base.code",
"position": [
2080,
200
],
"parameters": {
"jsCode": "function fromNode(name) {\n try {\n return $(name).first().json;\n } catch (error) {\n return null;\n }\n}\n\nconst prev =\n fromNode('Compile Airtable Data') ||\n fromNode('Skip Airtable Data Usage') ||\n $('Redact Email Content').first().json;\n\nconst sheetRows = $input\n .all()\n .map((item) => item.json)\n .filter((row) => !row._mockEmpty)\n .map((row) => row.fields || row);\n\nreturn [{ json: { ...prev, _lookupRows: [...(prev._lookupRows || []), ...sheetRows] } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000046",
"name": "Transfer Memory Data",
"type": "n8n-nodes-base.code",
"position": [
1820,
420
],
"parameters": {
"jsCode": "const item = $input.first().json;\nreturn [{ json: item }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000005",
"name": "Detect Email Duplication",
"type": "n8n-nodes-base.code",
"position": [
2320,
320
],
"parameters": {
"jsCode": "const admin = $input.first().json;\nconst rows = Array.isArray(admin._lookupRows) ? admin._lookupRows : [];\nconst windowDays = admin.duplicateWindowDays ?? 30;\nconst cutoff = Date.now() - windowDays * 24 * 60 * 60 * 1000;\n\nconst isDuplicate = rows.some((row) => {\n const record = row.fields || row;\n if (record._mockEmpty) return false;\n const existingMessageId = String(record['Message ID'] || record.messageId || '').trim();\n const existingHash = String(record['Admin Hash'] || record.adminHash || '').toLowerCase();\n const existingAt = Date.parse(record['Received At'] || record.receivedAt || record.createdTime || '');\n\n if (admin.messageId && existingMessageId && admin.messageId === existingMessageId) {\n if (!existingAt || existingAt >= cutoff) return true;\n }\n\n if (admin.adminHash && existingHash && admin.adminHash === existingHash) {\n if (!existingAt || existingAt >= cutoff) return true;\n }\n\n return false;\n});\n\nreturn [{ json: { ...admin, isDuplicate } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000006",
"name": "Check Email Item Status",
"type": "n8n-nodes-base.if",
"position": [
2580,
320
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "+1234567890",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.isDuplicate }}",
"rightValue": false
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000099",
"name": "Complete Duplicate Admin Processing",
"type": "n8n-nodes-base.code",
"position": [
2840,
520
],
"parameters": {
"jsCode": "const item = $input.first().json;\nreturn [\n {\n json: {\n status: 'skipped',\n reason: item.isDuplicate ? 'duplicate_admin_item' : 'filtered',\n messageId: item.messageId,\n adminHash: item.adminHash,\n testScenario: item._testScenario || '',\n },\n },\n];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000007",
"name": "AI Classify Email",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
3072,
320
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o-mini"
},
"options": {
"temperature": 0.1
},
"messages": {
"values": [
{
"role": "system",
"content": "You classify personal admin emails and extract task/reminder fields. Return ONLY valid JSON with keys: isAdminItem (boolean), extractionStatus (extracted|not_admin|failed), failureReason, category, priority (low|medium|high|urgent), dueDate, dueTime, organization, actionRequired (boolean), taskTitle, summary, recommendedNextStep, amount, accountLastFour, url, confidence, needsReview, notes. Treat bills, renewals, appointments, travel check-ins, warranties, school/home notices, insurance/admin notices, subscriptions, and deadlines as admin items. Treat newsletters, promotions, social updates, and purely informational email as not_admin. Do not give legal, financial, or medical advice; summarize next admin action only."
},
{
"role": "user",
"content": "=Admin email after privacy redaction:\nFrom: {{ $('Detect Email Duplication').item.json.fromEmail }}\nSubject: {{ $('Detect Email Duplication').item.json.originalSubject }}\nReceived: {{ $('Detect Email Duplication').item.json.receivedAt }}\nBody:\n{{ $('Detect Email Duplication').item.json.redactedText }}"
}
]
},
"resource": "text",
"operation": "message"
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.8,
"continueOnFail": true
},
{
"id": "99999999-9999-9999-9999-000000000008",
"name": "Examine Email Extraction",
"type": "n8n-nodes-base.code",
"position": [
3328,
320
],
"parameters": {
"jsCode": "const admin = $('Detect Email Duplication').first().json;\nconst aiItem = $input.first().json;\nconst raw =\n aiItem.message?.content ||\n aiItem.content ||\n aiItem.text ||\n aiItem.output ||\n aiItem.output_text ||\n aiItem.choices?.[0]?.message?.content ||\n '';\n\nlet extraction = {\n isAdminItem: true,\n extractionStatus: 'extracted',\n failureReason: '',\n category: 'General admin',\n priority: 'medium',\n dueDate: '',\n dueTime: '',\n organization: '',\n actionRequired: true,\n taskTitle: '',\n summary: '',\n recommendedNextStep: '',\n amount: '',\n accountLastFour: '',\n url: '',\n confidence: 50,\n needsReview: true,\n notes: '',\n};\n\nlet parsedOk = false;\ntry {\n const jsonMatch = String(raw).match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n extraction = { ...extraction, ...JSON.parse(jsonMatch[0]) };\n parsedOk = true;\n }\n} catch (error) {\n extraction.extractionStatus = 'failed';\n extraction.failureReason = 'OpenAI returned text that was not valid admin JSON';\n}\n\nif (!parsedOk && !extraction.failureReason) {\n extraction.extractionStatus = 'failed';\n extraction.failureReason = 'OpenAI did not return a JSON object';\n}\n\nconst confidence = Math.max(0, Math.min(100, Number(extraction.confidence) || 50));\nconst priority = String(extraction.priority || 'medium').toLowerCase();\nconst dueDateValue = String(extraction.dueDate || '');\nconst dueTimeValue = String(extraction.dueTime || '');\nconst dueAt = Date.parse(dueDateValue);\nconst dueSoon = Number.isFinite(dueAt)\n ? dueAt <= Date.now() + (admin.urgentDueDays ?? 3) * 24 * 60 * 60 * 1000\n : false;\nconst isAdminItem = extraction.isAdminItem !== false && extraction.extractionStatus !== 'not_admin';\nconst needsReview = Boolean(extraction.needsReview || confidence < 70 || (!extraction.taskTitle && !extraction.summary));\nconst extractionFailed = Boolean(aiItem.error || extraction.extractionStatus === 'failed' || !parsedOk);\nconst extractionSucceeded = Boolean(isAdminItem && !extractionFailed);\n\nlet route = 'standard';\nif (!isAdminItem) route = 'not_admin';\nelse if (extractionFailed) route = 'extraction_failed';\nelse if (needsReview) route = 'review';\nelse if (priority === 'urgent' || priority === 'high') route = 'priority';\nelse if (dueSoon) route = 'due_soon';\n\nconst shouldAlert = Boolean(\n admin.notifyChannels !== 'none' &&\n extractionSucceeded &&\n (priority === 'urgent' ||\n priority === 'high' ||\n dueSoon ||\n (admin.alertReviewItems && needsReview))\n);\n\nreturn [\n {\n json: {\n ...admin,\n category: String(extraction.category || 'General admin'),\n priority,\n dueDate: dueDateValue,\n dueTime: dueTimeValue,\n dueSoon,\n organization: String(extraction.organization || ''),\n actionRequired: Boolean(extraction.actionRequired),\n taskTitle: String(extraction.taskTitle || extraction.summary || 'Admin item needs review'),\n summary: String(extraction.summary || ''),\n recommendedNextStep: String(extraction.recommendedNextStep || ''),\n amount: String(extraction.amount || ''),\n accountLastFour: String(extraction.accountLastFour || ''),\n url: String(extraction.url || ''),\n isAdminItem,\n extractionStatus: extractionFailed ? 'failed' : isAdminItem ? 'extracted' : 'not_admin',\n extractionSucceeded,\n extractionFailureReason:\n aiItem.error?.message ||\n extraction.failureReason ||\n (!isAdminItem ? 'Email does not appear to be an admin task, reminder, bill, renewal, appointment, travel item, or deadline' : ''),\n confidence,\n needsReview,\n shouldAlert,\n route,\n notes: String(extraction.notes || ''),\n },\n },\n];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000072",
"name": "Verify Extraction Success",
"type": "n8n-nodes-base.if",
"position": [
3584,
320
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "+1234567890",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.extractionSucceeded }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000073",
"name": "Complete Skipped Admin Processing",
"type": "n8n-nodes-base.code",
"position": [
3840,
528
],
"parameters": {
"jsCode": "const item = $input.first().json;\nconst reason = item.isAdminItem === false ? 'non_admin_email' : 'extraction_failed';\n\nreturn [\n {\n json: {\n status: 'skipped',\n reason,\n message:\n reason === 'non_admin_email'\n ? 'Email was processed successfully but does not appear to be an admin task.'\n : 'Admin task extraction failed. Check the email content or OpenAI node output.',\n messageId: item.messageId,\n adminHash: item.adminHash,\n extractionStatus: item.extractionStatus,\n extractionFailureReason: item.extractionFailureReason || '',\n originalSubject: item.originalSubject,\n fromEmail: item.fromEmail,\n testScenario: item._testScenario || '',\n },\n },\n];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000009",
"name": "Format Alert Message",
"type": "n8n-nodes-base.code",
"position": [
3616,
0
],
"parameters": {
"jsCode": "const item = $input.first().json;\nconst reason =\n item.route === 'review'\n ? 'Needs review'\n : item.route === 'priority'\n ? 'Priority admin item'\n : item.route === 'due_soon'\n ? 'Due soon'\n : 'Admin item logged';\n\nconst details = [\n `Reason: ${reason}`,\n `Task: ${item.taskTitle}`,\n `Category: ${item.category}`,\n `Priority: ${item.priority}`,\n item.dueDate ? `Due: ${item.dueDate}${item.dueTime ? ` ${item.dueTime}` : ''}` : '',\n item.organization ? `Organization: ${item.organization}` : '',\n item.amount ? `Amount: ${item.amount}` : '',\n `Confidence: ${item.confidence}/100`,\n `Subject: ${item.originalSubject}`,\n].filter(Boolean);\n\nconst slackMessage = [`*${reason}: ${item.taskTitle}*`, '', ...details.map((line) => `- ${line}`)].join('\\n');\nconst teamsMessage = [`**${reason}: ${item.taskTitle}**`, '', ...details.map((line) => `- ${line}`)].join('\\n');\n\nreturn [\n {\n json: {\n ...item,\n useSlackAlert: Boolean(item.shouldAlert && item.useSlack),\n useTeamsAlert: Boolean(item.shouldAlert && item.useTeams),\n slackMessage,\n teamsMessage,\n },\n },\n];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000050",
"name": "Evaluate Slack Alert Need",
"type": "n8n-nodes-base.if",
"position": [
3888,
-96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "+1234567890",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.useSlackAlert }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000051",
"name": "Send Slack Alert",
"type": "n8n-nodes-base.slack",
"position": [
4140,
100
],
"parameters": {
"text": "={{ $json.slackMessage }}",
"select": "channel",
"resource": "message",
"channelId": {
"__rl": true,
"mode": "name",
"value": "={{ $json.slackChannel }}"
},
"operation": "post",
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000052",
"name": "Confirm Slack Alert Sent",
"type": "n8n-nodes-base.code",
"position": [
4400,
100
],
"parameters": {
"jsCode": "const item = $input.first().json;\nreturn [{ json: { ...item, alertSent: 'yes' } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000053",
"name": "Skip Slack Alert",
"type": "n8n-nodes-base.code",
"position": [
4140,
300
],
"parameters": {
"jsCode": "const item = $input.first().json;\nreturn [{ json: { ...item, alertSent: item.alertSent || 'no' } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000054",
"name": "Evaluate Teams Alert Need",
"type": "n8n-nodes-base.if",
"position": [
4640,
220
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "+1234567890",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.useTeamsAlert }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000055",
"name": "Post to Teams Webhook",
"type": "n8n-nodes-base.httpRequest",
"position": [
4928,
96
],
"parameters": {
"url": "={{ $json.teamsWebhookUrl }}",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ text: $json.teamsMessage }) }}",
"sendBody": true,
"specifyBody": "json"
},
"typeVersion": 4.2
},
{
"id": "99999999-9999-9999-9999-000000000056",
"name": "Confirm Teams Alert Sent",
"type": "n8n-nodes-base.code",
"position": [
5200,
96
],
"parameters": {
"jsCode": "const item = $input.first().json;\nreturn [{ json: { ...item, alertSent: 'yes' } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000057",
"name": "Skip Teams Alert",
"type": "n8n-nodes-base.code",
"position": [
4928,
304
],
"parameters": {
"jsCode": "const item = $input.first().json;\nreturn [{ json: { ...item, alertSent: item.alertSent || 'no' } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000010",
"name": "Create Task Log Entry",
"type": "n8n-nodes-base.code",
"position": [
5400,
220
],
"parameters": {
"jsCode": "const item = $input.first().json;\nreturn [\n {\n json: {\n taskLog: item.taskLog,\n useSheetsLog: item.useSheetsLog,\n useAirtableLog: item.useAirtableLog,\n googleSheetId: item.googleSheetId,\n sheetsTabName: item.sheetsTabName,\n airtableBaseId: item.airtableBaseId,\n adminTasksTableName: item.adminTasksTableName,\n 'Received At': item.receivedAt,\n Source: item.source,\n 'Message ID': item.messageId,\n 'Admin Hash': item.adminHash,\n Category: item.category,\n Priority: item.priority,\n 'Due Date': item.dueDate,\n 'Due Time': item.dueTime,\n Organization: item.organization,\n Sender: item.fromEmail,\n 'Action Required': item.actionRequired ? 'yes' : 'no',\n 'Task Title': item.taskTitle,\n Summary: item.summary,\n 'Recommended Next Step': item.recommendedNextStep,\n Amount: item.amount,\n 'Account Last Four': item.accountLastFour,\n URL: item.url,\n 'Is Admin Item': item.isAdminItem ? 'yes' : 'no',\n 'Extraction Status': item.extractionStatus,\n 'Extraction Failure Reason': item.extractionFailureReason,\n Confidence: item.confidence,\n 'Needs Review': item.needsReview ? 'yes' : 'no',\n 'Alert Sent': item.alertSent || 'no',\n Status:\n item.route === 'review'\n ? 'Needs Review'\n : item.route === 'priority'\n ? 'Priority'\n : item.route === 'due_soon'\n ? 'Due Soon'\n : 'Logged',\n 'Original Subject': item.originalSubject,\n 'From Email': item.fromEmail,\n 'Redaction Applied': item.redactionApplied ? 'yes' : 'no',\n 'Raw Text Snippet': item.rawTextSnippet,\n Notes: item.notes,\n _testScenario: item._testScenario || '',\n },\n },\n];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000060",
"name": "Evaluate Sheets Log Need",
"type": "n8n-nodes-base.if",
"position": [
5696,
224
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "+1234567890",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.useSheetsLog }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000061",
"name": "Append Task Log to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
5952,
96
],
"parameters": {
"columns": {
"value": {
"URL": "={{ $json[\"URL\"] }}",
"Notes": "={{ $json[\"Notes\"] }}",
"Amount": "={{ $json[\"Amount\"] }}",
"Sender": "={{ $json[\"Sender\"] }}",
"Source": "={{ $json[\"Source\"] }}",
"Status": "={{ $json[\"Status\"] }}",
"Summary": "={{ $json[\"Summary\"] }}",
"Category": "={{ $json[\"Category\"] }}",
"Due Date": "={{ $json[\"Due Date\"] }}",
"Due Time": "={{ $json[\"Due Time\"] }}",
"Priority": "={{ $json[\"Priority\"] }}",
"Admin Hash": "={{ $json[\"Admin Hash\"] }}",
"Alert Sent": "={{ $json[\"Alert Sent\"] }}",
"Confidence": "={{ $json[\"Confidence\"] }}",
"From Email": "={{ $json[\"From Email\"] }}",
"Message ID": "={{ $json[\"Message ID\"] }}",
"Task Title": "={{ $json[\"Task Title\"] }}",
"Received At": "={{ $json[\"Received At\"] }}",
"Needs Review": "={{ $json[\"Needs Review\"] }}",
"Organization": "={{ $json[\"Organization\"] }}",
"Is Admin Item": "={{ $json[\"Is Admin Item\"] }}",
"Action Required": "={{ $json[\"Action Required\"] }}",
"Original Subject": "={{ $json[\"Original Subject\"] }}",
"Raw Text Snippet": "={{ $json[\"Raw Text Snippet\"] }}",
"Account Last Four": "={{ $json[\"Account Last Four\"] }}",
"Extraction Status": "={{ $json[\"Extraction Status\"] }}",
"Redaction Applied": "={{ $json[\"Redaction Applied\"] }}",
"Recommended Next Step": "={{ $json[\"Recommended Next Step\"] }}",
"Extraction Failure Reason": "={{ $json[\"Extraction Failure Reason\"] }}"
},
"schema": [
{
"id": "Received At",
"type": "string",
"display": true,
"required": false,
"displayName": "Received At",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Source",
"type": "string",
"display": true,
"required": false,
"displayName": "Source",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Message ID",
"type": "string",
"display": true,
"required": false,
"displayName": "Message ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Admin Hash",
"type": "string",
"display": true,
"required": false,
"displayName": "Admin Hash",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Category",
"type": "string",
"display": true,
"required": false,
"displayName": "Category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Priority",
"type": "string",
"display": true,
"required": false,
"displayName": "Priority",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Due Date",
"type": "string",
"display": true,
"required": false,
"displayName": "Due Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Due Time",
"type": "string",
"display": true,
"required": false,
"displayName": "Due Time",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Organization",
"type": "string",
"display": true,
"required": false,
"displayName": "Organization",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Sender",
"type": "string",
"display": true,
"required": false,
"displayName": "Sender",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Action Required",
"type": "string",
"display": true,
"required": false,
"displayName": "Action Required",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Task Title",
"type": "string",
"display": true,
"required": false,
"displayName": "Task Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Summary",
"type": "string",
"display": true,
"required": false,
"displayName": "Summary",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Recommended Next Step",
"type": "string",
"display": true,
"required": false,
"displayName": "Recommended Next Step",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Amount",
"type": "string",
"display": true,
"required": false,
"displayName": "Amount",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Account Last Four",
"type": "string",
"display": true,
"required": false,
"displayName": "Account Last Four",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "URL",
"type": "string",
"display": true,
"required": false,
"displayName": "URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Is Admin Item",
"type": "string",
"display": true,
"required": false,
"displayName": "Is Admin Item",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Extraction Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Extraction Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Extraction Failure Reason",
"type": "string",
"display": true,
"required": false,
"displayName": "Extraction Failure Reason",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Confidence",
"type": "string",
"display": true,
"required": false,
"displayName": "Confidence",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Needs Review",
"type": "string",
"display": true,
"required": false,
"displayName": "Needs Review",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Alert Sent",
"type": "string",
"display": true,
"required": false,
"displayName": "Alert Sent",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Original Subject",
"type": "string",
"display": true,
"required": false,
"displayName": "Original Subject",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "From Email",
"type": "string",
"display": true,
"required": false,
"displayName": "From Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Redaction Applied",
"type": "string",
"display": true,
"required": false,
"displayName": "Redaction Applied",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Raw Text Snippet",
"type": "string",
"display": true,
"required": false,
"displayName": "Raw Text Snippet",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Notes",
"type": "string",
"display": true,
"required": false,
"displayName": "Notes",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {},
"resource": "sheet",
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $json.sheetsTabName }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.googleSheetId }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5
},
{
"id": "99999999-9999-9999-9999-000000000062",
"name": "Confirm Sheets Log Entry",
"type": "n8n-nodes-base.code",
"position": [
6208,
96
],
"parameters": {
"jsCode": "const prior = $('Create Task Log Entry').first().json;\nreturn [{ json: { ...prior, sheetsLogged: 'yes' } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000063",
"name": "Skip Sheets Logging",
"type": "n8n-nodes-base.code",
"position": [
5952,
304
],
"parameters": {
"jsCode": "const item = $input.first().json;\nreturn [{ json: { ...item, sheetsLogged: 'no' } }];"
},
"typeVersion": 2
},
{
"id": "99999999-9999-9999-9999-000000000064",
"name": "Evaluate Airtable Log Need",
"type": "n8n-nodes-base.if",
"position": [
6448,
224
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "+1234567890",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.useAirtableLog }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99999999-9999-9999-9999-000000000065",
"name": "Add Task Log to Airtable",
"type": "n8n-nodes-base.airtable",
"position": [
6720,
96
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $json.airtableBaseId }}"
},
"table": {
"__rl": true,
"mode": "name",
"value": "={{ $json.adminTasksTableName }}"
},
"columns": {
"value": {
"URL": "={{ $json[\"URL\"] }}",
"Notes": "={{ $json[\"Notes\"] }}",
"Amount": "={{ $json[\"Amount\"] }}",
"Sender": "={{ $json[\"Sender\"] }}",
"Source": "={{ $json[\"Source\"] }}",
"Status": "={{ $json[\"Status\"] }}",
"Summary": "={{ $json[\"Summary\"] }}",
"Category": "={{ $json[\"Category\"] }}",
"Due Date": "={{ $json[\"Due Date\"] }}",
"Due Time": "={{ $json[\"Due Time\"] }}",
"Priority": "={{ $json[\"Priority\"] }}",
"Admin Hash": "={{ $json[\"Admin Hash\"] }}",
"Alert Sent": "={{ $json[\"Alert Sent\"] }}",
"Confidence": "={{ $json[\"Confidence\"] }}",
"From Email": "={{ $json[\"From Email\"] }}",
"Message ID": "={{ $json[\"Message ID\"] }}",
"Task Title": "={{ $json[\"Task Title\"] }}",
"Received At": "={{ $json[\"Received At\"] }}",
"Needs Review": "={{ $json[\"Needs Review\"] }}",
"Organization": "={{ $json[\"Organization\"] }}",
"Is Admin Item": "={{ $json[\"Is Admin Item\"] }}",
"Action Required": "={{ $json[\"Action Required\"] }}",
"Original Subject": "={{ $json[\"Original Subject\"] }}",
"Raw Text Snippet": "={{ $json[\"Raw Text Snippet\"] }}",
"Account Last Four": "={{ $json[\"Account Last Four\"] }}",
"Extraction Status": "={{ $json[\"Extraction Status\"] }}",
"Redaction Applied": "={{ $json[\"Redaction Applied\"] }}",
"Recommended Next Step": "={{ $json[\"Recommended Next Step\"] }}",
"Extraction Failure Reason": "={{ $json[\"Extraction Failure Reason\"] }}"
},
"schema": [
{
"id": "Received At",
"type": "string",
"display": true,
"required": false,
"displayName": "Received At",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Source",
"type": "string",
"display": true,
"required": false,
"displayName": "Source",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Message ID",
"type": "string",
"display": true,
"required": false,
"displayName": "Message ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Admin Hash",
"type": "string",
"display": true,
"required": false,
"displayName": "Admin Hash",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Category",
"type": "string",
"display": true,
"required": false,
"displayName": "Category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Priority",
"type": "string",
"display": true,
"required": false,
"displayName": "Priority",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Due Date",
"type": "string",
"display": true,
"required": false,
"displayName": "Due Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Due Time",
"type": "string",
"display": true,
"required": false,
"displayName": "Due Time",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Organization",
"type": "string",
"display": true,
"required": false,
"displayName": "Organization",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Sender",
"type": "string",
"display": true,
"required": false,
"displayName": "Sender",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Action Required",
"type": "string",
"display": true,
"required": false,
"displayName": "Action Required",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Task Title",
"type": "string",
"display": true,
"required": false,
"displayName": "Task Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Summary",
"type": "string",
"display": true,
"required": false,
"displayName": "Summary",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Recommended Next Step",
"type": "string",
"display": true,
"required": false,
"displayName": "Recommended Next Step",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Amount",
"type": "string",
"display": true,
"required": false,
"displayName": "Amount",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Account Last Four",
"type": "string",
"display": true,
"required": false,
"displayName": "Account Last Four",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "URL",
"type": "string",
"display": true,
"required": false,
"displayName": "URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Is Admin Item",
"type": "string",
"display": true,
"required": false,
"displayName": "Is Admin Item",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Extraction Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Extraction Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Extraction Failure Reason",
"type": "string",
"display": true,
"required": false,
"displayName": "Extraction Failure Reason",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Confidence",
"type": "string",
"display": true,
"required": false
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.
airtableTokenApigmailOAuth2googleSheetsOAuth2ApiopenAiApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow monitors Gmail for messages labeled “Admin”, redacts sensitive data, uses OpenAI to extract structured admin tasks and reminders, skips duplicates by checking Google Sheets and/or Airtable, sends optional Slack or Microsoft Teams alerts for urgent items, and logs…
Source: https://n8n.io/workflows/17135/ — 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.
Splitout Code. Uses httpRequest, stickyNote, manualTrigger, splitOut. Event-driven trigger; 33 nodes.
This workflow automates the process of gathering LinkedIn advice articles, extracting their content, and generating unique contributions for each article using an AI model. The contributions are then
2491. Uses httpRequest, openAi, nocoDb, slack. Event-driven trigger; 33 nodes.
This workflow automatically monitors your Gmail support inbox every minute for new unread messages, instantly sending each email to OpenAI for intelligent analysis. The AI engine evaluates sentiment (
This workflow receives insurance quote submissions via a webhook, normalizes and deduplicates them against Airtable and/or Google Sheets, uses OpenAI to score and classify the lead, then routes hot/wa