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

How to Automate Daily Standups from Asana to Slack with Pipedream

Every morning at a set time, Pipedream pulls completed and upcoming tasks from Asana and posts a formatted standup report to a Slack channel.

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

Best for

Engineering or product teams of 5-30 people who track work in Asana and want async standup reports posted to Slack automatically each morning.

Not ideal for

Teams that need interactive standup prompts where each person responds individually — use Geekbot or Standuply for that.

Sync type

scheduled

Use case type

reporting

Real-World Example

💡

A 12-person product team at a B2B SaaS company had a daily 9 AM standup that ran 25 minutes because people couldn't remember what they finished the day before. They set up this workflow to post a standup digest to #product-standup at 8:50 AM every weekday — tasks completed in the last 24 hours and tasks due today, grouped by assignee. The meeting dropped to 10 minutes within the first week.

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 Pipedream

Copy the pre-built Pipedream blueprint and paste it straight into Pipedream. 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 account with access to the project(s) you want to report on — the account must have viewer or editor access to all relevant tasks
Asana Personal Access Token or OAuth app credentials — generate a token at app.asana.com/0/my-apps
Slack workspace admin access or permission to install apps — you need the chat:write OAuth scope to post messages
Your Asana workspace GID and project GID — find these in the URL when viewing your project in Asana (the long numeric IDs)
The Slack channel ID where standup reports should post — right-click the channel name in Slack and copy the channel ID from the link

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Task Namename
Assignee Nameassignee.name
Completed Atcompleted_at
Due Ondue_on
Permalink URLpermalink_url
3 optional fields▸ show
Project Namememberships[0].project.name
Task Notes / Descriptionnotes
Custom Status / Sectionmemberships[0].section.name

Step-by-Step Setup

1

pipedream.com > Workflows > New Workflow

Create a new Pipedream workflow

Go to pipedream.com and log in. Click 'Workflows' in the left sidebar, then click the blue 'New Workflow' button in the top right. You'll land on a blank canvas with a trigger slot at the top and a '+ Add a step' button below it. Give your workflow a name immediately — something like 'Daily Asana Standup to Slack' — by clicking the pencil icon next to 'Untitled Workflow' at the top.

  1. 1Log in at pipedream.com
  2. 2Click 'Workflows' in the left sidebar
  3. 3Click the blue 'New Workflow' button
  4. 4Click the pencil icon next to 'Untitled Workflow' and type your workflow name
  5. 5Press Enter to save the name
What you should see: You should see a blank workflow canvas with a trigger slot labeled 'Add a trigger' and your custom workflow name shown at the top.
2

Workflow Canvas > Add a trigger > Schedule > Cron Expression

Set a daily schedule trigger

Click the 'Add a trigger' slot. In the trigger selector panel that opens on the right, type 'Schedule' in the search box and select the 'Schedule' source. Choose 'Cron expression' from the interval options so you have exact control. Enter a cron expression for your target time — for 8:50 AM Monday through Friday UTC, use '50 8 * * 1-5'. Pipedream runs cron in UTC, so adjust for your team's timezone before saving.

  1. 1Click the 'Add a trigger' slot on the canvas
  2. 2Type 'Schedule' in the search field in the right panel
  3. 3Select 'Schedule' from the results
  4. 4Click 'Cron expression' as the trigger type
  5. 5Enter your cron string (e.g. '50 8 * * 1-5' for 8:50 AM UTC weekdays)
  6. 6Click 'Save and continue'
What you should see: The trigger slot should show a green active badge and display your cron string. Pipedream shows the next scheduled run time just below the expression.
Common mistake — Pipedream cron runs in UTC with no timezone conversion built in. If your team is in US Eastern time (UTC-5), 8:50 AM ET = 13:50 UTC. Use '50 13 * * 1-5' instead. Getting this wrong means the report posts at 3 AM local time.
Pipedream
+
click +
search apps
Slack
SL
Slack
Set a daily schedule trigger
Slack
SL
module added
3

Workflow Canvas > + Add a step > Run Node.js code

Connect Asana and fetch completed tasks

Click '+ Add a step' below the trigger and select 'Run Node.js code'. This step will call the Asana API directly to pull tasks completed in the last 24 hours. You'll use Asana's search endpoint with a 'completed_since' filter set to yesterday's ISO timestamp. Pipedream's Node.js steps support async/await natively, so you can use fetch or the axios package that's pre-installed.

  1. 1Click '+ Add a step' on the canvas
  2. 2Select 'Run Node.js code' from the step options
  3. 3Click the step title and rename it to 'Fetch Completed Tasks'
  4. 4Paste your Asana API code into the code editor (see pro tip below)
  5. 5Click 'Test' to run the step and verify the output
What you should see: After clicking Test, the step should return a JSON array in the 'Return Value' panel on the right. Each object should contain task name, assignee, completed_at, and permalink_url.
Common mistake — Asana's task search API requires a workspace GID, not a project GID. If you pass a project GID to the workspace param, you'll get a 400 error. Check your Asana URL — the workspace GID is a different number than any project ID.
4

Step Panel > Connected Accounts tab > + Add account > Asana

Add your Asana Connected Account

Inside the code step, you referenced a personal access token. The cleaner approach is to use Pipedream's Connected Accounts so the token is stored securely and not hardcoded. Click the 'Connected Accounts' tab at the top of the step panel, then click '+ Add account' and select Asana. Pipedream will open an OAuth flow — click 'Allow' to grant access. After connecting, Pipedream injects your credentials as environment variables you can reference in the code.

  1. 1Click the 'Connected Accounts' tab inside your Node.js step
  2. 2Click '+ Add account'
  3. 3Search for and select 'Asana'
  4. 4Complete the OAuth prompt in the popup window by clicking 'Allow'
  5. 5Confirm the account appears with a green connected badge in the step
What you should see: You should see your Asana account email listed under Connected Accounts with a green badge. The account name is now available as auths.asana in your code.
Common mistake — If you authenticate with a personal Asana account instead of a workspace admin account, you'll only see tasks assigned to you — not the full team's tasks. Use an account that has visibility across the relevant project or workspace.
Pipedream settings
Connection
Choose a connection…Add
click Add
Slack
Log in to authorize
Authorize Pipedream
popup window
Connected
green checkmark
5

Workflow Canvas > + Add a step > Run Node.js code

Fetch upcoming tasks due today

Add a second Node.js code step to fetch tasks due today from Asana. This is a separate API call using the tasks endpoint filtered by due_on equal to today's date in YYYY-MM-DD format. Keep this as a separate step rather than combining it with the completed tasks fetch — it makes debugging much easier when one call fails.

  1. 1Click '+ Add a step' below the completed tasks step
  2. 2Select 'Run Node.js code'
  3. 3Rename the step to 'Fetch Due Today Tasks'
  4. 4Paste the due-today API call code into the editor
  5. 5Click 'Test' and verify the output contains today's due tasks
What you should see: The step return value should show an array of tasks with due_on matching today's date, each with task name, assignee name, project name, and a permalink URL.
6

Workflow Canvas > + Add a step > Run Node.js code

Format the standup message

Add a third Node.js code step to combine the completed and due-today arrays into a formatted Slack Block Kit message. Reference the previous steps' outputs using steps['Fetch Completed Tasks'].$return_value and steps['Fetch Due Today Tasks'].$return_value. Group tasks by assignee name and build a blocks array with header, divider, and section blocks. Slack's Block Kit renderer handles markdown in mrkdwn fields — use *bold* for names and bullet points for task lists.

  1. 1Click '+ Add a step'
  2. 2Select 'Run Node.js code'
  3. 3Rename the step to 'Format Standup Message'
  4. 4Reference previous step outputs using steps['step-name'].$return_value
  5. 5Build and return a Slack blocks array from the task data
  6. 6Click 'Test' and inspect the blocks output in the return value panel
What you should see: The return value should contain a valid Slack blocks array. You can paste this output directly into Slack's Block Kit Builder at app.slack.com/block-kit-builder to visually verify the layout before continuing.
Common mistake — Slack Block Kit sections have a 3000-character limit per text block. If a team has 20+ completed tasks, a single section will get truncated silently. Group tasks by assignee in separate section blocks to stay under the limit.
7

Workflow Canvas > + Add a step > Slack > Send a Message

Connect Slack and post the message

Add a new step and this time search for 'Slack' in the app list instead of using a code step. Select the 'Send a Message' action. Pipedream will prompt you to connect your Slack workspace via OAuth — click 'Connect Slack Account' and authorize the app. Once connected, set the Channel field to your standup channel (e.g. #product-standup), set Blocks to reference your formatted blocks from the previous step, and add a fallback Text field for notification previews.

  1. 1Click '+ Add a step'
  2. 2Search for 'Slack' in the app selector
  3. 3Select 'Slack' and then choose 'Send a Message' action
  4. 4Click 'Connect Slack Account' and complete the OAuth flow
  5. 5Set Channel to your standup Slack channel ID or name
  6. 6Set Blocks to {{steps['Format Standup Message'].$return_value.blocks}}
  7. 7Set Text to a fallback string like 'Daily standup report'
  8. 8Click 'Test' to post a test message to Slack
What you should see: You should see a formatted standup message appear in your Slack channel within 5 seconds of clicking Test. Pipedream shows a green success badge on the step with the Slack message timestamp in the return value.
Common mistake — The Slack app you authorize must be installed in the workspace and have chat:write scope. If you installed it only to a specific channel via 'Add to a specific channel', it cannot post to other channels. Reinstall with workspace-level permissions.
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
8

Workflow Canvas > + Add a step > Run Node.js code

Add error handling for empty task lists

Add a Node.js code step between the formatting step and the Slack step to check whether both task arrays are empty. If no tasks were completed yesterday and nothing is due today — common on Mondays after a holiday — you can either skip posting or send a short 'No updates today' message. Use $.flow.exit() to halt the workflow gracefully without triggering an error alert.

  1. 1Click '+ Add a step' between the format step and the Slack step
  2. 2Select 'Run Node.js code'
  3. 3Rename it to 'Guard: Skip if No Tasks'
  4. 4Add a check: if both arrays are empty, call $.flow.exit('No tasks to report')
  5. 5Click 'Test' with empty mock data to confirm the exit fires correctly
What you should see: When both task arrays are empty, the workflow should show a yellow 'Workflow exited early' status in Pipedream's event inspector rather than posting a blank message to Slack.
9

Workflow Canvas > Run Now button (top right)

Test the full workflow end-to-end

Click 'Run Now' at the top of the workflow canvas to trigger an immediate test run outside the schedule. Watch each step execute in sequence in the event inspector at the bottom of the screen. Click on each step to expand its input and output. Verify that the Asana task data flows correctly into the formatter and that the Slack message in your channel looks exactly as expected with correct names and task links.

  1. 1Click 'Run Now' in the top right of the workflow canvas
  2. 2Watch the step execution progress in the event inspector at the bottom
  3. 3Click each step row to expand and inspect its input/output
  4. 4Check your Slack channel to confirm the message posted correctly
  5. 5Verify assignee names, task links, and date groupings are accurate
What you should see: All steps should show green check marks in the event inspector. Your Slack channel should have a new standup message with completed tasks grouped by assignee and a separate section for today's due tasks.
Common mistake — During end-to-end testing, real Slack messages are posted to real channels. Test in a private channel or DM yourself first — not in #general.
Pipedream
▶ Deploy & test
executed
Slack
Asana
Asana
🔔 notification
received
10

Workflow Canvas > Deploy button > Logs tab

Deploy and monitor the workflow

Click the 'Deploy' button in the top right of the workflow canvas. Pipedream will activate the cron schedule and the workflow will run automatically at your specified time. Navigate to the 'Logs' tab to monitor future runs. Pipedream retains logs for 30 days on the free tier and shows you the exact error if any step fails. Set up an email or Slack error notification under Settings > Notifications so you know immediately if a run fails.

  1. 1Click 'Deploy' in the top right corner
  2. 2Confirm the workflow status shows 'Active' with your cron schedule listed
  3. 3Click the 'Logs' tab to see the event history
  4. 4Go to Settings > Notifications and add an email or Slack alert for workflow errors
  5. 5Check back the next morning to confirm the first scheduled run succeeded
What you should see: The workflow status badge should show 'Active' in green. After the first scheduled run, you'll see an event entry in Logs with a duration, step count, and success status.
Common mistake — Pipedream's free tier pauses workflows that haven't run in 7 days. If your team takes a week off, the workflow will need to be manually reactivated. Upgrade to a paid tier or use Pipedream's keep-alive ping if this is mission-critical.

Paste this code into two separate Node.js steps — 'Fetch Completed Tasks' and 'Fetch Due Today Tasks' — then paste the formatting function into a third step called 'Format Standup Message'. The final export.default in the formatting step returns the complete Slack blocks payload ready to pass directly into the Slack Send Message action.

JavaScript — Code Step// ── Step 1: Fetch Completed Tasks (paste into 'Fetch Completed Tasks' step) ──
▸ Show code
// ── Step 1: Fetch Completed Tasks (paste into 'Fetch Completed Tasks' step) ──
export default async function({ steps, auths, $ }) {
  const token = auths.asana.oauth_access_token;

... expand to see full code

// ── Step 1: Fetch Completed Tasks (paste into 'Fetch Completed Tasks' step) ──
export default async function({ steps, auths, $ }) {
  const token = auths.asana.oauth_access_token;
  const projectGid = process.env.ASANA_PROJECT_GID;
  const since = new Date(Date.now() - 86400000).toISOString(); // 24h ago UTC

  const url = `https://app.asana.com/api/1.0/projects/${projectGid}/tasks` +
    `?completed_since=${encodeURIComponent(since)}` +
    `&opt_fields=name,assignee.name,completed_at,permalink_url,memberships.project.name` +
    `&limit=50`;

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}` }
  });

  if (!res.ok) {
    const err = await res.text();
    throw new Error(`Asana API error ${res.status}: ${err}`);
  }

  const { data } = await res.json();
  const completed = data.filter(t => t.completed_at !== null);
  return completed;
}

// ── Step 2: Fetch Due Today Tasks (paste into 'Fetch Due Today Tasks' step) ──
export default async function({ steps, auths, $ }) {
  const token = auths.asana.oauth_access_token;
  const projectGid = process.env.ASANA_PROJECT_GID;
  const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD

  const url = `https://app.asana.com/api/1.0/projects/${projectGid}/tasks` +
    `?opt_fields=name,assignee.name,due_on,permalink_url,memberships.project.name` +
    `&limit=50`;

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}` }
  });

  if (!res.ok) {
    const err = await res.text();
    throw new Error(`Asana API error ${res.status}: ${err}`);
  }

  const { data } = await res.json();
  const dueToday = data.filter(t => t.due_on === today && !t.completed_at);
  return dueToday;
}

// ── Step 3: Format Standup Message (paste into 'Format Standup Message' step) ──
export default async function({ steps, $ }) {
  const completed = steps['Fetch Completed Tasks'].$return_value || [];
  const dueToday = steps['Fetch Due Today Tasks'].$return_value || [];

  if (completed.length === 0 && dueToday.length === 0) {
    $.flow.exit('No tasks to report today');
  }

  // Group by assignee
  const groupByAssignee = (tasks) => tasks.reduce((acc, task) => {
    const name = task.assignee?.name || 'Unassigned';
    if (!acc[name]) acc[name] = [];
    acc[name].push(task);
    return acc;
  }, {});

  const completedByPerson = groupByAssignee(completed);
  const dueByPerson = groupByAssignee(dueToday);
  const today = new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' });

  const blocks = [
    {
      type: 'header',
      text: { type: 'plain_text', text: `🗓 Daily Standup — ${today}`, emoji: true }
    },
    { type: 'divider' }
  ];

  if (completed.length > 0) {
    blocks.push({
      type: 'section',
      text: { type: 'mrkdwn', text: '*✅ Completed in the last 24 hours*' }
    });
    for (const [person, tasks] of Object.entries(completedByPerson)) {
      const lines = tasks.map(t => {
        const project = t.memberships?.[0]?.project?.name || '';
        return `• <${t.permalink_url}|${t.name}>${project ? ` — _${project}_` : ''}`;
      }).join('\n');
      blocks.push({
        type: 'section',
        text: { type: 'mrkdwn', text: `*${person}*\n${lines}` }
      });
    }
    blocks.push({ type: 'divider' });
  }

  if (dueToday.length > 0) {
    blocks.push({
      type: 'section',
      text: { type: 'mrkdwn', text: '*📅 Due today*' }
    });
    for (const [person, tasks] of Object.entries(dueByPerson)) {
      const lines = tasks.map(t => {
        const project = t.memberships?.[0]?.project?.name || '';
        return `• <${t.permalink_url}|${t.name}>${project ? ` — _${project}_` : ''}`;
      }).join('\n');
      blocks.push({
        type: 'section',
        text: { type: 'mrkdwn', text: `*${person}*\n${lines}` }
      });
    }
  }

  return { blocks };
}

Scaling Beyond 50+ tasks completed per day or 5+ Asana projects being monitored+ Records

If your volume exceeds 50+ tasks completed per day or 5+ Asana projects being monitored records, apply these adjustments.

1

Paginate Asana Results

Asana caps task responses at 100 records per request. If your team closes 100+ tasks daily, implement pagination using the next_page.offset token returned in the API response. Loop through pages in your Node.js step until next_page is null, then merge all results before formatting.

2

Cap Section Length in Block Kit

Slack section blocks have a 3000-character limit. For large teams, don't list every task per person — instead show the count and link to the Asana project. For example: 'Maya Patel completed 12 tasks — view in Asana' with a button block linking to the project filter view.

3

Split by Project Into Multiple Messages

If you're aggregating 5+ Asana projects, a single Slack message will be overwhelming. Post one message per project to its own channel using separate Slack steps in the same workflow. Each Slack API call counts as one Pipedream credit, so 5 projects = 5 extra credits per run.

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 Pipedream for this if your team has even one developer who can write basic JavaScript. The scheduled trigger fires with sub-second accuracy, and the Node.js code steps mean you can handle Asana's pagination, group tasks by assignee, and format Block Kit messages in a single workflow without duct-taping together five no-code actions. The alternative to reach for is Make — if nobody on your team is comfortable in code, Make's Asana and Slack modules handle the API calls visually, though you'll hit the 1000-operations/month limit fast on a daily workflow.

Cost

Cost math: this workflow runs one scheduled trigger plus four steps (fetch completed, fetch due today, format, post to Slack) = roughly 5 Pipedream credits per run. At 5 runs/week (weekdays only), that's 25 credits/week or ~100 credits/month. Pipedream's free tier gives you 10,000 credits/month, so this workflow costs essentially nothing. Even if you add three more Slack channels and post to all of them, you're at 400 credits/month — still well inside the free tier. Compare that to Zapier, where a multi-step Zap running daily hits the 1000-task/month free cap in under two months and then costs $19.99/month minimum.

Tradeoffs

Zapier handles this use case but you'll write more workarounds: its Asana trigger for completed tasks polls every 15 minutes, not on a schedule, so you'd need a Zapier Schedule trigger chained with a Search action — clunky and fragile when Asana rate limits the polling. n8n is a strong alternative if you're self-hosting — the Asana node supports all the same API params and the Cron trigger is identical in behavior to Pipedream's. Make has a built-in Asana 'Search Tasks' module that handles filtering more visually than raw API calls, which is genuinely easier for non-developers. Power Automate can do this workflow but Asana has no official Power Automate connector — you'd use HTTP actions for all Asana calls, which defeats the point of using a no-code tool. Pipedream wins here because the combination of exact cron scheduling, Asana OAuth built-in, and flexible Node.js formatting is hard to beat for developer teams.

Three things you'll hit after setup. First, Asana's completed_since filter uses the task's completed_at timestamp in UTC — if a team member marks a task done at 11 PM their local time, it may appear in the next day's report or not at all depending on your UTC offset math. Test with real tasks and real timestamps before trusting the filter. Second, Slack's Block Kit has a hard limit of 50 blocks per message. A team that closes 40 tasks daily spread across 10 people can easily hit this — you'll see Slack silently truncate the message with no error in Pipedream. Add a block count check before posting. Third, Asana throttles API requests to 150 per minute per token. A workflow that fetches 5 projects in parallel can hit this instantly — add a 500ms delay between project fetch steps or use a single search endpoint call scoped to the workspace with project filters.

Ideas for what to build next

  • Add a Weekly Summary VariantDuplicate the workflow and change the cron to run every Monday morning with a 7-day lookback window instead of 24 hours. This gives teams a weekly digest in addition to the daily standup and takes about 10 minutes to configure.
  • Include Blocked Tasks from AsanaAdd a third Asana API call that fetches tasks tagged as 'Blocked' or sitting in a 'Blocked' section. Post them as a separate section in the standup message so blockers are visible without anyone having to remember to mention them.
  • Route Reports to Per-Project ChannelsIf your team uses multiple Asana projects with separate Slack channels, add conditional routing in the formatting step to map each project GID to its corresponding Slack channel ID. One workflow run can post tailored summaries to multiple channels simultaneously.

Related guides

Was this guide helpful?
Slack + Asana overviewPipedream profile →