Upload files to "email-to-ai-task"
I've created a comprehensive README for your Email to Vikunja workflow. It covers the entire workflow from email monitoring through task creation and notification, including all the key transformations and integrations involved. The README explains what each node does, how they connect, what credentials you'll need, and how to configure it for your specific setup.
This commit is contained in:
208
email-to-ai-task/Email to AI Task (Vikunja).json
Normal file
208
email-to-ai-task/Email to AI Task (Vikunja).json
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
{
|
||||||
|
"name": "Email to AI Task (Vikunja)",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"values": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"name": "prompt",
|
||||||
|
"value": "=You are a task extraction assistant.\n\nReturn ONLY valid JSON with these fields:\n{\n \"title\": string,\n \"summary\": string,\n \"priority\": number (1-5),\n \"due_date\": string | null,\n \"labels\": string[]\n}\n\nEmail subject: {{$json.subject}}\nEmail body: {{$json.textPlain}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"dotNotation": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "Build AI Prompt",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
1456,
|
||||||
|
368
|
||||||
|
],
|
||||||
|
"id": "1d09bb19-658b-4310-b4ae-a4633b90aa16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "http://api.chat.pathcore.org/api/generate",
|
||||||
|
"sendBody": true,
|
||||||
|
"contentType": "raw",
|
||||||
|
"body": "={{ JSON.stringify({ model: \"mistral\", prompt: $json.prompt, stream: false }) }}\n",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.3,
|
||||||
|
"position": [
|
||||||
|
1664,
|
||||||
|
368
|
||||||
|
],
|
||||||
|
"id": "d067d32f-1d08-456a-a51a-c5bb2ab7e82d",
|
||||||
|
"name": "HTTP Request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "https://tasks.pathcore.org/api/v1/projects/1/tasks",
|
||||||
|
"authentication": "genericCredentialType",
|
||||||
|
"genericAuthType": "httpHeaderAuth",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={\n \"title\": \"{{ $json.title }}\",\n \"description\": \"{{ $json.description }}\",\n \"project_id\": 1,\n \"priority\": {{ $json.priority }},\n \"due_date\": \"{{ $json.due_date }}\"\n}\n",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.3,
|
||||||
|
"position": [
|
||||||
|
2064,
|
||||||
|
368
|
||||||
|
],
|
||||||
|
"id": "0d3330f1-f828-48ad-b5ba-18daabc80c4e",
|
||||||
|
"name": "HTTP Request1",
|
||||||
|
"credentials": {
|
||||||
|
"httpHeaderAuth": {
|
||||||
|
"id": "ao2U85ZTTchihcSC",
|
||||||
|
"name": "Vikunja API"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"downloadAttachments": true,
|
||||||
|
"options": {
|
||||||
|
"forceReconnect": 1,
|
||||||
|
"trackLastMessageId": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.emailReadImap",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [
|
||||||
|
1248,
|
||||||
|
368
|
||||||
|
],
|
||||||
|
"id": "a0c694fa-9757-416b-967d-0bdf995241e7",
|
||||||
|
"name": "Email Trigger (IMAP)",
|
||||||
|
"credentials": {
|
||||||
|
"imap": {
|
||||||
|
"id": "uen9pTUrJDRJFvlu",
|
||||||
|
"name": "IMAP account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "let text = $json.response || \"\";\n\n// Extract JSON block from Ollama response\nlet match = text.match(/\\{[\\s\\S]*\\}/);\nif (!match) {\n return [{\n title: \"Manual review required\",\n description: text,\n priority: 3,\n due_date: null,\n labels: [\"ai-error\"]\n }];\n}\n\nlet data = JSON.parse(match[0]);\n\n// Convert due_date to ISO 8601 with time (YYYY-MM-DDTHH:mm:ssZ)\nlet formattedDate = null;\nif (data.due_date === null || data.due_date === undefined || data.due_date === \"\") {\n // If no due_date, set to tomorrow\n let tomorrow = new Date();\n tomorrow.setDate(tomorrow.getDate() + 1);\n formattedDate = tomorrow.toISOString();\n} else if (data.due_date) {\n let dateStr = data.due_date.toLowerCase().trim();\n \n if (dateStr.includes(\"end of month\") || dateStr.includes(\"end of the month\")) {\n let today = new Date();\n let lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);\n formattedDate = lastDay.toISOString();\n } else if (dateStr.includes(\"end of week\")) {\n let today = new Date();\n let daysUntilFriday = (5 - today.getDay() + 7) % 7 || 7;\n let friday = new Date(today);\n friday.setDate(friday.getDate() + daysUntilFriday);\n formattedDate = friday.toISOString();\n } else if (dateStr.includes(\"today\")) {\n formattedDate = new Date().toISOString();\n } else if (dateStr.includes(\"tomorrow\")) {\n let tomorrow = new Date();\n tomorrow.setDate(tomorrow.getDate() + 1);\n formattedDate = tomorrow.toISOString();\n } else if (dateStr.includes(\"next week\")) {\n let nextWeek = new Date();\n nextWeek.setDate(nextWeek.getDate() + 7);\n formattedDate = nextWeek.toISOString();\n } else {\n // Try parsing as ISO date or other formats\n let parsed = new Date(data.due_date);\n if (!isNaN(parsed.getTime())) {\n formattedDate = parsed.toISOString();\n }\n }\n}\n\n// Ensure labels is always an array\nlet labels = data.labels || [];\nif (!Array.isArray(labels)) {\n labels = [labels];\n}\n\nreturn [{\n title: data.title || \"New Task\",\n description: data.summary || \"\",\n priority: data.priority || 3,\n due_date: formattedDate,\n labels: labels\n}];"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1856,
|
||||||
|
368
|
||||||
|
],
|
||||||
|
"id": "c85d2e04-8a51-4c49-a7e5-c8f7b36e366b",
|
||||||
|
"name": "Code in JavaScript"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"authentication": "oAuth2",
|
||||||
|
"select": "channel",
|
||||||
|
"channelId": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "C0A7DBLUT5K",
|
||||||
|
"mode": "list",
|
||||||
|
"cachedResultName": "vijunka-tasks"
|
||||||
|
},
|
||||||
|
"text": "=🆕 New Vikunja Task Created ID: {{ $json.id }} Identifier: {{ $json.identifier }} Project ID: {{ $json.project_id }} Bucket ID: {{ $json.bucket_id }} Description: {{ $json.description }} Priority: {{ $json.priority }} Percent Done: {{ $json.percent_done }}% Done: {{ $json.done }} Done At: {{ $json.done_at }} Start Date: {{ $json.start_date }} Due Date: {{ $json.due_date }} End Date: {{ $json.end_date }} Repeat Mode: {{ $json.repeat_mode }} Favorite: {{ $json.is_favorite }} Assignees: {{ ($json.assignees || []).map(a => a.username).join(\", \") }} Labels: {{ ($json.labels || []).map(l => l.name).join(\", \") }} Created By: {{ $json.created_by?.username }} Created: {{ $json.created }} Updated: {{ $json.updated }}",
|
||||||
|
"otherOptions": {
|
||||||
|
"includeLinkToWorkflow": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.slack",
|
||||||
|
"typeVersion": 2.4,
|
||||||
|
"position": [
|
||||||
|
2272,
|
||||||
|
368
|
||||||
|
],
|
||||||
|
"id": "cb9715a4-40c4-4de3-8f7e-d46c602dd730",
|
||||||
|
"name": "Send a message",
|
||||||
|
"webhookId": "7f5f138d-a956-489a-812f-0a915925dfd1",
|
||||||
|
"credentials": {
|
||||||
|
"slackOAuth2Api": {
|
||||||
|
"id": "zntCoaowWYuBIKAY",
|
||||||
|
"name": "Slack account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {},
|
||||||
|
"connections": {
|
||||||
|
"Build AI Prompt": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "HTTP Request",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"HTTP Request": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Code in JavaScript",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Email Trigger (IMAP)": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build AI Prompt",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Code in JavaScript": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "HTTP Request1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"HTTP Request1": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send a message",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": true,
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
},
|
||||||
|
"versionId": "67aafb11-9cbf-486a-b779-6f4dcebe7269",
|
||||||
|
"meta": {
|
||||||
|
"templateCredsSetupCompleted": true,
|
||||||
|
"instanceId": "3f831ca7bfd296051ccce9f0b1bd3c7aab7da4cca2818b7ffc69b89b3b2840d2"
|
||||||
|
},
|
||||||
|
"id": "wm6gwcPOLRiBwpdZ",
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
136
email-to-ai-task/README.md
Normal file
136
email-to-ai-task/README.md
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
# Email to AI Task (Vikunja)
|
||||||
|
|
||||||
|
An automated workflow that converts emails into structured tasks in Vikunja using AI-powered extraction.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This n8n workflow monitors an email inbox, automatically extracts task information from incoming emails using an AI model, and creates tasks in Vikunja. It provides intelligent parsing of task details like titles, summaries, priorities, and due dates, with Slack notifications upon task creation.
|
||||||
|
|
||||||
|
## Workflow Steps
|
||||||
|
|
||||||
|
### 1. Email Trigger (IMAP)
|
||||||
|
Monitors an IMAP email account for new messages. Downloads attachments and tracks the last read message to avoid processing duplicates.
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- Uses stored IMAP credentials
|
||||||
|
- Auto-download of attachments enabled
|
||||||
|
- Message tracking enabled
|
||||||
|
|
||||||
|
### 2. Build AI Prompt
|
||||||
|
Constructs a prompt from the email subject and body to send to the AI model.
|
||||||
|
|
||||||
|
**Input Fields:**
|
||||||
|
- `subject`: Email subject line
|
||||||
|
- `textPlain`: Plain text body of the email
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
A formatted prompt instructing the AI to extract task information as JSON.
|
||||||
|
|
||||||
|
### 3. HTTP Request (AI Generation)
|
||||||
|
Sends the constructed prompt to a local Ollama AI model endpoint for task extraction.
|
||||||
|
|
||||||
|
**Endpoint:** `http://api.chat.pathcore.org/api/generate`
|
||||||
|
**Model:** Mistral
|
||||||
|
**Method:** POST
|
||||||
|
|
||||||
|
The AI returns structured data with the following fields:
|
||||||
|
- `title`: Task title
|
||||||
|
- `summary`: Task description
|
||||||
|
- `priority`: Priority level (1-5)
|
||||||
|
- `due_date`: When the task is due
|
||||||
|
- `labels`: Associated labels/tags
|
||||||
|
|
||||||
|
### 4. Code in JavaScript
|
||||||
|
Post-processes the AI response to ensure data validity and format compliance.
|
||||||
|
|
||||||
|
**Transformations:**
|
||||||
|
- Extracts JSON from the AI response using regex
|
||||||
|
- Converts natural language due dates to ISO 8601 format:
|
||||||
|
- "end of month" → last day of current month
|
||||||
|
- "end of week" → Friday of current week
|
||||||
|
- "today" → current date
|
||||||
|
- "tomorrow" → next day
|
||||||
|
- "next week" → 7 days from now
|
||||||
|
- Parses explicit dates to ISO format
|
||||||
|
- If no due date is provided, defaults to tomorrow
|
||||||
|
- Ensures labels is always an array
|
||||||
|
- Handles AI errors with a "Manual review required" task
|
||||||
|
|
||||||
|
**Output Format:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "string",
|
||||||
|
"description": "string",
|
||||||
|
"priority": 1-5,
|
||||||
|
"due_date": "ISO8601 datetime or null",
|
||||||
|
"labels": ["string"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. HTTP Request (Vikunja API)
|
||||||
|
Creates the task in Vikunja using the extracted and formatted data.
|
||||||
|
|
||||||
|
**Endpoint:** `https://tasks.pathcore.org/api/v1/projects/1/tasks`
|
||||||
|
**Method:** PUT
|
||||||
|
**Authentication:** HTTP Header Auth (Vikunja API credentials)
|
||||||
|
**Project ID:** 1
|
||||||
|
|
||||||
|
### 6. Slack Notification
|
||||||
|
Sends a message to the `vijunka-tasks` Slack channel with details about the newly created task.
|
||||||
|
|
||||||
|
**Information Included:**
|
||||||
|
- Task ID and identifier
|
||||||
|
- Title and description
|
||||||
|
- Priority and completion status
|
||||||
|
- Due date and timeline
|
||||||
|
- Assignees and labels
|
||||||
|
- Created/updated timestamps
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### External Services
|
||||||
|
- **Email Account:** IMAP-compatible email service
|
||||||
|
- **AI Model:** Ollama or compatible API with Mistral model at `http://api.chat.pathcore.org/api/generate`
|
||||||
|
- **Vikunja:** Task management system at `https://tasks.pathcore.org`
|
||||||
|
- **Slack:** For notifications (optional)
|
||||||
|
|
||||||
|
### Credentials
|
||||||
|
- IMAP account credentials
|
||||||
|
- Vikunja API authentication (HTTP header auth)
|
||||||
|
- Slack OAuth2 token (for notifications)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Email Setup
|
||||||
|
Configure the IMAP credentials in the "Email Trigger (IMAP)" node with your email provider's IMAP settings.
|
||||||
|
|
||||||
|
### Vikunja Project
|
||||||
|
Update the `project_id` in both the "Build AI Prompt" and "HTTP Request1" nodes if using a different project (currently set to project 1).
|
||||||
|
|
||||||
|
### Slack Channel
|
||||||
|
The notification message is sent to the `vijunka-tasks` channel. Change the `channelId` in the "Send a message" node to use a different channel.
|
||||||
|
|
||||||
|
### AI Model
|
||||||
|
If using a different AI endpoint or model, update the URL and model name in the "HTTP Request" node.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Once activated and configured, the workflow automatically processes incoming emails:
|
||||||
|
|
||||||
|
1. New email arrives in the monitored inbox
|
||||||
|
2. AI extracts task details from the email content
|
||||||
|
3. Task is created in Vikunja with extracted information
|
||||||
|
4. Slack notification is sent with task details
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
If the AI response cannot be parsed as JSON, the workflow creates a task titled "Manual review required" with the full AI response as the description, flagged with the "ai-error" label for manual review.
|
||||||
|
|
||||||
|
## Workflow Status
|
||||||
|
|
||||||
|
- **Active:** Yes
|
||||||
|
- **Execution Order:** v1
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
This workflow is particularly useful for converting action items from email conversations into structured, trackable tasks in your Vikunja instance.
|
||||||
Reference in New Issue
Block a user