# Clockify User API Guide
## Overview
The User API provides endpoints for managing user information, profiles, photos, custom fields, and manager relationships within Clockify workspaces.
## Authentication
All endpoints require authentication via header:
- `X-Api-Key`: Your personal API key
- `X-Addon-Token`: For addon authentication
## Base URL Structure
```
Global: https://api.clockify.me/api/v1/
Regional: https://{prefix}.clockify.me/api/v1/
Prefixes: euc1 (EU), use2 (USA), euw2 (UK), apse2 (AU)
```
---
## Endpoints
### 1. Upload User Photo
**Method:** `POST`
**Path:** `/v1/file/image`
**Purpose:** Upload a profile picture for the user
#### Request
```http
POST /v1/file/image
Content-Type: multipart/form-data
file: <binary image file>
```
#### Response (200 OK)
```json
{
"name": "image-01234567.jpg",
"url": "https://clockify.com/image-01234567.jpg"
}
```
#### Implementation Notes
- Accepts image files in binary format
- Returns URL that can be used in profile updates
- Image is stored on Clockify servers
---
### 2. Get Current User Info
**Method:** `GET`
**Path:** `/v1/user`
**Purpose:** Retrieve information about the currently logged-in user
#### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| include-memberships | boolean | No | Include membership details in response |
#### Request Example
```http
GET /v1/user?include-memberships=true
X-Api-Key: YOUR_API_KEY
```
#### Response (200 OK)
```json
{
"activeWorkspace": "64a687e29ae1f428e7ebe303",
"customFields": [
{
"customFieldId": "5e4117fe8c625f38930d57b7",
"customFieldName": "TIN",
"customFieldType": "TXT",
"userId": "5a0ab5acb07987125438b60f",
"value": "20231211-12345"
}
],
"defaultWorkspace": "64a687e29ae1f428e7ebe303",
"email": "johndoe@example.com",
"id": "5a0ab5acb07987125438b60f",
"memberships": [
{
"costRate": {
"amount": 10500,
"currency": "USD"
},
"hourlyRate": {
"amount": 10500,
"currency": "USD"
},
"membershipStatus": "PENDING",
"membershipType": "PROJECT",
"targetId": "64c777ddd3fcab07cfbb210c",
"userId": "5a0ab5acb07987125438b60f"
}
],
"name": "John Doe",
"profilePicture": "https://www.url.com/profile-picture1234567890.png",
"settings": {
"dateFormat": "MM/DD/YYYY",
"timeFormat": "HOUR24",
"timeZone": "Asia/Aden",
"weekStart": "MONDAY",
"theme": "DARK",
"lang": "en"
},
"status": "ACTIVE"
}
```
#### Data Model
- **customFields**: Array of user-specific custom field values
- **memberships**: Workspace/project associations with rates
- **settings**: User preferences for UI and formatting
- **status**: User account status (ACTIVE, INACTIVE, PENDING)
---
### 3. Get Member Profile
**Method:** `GET`
**Path:** `/v1/workspaces/{workspaceId}/member-profile/{userId}`
**Purpose:** Retrieve detailed profile information for a specific workspace member
#### Path Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| workspaceId | string | Yes | Workspace identifier |
| userId | string | Yes | User identifier |
#### Response (200 OK)
```json
{
"email": "johndoe@example.com",
"hasPassword": true,
"hasPendingApprovalRequest": true,
"imageUrl": "https://www.url.com/imageurl-1234567890.jpg",
"name": "John Doe",
"userCustomFieldValues": [
{
"customField": {
"allowedValues": ["New York", "London", "Manila"],
"description": "This field contains a location.",
"entityType": "USER",
"id": "44a687e29ae1f428e7ebe305",
"name": "location",
"onlyAdminCanEdit": true,
"placeholder": "Location",
"required": true,
"status": "VISIBLE",
"type": "DROPDOWN_MULTIPLE",
"workspaceDefaultValue": "Manila",
"workspaceId": "64a687e29ae1f428e7ebe303"
},
"customFieldId": "5e4117fe8c625f38930d57b7",
"name": "race",
"sourceType": "WORKSPACE",
"type": "DROPDOWN_MULTIPLE",
"userId": "5a0ab5acb07987125438b60f",
"value": "Asian"
}
],
"weekStart": "MONDAY",
"workCapacity": "PT7H",
"workingDays": "[\"MONDAY\",\"TUESDAY\",\"WEDNESDAY\",\"THURSDAY\",\"FRIDAY\"]",
"workspaceNumber": 3
}
```
#### Key Fields
- **workCapacity**: ISO-8601 duration format (e.g., PT7H = 7 hours)
- **workingDays**: JSON array of day names
- **userCustomFieldValues**: Full custom field definitions with values
---
### 4. Update Member Profile
**Method:** `PATCH`
**Path:** `/v1/workspaces/{workspaceId}/member-profile/{userId}`
**Purpose:** Update a member's profile information
#### Request Body
```json
{
"imageUrl": "https://www.url.com/imageurl-1234567890.jpg",
"name": "John Doe",
"removeProfileImage": false,
"userCustomFields": [
{
"customFieldId": "5e4117fe8c625f38930d57b7",
"value": "20231211-12345"
}
],
"weekStart": "MONDAY",
"workCapacity": "PT7H",
"workingDays": "[\"MONDAY\",\"TUESDAY\",\"WEDNESDAY\",\"THURSDAY\",\"FRIDAY\"]"
}
```
#### Field Constraints
- **name**: 1-100 characters (deprecated, limited users only)
- **weekStart**: Enum values: MONDAY-SUNDAY
- **workCapacity**: ISO-8601 duration format
- **workingDays**: JSON string array of day names
#### Response
Same structure as Get Member Profile response
---
### 5. Find All Users on Workspace
**Method:** `GET`
**Path:** `/v1/workspaces/{workspaceId}/users`
**Purpose:** List all users in a workspace with filtering and pagination
#### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| email | string | No | Filter by email substring |
| project-id | string | No | Filter users with access to project |
| status | string | No | Filter by status (PENDING, ACTIVE, DECLINED, INACTIVE, ALL) |
| account-statuses | string | No | Filter by account status (LIMITED, etc.) |
| name | string | No | Filter by name substring |
| sort-column | string | No | Sort by: ID, EMAIL, NAME, ACCESS, HOURLYRATE, COSTRATE |
| sort-order | string | No | ASCENDING or DESCENDING (default: ASCENDING) |
| page | integer | No | Page number (default: 1) |
| page-size | integer | No | Page size 1-∞ (default: 50) |
| memberships | string | No | Include memberships: ALL, NONE, WORKSPACE, PROJECT, USERGROUP |
| include-roles | string | Yes | Include manager role details (true/false) |
#### Request Example
```http
GET /v1/workspaces/64a687e29ae1f428e7ebe303/users?status=ACTIVE&page=1&page-size=50&include-roles=true
X-Api-Key: YOUR_API_KEY
```
#### Response
Returns array of user objects (same structure as Get Current User Info)
---
### 6. Filter Workspace Users (POST)
**Method:** `POST`
**Path:** `/v1/workspaces/{workspaceId}/users/info`
**Purpose:** Advanced filtering of workspace users with POST body
#### Request Body
```json
{
"accountStatuses": ["LIMITED", "ACTIVE"],
"email": "mail@example.com",
"includeRoles": true,
"memberships": "NONE",
"name": "John",
"page": 1,
"pageSize": 50,
"projectId": "21a687e29ae1f428e7ebe606",
"roles": ["WORKSPACE_ADMIN", "OWNER"],
"sortColumn": "ID",
"sortOrder": "ASCENDING",
"status": "ACTIVE",
"userGroups": [
"5a0ab5acb07987125438b60f",
"72wab5acb07987125438b564"
]
}
```
#### Filter Options
- **roles**: WORKSPACE_ADMIN, OWNER, TEAM_MANAGER, PROJECT_MANAGER
- **status**: PENDING, ACTIVE, DECLINED, INACTIVE, ALL
- **memberships**: ALL, NONE, WORKSPACE, PROJECT, USERGROUP
- **sortColumn**: ID, EMAIL, NAME, NAME_LOWERCASE, ACCESS, HOURLYRATE, COSTRATE
#### Response
Returns array of user objects with filtered results
---
### 7. Update User Custom Field
**Method:** `PUT`
**Path:** `/v1/workspaces/{workspaceId}/users/{userId}/custom-field/{customFieldId}/value`
**Purpose:** Update a specific custom field value for a user
#### Path Parameters
- workspaceId: Workspace identifier
- userId: User identifier
- customFieldId: Custom field identifier
#### Request Body
```json
{
"value": "20231211-12345"
}
```
#### Response (201 Created)
```json
{
"customFieldId": "5e4117fe8c625f38930d57b7",
"customFieldName": "TIN",
"customFieldType": "TXT",
"userId": "5a0ab5acb07987125438b60f",
"value": "20231211-12345"
}
```
---
### 8. Find User's Team Managers
**Method:** `GET`
**Path:** `/v1/workspaces/{workspaceId}/users/{userId}/managers`
**Purpose:** Get list of managers for a specific user
#### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| sort-column | string | No | Sorting column criteria |
| sort-order | string | No | ASCENDING or DESCENDING |
| page | integer | No | Page number (default: 1) |
| page-size | integer | No | Page size (default: 50) |
#### Response
Returns array of user objects representing managers
---
### 9. Remove User Manager Role
**Method:** `DELETE`
**Path:** `/v1/workspaces/{workspaceId}/users/{userId}/roles`
**Purpose:** Remove a manager role from a user
#### Request Body
```json
{
"entityId": "60f924bafdaf031696ec6218",
"role": "WORKSPACE_ADMIN",
"sourceType": "USER_GROUP"
}
```
#### Field Definitions
- **role**: WORKSPACE_ADMIN, TEAM_MANAGER, PROJECT_MANAGER
- **sourceType**: Currently only USER_GROUP supported
- **entityId**: ID of the entity (e.g., user group ID)
#### Response
204 No Content (success with no body)
---
### 10. Give User Manager Role
**Method:** `POST`
**Path:** `/v1/workspaces/{workspaceId}/users/{userId}/roles`
**Purpose:** Assign a manager role to a user
#### Request Body
```json
{
"entityId": "60f924bafdaf031696ec6218",
"role": "WORKSPACE_ADMIN",
"sourceType": "USER_GROUP"
}
```
#### Response (201 Created)
```json
[
{
"role": {
"id": "60f91b3ffdaf031696ec61a8",
"name": "Administrator",
"source": {
"id": "5b715612b079875110791234",
"type": "USER_GROUP"
}
},
"userId": "5a0ab5acb07987125438b60f",
"workspaceId": "64a687e29ae1f428e7ebe303"
}
]
```
---
## Common Data Types
### User Object
```typescript
{
activeWorkspace: string;
customFields: CustomFieldValue[];
defaultWorkspace: string;
email: string;
id: string;
memberships: Membership[];
name: string;
profilePicture: string;
settings: UserSettings;
status: "ACTIVE" | "INACTIVE" | "PENDING";
}
```
### Membership Object
```typescript
{
costRate: Rate;
hourlyRate: Rate;
membershipStatus: "PENDING" | "ACTIVE" | "DECLINED" | "INACTIVE";
membershipType: "PROJECT" | "WORKSPACE" | "USERGROUP";
targetId: string;
userId: string;
}
```
### Rate Object
```typescript
{
amount: number; // In cents (e.g., 10500 = $105.00)
currency: string; // ISO currency code
}
```
### UserSettings Object
```typescript
{
dateFormat: string; // "MM/DD/YYYY", etc.
timeFormat: string; // "HOUR24", "HOUR12"
timeZone: string; // IANA timezone
weekStart: string; // "MONDAY" - "SUNDAY"
theme: string; // "DARK", "LIGHT"
lang: string; // ISO language code
}
```
---
## Error Handling
### Common HTTP Status Codes
- **200 OK**: Successful GET/PUT/PATCH
- **201 Created**: Successful POST creation
- **204 No Content**: Successful DELETE
- **400 Bad Request**: Invalid request parameters
- **401 Unauthorized**: Missing or invalid authentication
- **403 Forbidden**: Insufficient permissions
- **404 Not Found**: Resource doesn't exist
- **429 Too Many Requests**: Rate limit exceeded (50 req/sec for addons)
### Error Response Format
```json
{
"message": "Error description",
"code": 400
}
```
---
## Implementation Examples
### Python Example: Get Current User
```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"
}
response = requests.get(f"{BASE_URL}/user", headers=headers)
user = response.json()
print(f"User: {user['name']} ({user['email']})")
```
### Python Example: Update Custom Field
```python
def update_custom_field(workspace_id, user_id, custom_field_id, value):
url = f"{BASE_URL}/workspaces/{workspace_id}/users/{user_id}/custom-field/{custom_field_id}/value"
payload = {"value": value}
response = requests.put(url, headers=headers, json=payload)
return response.json()
```
### Python Example: Filter Users with Roles
```python
def get_workspace_admins(workspace_id):
url = f"{BASE_URL}/workspaces/{workspace_id}/users/info"
payload = {
"roles": ["WORKSPACE_ADMIN"],
"status": "ACTIVE",
"includeRoles": True,
"page": 1,
"pageSize": 100
}
response = requests.post(url, headers=headers, json=payload)
return response.json()
```
---
## Best Practices
1. **Pagination**: Always implement pagination for user lists in large workspaces
2. **Filtering**: Use POST `/users/info` endpoint for complex filtering instead of GET with many query params
3. **Rate Limiting**: Implement exponential backoff when hitting rate limits
4. **Error Handling**: Always check response status codes before processing data
5. **Custom Fields**: Validate custom field types before setting values
6. **Caching**: Cache user data appropriately to reduce API calls
7. **Regional Endpoints**: Use the correct regional prefix for your workspace location
---
## Rate Limiting
- **Addon Token**: 50 requests per second per addon per workspace
- **Exceeding Limit**: Returns "Too many requests" error
---
## Notes
- User `name` field is deprecated and can only be updated for limited users
- Profile images should be updated using the separate image upload endpoint first
- Custom field values must match the field type (TXT, NUMBER, DROPDOWN, etc.)
- Manager roles require appropriate workspace permissions
- ISO-8601 duration format: PT{hours}H{minutes}M (e.g., PT7H30M = 7.5 hours)