# Clockify Workspace API Guide
## Overview
The Workspace API manages workspace creation, configuration, user membership, and billing rates within Clockify.
## Authentication
- `X-Api-Key`: Personal API key
- `X-Addon-Token`: For addon authentication
## Base URL
```
https://api.clockify.me/api/v1
```
---
## Endpoints
### 1. Get All My Workspaces
**Method:** `GET`
**Path:** `/v1/workspaces`
**Purpose:** Retrieve all workspaces the authenticated user has access to
#### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| roles | string | No | Filter by roles (repeatable): WORKSPACE_ADMIN, OWNER, TEAM_MANAGER, PROJECT_MANAGER |
#### Request Example
```http
GET /v1/workspaces?roles=WORKSPACE_ADMIN&roles=OWNER
X-Api-Key: YOUR_API_KEY
```
#### Response (200 OK)
```json
[
{
"cakeOrganizationId": "67d471fb56aa9668b7bfa295",
"costRate": {
"amount": 10500,
"currency": "USD"
},
"currencies": [
{
"code": "USD",
"id": "5b641568b07987035750505e",
"isDefault": true
}
],
"featureSubscriptionType": "PREMIUM",
"features": [
"ADD_TIME_FOR_OTHERS",
"ADMIN_PANEL",
"ALERTS",
"APPROVAL"
],
"hourlyRate": {
"amount": 10500,
"currency": "USD"
},
"id": "64a687e29ae1f428e7ebe303",
"imageUrl": "https://www.url.com/imageurl-1234567890.jpg",
"memberships": [
{
"costRate": {
"amount": 10500,
"currency": "USD"
},
"hourlyRate": {
"amount": 10500,
"currency": "USD"
},
"membershipStatus": "PENDING",
"membershipType": "PROJECT",
"targetId": "64c777ddd3fcab07cfbb210c",
"userId": "5a0ab5acb07987125438b60f"
}
],
"name": "Cool Company",
"subdomain": {
"enabled": true,
"name": "coolcompany"
},
"workspaceSettings": {
"activeBillableHours": true,
"adminOnlyPages": "[\"PROJECT\",\"TEAM\",\"REPORTS\"]",
"automaticLock": {
"changeDay": "FRIDAY",
"dayOfMonth": 15,
"firstDay": "MONDAY",
"olderThanPeriod": "DAYS",
"olderThanValue": 5,
"type": "WEEKLY"
},
"canSeeTimeSheet": true,
"canSeeTracker": true,
"currencyFormat": "CURRENCY_SPACE_VALUE",
"defaultBillableProjects": true,
"durationFormat": "FULL",
"entityCreationPermissions": {
"whoCanCreateProjectsAndClients": "EVERYONE",
"whoCanCreateTags": "EVERYONE",
"whoCanCreateTasks": "EVERYONE"
},
"forceDescription": true,
"forceProjects": true,
"forceTags": true,
"forceTasks": true,
"isProjectPublicByDefault": true,
"lockTimeEntries": "2024-02-25T23:00:00Z",
"lockTimeZone": "Europe/Belgrade",
"multiFactorEnabled": true,
"numberFormat": "COMMA_PERIOD",
"onlyAdminsCanChangeBillableStatus": true,
"onlyAdminsCreateProject": true,
"onlyAdminsCreateTag": true,
"onlyAdminsCreateTask": true,
"onlyAdminsSeeAllTimeEntries": true,
"onlyAdminsSeeBillableRates": true,
"onlyAdminsSeeDashboard": true,
"onlyAdminsSeePublicProjectsEntries": true,
"projectFavorites": true,
"projectGroupingLabel": "Project Label",
"projectLabel": "string",
"projectPickerSpecialFilter": true,
"round": {
"minutes": "15",
"round": "Round to nearest"
},
"taskLabel": "string",
"timeRoundingInReports": true,
"timeTrackingMode": "DEFAULT",
"trackTimeDownToSecond": true,
"workingDays": ["MONDAY"]
}
}
]
```
---
### 2. Add Workspace
**Method:** `POST`
**Path:** `/v1/workspaces`
**Purpose:** Create a new workspace
#### Request Body
```json
{
"name": "Cool Company",
"organizationId": "67d471fb56aa9668b7bfa295"
}
```
#### Field Constraints
- **name**: 1-50 characters, required
- **organizationId**: CAKE organization identifier, required
#### Response (201 Created)
Returns complete workspace object (same structure as Get All My Workspaces)
#### Implementation Note
- Creates workspace associated with CAKE organization
- User becomes workspace owner automatically
---
### 3. Get Workspace Info
**Method:** `GET`
**Path:** `/v1/workspaces/{workspaceId}`
**Purpose:** Retrieve detailed information about a specific workspace
#### Path Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| workspaceId | string | Yes | Workspace identifier |
#### Request Example
```http
GET /v1/workspaces/64a687e29ae1f428e7ebe303
X-Api-Key: YOUR_API_KEY
```
#### Response (200 OK)
Same structure as Get All My Workspaces response (single workspace object)
---
### 4. Update Workspace Cost Rate
**Method:** `PUT`
**Path:** `/v1/workspaces/{workspaceId}/cost-rate`
**Purpose:** Set or update the default cost rate for the workspace
#### Request Body
```json
{
"amount": 20000,
"since": "2020-01-01T00:00:00Z"
}
```
#### Field Definitions
- **amount**: Cost rate in cents (20000 = $200.00), required, minimum 0
- **since**: Optional datetime when this rate becomes effective (ISO-8601 format)
#### Response (200 OK)
Returns updated workspace object
#### Implementation Notes
- Amount is stored in smallest currency unit (cents for USD)
- If `since` is provided, rate applies from that date forward
- Affects cost calculations for time entries
---
### 5. Update Workspace Billable Rate
**Method:** `PUT`
**Path:** `/v1/workspaces/{workspaceId}/hourly-rate`
**Purpose:** Set or update the default billable hourly rate for the workspace
#### Request Body
```json
{
"amount": 2000,
"currency": "USD",
"since": "2020-01-01T00:00:00Z"
}
```
#### Field Definitions
- **amount**: Hourly rate in cents, required, minimum 0
- **currency**: 1-100 characters, defaults to "USD", required
- **since**: Optional datetime for rate effective date
#### Response (200 OK)
Returns updated workspace object
#### Currency Support
- Must match one of the workspace's configured currencies
- Validates against workspace currency list
---
### 6. Add User to Workspace
**Method:** `POST`
**Path:** `/v1/workspaces/{workspaceId}/users`
**Purpose:** Invite a new user to the workspace
#### Prerequisites
- Workspace must have a paid subscription
- Must have available paid user seats
- User must not already be a member
#### Query Parameters
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| send-email | string | Yes | "true" | Send invitation email to user |
#### Request Body
```json
{
"email": "johndoe@example.com"
}
```
#### Request Example
```http
POST /v1/workspaces/64a687e29ae1f428e7ebe303/users?send-email=true
X-Api-Key: YOUR_API_KEY
Content-Type: application/json
{
"email": "johndoe@example.com"
}
```
#### Response (200 OK)
Returns updated workspace object
#### Error Scenarios
- 402: Workspace doesn't have paid subscription
- 403: No available user seats
- 409: User already exists in workspace
---
### 7. Update User Status on Workspace
**Method:** `PUT`
**Path:** `/v1/workspaces/{workspaceId}/users/{userId}`
**Purpose:** Activate or deactivate a user's membership in the workspace
#### Path Parameters
- **workspaceId**: Workspace identifier
- **userId**: User identifier (different from authenticated user)
#### Request Body
```json
{
"status": "ACTIVE"
}
```
#### Status Values
- **ACTIVE**: User can access workspace and track time
- **INACTIVE**: User cannot access workspace (deactivated)
#### Response (200 OK)
Returns updated workspace object
#### Use Cases
- Temporarily deactivate users who are on leave
- Reactivate returning users
- Manage seat allocation
---
### 8. Update User Cost Rate on Workspace
**Method:** `PUT`
**Path:** `/v1/workspaces/{workspaceId}/users/{userId}/cost-rate`
**Purpose:** Set user-specific cost rate (overrides workspace default)
#### Request Body
```json
{
"amount": 20000,
"since": "2020-01-01T00:00:00Z"
}
```
#### Field Definitions
- **amount**: Cost rate in cents, required, minimum 0
- **since**: Optional effective date
#### Response (200 OK)
Returns updated workspace object
#### Rate Hierarchy
1. User-specific cost rate (this endpoint)
2. Workspace default cost rate
3. No cost rate (if neither set)
---
### 9. Update User Hourly Rate on Workspace
**Method:** `PUT`
**Path:** `/v1/workspaces/{workspaceId}/users/{userId}/hourly-rate`
**Purpose:** Set user-specific billable hourly rate
#### Request Body
```json
{
"amount": 20000,
"since": "2020-01-01T00:00:00Z"
}
```
#### Field Definitions
- **amount**: Hourly rate in cents, required, minimum 0
- **since**: Optional effective date
#### Response (200 OK)
Returns updated workspace object
#### Rate Hierarchy
1. Task-specific rate (highest priority)
2. Project-specific user rate
3. User-specific hourly rate (this endpoint)
4. Workspace default hourly rate
5. No billable rate (if none set)
---
## Data Models
### Workspace Object
```typescript
{
cakeOrganizationId: string;
costRate: Rate;
currencies: Currency[];
featureSubscriptionType: "FREE" | "BASIC" | "PREMIUM" | "ENTERPRISE";
features: string[]; // Feature flags
hourlyRate: Rate;
id: string;
imageUrl: string;
memberships: Membership[];
name: string;
subdomain: Subdomain;
workspaceSettings: WorkspaceSettings;
}
```
### WorkspaceSettings Object
```typescript
{
activeBillableHours: boolean;
adminOnlyPages: string; // JSON array of page names
automaticLock: AutomaticLock;
canSeeTimeSheet: boolean;
canSeeTracker: boolean;
currencyFormat: "CURRENCY_SPACE_VALUE" | "VALUE_SPACE_CURRENCY";
defaultBillableProjects: boolean;
durationFormat: "FULL" | "COMPACT" | "DECIMAL";
entityCreationPermissions: EntityCreationPermissions;
forceDescription: boolean;
forceProjects: boolean;
forceTags: boolean;
forceTasks: boolean;
isProjectPublicByDefault: boolean;
lockTimeEntries: string; // ISO-8601 datetime
lockTimeZone: string; // IANA timezone
multiFactorEnabled: boolean;
numberFormat: "COMMA_PERIOD" | "PERIOD_COMMA" | "SPACE_PERIOD" | "SPACE_COMMA";
onlyAdminsCanChangeBillableStatus: boolean;
onlyAdminsCreateProject: boolean;
onlyAdminsCreateTag: boolean;
onlyAdminsCreateTask: boolean;
onlyAdminsSeeAllTimeEntries: boolean;
onlyAdminsSeeBillableRates: boolean;
onlyAdminsSeeDashboard: boolean;
onlyAdminsSeePublicProjectsEntries: boolean;
projectFavorites: boolean;
projectGroupingLabel: string;
projectLabel: string;
projectPickerSpecialFilter: boolean;
round: RoundSettings;
taskLabel: string;
timeRoundingInReports: boolean;
timeTrackingMode: "DEFAULT" | "STOPWATCH_ONLY" | "MANUAL_ONLY";
trackTimeDownToSecond: boolean;
workingDays: string[]; // Day names
}
```
### AutomaticLock Object
```typescript
{
changeDay: "MONDAY" | "TUESDAY" | ... | "SUNDAY";
dayOfMonth: number; // 1-31
firstDay: "MONDAY" | "TUESDAY" | ... | "SUNDAY";
olderThanPeriod: "DAYS" | "WEEKS" | "MONTHS";
olderThanValue: number;
type: "WEEKLY" | "MONTHLY" | "OLDER_THAN";
}
```
### EntityCreationPermissions Object
```typescript
{
whoCanCreateProjectsAndClients: "EVERYONE" | "ADMINS";
whoCanCreateTags: "EVERYONE" | "ADMINS";
whoCanCreateTasks: "EVERYONE" | "ADMINS";
}
```
### RoundSettings Object
```typescript
{
minutes: string; // "15", "30", "60", etc.
round: string; // "Round to nearest", "Round up", "Round down"
}
```
### Currency Object
```typescript
{
code: string; // ISO currency code
id: string;
isDefault: boolean;
}
```
### Subdomain Object
```typescript
{
enabled: boolean;
name: string; // Subdomain name (e.g., "coolcompany" for coolcompany.clockify.me)
}
```
---
## Feature Flags
Common workspace features include:
- `ADD_TIME_FOR_OTHERS`: Can add time for other users
- `ADMIN_PANEL`: Access to admin panel
- `ALERTS`: Time tracking alerts enabled
- `APPROVAL`: Time entry approval workflow
- `EXPENSES`: Expense tracking
- `INVOICING`: Invoice generation
- `PROJECTS`: Project management
- `SCHEDULING`: Resource scheduling
- `REPORTS`: Advanced reporting
- `TIME_OFF`: Time off/PTO management
---
## Implementation Examples
### Python: Get All Workspaces
```python
import requests
API_KEY = "your_api_key_here"
BASE_URL = "https://api.clockify.me/api/v1"
headers = {
"X-Api-Key": API_KEY,
"Content-Type": "application/json"
}
def get_all_workspaces():
response = requests.get(f"{BASE_URL}/workspaces", headers=headers)
return response.json()
workspaces = get_all_workspaces()
for ws in workspaces:
print(f"{ws['name']} ({ws['id']})")
```
### Python: Create Workspace
```python
def create_workspace(name, org_id):
url = f"{BASE_URL}/workspaces"
payload = {
"name": name,
"organizationId": org_id
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 201:
return response.json()
else:
raise Exception(f"Failed to create workspace: {response.text}")
new_workspace = create_workspace("My New Workspace", "org_id_here")
```
### Python: Add User to Workspace
```python
def add_user_to_workspace(workspace_id, email, send_email=True):
url = f"{BASE_URL}/workspaces/{workspace_id}/users"
params = {"send-email": str(send_email).lower()}
payload = {"email": email}
response = requests.post(url, headers=headers, params=params, json=payload)
if response.status_code == 200:
return response.json()
elif response.status_code == 402:
raise Exception("Workspace requires paid subscription")
elif response.status_code == 403:
raise Exception("No available user seats")
elif response.status_code == 409:
raise Exception("User already exists in workspace")
else:
raise Exception(f"Failed to add user: {response.text}")
```
### Python: Update Rates
```python
from datetime import datetime
def update_workspace_hourly_rate(workspace_id, amount_cents, currency="USD", since=None):
url = f"{BASE_URL}/workspaces/{workspace_id}/hourly-rate"
payload = {
"amount": amount_cents,
"currency": currency
}
if since:
payload["since"] = since.isoformat() + "Z"
response = requests.put(url, headers=headers, json=payload)
return response.json()
def update_user_cost_rate(workspace_id, user_id, amount_cents, since=None):
url = f"{BASE_URL}/workspaces/{workspace_id}/users/{user_id}/cost-rate"
payload = {"amount": amount_cents}
if since:
payload["since"] = since.isoformat() + "Z"
response = requests.put(url, headers=headers, json=payload)
return response.json()
# Set workspace hourly rate to $150/hour starting Jan 1, 2024
update_workspace_hourly_rate(
"workspace_id",
15000, # $150.00
since=datetime(2024, 1, 1)
)
# Set user cost rate to $75/hour
update_user_cost_rate("workspace_id", "user_id", 7500)
```
### Python: Deactivate/Reactivate User
```python
def set_user_status(workspace_id, user_id, active=True):
url = f"{BASE_URL}/workspaces/{workspace_id}/users/{user_id}"
status = "ACTIVE" if active else "INACTIVE"
payload = {"status": status}
response = requests.put(url, headers=headers, json=payload)
return response.json()
# Deactivate user
set_user_status("workspace_id", "user_id", active=False)
# Reactivate user
set_user_status("workspace_id", "user_id", active=True)
```
---
## Best Practices
1. **Subscription Check**: Always verify workspace has paid subscription before adding users
2. **Rate Management**: Use hierarchical rates (workspace → user → project → task) strategically
3. **Seat Management**: Monitor user seat count before adding new users
4. **Currency Consistency**: Ensure currency codes match workspace configuration
5. **Date Handling**: Always use ISO-8601 format with timezone (Z for UTC)
6. **Feature Checks**: Verify workspace has required features before attempting operations
7. **Error Handling**: Implement proper error handling for subscription/seat-related errors
8. **Subdomain Usage**: Use subdomain URLs when workspace has subdomain enabled
---
## Common Use Cases
### Onboarding New Organization
```python
def onboard_organization(org_id, workspace_name, team_emails):
# Create workspace
workspace = create_workspace(workspace_name, org_id)
workspace_id = workspace['id']
# Set default rates
update_workspace_hourly_rate(workspace_id, 10000) # $100/hr
# Add team members
for email in team_emails:
try:
add_user_to_workspace(workspace_id, email)
except Exception as e:
print(f"Failed to add {email}: {e}")
return workspace
```
### Bulk Rate Update
```python
def update_all_user_rates(workspace_id, rate_cents, effective_date):
# Get all users
users_response = requests.get(
f"{BASE_URL}/workspaces/{workspace_id}/users",
headers=headers,
params={"status": "ACTIVE"}
)
users = users_response.json()
# Update each user's rate
for user in users:
update_user_cost_rate(
workspace_id,
user['id'],
rate_cents,
since=effective_date
)
```
---
## Rate Limiting
- Addon token: 50 requests/second per workspace
- Exceeding limit returns "Too many requests" error
---
## Notes
- Workspace settings are extensive and affect time tracking behavior
- Rate amounts are always in smallest currency unit (cents for USD)
- Automatic lock settings prevent editing of old time entries
- Entity creation permissions control who can create projects, tasks, tags
- Feature availability depends on subscription type
- Subdomain workspaces use different URL structure for reports API