Source: https://cli.nylas.com/guides/email-to-asana-tasks

# Create Asana Tasks from Email (CLI)

Client requests and bug reports arrive by email, but the work gets tracked in Asana. Most automation tools that bridge the two bill for every task they create. The CLI hands you each message as JSON; the Asana API turns it into a task in a chosen project. This guide builds an email-to-Asana pipeline you control, mapping subject and sender to task fields and running it on a schedule for free.

Written by [Caleb Geene](https://cli.nylas.com/authors/caleb-geene) Director, Site Reliability Engineering

Reviewed by [Qasim Muhammad](https://cli.nylas.com/authors/qasim-muhammad)

Updated June 9, 2026

> **TL;DR:** Pull messages with `nylas email search --json`, then create one Asana task per message by POSTing to the Asana API `/api/1.0/tasks` endpoint with a project ID and a personal access token. The CLI handles the inbox across six providers; Asana handles the write. The trick that keeps the project clean — never filing the same email twice — is at the end.

Command references used in this guide: [`nylas email search`](https://cli.nylas.com/docs/commands/email-search), [`nylas email list`](https://cli.nylas.com/docs/commands/email-list), and [`nylas email read`](https://cli.nylas.com/docs/commands/email-read).

## How do I create an Asana task from email?

Creating an Asana task from email takes two calls: pull the message as JSON, then POST it to Asana. The CLI returns structured messages with `nylas email search --json`, and a single request to the [Asana API create task endpoint](https://developers.asana.com/reference/createtask) turns each one into a task in a project you pick. The whole flow is 2 commands, with no SMTP, no OAuth app registration, and no per-task fee.

First, get a token and a project ID. Asana issues a personal access token from the developer console, and you pass it as a bearer credential in the `Authorization` header per the [token docs](https://developers.asana.com/docs/personal-access-token). The 2 inputs you need are that token and the numeric project GID from a project's URL. If the CLI isn't installed yet, run the Homebrew one-liner below, then see [getting started](https://cli.nylas.com/guides/getting-started) for the other three install methods.

```bash
# Install the CLI (macOS / Linux)
brew install nylas/nylas-cli/nylas

# Pull the messages you want to file into Asana
nylas email search "*" --subject "bug" --after 2026-06-08 --json --limit 50 > items.json
```

## How do I map an email to Asana task fields?

Map each email to an Asana task by sending a JSON body where `name` holds the subject and `notes` holds the sender and body. Asana wraps every request in a top-level `data` object, and a task needs a workspace or at least one project. Build the body with `jq` so values escape correctly and a missing subject never produces a nameless task.

The loop below reads one message at a time, extracts the subject and sender, and POSTs a task to the chosen project. Using `jq -n` to assemble the payload keeps the JSON valid even when a subject contains quotes or newlines — a common cause of a 400 response from the Asana API. The `projects` field takes an array of GIDs, so one task can land in several projects at once.

```bash
PROJECT_GID="1201234567890123"
jq -c '.[]' items.json | while read -r msg; do
  subject=$(echo "$msg" | jq -r '.subject // "(no subject)"')
  sender=$(echo "$msg"  | jq -r '.from[0].email // "unknown"')
  body=$(jq -n --arg n "$subject" --arg notes "From: $sender" --arg p "$PROJECT_GID" \
    '{data: {name: $n, notes: $notes, projects: [$p]}}')
  curl -s -X POST "https://app.asana.com/api/1.0/tasks" \
    -H "Authorization: Bearer $ASANA_TOKEN" \
    -H "Content-Type: application/json" \
    -d "$body"
done
```

## What Asana fields can I set from an email?

Beyond the name, the Asana create task endpoint accepts `due_on`, `assignee`, `notes`, and `html_notes` in the same `data` object. Due dates use a plain `YYYY-MM-DD` string for `due_on`, per the [create task reference](https://developers.asana.com/reference/createtask). The `assignee` field takes a user GID or the literal `me`.

That lets you route by sender. A message from your on-call alias gets a due date 1 day out and lands on you; everything else keeps the project default. The snippet picks a due date from the current date plus 1 day and assigns the task with `me`. Asana's `html_notes` field accepts a restricted HTML subset documented in the [rich text reference](https://developers.asana.com/docs/rich-text), so you can keep links from the email body clickable.

```bash
subject=$(echo "$msg" | jq -r '.subject // "(no subject)"')
sender=$(echo "$msg"  | jq -r '.from[0].email // "unknown"')
due=$(date -u -d "+1 day" +%Y-%m-%d 2>/dev/null || date -u -v+1d +%Y-%m-%d)
jq -n --arg n "$subject" --arg notes "From: $sender" \
  --arg due "$due" --arg p "$PROJECT_GID" \
  '{data: {name: $n, notes: $notes, due_on: $due, assignee: "me", projects: [$p]}}'
```

## How do I avoid creating duplicate tasks?

Avoid duplicate Asana tasks by recording each email's message ID after you file it and skipping IDs you have already seen. Every Nylas message carries a stable `id` field, so a flat file of processed IDs is enough to make the pipeline safe to run every 5 minutes from cron without ever double-filing a message.

The loop checks a `seen.txt` file before each POST and appends the ID after a successful create. Scoping the search to `newer_than:1d` caps the working set, and the ID check guards the edge where the same message matches two consecutive runs. Combined, the two limits keep a 1-minute cron job idempotent and stay well under Asana's default rate limit of 150 requests per minute.

```bash
touch seen.txt
jq -c '.[]' items.json | while read -r msg; do
  id=$(echo "$msg" | jq -r '.id')
  grep -qF "$id" seen.txt && continue
  subject=$(echo "$msg" | jq -r '.subject // "(no subject)"')
  curl -s -X POST "https://app.asana.com/api/1.0/tasks" \
    -H "Authorization: Bearer $ASANA_TOKEN" -H "Content-Type: application/json" \
    -d "$(jq -n --arg n "$subject" --arg p "$PROJECT_GID" '{data: {name: $n, projects: [$p]}}')" \
    && echo "$id" >> seen.txt
done
```

## Why use a webhook instead of polling?

A webhook files an Asana task the instant an email arrives, instead of waiting for the next poll. The CLI ships a webhook server; run it with `--tunnel cloudflared` to expose a temporary public URL, so you can develop against live `message.created` events without deploying anything. A 5-minute poll adds up to 300 seconds of latency; a webhook fires within seconds of delivery.

The `nylas webhook create` command registers the endpoint and the triggers; `nylas webhook server` runs a local listener on port 9000 for testing. The mapping code from earlier is identical — only the trigger changes from a cron timer to an inbound event. Your Asana token stays separate from the mailbox grant the tool manages, so you rotate each one independently.

```bash
# Register a webhook for new mail
nylas webhook create \
  --url https://your-endpoint.example.com/hooks \
  --triggers message.created \
  --description "Email to Asana"

# Or test locally with an auto-generated tunnel
nylas webhook server --port 9000
```

## Next steps

- [Create Confluence Pages from Email](https://cli.nylas.com/guides/email-to-confluence-pages) — Pull email as JSON with the Nylas CLI and POST to the Confluence…
- [Create ClickUp tasks from email](https://cli.nylas.com/guides/email-to-clickup-tasks) — the same pattern into a ClickUp list
- [Email to Trello cards](https://cli.nylas.com/guides/email-to-trello-cards) — file messages as Trello cards
- [Send email to a Notion database](https://cli.nylas.com/guides/email-to-notion) — create Notion pages from email
- [Email to GitHub issues](https://cli.nylas.com/guides/email-to-github-issues) — open issues from inbound mail
- [Email to monday.com items](https://cli.nylas.com/guides/email-to-monday-items) — write board items from email
- [Full command reference](https://cli.nylas.com/docs/commands) — every flag and subcommand documented
- Asana docs: [create task](https://developers.asana.com/reference/createtask), [personal access token](https://developers.asana.com/docs/personal-access-token), and [rate limits](https://developers.asana.com/reference/rate-limits)
