

How to Turn Slack Client Messages into Asana Tasks with n8n
Watches shared Slack channels for client-flagged messages and automatically creates Asana tasks with the message text, sender, channel context, and a parsed priority level.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.
Best for
Client-services teams running multiple shared Slack channels who need every client request tracked in Asana without manual copy-paste
Not ideal for
Teams where all client communication already goes through a ticketing system like Zendesk — the tasks would be redundant
Sync type
real-timeUse case type
routingReal-World Example
A 12-person agency manages eight client Slack channels and one project manager was manually copying issue reports into Asana tasks — taking 20-30 minutes a day and still missing things buried in threads. After this workflow, every message containing trigger words like 'issue', 'broken', or 'urgent' in those channels creates a task in the matching Asana project within 15 seconds. The PM now reviews a clean Asana board instead of scrolling through Slack history.
What Will This Cost?
Drag the slider to your expected monthly volume.
Each platform counts differently — Zapier: 1 task per trigger. Make: 1 operation per module per record. n8n: 1 execution per run.





Prices shown for annual billing. Based on published pricing as of April 2026.
Estimated ROI
1000
min saved/mo
$583
labor value/mo
Free
no platform cost
Based on ~2 min manual effort per operation at $35/hr fully loaded labor cost.
Implementation
Import this workflow directly into n8n
Copy the pre-built n8n blueprint and paste it straight into n8n. All modules, filters, and field mappings are already configured — you just need to connect your accounts.
Before You Start
Make sure you have everything ready.
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Task Name | name | |
| Task Notes / Description | notes | |
| Project GID | projects | |
| Reporter Name | notes | |
4 optional fields▸ show
| Due Date | due_on |
| Priority Level | notes |
| Slack Message Link | notes |
| Source Tag | custom_fields |
Step-by-Step Setup
api.slack.com/apps > Create New App > Event Subscriptions
Create a Slack App and Enable Event Subscriptions
Go to api.slack.com/apps and click 'Create New App', then choose 'From scratch'. Name it something recognizable like 'n8n Task Router'. You need to create this app — not just connect your existing Slack account — because n8n's Slack trigger relies on Slack's Events API, which requires a verified webhook URL. Once created, navigate to 'Event Subscriptions' in the left sidebar and toggle it ON.
- 1Go to api.slack.com/apps and click 'Create New App'
- 2Select 'From scratch', enter your app name, and pick your workspace
- 3In the left sidebar click 'Event Subscriptions'
- 4Toggle 'Enable Events' to ON — leave the Request URL blank for now
Paste this into the first Code node (Step 5). It parses the Slack webhook payload, extracts message context, scores priority by keyword weight, builds the Slack deep link, and calculates the due date — all before the Asana node runs. The $input.first().json path targets n8n's Webhook node output structure.
JavaScript — Code Node// n8n Code Node — Slack payload parser with priority scoring▸ Show code
// n8n Code Node — Slack payload parser with priority scoring // Place this as the first Code node after your Webhook node const event = $input.first().json.body.event;
... expand to see full code
// n8n Code Node — Slack payload parser with priority scoring
// Place this as the first Code node after your Webhook node
const event = $input.first().json.body.event;
if (!event || !event.text) {
throw new Error('No message text found in Slack event payload');
}
const messageText = event.text || '';
const channelId = event.channel || '';
const userId = event.user || '';
const ts = event.ts || '';
// Priority keyword scoring — higher score = higher priority
const highKeywords = ['urgent', 'critical', 'asap', 'down', 'outage', 'emergency'];
const normalKeywords = ['issue', 'broken', 'error', 'problem', 'bug', 'failing', 'wrong', 'off'];
const lowKeywords = ['question', 'wondering', 'when', 'update', 'fyi', 'heads up'];
const lowerText = messageText.toLowerCase();
let priority = 'LOW';
let priorityScore = 0;
for (const word of highKeywords) {
if (lowerText.includes(word)) {
priorityScore += 3;
}
}
for (const word of normalKeywords) {
if (lowerText.includes(word)) {
priorityScore += 1;
}
}
for (const word of lowKeywords) {
if (lowerText.includes(word)) {
priorityScore -= 1;
}
}
if (priorityScore >= 3) {
priority = 'HIGH';
} else if (priorityScore >= 1) {
priority = 'NORMAL';
}
// Build Slack deep link from channel ID and message timestamp
// Slack deep link format: /archives/CHANNEL/pTIMESTAMP (ts dots removed)
const tsFormatted = ts.replace('.', '');
const workspaceSlug = 'your-workspace'; // Replace with your actual workspace slug
const slackLink = `https://${workspaceSlug}.slack.com/archives/${channelId}/p${tsFormatted}`;
// Calculate due date based on priority
const now = new Date();
const daysToAdd = priority === 'HIGH' ? 1 : priority === 'NORMAL' ? 3 : 7;
const dueDate = new Date(now);
dueDate.setDate(now.getDate() + daysToAdd);
// Skip weekends for due date calculation
while (dueDate.getDay() === 0 || dueDate.getDay() === 6) {
dueDate.setDate(dueDate.getDate() + 1);
}
const dueDateFormatted = dueDate.toISOString().split('T')[0]; // YYYY-MM-DD for Asana
// Truncate message for task name (Asana has 255 char limit on task names)
const truncatedText = messageText.length > 80
? messageText.substring(0, 77) + '...'
: messageText;
return [
{
json: {
messageText,
truncatedText,
channelId,
userId,
timestamp: ts,
priority,
priorityScore,
slackLink,
dueDate: dueDateFormatted,
taskName: `[${priority}] ${truncatedText}`,
}
}
];n8n > + New Workflow > + Add Node > Webhook
Build the n8n Workflow and Copy the Webhook URL
Open your n8n instance, click '+ New Workflow', and add your first node. Search for 'Webhook' (not the Slack node) and select it. Set the HTTP method to POST and note the generated webhook URL shown in the node panel — it looks like https://your-n8n-instance.com/webhook/abc123. This URL is what you'll paste into Slack's Event Subscriptions page. Click 'Listen for Test Event' to activate the URL temporarily for verification.
- 1Click '+ New Workflow' in the top left of n8n
- 2Click the '+' node button and search for 'Webhook'
- 3Set HTTP Method to 'POST'
- 4Copy the 'Test URL' shown — you'll need this in the next step
- 5Click 'Listen for Test Event' so n8n accepts the Slack verification ping
api.slack.com/apps > Your App > Event Subscriptions > Subscribe to Bot Events
Register the Webhook in Slack and Subscribe to Message Events
Go back to your Slack app's Event Subscriptions page and paste the n8n test webhook URL into the Request URL field. Slack sends a challenge POST request immediately — n8n's Webhook node handles this automatically by responding with the challenge token. Once Slack shows a green 'Verified' checkmark, scroll down to 'Subscribe to Bot Events' and add the event type you need.
- 1Paste the n8n webhook test URL into the 'Request URL' field
- 2Wait for the green 'Verified' checkmark to appear
- 3Scroll to 'Subscribe to Bot Events' and click 'Add Bot User Event'
- 4Search for and select 'message.channels' to capture public channel messages
- 5Click 'Save Changes' at the bottom of the page
api.slack.com/apps > Your App > OAuth & Permissions > Bot Token Scopes
Grant Bot Permissions and Install the App to Your Workspace
In the Slack app dashboard, go to 'OAuth & Permissions' in the left sidebar. Scroll to 'Bot Token Scopes' and add the required scopes. Then scroll to the top and click 'Install to Workspace' — Slack will ask you to authorize the permissions. After authorization, copy the 'Bot User OAuth Token' (starts with xoxb-) from this page. You'll also need to manually invite this bot to each client Slack channel it should monitor.
- 1Click 'OAuth & Permissions' in the left sidebar
- 2Under 'Bot Token Scopes' click 'Add an OAuth Scope'
- 3Add: channels:history, channels:read, users:read
- 4Scroll up and click 'Install to Workspace', then click 'Allow'
- 5Copy the 'Bot User OAuth Token' starting with xoxb-
n8n Workflow Canvas > + Add Node > Code
Add a Code Node to Parse Priority and Extract Context
Add a 'Code' node after the Webhook node in n8n. This node reads the Slack event payload, extracts the message text, channel ID, user ID, and timestamp, then parses a priority level based on keywords in the message. This is where n8n earns its value — you'd need a separate Zapier step and a Formatter step to approximate this logic, whereas here it's one block. Paste the code from the pro tip section into this node.
- 1Click '+' after the Webhook node and search for 'Code'
- 2Select 'Run Once for All Items' mode
- 3Paste the priority-parsing code into the JavaScript editor
- 4Click 'Test Step' to run it against the sample Slack payload
Paste this into the first Code node (Step 5). It parses the Slack webhook payload, extracts message context, scores priority by keyword weight, builds the Slack deep link, and calculates the due date — all before the Asana node runs. The $input.first().json path targets n8n's Webhook node output structure.
JavaScript — Code Node// n8n Code Node — Slack payload parser with priority scoring▸ Show code
// n8n Code Node — Slack payload parser with priority scoring // Place this as the first Code node after your Webhook node const event = $input.first().json.body.event;
... expand to see full code
// n8n Code Node — Slack payload parser with priority scoring
// Place this as the first Code node after your Webhook node
const event = $input.first().json.body.event;
if (!event || !event.text) {
throw new Error('No message text found in Slack event payload');
}
const messageText = event.text || '';
const channelId = event.channel || '';
const userId = event.user || '';
const ts = event.ts || '';
// Priority keyword scoring — higher score = higher priority
const highKeywords = ['urgent', 'critical', 'asap', 'down', 'outage', 'emergency'];
const normalKeywords = ['issue', 'broken', 'error', 'problem', 'bug', 'failing', 'wrong', 'off'];
const lowKeywords = ['question', 'wondering', 'when', 'update', 'fyi', 'heads up'];
const lowerText = messageText.toLowerCase();
let priority = 'LOW';
let priorityScore = 0;
for (const word of highKeywords) {
if (lowerText.includes(word)) {
priorityScore += 3;
}
}
for (const word of normalKeywords) {
if (lowerText.includes(word)) {
priorityScore += 1;
}
}
for (const word of lowKeywords) {
if (lowerText.includes(word)) {
priorityScore -= 1;
}
}
if (priorityScore >= 3) {
priority = 'HIGH';
} else if (priorityScore >= 1) {
priority = 'NORMAL';
}
// Build Slack deep link from channel ID and message timestamp
// Slack deep link format: /archives/CHANNEL/pTIMESTAMP (ts dots removed)
const tsFormatted = ts.replace('.', '');
const workspaceSlug = 'your-workspace'; // Replace with your actual workspace slug
const slackLink = `https://${workspaceSlug}.slack.com/archives/${channelId}/p${tsFormatted}`;
// Calculate due date based on priority
const now = new Date();
const daysToAdd = priority === 'HIGH' ? 1 : priority === 'NORMAL' ? 3 : 7;
const dueDate = new Date(now);
dueDate.setDate(now.getDate() + daysToAdd);
// Skip weekends for due date calculation
while (dueDate.getDay() === 0 || dueDate.getDay() === 6) {
dueDate.setDate(dueDate.getDate() + 1);
}
const dueDateFormatted = dueDate.toISOString().split('T')[0]; // YYYY-MM-DD for Asana
// Truncate message for task name (Asana has 255 char limit on task names)
const truncatedText = messageText.length > 80
? messageText.substring(0, 77) + '...'
: messageText;
return [
{
json: {
messageText,
truncatedText,
channelId,
userId,
timestamp: ts,
priority,
priorityScore,
slackLink,
dueDate: dueDateFormatted,
taskName: `[${priority}] ${truncatedText}`,
}
}
];n8n Workflow Canvas > + Add Node > HTTP Request
Resolve Slack User ID to a Real Name
Slack's event payload gives you a user ID like U04KFGH21 — not a name. Add an HTTP Request node to call the Slack API's users.info endpoint and get the user's real name. This matters because the Asana task description should say 'Reported by: Sarah Chen' not 'Reported by: U04KFGH21'. Use the Bot Token you copied in Step 4 as the Bearer token for this request.
- 1Add an 'HTTP Request' node after the Code node
- 2Set Method to GET
- 3Set URL to: https://slack.com/api/users.info
- 4Add Query Parameter: user = {{ $json.userId }}
- 5Under Authentication, select 'Header Auth', set Header Name to 'Authorization', and value to 'Bearer xoxb-your-token-here'
Paste this into the first Code node (Step 5). It parses the Slack webhook payload, extracts message context, scores priority by keyword weight, builds the Slack deep link, and calculates the due date — all before the Asana node runs. The $input.first().json path targets n8n's Webhook node output structure.
JavaScript — Code Node// n8n Code Node — Slack payload parser with priority scoring▸ Show code
// n8n Code Node — Slack payload parser with priority scoring // Place this as the first Code node after your Webhook node const event = $input.first().json.body.event;
... expand to see full code
// n8n Code Node — Slack payload parser with priority scoring
// Place this as the first Code node after your Webhook node
const event = $input.first().json.body.event;
if (!event || !event.text) {
throw new Error('No message text found in Slack event payload');
}
const messageText = event.text || '';
const channelId = event.channel || '';
const userId = event.user || '';
const ts = event.ts || '';
// Priority keyword scoring — higher score = higher priority
const highKeywords = ['urgent', 'critical', 'asap', 'down', 'outage', 'emergency'];
const normalKeywords = ['issue', 'broken', 'error', 'problem', 'bug', 'failing', 'wrong', 'off'];
const lowKeywords = ['question', 'wondering', 'when', 'update', 'fyi', 'heads up'];
const lowerText = messageText.toLowerCase();
let priority = 'LOW';
let priorityScore = 0;
for (const word of highKeywords) {
if (lowerText.includes(word)) {
priorityScore += 3;
}
}
for (const word of normalKeywords) {
if (lowerText.includes(word)) {
priorityScore += 1;
}
}
for (const word of lowKeywords) {
if (lowerText.includes(word)) {
priorityScore -= 1;
}
}
if (priorityScore >= 3) {
priority = 'HIGH';
} else if (priorityScore >= 1) {
priority = 'NORMAL';
}
// Build Slack deep link from channel ID and message timestamp
// Slack deep link format: /archives/CHANNEL/pTIMESTAMP (ts dots removed)
const tsFormatted = ts.replace('.', '');
const workspaceSlug = 'your-workspace'; // Replace with your actual workspace slug
const slackLink = `https://${workspaceSlug}.slack.com/archives/${channelId}/p${tsFormatted}`;
// Calculate due date based on priority
const now = new Date();
const daysToAdd = priority === 'HIGH' ? 1 : priority === 'NORMAL' ? 3 : 7;
const dueDate = new Date(now);
dueDate.setDate(now.getDate() + daysToAdd);
// Skip weekends for due date calculation
while (dueDate.getDay() === 0 || dueDate.getDay() === 6) {
dueDate.setDate(dueDate.getDate() + 1);
}
const dueDateFormatted = dueDate.toISOString().split('T')[0]; // YYYY-MM-DD for Asana
// Truncate message for task name (Asana has 255 char limit on task names)
const truncatedText = messageText.length > 80
? messageText.substring(0, 77) + '...'
: messageText;
return [
{
json: {
messageText,
truncatedText,
channelId,
userId,
timestamp: ts,
priority,
priorityScore,
slackLink,
dueDate: dueDateFormatted,
taskName: `[${priority}] ${truncatedText}`,
}
}
];n8n Workflow Canvas > + Add Node > Code
Map Slack Channels to Asana Projects
Add another Code node to translate the Slack channel ID into an Asana project GID. You'll maintain a lookup object inside this node — a simple key-value map of channel IDs to Asana project GIDs. This is the most brittle part of the workflow, but it's the right approach: trying to auto-match by name causes errors when names drift. Get your Asana project GIDs from the URL when you open a project — it's the number after /0/ in the URL bar.
- 1Add a second 'Code' node after the HTTP Request node
- 2Open each Asana client project in your browser and copy the GID from the URL (e.g., asana.com/0/1234567890/list — GID is 1234567890)
- 3In the Code node, build a lookup object mapping Slack channel IDs to Asana GIDs
- 4Return the matched Asana project GID as 'asanaProjectId' in the output
n8n Workflow Canvas > + Add Node > Asana > Create Task
Connect n8n to Asana
Add an Asana node to your workflow. Click the credential field and select 'Create New Credential'. n8n supports Asana via OAuth2 — click 'Connect with Asana' and authorize access in the browser popup. The scope you need is to create tasks in specific projects. Once connected, set the Operation to 'Create Task' and the Resource to 'Task'.
- 1Add an 'Asana' node after the channel-mapping Code node
- 2Set Resource to 'Task' and Operation to 'Create'
- 3Click the Credential dropdown and select 'Create New'
- 4Click 'Connect with Asana OAuth2' and authorize in the popup
- 5Confirm the green 'Connected' badge appears next to the credential name
n8n > Asana Node > Create Task > Field Configuration
Map Fields from Slack Data to the Asana Task
Now fill in the Asana task fields using the data flowing through your workflow. The task name should include the client channel name and a truncated version of the message. The description should include the full message, reporter name, Slack message link, and timestamp. Set the Project field using the asanaProjectId from your lookup node. Map the due date to 3 business days from now if priority is 'high', or 7 days for 'normal'.
- 1Set 'Name' to: [{{ $json.priority.toUpperCase() }}] {{ $json.messageText.substring(0,60) }}
- 2Set 'Projects' to: {{ $json.asanaProjectId }}
- 3Set 'Notes' to a multi-line block with message, reporter, timestamp, and Slack link
- 4Toggle 'Due On' and set using an expression based on priority level
- 5Add a custom field for 'Source' and set it to 'Slack'
n8n Workflow Canvas > + Add Node > IF (insert between Webhook and Code nodes)
Add an IF Node to Filter Out Bot and Non-Issue Messages
Not every Slack message should become a task. Add an IF node between the Webhook node and the Code node to filter out bot messages, system notifications, and messages that don't contain issue-related keywords. Slack sends a subtype field on bot messages — check for its absence. Also check that the message text contains at least one trigger word from your defined list.
- 1Click the connector line between Webhook and Code nodes, then click the '+' that appears
- 2Add an 'IF' node
- 3Add Condition 1: {{ $json.body.event.subtype }} is empty (filters out bot messages)
- 4Add Condition 2: {{ $json.body.event.text }} contains any of: issue, broken, error, urgent, problem, bug, failing
- 5Set 'Combine Conditions' to AND
n8n > Workflow Canvas > Activate Toggle | api.slack.com/apps > Event Subscriptions
Activate the Workflow and Switch to the Production Webhook URL
Click 'Save' in the top right, then click the toggle in the top right to activate the workflow. Once active, n8n switches from the test URL to the production URL. Go back to your Slack app's Event Subscriptions page and replace the test URL with the production URL — it looks the same but without '/test' in the path. Save the Slack settings. Send a test message in one of your monitored channels containing the word 'issue' and watch the Asana task appear.
- 1Click 'Save' in n8n's top right toolbar
- 2Click the inactive/active toggle to set the workflow to Active
- 3Copy the Production URL from the Webhook node (open the node to find it)
- 4Go back to api.slack.com/apps > Your App > Event Subscriptions
- 5Replace the test URL with the production URL and click 'Save Changes'
Going live
Production Checklist
Before you turn this on for real, confirm each item.
Troubleshooting
Common errors and how to fix them.
Frequently Asked Questions
Common questions about this workflow.
Analysis
Use n8n for this if your team self-hosts and you want zero per-execution cost, or if you need keyword-weighted priority parsing that would require three separate steps in Zapier. The Code node lets you score priority, build the Slack deep link, calculate business-day due dates, and look up the project mapping all in one place — no extra modules, no per-operation charges. The one scenario where you'd pick something else: if nobody on your team can read JavaScript at all, and the workflow breaks at 2am, you need someone who can debug a Code node. If that's a real concern, Make's router + text parser combo covers 80% of this use case with no code.
Cost math: this workflow runs one execution per qualifying Slack message. At 50 client messages per day that trigger the filter, that's roughly 1,500 executions per month. n8n Cloud's Starter plan costs $20/month and includes 2,500 executions — you're covered with room to spare. Self-hosted n8n on a $6/month VPS costs $6/month flat regardless of execution count. Compare that to Zapier's Professional plan at $49/month for 2,000 tasks (each node hop in Zapier counts as a task, so this workflow burns 4-5 tasks per message — roughly 6,000-7,500 tasks/month at the same volume, which pushes you to the $69/month tier). n8n is $43-63 cheaper per month at this volume.
Here's what the competition does better for this specific use case: Zapier's Slack trigger fires in under 60 seconds consistently and requires no Slack App setup — you just authenticate your existing Slack account. Make has a cleaner visual router for splitting messages to different Asana projects without writing a lookup table in code. Power Automate's 'Post message in a channel (V3)' trigger works natively in Microsoft environments and is the right call if your clients are already in Teams rather than Slack. Pipedream's Slack source handles threading context more cleanly out of the box and auto-resolves user IDs through its built-in Slack prop. n8n is still the right call here because the priority scoring, due date calculation, and project routing genuinely benefit from a real Code node, and at production volume the cost difference is significant.
Three things you'll run into after setup. First: Slack's Events API delivers webhooks with a 3-second timeout — if your n8n Code node takes longer than that to respond (can happen on cold starts with self-hosted instances), Slack marks the delivery as failed and retries up to three times, which creates duplicate Asana tasks. Keep your Code nodes fast and add deduplication logic checking for an existing task with the same Slack timestamp before creating a new one. Second: Asana's rate limit is 1,500 requests per minute per user — not a problem at normal volume, but if a client pastes 40 messages in a burst (it happens during incidents), you'll hit it. Add a Wait node with a 200ms delay if you're seeing 429 errors in your execution logs. Third: Slack's message.channels event doesn't fire for messages in threads — only for top-level channel messages. If your clients tend to pile on in threads, you also need the message event with the thread_ts field populated, which requires subscribing to the reactions.added event or checking thread_ts in your IF filter.
Ideas for what to build next
- →Post Asana Task Link Back to Slack — After creating the Asana task, add a Slack node to post the task URL as a reply in the original Slack thread. This closes the loop — clients see their issue was captured without needing to check Asana.
- →Sync Asana Task Completion Back to Slack — Build a second workflow that triggers when an Asana task is marked complete and sends a message in the originating Slack channel. This tells clients their issue is resolved without anyone having to remember to update them manually.
- →Add a Digest Workflow for Daily Unresolved Task Summaries — Create a scheduled n8n workflow that runs each morning, queries Asana for open tasks created from Slack that are overdue, and posts a summary to your team's internal Slack channel — useful for keeping SLAs visible.
Related guides
How to Share Notion Meeting Notes to Slack with Pipedream
~15 min setup
How to Share Notion Meeting Notes to Slack with Power Automate
~15 min setup
How to Share Notion Meeting Notes to Slack with n8n
~20 min setup
How to Send Notion Meeting Notes to Slack with Zapier
~8 min setup
How to Share Notion Meeting Notes to Slack with Make
~12 min setup
How to Create Notion Tasks from Slack with Pipedream
~15 min setup