- name
- asana
- description
- |
- compatibility
- Requires network access and valid Maton API key
- metadata
- author
- maton
- version
- 1.0
- clawdbot
- emoji
- 🧠
- requires
- env
Asana
Access the Asana API with managed OAuth authentication. Manage tasks, projects, workspaces, users, and webhooks for work management.
Quick Start
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))
EOFBase URL
https://api.maton.ai/asana/{native-api-path}Maton proxies requests to app.asana.com and automatically injects your OAuth token.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEYEnvironment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Asana OAuth connections at https://api.maton.ai.
List Connections
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))
EOFCreate Connection
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))
EOFGet Connection
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))
EOFResponse:
{
"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.
Delete Connection
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))
EOFSpecifying Connection
If you have multiple Asana connections, specify which one to use with the Maton-Connection header:
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))
EOFIf you have multiple connections, always include this header to ensure requests go to the intended account.
Security & Permissions
- Access is scoped to tasks, projects, workspaces, users, and manage webhooks within the connected Asana account.
- All write operations require explicit user approval. Before executing any create, update, or delete call, confirm the target resource and intended effect with the user.
API Reference
Tasks
Get Multiple Tasks
GET /asana/api/1.0/tasksQuery 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 include
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/tasks?project=1234567890&opt_fields=name,completed,due_on')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFResponse:
{
"data": [
{
"gid": "1234567890",
"name": "Review quarterly report",
"completed": false,
"due_on": "2025-03-15"
}
]
}Get a Task
GET /asana/api/1.0/tasks/{task_gid}Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/tasks/1234567890')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFCreate a Task
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:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'name': 'Complete API integration', 'projects': ['1234567890'], 'due_on': '2025-03-20'}}).encode()
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/tasks', 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))
EOFUpdate a Task
PUT /asana/api/1.0/tasks/{task_gid}Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'completed': True}}).encode()
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/tasks/1234567890', data=data, method='PUT')
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))
EOFDelete a Task
DELETE /asana/api/1.0/tasks/{task_gid}Get Tasks from a Project
GET /asana/api/1.0/projects/{project_gid}/tasksGet Subtasks
GET /asana/api/1.0/tasks/{task_gid}/subtasksCreate Subtask
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"
}
}Search Tasks (Premium)
Note: This endpoint requires an Asana Premium subscription.
GET /asana/api/1.0/workspaces/{workspace_gid}/tasks/searchQuery parameters:
text- Text to search forassignee.any- Filter by assigneesprojects.any- Filter by projectscompleted- Filter by completion status
Projects
Get Multiple Projects
GET /asana/api/1.0/projectsQuery parameters:
workspace- Workspace GIDteam- Team GIDopt_fields- Comma-separated list of fields
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/projects?workspace=1234567890&opt_fields=name,owner,due_date')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFResponse:
{
"data": [
{
"gid": "1234567890",
"name": "Q1 Marketing Campaign",
"owner": {
"gid": "0987654321",
"name": "Alice Johnson"
},
"due_date": "2025-03-31"
}
]
}Get a Project
GET /asana/api/1.0/projects/{project_gid}Create a Project
POST /asana/api/1.0/projectsExample:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'name': 'New Project', 'workspace': '1234567890', 'notes': 'Project description'}}).encode()
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/projects', 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))
EOFUpdate a Project
PUT /asana/api/1.0/projects/{project_gid}Delete a Project
DELETE /asana/api/1.0/projects/{project_gid}Workspaces
Get Multiple Workspaces
GET /asana/api/1.0/workspacesExample:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/workspaces')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFResponse:
{
"data": [
{
"gid": "1234567890",
"name": "Acme Corp",
"is_organization": true
}
]
}Get a Workspace
GET /asana/api/1.0/workspaces/{workspace_gid}Update a Workspace
PUT /asana/api/1.0/workspaces/{workspace_gid}Add User to Workspace
POST /asana/api/1.0/workspaces/{workspace_gid}/addUserRemove User from Workspace
POST /asana/api/1.0/workspaces/{workspace_gid}/removeUserUsers
Get Multiple Users
GET /asana/api/1.0/usersQuery parameters:
workspace- Workspace GID to filter users
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/users?workspace=1234567890&opt_fields=name,email')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFResponse:
{
"data": [
{
"gid": "1234567890",
"name": "Alice Johnson",
"email": "alice.johnson@acme.com"
}
]
}Get Current User
GET /asana/api/1.0/users/meGet a User
GET /asana/api/1.0/users/{user_gid}Get Users in a Team
GET /asana/api/1.0/teams/{team_gid}/usersGet Users in a Workspace
GET /asana/api/1.0/workspaces/{workspace_gid}/usersWebhooks
Get Multiple Webhooks
GET /asana/api/1.0/webhooksQuery parameters:
workspace- Workspace GID (required)resource- Resource GID to filter by
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/webhooks?workspace=1234567890')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFCreate Webhook
Note: 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"]
}
]
}
}Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'resource': '1234567890', 'target': 'https://example.com/webhook'}}).encode()
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/webhooks', 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))
EOFResponse:
{
"data": {
"gid": "1234567890",
"resource": {
"gid": "1234567890",
"name": "Q1 Project"
},
"target": "https://example.com/webhook",
"active": true
}
}Get a Webhook
GET /asana/api/1.0/webhooks/{webhook_gid}Update a Webhook
PUT /asana/api/1.0/webhooks/{webhook_gid}Delete a Webhook
DELETE /asana/api/1.0/webhooks/{webhook_gid}Returns 200 OK with empty data on success.
Pagination
Asana uses cursor-based pagination. Use offset for pagination:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/asana/api/1.0/tasks?project=1234567890&limit=50&offset=OFFSET_TOKEN')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFResponse includes next_page when more results exist:
{
"data": [...],
"next_page": {
"offset": "eyJ0eXBlIjoib2Zmc2V0IiwidmFsdWUiOjUwfQ",
"path": "/tasks?project=1234567890&limit=50&offset=eyJ0eXBlIjoib2Zmc2V0IiwidmFsdWUiOjUwfQ",
"uri": "https://app.asana.com/api/1.0/tasks?project=1234567890&limit=50&offset=eyJ0eXBlIjoib2Zmc2V0IiwidmFsdWUiOjUwfQ"
}
}Code Examples
JavaScript
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();Python
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()Notes
- Resource IDs (GIDs) are strings
- Timestamps are in ISO 8601 format
- Use
opt_fieldsto specify which fields to return - Workspaces are the highest-level organizational unit
- Organizations are specialized workspaces representing companies
- IMPORTANT: When using curl commands, use
curl -gwhen URLs contain brackets (fields[],sort[],records[]) to disable glob parsing - IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay not expand correctly in some shell environments. You may get "Invalid API key" errors when piping.
Error Handling
| 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 |
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY- Verify the API key is valid by listing connections:
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))
EOFTroubleshooting: Invalid App Name
- Ensure your URL path starts with
asana. For example:
- Correct:
https://api.maton.ai/asana/api/1.0/tasks - Incorrect:
https://api.maton.ai/api/1.0/tasks