Intermediate~20 min setupCommunication & Project ManagementVerified April 2026
Slack logo
Asana logo

How to Send Asana Task Notifications to Slack with n8n

Sends a direct Slack message to a team member the moment they're assigned an Asana task or when a task's priority changes — no polling, fires in under 10 seconds.

Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.

Best for

Engineering or ops teams where missed task assignments cause delays and team members shouldn't need Asana open all day.

Not ideal for

Teams that reassign tasks dozens of times per hour — the Slack noise will outweigh the benefit; use a daily digest instead.

Sync type

real-time

Use case type

notification

Real-World Example

💡

A 22-person product team at a SaaS company uses this to DM engineers in Slack the instant a bug task gets assigned or escalated to urgent priority. Before this, engineers checked Asana once or twice a day and high-priority bugs sometimes sat unnoticed for 3-4 hours. With the webhook firing in under 10 seconds, the assigned engineer gets a Slack DM with the task name, due date, priority, and a direct link to Asana.

What Will This Cost?

Drag the slider to your expected monthly volume.

/mo
505005K50K

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

Skip the setup

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.

Asana Personal Access Token or Service Account token with read access to tasks, projects, and workspace members
Slack Bot Token (xoxb-) with scopes: chat:write, users:read, users:read.email — the bot must be invited to any channel you post to
n8n instance (cloud or self-hosted) with a publicly accessible URL so Asana can deliver webhook payloads to it
Asana workspace admin access to register webhooks — standard member accounts cannot register org-level webhooks
Team members must use the same email address in both Asana and Slack, or you need a manual mapping table to resolve IDs

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Task Namename
Assignee Nameassignee.name
Assignee Emailassignee.email
Task GIDgid
Task Permalink URLpermalink_url
Event Typechange.field
4 optional fields▸ show
Due Datedue_on
Prioritycustom_fields[priority].display_value
Project Nameprojects[0].name
Task Notesnotes

Step-by-Step Setup

1

n8n Canvas > + New Workflow

Create a new workflow in n8n

Log into your n8n instance (cloud at app.n8n.io or your self-hosted URL). Click the orange 'New Workflow' button in the top-right of the canvas. A blank canvas opens with a prompt to add your first node. Name the workflow 'Asana → Slack Task Notifications' using the pencil icon at the top so you can find it later.

  1. 1Click 'New Workflow' in the top-right corner
  2. 2Click the pencil icon next to 'My Workflow' at the top
  3. 3Type 'Asana → Slack Task Notifications' and press Enter
  4. 4Click the '+' node button in the center of the canvas to add your trigger
What you should see: You should see a blank canvas with your workflow name saved and the node picker panel open on the right side.
Common mistake — n8n auto-saves but does NOT activate workflows automatically. A workflow sitting in 'inactive' state will not fire even if the webhook is registered. You'll activate it in the final step.
2

Canvas > Add Node > Search 'Asana Trigger'

Add the Asana Trigger node

In the node picker, search for 'Asana' and select the 'Asana Trigger' node — not the regular Asana node, which is for actions. This trigger listens via Asana's webhook API, so events fire in real time rather than on a schedule. You'll see a configuration panel open on the right with fields for credentials, resource, and event type.

  1. 1Type 'Asana' in the node search bar
  2. 2Click 'Asana Trigger' (look for the lightning bolt icon indicating it's a trigger)
  3. 3Click 'Create new credential' under the Credentials dropdown
  4. 4Paste your Asana Personal Access Token and click Save
What you should see: The credentials field should show a green checkmark and your Asana workspace name should populate in the Workspace dropdown.
Common mistake — Asana Trigger requires a Personal Access Token, not OAuth, for webhook registration to work in n8n. Service accounts tokens (from Asana's developer console) are preferred over personal tokens so the webhook doesn't break when someone leaves the team.
n8n
+
click +
search apps
Slack
SL
Slack
Add the Asana Trigger node
Slack
SL
module added
3

Asana Trigger Node > Parameters Panel

Configure the Asana Trigger events

Set the Resource to 'Task' in the dropdown. Under Events, select both 'Task Assigned' and 'Task Changed' — you need both to catch new assignments and priority changes. In the Project field, either select a specific project to scope notifications or leave it blank to catch all tasks across the workspace. Set the Workspace field to your team's Asana workspace.

  1. 1Set 'Resource' to 'Task'
  2. 2Check 'Task Assigned' under Events
  3. 3Check 'Task Changed' under Events
  4. 4Set 'Workspace' to your Asana workspace from the dropdown
  5. 5Optionally select a specific Project to limit scope
What you should see: You should see a webhook URL generated at the bottom of the node panel — something like 'https://your-n8n.com/webhook/abc123'. Copy this URL; Asana will use it.
Common mistake — If you scope to a specific project here, priority-change events from tasks in other projects will be silently ignored. If your team works across multiple projects, leave Project blank and add filtering logic in a later node instead.
4

Asana Trigger Node > Listen for Test Event button

Test the Asana trigger with sample data

Click 'Listen for Test Event' at the bottom of the Asana Trigger panel. n8n will register the webhook with Asana's API automatically when you enter test mode. Go to Asana, find any task, and assign it to someone (or reassign it) to fire a test event. Switch back to n8n — you should see the raw JSON payload appear in the Output panel on the right within a few seconds.

  1. 1Click 'Listen for Test Event' in the node panel
  2. 2Open Asana in a new tab
  3. 3Find any task and change the assignee or priority
  4. 4Return to n8n and wait for the payload to appear in the Output panel
What you should see: The Output panel should show a JSON object containing fields like 'gid', 'name', 'assignee.gid', 'assignee.name', 'priority', 'due_on', and 'permalink_url'.
Common mistake — Asana's webhook payload for 'Task Changed' events only tells you THAT something changed — it does not tell you what the old value was. You cannot diff old vs. new priority in the webhook body alone. If you need to filter to only priority increases, you'll need to call the Asana API in a separate node to fetch the current task details.
n8n
▶ Run once
executed
Slack
Asana
Asana
🔔 notification
received
5

Canvas > Add Node > Search 'IF'

Add an IF node to filter relevant events

Not every task event should fire a Slack notification. Add an 'IF' node after the trigger to filter out events where no assignee is present (unassigned tasks) and to separate 'assignment' events from 'priority change' events. This prevents spammy notifications for minor edits like description changes. Connect the Asana Trigger output to the IF node.

  1. 1Click the '+' button on the Asana Trigger node output connector
  2. 2Search for 'IF' and select the IF node
  3. 3Set Condition 1: Value 1 = '{{ $json.assignee }}', Operation = 'Is Not Empty'
  4. 4Click 'Add Condition' and set Condition 2: Value 1 = '{{ $json.assignee.gid }}', Operation = 'Is Not Empty'
  5. 5Set the Combine field to 'AND'
What you should see: The IF node should show two branches: 'true' (has an assignee) and 'false' (no assignee). You can connect the false branch to a No-Op or leave it unconnected to drop those events.
Common mistake — Filters are the most common place setups break. Double-check the field name and value exactly match what your app sends — a single capital letter difference will block everything.
Slack
SL
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Asana
AS
notified
6

Canvas > Add Node > Search 'Asana' (action node)

Fetch full task details from Asana API

The webhook payload is sparse — it often omits due dates, priority, and project names. Add an Asana node (action, not trigger) to fetch the complete task record using the task GID from the webhook. This gives you all the fields you need to build a rich Slack message. Connect this node to the 'true' output of your IF node.

  1. 1Click '+' on the IF node's true output
  2. 2Search 'Asana' and select the regular Asana node (not the trigger)
  3. 3Set Resource to 'Task', Operation to 'Get'
  4. 4Set Task ID to '{{ $json.gid }}' using the expression editor
  5. 5Use the same Asana credential you configured in Step 2
What you should see: Running this node against your test data should return a full task object including 'name', 'assignee.name', 'assignee.email', 'due_on', 'priority', 'permalink_url', and 'projects' array.
Common mistake — Asana's 'priority' field only exists if your workspace uses the Priority custom field. If your team hasn't added it, the field returns null and your Slack message template will show 'undefined'. Add a fallback like '{{ $json.priority ?? "No priority set" }}' in your Code node.
7

Canvas > Add Node > Search 'Code'

Add a Code node to build the Slack message

Add an n8n Code node to transform the Asana task data into a formatted Slack Block Kit message. This is where you handle edge cases: null due dates, missing priority labels, and mapping the event type to a human-readable header ('You've been assigned a new task' vs. 'A task priority has changed'). The Code node outputs a structured object ready for the Slack node.

  1. 1Click '+' on the Asana Get Task node output
  2. 2Search 'Code' and select the Code node
  3. 3Set Language to 'JavaScript'
  4. 4Paste the transformation code from the Pro Tip section below
  5. 5Click 'Execute Node' to verify the output structure
What you should see: The Code node output should show a JSON object with 'channel' (the assignee's Slack user ID or email), 'text' (fallback), and 'blocks' (Block Kit array) fields ready to pass to the Slack node.
Common mistake — Asana stores assignee identity as an email address. Slack's chat.postMessage API requires a Slack user ID (U012AB3CD), not an email. You must call Slack's users.lookupByEmail method first, or use the Code node to do the lookup — otherwise the Slack node will throw a 'channel_not_found' error.

This Code node runs after the Asana 'Get Task' API call. It builds a Slack Block Kit message with conditional headers (assignment vs. priority change), formats the due date, handles null priority gracefully, and outputs the assignee email for the users.lookupByEmail step. Paste this into the Code node in Step 7, using the JavaScript language setting.

JavaScript — Code Node// n8n Code Node — Asana → Slack Block Kit Message Builder
▸ Show code
// n8n Code Node — Asana → Slack Block Kit Message Builder
// Runs after Asana 'Get Task' node. Outputs structured Slack message + assignee email.
const task = $input.item.json;

... expand to see full code

// n8n Code Node — Asana → Slack Block Kit Message Builder
// Runs after Asana 'Get Task' node. Outputs structured Slack message + assignee email.

const task = $input.item.json;
const triggerData = $node['Asana Trigger'].json;

// Determine event type from the webhook change field
const changeField = triggerData?.change?.field || 'assignee';
const isAssignment = changeField === 'assignee';
const isPriorityChange = changeField === 'custom_fields';

// Build human-readable header based on event type
const header = isAssignment
  ? '📋 You\'ve been assigned a new task'
  : isPriorityChange
  ? '🔺 A task priority has changed'
  : '🔔 A task was updated';

// Format due date — Asana returns ISO date string 'YYYY-MM-DD' or null
let dueDateDisplay = 'No due date set';
if (task.due_on) {
  const d = new Date(task.due_on + 'T12:00:00Z'); // noon UTC avoids timezone-off-by-one
  dueDateDisplay = d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}

// Resolve priority from custom fields array (null-safe)
const priorityField = (task.custom_fields || []).find(f => f.name === 'Priority');
const priority = priorityField?.display_value ?? 'No priority set';

// Truncate task notes to 200 chars for Slack context block
const rawNotes = task.notes || '';
const notesSnippet = rawNotes.length > 200
  ? rawNotes.substring(0, 197) + '...'
  : rawNotes || '_No description provided_';

// Get project name (tasks can belong to multiple projects; show the first)
const projectName = (task.projects || [])[0]?.name ?? 'No project';

// Slack Block Kit structure
const blocks = [
  {
    type: 'header',
    text: { type: 'plain_text', text: header, emoji: true }
  },
  {
    type: 'section',
    fields: [
      { type: 'mrkdwn', text: `*Task:*\n<${task.permalink_url}|${task.name}>` },
      { type: 'mrkdwn', text: `*Project:*\n${projectName}` },
      { type: 'mrkdwn', text: `*Due Date:*\n${dueDateDisplay}` },
      { type: 'mrkdwn', text: `*Priority:*\n${priority}` }
    ]
  },
  {
    type: 'context',
    elements: [
      { type: 'mrkdwn', text: notesSnippet }
    ]
  },
  {
    type: 'actions',
    elements: [
      {
        type: 'button',
        text: { type: 'plain_text', text: 'Open in Asana', emoji: true },
        url: task.permalink_url,
        action_id: 'open_asana_task'
      }
    ]
  }
];

// Output: blocks for Slack node + email for users.lookupByEmail HTTP request
return [
  {
    json: {
      blocks,
      text: `${header}: ${task.name}`, // Fallback text for Slack notifications
      assigneeEmail: task.assignee?.email ?? null,
      assigneeName: task.assignee?.name ?? 'Unknown',
      taskGid: task.gid
    }
  }
];
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
8

Canvas > Add Node > Search 'HTTP Request'

Resolve Asana email to Slack user ID

Add an HTTP Request node to call Slack's users.lookupByEmail endpoint with the assignee's Asana email. This returns the Slack user ID needed to send a DM. Use your Slack Bot Token in the Authorization header. Connect this node between the Code node and the final Slack node.

  1. 1Click '+' after the Code node
  2. 2Add an HTTP Request node
  3. 3Set Method to GET
  4. 4Set URL to 'https://slack.com/api/users.lookupByEmail'
  5. 5Add Query Parameter: email = '{{ $json.assigneeEmail }}'
  6. 6Add Header: Authorization = 'Bearer xoxb-your-bot-token'
What you should see: The response body should contain 'ok: true' and a 'user' object with an 'id' field like 'U012AB3CD'. If 'ok' is false, the email doesn't match any Slack user in your workspace.
Common mistake — This lookup will fail silently if your Asana users have different email addresses than their Slack accounts. This is common when people use work aliases. Build an error branch off this node to catch 'ok: false' responses and route them to a fallback channel notification.

This Code node runs after the Asana 'Get Task' API call. It builds a Slack Block Kit message with conditional headers (assignment vs. priority change), formats the due date, handles null priority gracefully, and outputs the assignee email for the users.lookupByEmail step. Paste this into the Code node in Step 7, using the JavaScript language setting.

JavaScript — Code Node// n8n Code Node — Asana → Slack Block Kit Message Builder
▸ Show code
// n8n Code Node — Asana → Slack Block Kit Message Builder
// Runs after Asana 'Get Task' node. Outputs structured Slack message + assignee email.
const task = $input.item.json;

... expand to see full code

// n8n Code Node — Asana → Slack Block Kit Message Builder
// Runs after Asana 'Get Task' node. Outputs structured Slack message + assignee email.

const task = $input.item.json;
const triggerData = $node['Asana Trigger'].json;

// Determine event type from the webhook change field
const changeField = triggerData?.change?.field || 'assignee';
const isAssignment = changeField === 'assignee';
const isPriorityChange = changeField === 'custom_fields';

// Build human-readable header based on event type
const header = isAssignment
  ? '📋 You\'ve been assigned a new task'
  : isPriorityChange
  ? '🔺 A task priority has changed'
  : '🔔 A task was updated';

// Format due date — Asana returns ISO date string 'YYYY-MM-DD' or null
let dueDateDisplay = 'No due date set';
if (task.due_on) {
  const d = new Date(task.due_on + 'T12:00:00Z'); // noon UTC avoids timezone-off-by-one
  dueDateDisplay = d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}

// Resolve priority from custom fields array (null-safe)
const priorityField = (task.custom_fields || []).find(f => f.name === 'Priority');
const priority = priorityField?.display_value ?? 'No priority set';

// Truncate task notes to 200 chars for Slack context block
const rawNotes = task.notes || '';
const notesSnippet = rawNotes.length > 200
  ? rawNotes.substring(0, 197) + '...'
  : rawNotes || '_No description provided_';

// Get project name (tasks can belong to multiple projects; show the first)
const projectName = (task.projects || [])[0]?.name ?? 'No project';

// Slack Block Kit structure
const blocks = [
  {
    type: 'header',
    text: { type: 'plain_text', text: header, emoji: true }
  },
  {
    type: 'section',
    fields: [
      { type: 'mrkdwn', text: `*Task:*\n<${task.permalink_url}|${task.name}>` },
      { type: 'mrkdwn', text: `*Project:*\n${projectName}` },
      { type: 'mrkdwn', text: `*Due Date:*\n${dueDateDisplay}` },
      { type: 'mrkdwn', text: `*Priority:*\n${priority}` }
    ]
  },
  {
    type: 'context',
    elements: [
      { type: 'mrkdwn', text: notesSnippet }
    ]
  },
  {
    type: 'actions',
    elements: [
      {
        type: 'button',
        text: { type: 'plain_text', text: 'Open in Asana', emoji: true },
        url: task.permalink_url,
        action_id: 'open_asana_task'
      }
    ]
  }
];

// Output: blocks for Slack node + email for users.lookupByEmail HTTP request
return [
  {
    json: {
      blocks,
      text: `${header}: ${task.name}`, // Fallback text for Slack notifications
      assigneeEmail: task.assignee?.email ?? null,
      assigneeName: task.assignee?.name ?? 'Unknown',
      taskGid: task.gid
    }
  }
];
9

Canvas > Add Node > Search 'Slack'

Add the Slack node to send the DM

Add a Slack node configured to send a direct message. Set the channel to the Slack user ID resolved in the previous step. Use the Block Kit blocks from your Code node output for a formatted message. The Slack node handles the API call to chat.postMessage under the hood — you just map the fields.

  1. 1Click '+' on the HTTP Request node output
  2. 2Search 'Slack' and select the Slack node
  3. 3Set Resource to 'Message', Operation to 'Post'
  4. 4Set Channel to '{{ $node["HTTP Request"].json.user.id }}'
  5. 5Set Message Type to 'Block' and paste '{{ $node["Code"].json.blocks }}' in Blocks field
  6. 6Set the Fallback Text to '{{ $node["Code"].json.text }}'
What you should see: After clicking 'Execute Node', you should see a 'ts' timestamp in the output and receive the formatted DM in Slack within 2-3 seconds.
Common mistake — Block Kit JSON must be passed as a parsed object, not a string. If you pass it as a stringified JSON, Slack renders the raw JSON text instead of the formatted blocks. Use n8n's expression editor with the JSON object directly, not JSON.stringify().
10

Canvas > Slack Node > Error Output > Add Node

Add an error handler for failed notifications

Add a second Slack node connected to the error output of your main Slack send node. Route failures to a shared ops channel like #automation-alerts rather than losing them silently. Include the error message and the task GID so someone can manually follow up. This is especially important for the email-to-Slack-ID lookup failures from Step 8.

  1. 1Hover over the Slack node to reveal the red error output connector
  2. 2Click the red error connector and add a new Slack node
  3. 3Set Channel to your ops or alerts channel ID (e.g. C04ABCDEF)
  4. 4Set Message to 'Task notification failed for task {{ $node["Asana Trigger"].json.gid }}: {{ $json.error.message }}'
  5. 5Use the same Slack credential
What you should see: Your canvas should now show a parallel error path. If the main Slack DM fails for any reason, a plain-text alert posts to your ops channel within seconds.
11

n8n Canvas > Inactive toggle (top-right)

Activate the workflow and verify end-to-end

Click the 'Inactive' toggle in the top-right of the canvas to activate the workflow. Once active, n8n keeps the Asana webhook registration alive and processes events as they arrive. Go to Asana, assign a real task to a team member, and confirm the Slack DM arrives within 10 seconds. Check the Executions tab in n8n to see the run log.

  1. 1Click the 'Inactive' toggle — it should flip to 'Active' and turn green
  2. 2Open Asana and assign a task to a team member who is also in Slack
  3. 3Check that person's Slack DMs within 10 seconds
  4. 4Return to n8n and click 'Executions' in the left sidebar to confirm a successful run appears
What you should see: The Executions tab should show a green 'Success' entry with the correct timestamp. The assigned team member should have received a formatted Slack DM with the task name, priority, due date, and link to Asana.
Common mistake — If you're on n8n cloud's free tier, workflows deactivate after 5 days of inactivity. Set a calendar reminder to check the Active toggle weekly, or upgrade to a paid plan if this workflow needs to run reliably long-term.

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

VerdictWhy n8n for this workflow

Use n8n for this if you self-host your infrastructure and can't send internal task data through a third-party cloud automation service. Asana task notifications can include sensitive project names, deadlines, and personnel — n8n running on your own servers keeps all of that in-house. The second reason to pick n8n: you need the Code node. The Asana webhook payload is sparse, the email-to-Slack-ID lookup requires a raw HTTP call, and the Block Kit message needs conditional logic based on event type. n8n handles all three in a single workflow. If your team has no one comfortable with JavaScript and you're on Asana Premium or above, Zapier's Asana integration is genuinely easier to configure and will have you sending notifications in 15 minutes without writing a line of code.

Cost

n8n cloud starts at $20/month for 2,500 executions. This workflow uses 4 executions per task event (trigger, IF, Asana Get, HTTP lookup, Slack send — n8n counts trigger + nodes differently depending on version, so budget 3-5 per event). At 200 task assignments per month, you're at roughly 800-1,000 executions — well within the base plan. Self-hosted n8n is free with no execution limits, which is the real cost story: if you're already running n8n for other workflows, this one adds effectively zero cost. Zapier would run you $49/month on the Team plan to access multi-step Zaps with the same logic.

Tradeoffs

Zapier has a pre-built 'Asana New Task Assigned' trigger that requires zero configuration — you pick the project and it works. No webhook setup, no API calls. That's the one thing Zapier does better here. Make's Asana module includes a built-in task watcher that handles the email-to-user lookup inside the Slack module without a separate HTTP step, which saves one node. Power Automate has an Asana connector but it's through a third-party premium connector that costs extra and has a 15-minute polling minimum — not real-time. Pipedream's Asana source component handles webhook registration automatically, same as n8n, and the code step is nearly identical syntax. n8n wins here specifically because the self-hosted option removes data residency concerns and the execution cost math works out cheaper than Zapier at any meaningful task volume.

Three things you'll hit after setup. First: Asana sends duplicate webhook events for tasks created with an assignee already set — you get both a 'task created' and 'assignee added' event within milliseconds. Without deduplication logic using n8n's static data, team members get two Slack DMs per task. Second: the Asana webhook registration silently expires if your n8n instance goes offline for more than a few hours during Asana's retry window. Asana retries for about 24 hours, then deletes the webhook. You won't know until someone notices they stopped getting notifications — check the Executions tab and the Asana webhook list weekly. Third: Asana's custom fields API returns an array of all custom fields on the task, not just Priority. If your workspace has 10 custom fields, you need to find Priority by name using Array.find() — the index position is not stable across different projects or task types.

Ideas for what to build next

  • Add a daily digest for low-priority tasksReplace the real-time DM for Medium and Low priority assignments with a scheduled n8n workflow that batches them into a single Slack message each morning at 9am, reducing notification noise without losing visibility.
  • Post to a shared project channel in addition to DMsExtend the workflow to also post task assignments to the relevant project's Slack channel (e.g. #project-q1-bugs) so project managers can see assignment activity without receiving personal DMs.
  • Track notification acknowledgment with Slack button interactionsAdd a Slack Interactivity webhook to n8n that listens for clicks on the 'Open in Asana' button, then updates a Google Sheet or Asana custom field to log when the assignee acknowledged the task.

Related guides

Was this guide helpful?
Slack + Asana overviewn8n profile →