Clockify
by @byungkyu
Clockify API integration with managed OAuth. Track time, manage projects, clients, tasks, and workspaces. Use this skill when users want to track time, creat...
clawhub install clockifyπ About This Skill
name: clockify description: | Clockify API integration with managed OAuth. Track time, manage projects, clients, tasks, and workspaces. Use this skill when users want to track time, create or manage projects, view time entries, or manage workspace members. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). compatibility: Requires network access and valid Maton API key metadata: author: maton version: "1.0" clawdbot: emoji: π§ homepage: "https://maton.ai" requires: env: - MATON_API_KEY
Clockify
Access the Clockify API with managed OAuth authentication. Track time, manage projects, clients, tasks, tags, and workspaces.
Quick Start
# Get current user
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/clockify/api/v1/user')
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://api.maton.ai/clockify/{native-api-path}
Maton proxies requests to api.clockify.me and automatically injects your credentials.
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
1. Sign in or create an account at maton.ai 2. Go to maton.ai/settings 3. Copy your API key
Connection Management
Manage your Clockify 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=clockify&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': 'clockify'}).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
Get 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))
EOF
Response:
{
"connection": {
"connection_id": "{connection_id}",
"status": "ACTIVE",
"creation_time": "2026-02-13T09:18:02.529448Z",
"last_updated_time": "2026-02-13T09:18:09.334540Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "clockify",
"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))
EOF
Specifying Connection
If you have multiple Clockify 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/clockify/api/v1/user')
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.
Security & Permissions
API Reference
User Operations
#### Get Current User
GET /clockify/api/v1/user
Response:
{
"id": "698eeb9f5cd3a921db12069f",
"email": "user@example.com",
"name": "John Doe",
"activeWorkspace": "698eeb9e5cd3a921db120693",
"defaultWorkspace": "698eeb9e5cd3a921db120693",
"status": "ACTIVE"
}
Workspace Operations
#### List Workspaces
GET /clockify/api/v1/workspaces
#### Get Workspace
GET /clockify/api/v1/workspaces/{workspaceId}
#### Create Workspace
POST /clockify/api/v1/workspaces
Content-Type: application/json{
"name": "My Workspace"
}
#### List Workspace Users
GET /clockify/api/v1/workspaces/{workspaceId}/users
Project Operations
#### List Projects
GET /clockify/api/v1/workspaces/{workspaceId}/projects
#### Get Project
GET /clockify/api/v1/workspaces/{workspaceId}/projects/{projectId}
#### Create Project
POST /clockify/api/v1/workspaces/{workspaceId}/projects
Content-Type: application/json{
"name": "My Project",
"isPublic": true,
"clientId": "optional-client-id"
}
Response:
{
"id": "698f7cba4f748f6209ea8995",
"name": "My Project",
"clientId": "",
"workspaceId": "698eeb9e5cd3a921db120693",
"billable": true,
"color": "#1976D2",
"archived": false,
"public": true
}
#### Update Project
PUT /clockify/api/v1/workspaces/{workspaceId}/projects/{projectId}
Content-Type: application/json{
"name": "Updated Project Name",
"archived": true
}
#### Delete Project
DELETE /clockify/api/v1/workspaces/{workspaceId}/projects/{projectId}
Note: You cannot delete active projects. Set archived: true first.
Client Operations
#### List Clients
GET /clockify/api/v1/workspaces/{workspaceId}/clients
#### Get Client
GET /clockify/api/v1/workspaces/{workspaceId}/clients/{clientId}
#### Create Client
POST /clockify/api/v1/workspaces/{workspaceId}/clients
Content-Type: application/json{
"name": "Acme Corp",
"address": "123 Main St",
"note": "Important client"
}
Response:
{
"id": "698f7cba0705b7d880830262",
"name": "Acme Corp",
"workspaceId": "698eeb9e5cd3a921db120693",
"archived": false,
"address": "123 Main St",
"note": "Important client"
}
#### Update Client
PUT /clockify/api/v1/workspaces/{workspaceId}/clients/{clientId}
Content-Type: application/json{
"name": "Acme Corporation"
}
#### Delete Client
DELETE /clockify/api/v1/workspaces/{workspaceId}/clients/{clientId}
Tag Operations
#### List Tags
GET /clockify/api/v1/workspaces/{workspaceId}/tags
#### Get Tag
GET /clockify/api/v1/workspaces/{workspaceId}/tags/{tagId}
#### Create Tag
POST /clockify/api/v1/workspaces/{workspaceId}/tags
Content-Type: application/json{
"name": "urgent"
}
Response:
{
"id": "698f7cbbaa9e9f33e5fc0126",
"name": "urgent",
"workspaceId": "698eeb9e5cd3a921db120693",
"archived": false
}
#### Update Tag
PUT /clockify/api/v1/workspaces/{workspaceId}/tags/{tagId}
Content-Type: application/json{
"name": "high-priority"
}
#### Delete Tag
DELETE /clockify/api/v1/workspaces/{workspaceId}/tags/{tagId}
Task Operations
#### List Tasks on Project
GET /clockify/api/v1/workspaces/{workspaceId}/projects/{projectId}/tasks
#### Get Task
GET /clockify/api/v1/workspaces/{workspaceId}/projects/{projectId}/tasks/{taskId}
#### Create Task
POST /clockify/api/v1/workspaces/{workspaceId}/projects/{projectId}/tasks
Content-Type: application/json{
"name": "Implement feature",
"assigneeIds": ["user-id-1"],
"estimate": "PT2H",
"billable": true
}
Response:
{
"id": "698f7cc4aa9e9f33e5fc017b",
"name": "Implement feature",
"projectId": "698f7cba4f748f6209ea8995",
"assigneeIds": [],
"estimate": "PT0S",
"status": "ACTIVE",
"billable": true
}
#### Update Task
PUT /clockify/api/v1/workspaces/{workspaceId}/projects/{projectId}/tasks/{taskId}
Content-Type: application/json{
"name": "Updated task name",
"status": "DONE"
}
#### Delete Task
DELETE /clockify/api/v1/workspaces/{workspaceId}/projects/{projectId}/tasks/{taskId}
Note: You cannot delete active tasks. Set status: "DONE" first.
Time Entry Operations
#### Get User's Time Entries
GET /clockify/api/v1/workspaces/{workspaceId}/user/{userId}/time-entries
Response:
[
{
"id": "698f7cc4aa9e9f33e5fc0180",
"description": "Working on project",
"userId": "698eeb9f5cd3a921db12069f",
"billable": true,
"projectId": "698f7cba4f748f6209ea8995",
"taskId": null,
"workspaceId": "698eeb9e5cd3a921db120693",
"timeInterval": {
"start": "2026-02-13T18:34:28Z",
"end": "2026-02-13T19:34:28Z",
"duration": "PT1H"
}
}
]
#### Create Time Entry
POST /clockify/api/v1/workspaces/{workspaceId}/time-entries
Content-Type: application/json{
"start": "2026-02-13T09:00:00Z",
"end": "2026-02-13T10:00:00Z",
"description": "Team meeting",
"projectId": "project-id",
"taskId": "task-id",
"tagIds": ["tag-id-1", "tag-id-2"],
"billable": true
}
#### Create Time Entry for Another User
POST /clockify/api/v1/workspaces/{workspaceId}/user/{userId}/time-entries
Content-Type: application/json{
"start": "2026-02-13T09:00:00Z",
"end": "2026-02-13T10:00:00Z",
"description": "Team meeting"
}
#### Get Time Entry
GET /clockify/api/v1/workspaces/{workspaceId}/time-entries/{timeEntryId}
#### Update Time Entry
PUT /clockify/api/v1/workspaces/{workspaceId}/time-entries/{timeEntryId}
Content-Type: application/json{
"start": "2026-02-13T09:00:00Z",
"end": "2026-02-13T11:00:00Z",
"description": "Extended meeting"
}
#### Delete Time Entry
DELETE /clockify/api/v1/workspaces/{workspaceId}/time-entries/{timeEntryId}
#### Stop Running Timer
PATCH /clockify/api/v1/workspaces/{workspaceId}/user/{userId}/time-entries
Content-Type: application/json{
"end": "2026-02-13T17:00:00Z"
}
#### Get In-Progress Time Entries
GET /clockify/api/v1/workspaces/{workspaceId}/time-entries
Pagination
Clockify uses page-based pagination:
GET /clockify/api/v1/workspaces/{workspaceId}/projects?page=1&page-size=50
Query Parameters:
page - Page number (1-indexed, default: 1)page-size - Items per page (default varies by endpoint)Response includes a Last-Page header indicating if there are more pages.
Code Examples
JavaScript
const response = await fetch(
'https://api.maton.ai/clockify/api/v1/workspaces',
{
headers: {
'Authorization': Bearer ${process.env.MATON_API_KEY}
}
}
);
const workspaces = await response.json();
Python
import os
import requestsresponse = requests.get(
'https://api.maton.ai/clockify/api/v1/workspaces',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
workspaces = response.json()
Create Time Entry (Python)
import os
import requests
from datetime import datetime, timedelta, timezoneworkspace_id = "your-workspace-id"
start_time = (datetime.now(timezone.utc) - timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
end_time = datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z')
response = requests.post(
f'https://api.maton.ai/clockify/api/v1/workspaces/{workspace_id}/time-entries',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'start': start_time,
'end': end_time,
'description': 'Working on feature'
}
)
Notes
2026-02-13T09:00:00Z)PT1H for 1 hour, PT30M for 30 minutes)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 environmentsError Handling
| Status | Meaning | |--------|---------| | 400 | Missing Clockify connection or invalid request | | 401 | Invalid or missing Maton API key | | 403 | Insufficient permissions | | 404 | Resource not found | | 429 | Rate limited (50 req/sec per workspace) | | 4xx/5xx | Passthrough error from Clockify API |
Troubleshooting: API Key Issues
1. Check that the MATON_API_KEY environment variable is set:
echo $MATON_API_KEY
2. 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))
EOF
Troubleshooting: Invalid App Name
1. Ensure your URL path starts with clockify. For example:
https://api.maton.ai/clockify/api/v1/userhttps://api.maton.ai/api/v1/userResources
π‘ Examples
# Get current user
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/clockify/api/v1/user')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
π Tips & Best Practices
2026-02-13T09:00:00Z)PT1H for 1 hour, PT30M for 30 minutes)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