This workflow follows the Emailsend → 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": "Scheduling Assistant - 06 Notify",
"nodes": [
{
"parameters": {},
"id": "workflow-trigger",
"name": "When called by another workflow",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1,
"position": [
250,
300
]
},
{
"parameters": {
"jsCode": "// Determine notification type and prepare message\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n const data = item.json.exportedData || item.json.conflictData || item.json.invalidData || item.json;\n const notificationType = item.json.notificationType || 'event_created';\n\n let subject = '';\n let messageBody = '';\n let recipients = [];\n\n // Get requestor email\n const requestorEmail = data.requestorEmail || data.normalized?.attendees?.[0] || '';\n if (requestorEmail) {\n recipients.push(requestorEmail);\n }\n\n // Build notification based on type\n switch (notificationType) {\n case 'event_created':\n subject = `[SCHEDULED] Meeting Scheduled: ${data.normalized.title}`;\n messageBody = `Your meeting has been successfully scheduled.\n\nEvent: ${data.normalized.title}\nTime: ${new Date(data.normalized.startTime).toLocaleString()}\nDuration: ${data.normalized.duration} minutes\nLocation: ${data.normalized.location}\nAttendees: ${data.normalized.attendees.join(', ')}\n\nCalendar Link: ${data.export?.eventUrl || 'Check your calendar'}\nEvent ID: ${data.export?.eventId || data.requestId}\n\nAll attendees have been notified via calendar invitation.`;\n break;\n\n case 'conflict_detected':\n subject = `[CONFLICT] Scheduling Conflict Detected: ${data.normalized.title}`;\n messageBody = `A conflict was detected with your requested meeting time.\n\nRequested: ${data.normalized.title}\nTime: ${new Date(data.normalized.startTime).toLocaleString()}\nConflicts Found: ${data.conflictScan.conflictCount}\n\nConflicting Events:\n${data.conflictScan.conflicts.map(c => ` - ${c.title} (${new Date(c.start).toLocaleString()} - ${new Date(c.end).toLocaleString()})`).join('\\n')}\n\nSuggested Alternative Times:\n${data.conflictScan.alternatives.map((alt, idx) => ` ${idx + 1}. ${new Date(alt.startTime).toLocaleString()} - ${alt.reason}`).join('\\n')}\n\nPlease reply with your preferred alternative time or suggest a different slot.`;\n break;\n\n case 'validation_failed':\n subject = `[FAILED] Scheduling Request Failed: ${data.normalized?.title || 'Meeting'}`;\n messageBody = `Your scheduling request could not be processed due to validation errors.\n\nRequested: ${data.normalized?.title || 'Meeting'}\nTime: ${data.normalized?.startTime ? new Date(data.normalized.startTime).toLocaleString() : 'Not specified'}\n\nErrors:\n${data.validation.errors.map(e => ` - ${e}`).join('\\n')}${data.validation.warnings.length > 0 ? `\\n\\nWarnings:\\n${data.validation.warnings.map(w => ` - ${w}`).join('\\n')}` : ''}\n\nPlease review the errors above and submit a corrected request.`;\n break;\n\n default:\n subject = `Meeting Update: ${data.normalized?.title || 'Notification'}`;\n messageBody = `Your scheduling request has been processed.\n\nRequest ID: ${data.requestId}\nStatus: ${data.status}`;\n }\n\n const notificationData = {\n ...data,\n notification: {\n type: notificationType,\n subject: subject,\n body: messageBody,\n recipients: recipients,\n preparedAt: new Date().toISOString(),\n confirmationCode: `CONF-${Date.now()}`\n },\n stage: 'notification_prepared',\n processedAt: new Date().toISOString()\n };\n\n results.push({ json: notificationData });\n}\n\nreturn results;"
},
"id": "code-prepare-notification",
"name": "Prepare Notification",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
450,
300
]
},
{
"parameters": {
"fromEmail": "={{$env.NOTIFICATION_EMAIL_FROM || 'noreply@scheduling.com'}}",
"toEmail": "={{$json.notification.recipients.join(',')}}",
"subject": "={{$json.notification.subject}}",
"text": "={{$json.notification.body}}",
"options": {
"allowUnauthorizedCerts": false
}
},
"id": "send-notification",
"name": "Send Notification",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2,
"position": [
650,
300
],
"disabled": true,
"notes": "Placeholder delivery node, disabled by default. Replace this node with your actual notification channel: Telegram sendMessage, HTTP Request to a webhook, or any messaging integration supported by n8n. The notification payload (subject, body, recipients) is already prepared by the Prepare Notification node above.",
"credentials": {
"smtp": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Log notification delivery\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n const data = item.json;\n \n const finalResult = {\n ...data,\n notification: {\n ...data.notification,\n sent: true,\n sentAt: new Date().toISOString(),\n deliveryStatus: 'success'\n },\n stage: 'completed',\n completedAt: new Date().toISOString(),\n summary: {\n requestId: data.requestId,\n status: data.status,\n notificationType: data.notification.type,\n recipients: data.notification.recipients,\n processingTime: new Date() - new Date(data.timestamp)\n }\n };\n \n results.push({ json: finalResult });\n}\n\nreturn results;"
},
"id": "code-log-completion",
"name": "Log Completion",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
300
]
},
{
"parameters": {
"operation": "insert",
"table": "scheduling_logs",
"columns": "request_id, status, notification_type, completed_at, processing_time_ms",
"options": {}
},
"id": "database-log",
"name": "Store in Database (Optional)",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
1050,
300
],
"disabled": true,
"notes": "Enable this node if you want to persist scheduling logs to a database"
}
],
"connections": {
"When called by another workflow": {
"main": [
[
{
"node": "Prepare Notification",
"type": "main",
"index": 0
}
]
]
},
"Prepare Notification": {
"main": [
[
{
"node": "Send Email Notification",
"type": "main",
"index": 0
}
]
]
},
"Send Email Notification": {
"main": [
[
{
"node": "Log Completion",
"type": "main",
"index": 0
}
]
]
},
"Log Completion": {
"main": [
[
{
"node": "Store in Database (Optional)",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveExecutionProgress": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all"
},
"staticData": null,
"tags": [
{
"name": "scheduling-assistant",
"id": "scheduling-tag-001"
},
{
"name": "notification",
"id": "notification-tag-001"
}
],
"triggerCount": 0,
"updatedAt": "2026-01-19T20:02:35.000Z",
"versionId": "v1.0.0"
}
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.
smtp
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Scheduling Assistant - 06 Notify. Uses executeWorkflowTrigger, emailSend, postgres. Event-driven trigger; 5 nodes.
Source: https://github.com/jasonv2610/multi-agent-orchestration-architecture/blob/main/examples/scheduling-assistant/workflows/06_notify.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.
Memorybufferwindow Workflow. Uses emailSend, httpRequest, executeWorkflowTrigger, formTrigger. Event-driven trigger; 45 nodes.
W4.1 - ROUTER (State + Voice). Uses executeWorkflowTrigger, postgres, redis. Event-driven trigger; 30 nodes.
SHEETS RAG. Uses googleDriveTrigger, postgres, googleSheets, executeWorkflowTrigger. Event-driven trigger; 24 nodes.
Gmail-Calendar. Uses executeWorkflowTrigger, postgres, googleCalendar, httpRequest. Event-driven trigger; 12 nodes.
Gmail-Triage. Uses executeWorkflowTrigger, httpRequest, postgres. Event-driven trigger; 10 nodes.