Motion
Verified@byungkyu
npx machina-cli add skill @byungkyu/motion --openclawMotion
Access the Motion API with managed OAuth authentication. Manage tasks, projects, workspaces, comments, and recurring tasks with full CRUD operations.
Quick Start
# List tasks
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.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
Base URL
https://gateway.maton.ai/motion/{native-api-path}
Replace {native-api-path} with the actual Motion API endpoint path. The gateway proxies requests to api.usemotion.com and automatically injects your OAuth token.
Authentication
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"
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 Motion OAuth connections at https://ctrl.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.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
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'motion'}).encode()
req = urllib.request.Request('https://ctrl.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
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.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": "21fd90f9-5935-43cd-b6c8-bde9d915ca80",
"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.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.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
Specifying Connection
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://gateway.maton.ai/motion/v1/tasks')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '21fd90f9-5935-43cd-b6c8-bde9d915ca80')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
Task Operations
List Tasks
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 withincludeAllStatuses)includeAllStatuses(boolean) - Return tasks across all statuseslabel(string) - Filter by labelname(string) - Search task names (case-insensitive)cursor(string) - Pagination cursor
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.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 Task
GET /motion/v1/tasks/{taskId}
Create Task
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 ID
Optional 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 withstartDate,deadlineType(HARD, SOFT, NONE), andschedule
Example:
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://gateway.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
Update Task
PATCH /motion/v1/tasks/{taskId}
Content-Type: application/json
{
"name": "Updated task name",
"status": "Completed",
"priority": "LOW"
}
Delete Task
DELETE /motion/v1/tasks/{taskId}
Move Task
POST /motion/v1/tasks/{taskId}/move
Content-Type: application/json
{
"workspaceId": "NEW_WORKSPACE_ID"
}
Unassign Task
POST /motion/v1/tasks/{taskId}/unassign
Project Operations
List Projects
GET /motion/v1/projects?workspaceId={workspaceId}
Query Parameters:
workspaceId(string, required) - Workspace IDcursor(string) - Pagination cursor
Get Project
GET /motion/v1/projects/{projectId}
Create Project
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 ID
Optional Fields:
dueDate(datetime, ISO 8601) - Project deadlinedescription(string) - HTML input acceptedlabels(array) - Label namespriority(string) - ASAP, HIGH, MEDIUM (default), or LOWprojectDefinitionId(string) - Template ID (requiresstagesarray if provided)stages(array) - Stage objects for project templates
Workspace Operations
List Workspaces
GET /motion/v1/workspaces
User Operations
List Users
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 Current User
GET /motion/v1/users/me
Comment Operations
List Comments
GET /motion/v1/comments?taskId={taskId}
Query Parameters:
taskId(string, required) - Filter comments by taskcursor(string) - Pagination cursor
Create Comment
POST /motion/v1/comments
Content-Type: application/json
{
"taskId": "TASK_ID",
"content": "Comment in GitHub Flavored Markdown"
}
Required Fields:
taskId(string) - Task to comment on
Optional Fields:
content(string) - Comment content in GitHub Flavored Markdown
Recurring Task Operations
List Recurring Tasks
GET /motion/v1/recurring-tasks?workspaceId={workspaceId}
Query Parameters:
workspaceId(string, required) - Filter by workspacecursor(string) - Pagination cursor
Create Recurring Task
POST /motion/v1/recurring-tasks
Content-Type: application/json
{
"name": "Weekly review",
"workspaceId": "WORKSPACE_ID",
"frequency": "weekly"
}
Delete Recurring Task
DELETE /motion/v1/recurring-tasks/{recurringTaskId}
Schedule Operations
List Schedules
GET /motion/v1/schedules
Status Operations
List Statuses
GET /motion/v1/statuses?workspaceId={workspaceId}
Query Parameters:
workspaceId(string, required) - Filter by workspace
Custom Field Operations
List Custom Fields
GET /motion/v1/custom-fields
Create Custom Field
POST /motion/v1/custom-fields
Content-Type: application/json
{
"name": "Field name",
"type": "text"
}
Delete Custom Field
DELETE /motion/v1/custom-fields/{customFieldId}
Add Custom Field to Project
POST /motion/v1/custom-fields/{customFieldId}/project
Content-Type: application/json
{
"projectId": "PROJECT_ID"
}
Add Custom Field to Task
POST /motion/v1/custom-fields/{customFieldId}/task
Content-Type: application/json
{
"taskId": "TASK_ID"
}
Remove Custom Field from Project
DELETE /motion/v1/custom-fields/{customFieldId}/project
Remove Custom Field from Task
DELETE /motion/v1/custom-fields/{customFieldId}/task
Pagination
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.
Code Examples
JavaScript
const response = await fetch(
'https://gateway.maton.ai/motion/v1/tasks',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
Python
import os
import requests
response = requests.get(
'https://gateway.maton.ai/motion/v1/tasks',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
data = response.json()
Notes
- All timestamps use ISO 8601 format
- Task descriptions support GitHub Flavored Markdown
- Project descriptions accept HTML input
- Priority values: ASAP, HIGH, MEDIUM, LOW
- Deadline types for auto-scheduling: HARD, SOFT, NONE
- Rate limits: 12 req/min (Individual), 120 req/min (Team)
- IMPORTANT: When using curl commands, use
curl -gwhen URLs contain brackets 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
Error Handling
| 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 |
Resources
Overview
Motion API integrates with managed OAuth to let you create, read, update, and delete tasks, projects, workspaces, and more. It supports CRuD operations and scheduled queries via a gateway that injects OAuth tokens, making secure automation for Motion seamless.
How This Skill Works
Requests target the Motion gateway at https://gateway.maton.ai/motion/{native-api-path} with an Authorization: Bearer MATON_API_KEY header. The gateway proxies to api.usemotion.com and handles token injection. When multiple Motion connections exist, specify the desired account with the Maton-Connection header to route requests to the correct OAuth session.
When to Use It
- Create, update, or delete tasks and projects in Motion via the API.
- Query scheduled work and retrieve timelines across workspaces.
- Manage workspaces and recurring tasks using full CRUD operations.
- Integrate Motion into backend services with managed OAuth via the gateway.
- Specify the correct Motion connection using Maton-Connection when multiple connections exist.
Quick Start
- Step 1: Export your API key and set MATON_API_KEY in your environment (e.g., export MATON_API_KEY="YOUR_API_KEY").
- Step 2: Call a Motion endpoint through the gateway, for example: curl -H "Authorization: Bearer $MATON_API_KEY" https://gateway.maton.ai/motion/v1/tasks.
- Step 3: Replace {native-api-path} in the Base URL with your target endpoint path and handle the JSON response.
Best Practices
- Always include Authorization: Bearer $MATON_API_KEY in every request.
- Use the gateway base URL and substitute {native-api-path} with the target Motion endpoint.
- Securely store and rotate MATON_API_KEY; use environment variables like MATON_API_KEY.
- Use the Maton-Connection header to select the correct Motion connection when multiple connections exist.
- Handle responses and errors gracefully, and paginate list endpoints as needed.
Example Use Cases
- List tasks via GET https://gateway.maton.ai/motion/v1/tasks with Authorization header and MATON_API_KEY.
- Create a new task by POSTing to https://gateway.maton.ai/motion/v1/tasks with task payload and MATON_API_KEY.
- Update a task or project via PUT/PATCH to /motion/v1/tasks/{id} or /motion/v1/projects/{id}.
- List active connections for Motion with GET https://ctrl.maton.ai/connections?app=motion&status=ACTIVE.
- Get a specific connection by ID to inspect status and metadata.