Install
openclaw skills install motionMotion API integration with managed OAuth. Manage tasks, projects, workspaces, and more with AI-powered scheduling. Use this skill when users want to create, update, or manage tasks and projects in Motion, or query their scheduled work. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
openclaw skills install motionAccess the Motion API with managed OAuth authentication. Manage tasks, projects, workspaces, comments, and recurring tasks with full CRUD operations.
# List tasks
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/motion/v1/tasks')
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/motion/{native-api-path}
Maton proxies requests to api.usemotion.com and automatically injects your OAuth token.
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Manage your Motion OAuth connections at https://api.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=motion&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
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'motion'}).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
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": "motion",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
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 Motion 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/motion/v1/tasks')
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 include this header to ensure requests go to the intended account.
GET /motion/v1/tasks
Query Parameters:
workspaceId (string) - Filter by workspaceprojectId (string) - Filter by projectassigneeId (string) - Filter by assigneestatus (array) - Filter by status (cannot combine with includeAllStatuses)includeAllStatuses (boolean) - Return tasks across all statuseslabel (string) - Filter by labelname (string) - Search task names (case-insensitive)cursor (string) - Pagination cursorExample:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/motion/v1/tasks?workspaceId=WORKSPACE_ID')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
GET /motion/v1/tasks/{taskId}
POST /motion/v1/tasks
Content-Type: application/json
{
"name": "Task name",
"workspaceId": "WORKSPACE_ID",
"dueDate": "2024-03-15T10:00:00Z",
"duration": 60,
"priority": "HIGH",
"description": "Task description in markdown",
"projectId": "PROJECT_ID",
"assigneeId": "USER_ID",
"labels": ["label1", "label2"],
"autoScheduled": {
"startDate": "2024-03-14T09:00:00Z",
"deadlineType": "SOFT",
"schedule": "Work Hours"
}
}
Required Fields:
name (string) - Task titleworkspaceId (string) - Workspace IDOptional Fields:
dueDate (datetime, ISO 8601) - Task deadline (required for scheduled tasks)duration (string | number) - "NONE", "REMINDER", or minutes (integer > 0)status (string) - Defaults to workspace default statusprojectId (string) - Associated projectdescription (string) - GitHub Flavored Markdown supportedpriority (string) - ASAP, HIGH, MEDIUM, or LOWlabels (array) - Label names to addassigneeId (string) - User ID for task assignmentautoScheduled (object) - Auto-scheduling settings with startDate, deadlineType (HARD, SOFT, NONE), and scheduleExample:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({
'name': 'New task',
'workspaceId': 'WORKSPACE_ID',
'priority': 'HIGH',
'duration': 30
}).encode()
req = urllib.request.Request('https://api.maton.ai/motion/v1/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))
EOF
PATCH /motion/v1/tasks/{taskId}
Content-Type: application/json
{
"name": "Updated task name",
"status": "Completed",
"priority": "LOW"
}
DELETE /motion/v1/tasks/{taskId}
POST /motion/v1/tasks/{taskId}/move
Content-Type: application/json
{
"workspaceId": "NEW_WORKSPACE_ID"
}
POST /motion/v1/tasks/{taskId}/unassign
GET /motion/v1/projects?workspaceId={workspaceId}
Query Parameters:
workspaceId (string, required) - Workspace IDcursor (string) - Pagination cursorGET /motion/v1/projects/{projectId}
POST /motion/v1/projects
Content-Type: application/json
{
"name": "Project name",
"workspaceId": "WORKSPACE_ID",
"description": "Project description",
"dueDate": "2024-06-30T00:00:00Z",
"priority": "HIGH",
"labels": ["label1"]
}
Required Fields:
name (string) - Project nameworkspaceId (string) - Workspace IDOptional Fields:
dueDate (datetime, ISO 8601) - Project deadlinedescription (string) - HTML input acceptedlabels (array) - Label namespriority (string) - ASAP, HIGH, MEDIUM (default), or LOWprojectDefinitionId (string) - Template ID (requires stages array if provided)stages (array) - Stage objects for project templatesGET /motion/v1/workspaces
GET /motion/v1/users?workspaceId={workspaceId}
Query Parameters:
workspaceId (string) - Workspace ID (required if no teamId)teamId (string) - Team ID (required if no workspaceId)Note: You must provide either workspaceId or teamId.
GET /motion/v1/users/me
GET /motion/v1/comments?taskId={taskId}
Query Parameters:
taskId (string, required) - Filter comments by taskcursor (string) - Pagination cursorPOST /motion/v1/comments
Content-Type: application/json
{
"taskId": "TASK_ID",
"content": "Comment in GitHub Flavored Markdown"
}
Required Fields:
taskId (string) - Task to comment onOptional Fields:
content (string) - Comment content in GitHub Flavored MarkdownGET /motion/v1/recurring-tasks?workspaceId={workspaceId}
Query Parameters:
workspaceId (string, required) - Filter by workspacecursor (string) - Pagination cursorPOST /motion/v1/recurring-tasks
Content-Type: application/json
{
"name": "Weekly review",
"workspaceId": "WORKSPACE_ID",
"frequency": "weekly"
}
DELETE /motion/v1/recurring-tasks/{recurringTaskId}
GET /motion/v1/schedules
GET /motion/v1/statuses?workspaceId={workspaceId}
Query Parameters:
workspaceId (string, required) - Filter by workspaceGET /motion/v1/custom-fields
POST /motion/v1/custom-fields
Content-Type: application/json
{
"name": "Field name",
"type": "text"
}
DELETE /motion/v1/custom-fields/{customFieldId}
POST /motion/v1/custom-fields/{customFieldId}/project
Content-Type: application/json
{
"projectId": "PROJECT_ID"
}
POST /motion/v1/custom-fields/{customFieldId}/task
Content-Type: application/json
{
"taskId": "TASK_ID"
}
DELETE /motion/v1/custom-fields/{customFieldId}/project
DELETE /motion/v1/custom-fields/{customFieldId}/task
Motion uses cursor-based pagination:
GET /motion/v1/tasks?cursor=CURSOR_VALUE
Response includes pagination metadata:
{
"tasks": [...],
"meta": {
"nextCursor": "abc123",
"pageSize": 20
}
}
Use the nextCursor value in subsequent requests to retrieve more results.
const response = await fetch(
'https://api.maton.ai/motion/v1/tasks',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
import os
import requests
response = requests.get(
'https://api.maton.ai/motion/v1/tasks',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
data = response.json()
curl -g when URLs contain brackets to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments| Status | Meaning |
|---|---|
| 400 | Missing Motion connection or invalid request |
| 401 | Invalid or missing Maton API key |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Motion API |