Install
openclaw skills install asana-apiAsana API integration with managed OAuth. Access tasks, projects, workspaces, users, and manage webhooks. Use this skill when users want to manage work items, track projects, or integrate with Asana workflows. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
openclaw skills install asana-apiAccess the Asana API with managed OAuth authentication. Manage tasks, projects, workspaces, users, and webhooks for work management.
CLI:
maton asana task list --project <project-gid>
maton api '/asana/api/1.0/tasks?project=PROJECT_GID'
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/tasks?project=PROJECT_GID')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
https://api.maton.ai/asana/{native-api-path}
Maton proxies requests to app.asana.com and automatically injects your OAuth token.
NPM:
npm install -g @maton-ai/cli
Homebrew:
brew install maton-ai/cli/maton
CLI:
maton login # Opens browser for API key
maton login --interactive # Skip browser, paste API key directly
maton whoami # Show current auth state
Manual:
MATON_API_KEY:export MATON_API_KEY="YOUR_API_KEY"
Manage your Asana OAuth connections at https://api.maton.ai.
CLI:
maton connection list asana --status ACTIVE
maton api -X GET /connections -f app=asana -f status=ACTIVE
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=asana&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
CLI:
maton connection create asana
maton api /connections -f app=asana
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'asana'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
CLI:
maton connection view {connection_id}
maton api /connections/{connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "{connection_id}",
"status": "ACTIVE",
"creation_time": "2025-12-08T07:20:53.488460Z",
"last_updated_time": "2026-01-31T20:03:32.593153Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "asana",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
CLI:
maton connection delete {connection_id}
maton api -X DELETE /connections/{connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple Asana connections, specify which one to use:
CLI:
maton asana task list --project <project-gid> --connection {connection_id}
maton api /asana/api/1.0/tasks?project=PROJECT_GID --connection {connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/tasks?project=PROJECT_GID')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always specify the connection to ensure requests go to the intended account.
GET /asana/api/1.0/tasks
Query parameters:
project - Project GID to filter tasksassignee - User GID or "me" for assigned tasksworkspace - Workspace GID (required if no project specified)completed_since - ISO 8601 date to filter tasks completed after this dateopt_fields - Comma-separated list of fields to includeExample:
maton asana task list --project 1234567890 --opt-fields name,completed,due_on
GET /asana/api/1.0/tasks/{task_gid}
Example:
maton asana task view 1234567890
POST /asana/api/1.0/tasks
Content-Type: application/json
{
"data": {
"name": "New task",
"projects": ["PROJECT_GID"],
"assignee": "USER_GID",
"due_on": "2025-03-20",
"notes": "Task description here"
}
}
Example:
maton asana task create --name 'New task' --projects PROJECT_GID --assignee USER_GID --due-on 2025-03-20 --notes 'Task description here'
PUT /asana/api/1.0/tasks/{task_gid}
Example:
maton asana task update 1234567890 --completed
DELETE /asana/api/1.0/tasks/{task_gid}
Example:
maton asana task delete 1234567890
GET /asana/api/1.0/projects/{project_gid}/tasks
Example:
maton asana task list --project 1234567890
GET /asana/api/1.0/tasks/{task_gid}/subtasks
Example:
maton asana task list --parent 1234567890
POST /asana/api/1.0/tasks/{task_gid}/subtasks
Content-Type: application/json
{
"data": {
"name": "Subtask name",
"assignee": "USER_GID",
"due_on": "2025-03-20"
}
}
Example:
maton asana task create --name 'Subtask name' --parent 1234567890 --assignee USER_GID --due-on 2025-03-20
Note: This endpoint requires an Asana Premium subscription.
GET /asana/api/1.0/workspaces/{workspace_gid}/tasks/search
Query parameters:
text - Text to search forassignee.any - Filter by assigneesprojects.any - Filter by projectscompleted - Filter by completion statusExample:
maton asana task search -w 1234567890 --text 'quarterly report' --completed=false
GET /asana/api/1.0/projects
Query parameters:
workspace - Workspace GIDteam - Team GIDopt_fields - Comma-separated list of fieldsExample:
maton asana project list --workspace <workspace-gid> --opt-fields name,owner,due_date
GET /asana/api/1.0/projects/{project_gid}
Example:
maton asana project view <project-gid>
POST /asana/api/1.0/projects
Example:
maton asana project create --workspace <workspace-gid> --name 'New Project' --notes 'Project description'
PUT /asana/api/1.0/projects/{project_gid}
Example:
maton asana project update PROJECT_GID --name 'Updated Name'
DELETE /asana/api/1.0/projects/{project_gid}
Example:
maton asana project delete <project-gid>
GET /asana/api/1.0/workspaces
Example:
maton asana workspace list
GET /asana/api/1.0/workspaces/{workspace_gid}
Example:
maton asana workspace view 1234567890
PUT /asana/api/1.0/workspaces/{workspace_gid}
POST /asana/api/1.0/workspaces/{workspace_gid}/addUser
POST /asana/api/1.0/workspaces/{workspace_gid}/removeUser
GET /asana/api/1.0/users
Query parameters:
workspace - Workspace GID to filter usersGET /asana/api/1.0/users/me
Example:
maton asana whoami
GET /asana/api/1.0/users/{user_gid}
GET /asana/api/1.0/teams/{team_gid}/users
GET /asana/api/1.0/workspaces/{workspace_gid}/users
GET /asana/api/1.0/webhooks
Query parameters:
workspace - Workspace GID (required)resource - Resource GID to filter byNote: Asana verifies the target URL is reachable and responds with a 200 status during webhook creation.
POST /asana/api/1.0/webhooks
Content-Type: application/json
{
"data": {
"resource": "PROJECT_OR_TASK_GID",
"target": "https://example.com/webhook",
"filters": [
{
"resource_type": "task",
"action": "changed",
"fields": ["completed", "due_on"]
}
]
}
}
GET /asana/api/1.0/webhooks/{webhook_gid}
PUT /asana/api/1.0/webhooks/{webhook_gid}
DELETE /asana/api/1.0/webhooks/{webhook_gid}
Returns 200 OK with empty data on success.
Asana uses cursor-based pagination. The CLI automatically paginates with '--paginate'.
Example:
maton asana task list --project <project-gid> --paginate
# Get tasks as JSON (default format); select fields with --opt-fields
maton asana task list --project 1234567890 --opt-fields name,completed,due_on
# Filter with jq — e.g., only incomplete tasks (responses are wrapped in {"data": [...]})
# Note: --jq requires --json
maton asana task list --project 1234567890 --opt-fields name,completed,due_on \
--json --jq '.data | map(select(.completed == false))'
# Extract specific fields
maton asana project list --workspace 1234567890 --opt-fields name --json --jq '.data[].name'
const response = await fetch(
'https://api.maton.ai/asana/api/1.0/tasks?project=1234567890',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
import os
import requests
response = requests.get(
'https://api.maton.ai/asana/api/1.0/tasks',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
params={'project': '1234567890'}
)
data = response.json()
opt_fields to specify which fields to returncurl -g when URLs contain brackets (fields[], sort[], records[]) to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments. You may get "Invalid API key" errors when piping.| Status | Meaning |
|---|---|
| 400 | Bad request or missing Asana connection |
| 401 | Invalid or missing Maton API key |
| 403 | Forbidden - insufficient permissions |
| 404 | Resource not found |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Asana API |
CLI:
maton whoami
maton connection list
Manual:
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
asana. For example:https://api.maton.ai/asana/api/1.0/taskshttps://api.maton.ai/api/1.0/tasks