Install
openclaw skills install asana-agent-skillManage Asana tasks, projects, briefs, status updates, custom fields, dependencies, attachments, events, and timelines via Personal Access Token (PAT).
openclaw skills install asana-agent-skillThis skill provides a dependency-free Node.js CLI that calls the Asana REST API (v1) using a Personal Access Token (PAT).
{baseDir}/scripts/asana.mjsASANA_PAT (preferred) or ASANA_TOKENASANA_PAT.Shell env (local testing):
export ASANA_PAT="..."
OpenClaw config (recommended): set skills.entries.asana.apiKey (or env.ASANA_PAT) so the secret is injected only for the agent run.
This is the safest way to set the PAT because it keeps secrets out of prompts and ad-hoc shell history.
Recommended (apiKey → ASANA_PAT):
openclaw config set skills.entries.asana.enabled true
openclaw config set skills.entries.asana.apiKey "ASANA_PAT_HERE"
skills.entries.asana.apiKey is a convenience field: for skills that declare metadata.openclaw.primaryEnv, OpenClaw injects apiKey into that env var for the agent run (this skill’s primary env is ASANA_PAT).
Alternative (explicit env):
openclaw config set skills.entries.asana.enabled true
openclaw config set skills.entries.asana.env.ASANA_PAT "ASANA_PAT_HERE"
Verify what is stored:
openclaw config get skills.entries.asana
openclaw config get skills.entries.asana.enabled
openclaw config get skills.entries.asana.apiKey
Remove a stored token:
openclaw config unset skills.entries.asana.apiKey
# or
openclaw config unset skills.entries.asana.env.ASANA_PAT
When a session is sandboxed, skill processes run inside Docker and do not inherit the host environment. In that case, skills.entries.*.env/apiKey applies to host runs only.
Set Docker env via:
agents.defaults.sandbox.docker.env (or per-agent agents.list[].sandbox.docker.env)Who am I:
node {baseDir}/scripts/asana.mjs me
List workspaces:
node {baseDir}/scripts/asana.mjs workspaces
(Recommended) Set a default workspace once:
node {baseDir}/scripts/asana.mjs set-default-workspace --workspace <workspace_gid>
When the user provides names (project/task/user), resolve to GIDs using one of:
typeahead --workspace <gid> --resource_type project|task|user --query "..." (fast, best default)projects --workspace <gid> --all (enumerate)users --workspace <gid> --all (enumerate)Avoid guessing a GID when multiple matches exist.
node {baseDir}/scripts/asana.mjs tasks-assigned --assignee me --workspace <workspace_gid> --all
node {baseDir}/scripts/asana.mjs tasks-in-project --project <project_gid> --all
Canonical primitive: search-tasks (supports many filters; preferred over adding narrow “search helper” commands).
One-liner example (search within a project):
node {baseDir}/scripts/asana.mjs search-tasks --workspace <gid> --project <project_gid> --text "..." --all
Useful filters:
--assignee me|<gid|email> (maps to assignee.any)--completed true|false--created_at.after <iso> / --modified_at.after <iso>--due_on.before YYYY-MM-DD / --due_at.before <iso>--is_blocked true|false / --is_blocking true|falseCreate:
node {baseDir}/scripts/asana.mjs create-task --workspace <gid> --name "..." --projects <project_gid> --assignee me
Update:
node {baseDir}/scripts/asana.mjs update-task <task_gid> --name "..." --due_on 2026-02-01
Complete:
node {baseDir}/scripts/asana.mjs complete-task <task_gid>
This skill supports the workflows commonly expected from a PM in Asana:
upsert-project-brief)create-status-update)project-blockers, dependencies, dependents)Read:
node {baseDir}/scripts/asana.mjs project-brief <project_gid>
Upsert (create or update):
node {baseDir}/scripts/asana.mjs upsert-project-brief <project_gid> --title "Project brief" --html_text "<body>...</body>"
Create:
node {baseDir}/scripts/asana.mjs create-status-update --parent <project_gid> --status_type on_track --text "Weekly update..."
List:
node {baseDir}/scripts/asana.mjs status-updates --parent <project_gid> --all
List sections:
node {baseDir}/scripts/asana.mjs sections --project <project_gid> --all
Create section:
node {baseDir}/scripts/asana.mjs create-section --project <project_gid> --name "Blocked"
Command: add-task-to-project
Calls POST /tasks/{task_gid}/addProject and supports optional section placement and ordering.
Examples:
node {baseDir}/scripts/asana.mjs add-task-to-project <task_gid> --project <project_gid>
With section + ordering:
node {baseDir}/scripts/asana.mjs add-task-to-project <task_gid> --project <project_gid> --section <section_gid> --insert_before null --insert_after null
(--section, --insert_before, and --insert_after are optional; when provided they are passed through in the request body.)
Command: remove-task-from-project
Calls POST /tasks/{task_gid}/removeProject.
Example:
node {baseDir}/scripts/asana.mjs remove-task-from-project <task_gid> --project <project_gid>
Custom fields are critical for reliable PM automation.
List a project’s custom fields:
node {baseDir}/scripts/asana.mjs project-custom-fields <project_gid> --all
Read a custom field definition:
node {baseDir}/scripts/asana.mjs custom-field <custom_field_gid>
Set task custom fields on create/update:
node {baseDir}/scripts/asana.mjs update-task <task_gid> --custom_fields '{"<custom_field_gid>":"<value>"}'
Notes:
Asana rich text fields are XML-valid HTML fragments wrapped in a <body> root element. The API rejects invalid XML or unsupported tags.
Key points:
html_notes for task descriptions.html_text for comments/stories and status updates.<p> and <br>; prefer literal newlines (\n) and <hr/> separators.<a data-asana-gid="..."></a> (or a self-closing <a .../>).Creating a mention link does not guarantee notification delivery if the user is not already assigned or following.
For reliable pings, do one of:
This skill supports the “add follower + wait” pattern:
node {baseDir}/scripts/asana.mjs comment <task_gid> --html_text "<body>Hi <a data-asana-gid=\"<user_gid>\"/>...</body>" --ensure_followers <user_gid> --wait_ms 2500
Plain text comments (--text) do not create real @-mentions via the API; they remain plain text.
Upload a file attachment to a task:
node {baseDir}/scripts/asana.mjs upload-attachment --parent <task_gid> --file /path/to/file.png
Embed an existing image attachment inline (tasks + project briefs only):
node {baseDir}/scripts/asana.mjs append-inline-image --attachment <attachment_gid> --task <task_gid>
Asana does not provide a single universal “inbox” API for all notifications. The closest stable primitive is the Events endpoint scoped to a specific resource (project, task, etc.).
Use:
events --resource <gid> to pull incremental changes on a project (or a user's "My Tasks" project)Shift one task (optionally include subtasks):
node {baseDir}/scripts/asana.mjs shift-task-dates <task_gid> --delta_days 7 --dry_run true
Shift an entire project’s tasks:
node {baseDir}/scripts/asana.mjs shift-project-tasks --project <project_gid> --delta_days -3 --dry_run true --all
Run with --dry_run true first, then re-run with --dry_run false.