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

# Create ClickUp Tasks from Email (CLI)

Support requests, bug reports, and client asks land in email, but the work gets tracked in ClickUp. The no-code tools that bridge the two meter each task they file. The CLI hands you each message as JSON; the ClickUp API turns it into a task in a chosen list. This guide builds an email-to-ClickUp pipeline you control, mapping subject and sender to task fields and running it on a schedule for free.

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

Updated June 9, 2026

> **TL;DR:** Pull messages with `nylas email search --json`, then create one ClickUp task per message by POSTing to the ClickUp API `/api/v2/list/{list_id}/task` endpoint with a personal token. Authentication is a personal token plus a list ID — two values, one POST per email. The trick that keeps the queue 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 a ClickUp task from email?

Creating a ClickUp task from email takes two calls: pull the message as JSON, then POST it to ClickUp. The CLI returns structured messages with `nylas email search --json`, and a single request to the [ClickUp API create task endpoint](https://developer.clickup.com/reference/createtask) turns each one into a task in a list 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 list ID. ClickUp issues a personal token under Settings → Apps that starts with `pk_`; you pass it in an `Authorization` header. The 2 inputs you need are that token and the numeric list ID found in a list'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 ClickUp
nylas email search "*" --subject "bug" --after 2026-06-08 --json --limit 50 > items.json
```

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

Map each email to a ClickUp task by sending a JSON body where `name` holds the subject and `description` holds the sender and body. ClickUp requires only the `name` field; everything else is optional. Build the body with `jq` so values are escaped correctly and a missing subject never produces a nameless task. The endpoint returns the created task with its ID in under 1 second on a typical workspace.

The loop below reads one message at a time, extracts the subject and sender, and POSTs a task to the chosen list. 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 ClickUp API. Each task lands in the list ID you set in the first line.

```bash
LIST_ID="901234567"
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 d "From: $sender" \
    '{name: $n, description: $d}')
  curl -s -X POST "https://api.clickup.com/api/v2/list/$LIST_ID/task" \
    -H "Authorization: $CLICKUP_TOKEN" \
    -H "Content-Type: application/json" \
    -d "$body"
done
```

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

Beyond the name, the ClickUp create task endpoint accepts `priority`, `due_date`, `tags`, and `assignees` in the same JSON body. Priority is an integer from 1 (urgent) to 4 (low), per the [ClickUp API reference](https://clickup.com/api/clickupreference/operation/CreateTask/). Due dates use Unix time in milliseconds.

That lets you route by sender. A message from your on-call alias becomes priority 1; everything else stays at the default. The snippet picks priority based on the sender address and sets a due date 24 hours out (86,400,000 milliseconds). Assignees take an array of numeric ClickUp member IDs, which you can read once from the team endpoint and hard-code, so the loop stays a single call per message.

```bash
subject=$(echo "$msg" | jq -r '.subject // "(no subject)"')
sender=$(echo "$msg"  | jq -r '.from[0].email // "unknown"')
case "$sender" in
  *oncall@*) prio=1 ;;
  *)         prio=3 ;;
esac
due=$(( ($(date +%s) + 86400) * 1000 ))
jq -n --arg n "$subject" --arg d "From: $sender" \
  --argjson p "$prio" --argjson due "$due" \
  '{name: $n, description: $d, priority: $p, due_date: $due}'
```

## How do I avoid creating duplicate tasks?

Avoid duplicates 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.

```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://api.clickup.com/api/v2/list/$LIST_ID/task" \
    -H "Authorization: $CLICKUP_TOKEN" -H "Content-Type: application/json" \
    -d "$(jq -n --arg n "$subject" '{name: $n}')" \
    && echo "$id" >> seen.txt
done
```

## Why use a webhook instead of polling?

A webhook files a 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 ClickUp 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 ClickUp"

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

## Next steps

- [Email to Trello cards](https://cli.nylas.com/guides/email-to-trello-cards) — the same pattern into a Trello board
- [Email to Jira issues](https://cli.nylas.com/guides/email-to-jira-issues) — file messages as Jira tickets
- [Send email to a Notion database](https://cli.nylas.com/guides/email-to-notion) — create Notion pages from email
- [Email to Airtable](https://cli.nylas.com/guides/email-to-airtable) — write rows to an Airtable base
- [Full command reference](https://cli.nylas.com/docs/commands) — every flag and subcommand documented
- ClickUp docs: [create task](https://developer.clickup.com/reference/createtask), [authentication](https://developer.clickup.com/docs/authentication), and the [full API reference](https://clickup.com/api/clickupreference/operation/CreateTask/)
