FlowyTeam OKR MCP API Reference
Complete reference for every tool, action, parameter and response in the FlowyTeam OKR MCP server — objectives, key results, indicators, performance cycles, tasks, projects, HR and CRM.
https://flowyteam.com/api/mcp/gateway
auth_register & auth_verify_email for new account setup (user-initiated only).
All other tools read the Bearer token from the Authorization header.
https://flowyteam.com/api/v2/mcp/rpcAuthorization: Bearer <api_token>.
Existing account? Copy your token from FlowyTeam Settings → MCP & AI Integration.
New account? Ask Claude to call
auth_register — password is optional, a temporary one is included in the verification email. Share the 6-digit code from the email, then Claude calls auth_verify_email to activate your account and return your api_token immediately.
Common Error Codes
| Code | Meaning |
|---|---|
200 | Success |
401 | Unauthenticated — missing or invalid Bearer token |
403 | Forbidden — authenticated but lacks permission |
404 | Resource not found |
422 | Validation error — check the errors object in the response |
429 | Too many requests — rate limit exceeded |
500 | Server error |
Account Tools Gateway only — explicit user action
These tools are available on the Gateway endpoint
(https://flowyteam.com/api/mcp/gateway) without a Bearer token,
but must only be invoked when the user explicitly requests it — never autonomously.
New account: call auth_register — password is optional (a temporary one is generated and included in the verification email). Ask the user to share the 6-digit code from the email, then call auth_verify_email → api_token returned immediately.
Existing account: copy the token from FlowyTeam Settings → MCP & AI Integration and reconnect using the RPC endpoint with Authorization: Bearer <token>.
Users should change their temporary password after first login.
Auth Register
Create a new FlowyTeam company account. Only call this tool when the user explicitly requests account creation and provides all required details. Requires MCP registration to be enabled by the platform administrator. Password is optional — if omitted, a secure temporary password is generated and included in the verification email. A verification email with a 6-digit code is sent — ask the user to share the code, then call auth_verify_email to activate the account and receive the api_token immediately. Alternatively the user can click the verification link in the email to activate via the browser, then copy the api_token from FlowyTeam Settings → MCP & AI Integration.
| Name | Type | Description |
|---|---|---|
name
|
string | Full name of the admin user. |
email
|
string | Email address. Used as login credential. |
password
|
string | Optional. If omitted, a secure temporary password is generated and included in the verification email. The user can change it after first login. |
company_name
|
string | Name of the company / organisation. |
phone
|
string | Phone number (optional). |
{
"name": "Alice Smith",
"email": "[email protected]",
"company_name": "Acme Corp"
}
{
"status": "pending_verification",
"message": "Account created. A verification email has been sent to [email protected]. To activate, choose one of these options: (A) Check your email for the 6-digit verification code and paste it here. (B) Click the verification link in your email to activate via the browser.",
"user_id": 42,
"company_id": 7,
"email": "[email protected]"
}
Auth Verify Email
Activate a newly registered FlowyTeam account and receive the api_token. Call this after auth_register once the user shares the 6-digit code from their verification email. Also accepts the long token from the verification link URL as a fallback. On success, the account is activated and an api_token is returned — use it as the Bearer token and reconfigure the MCP connection.
| Name | Type | Description |
|---|---|---|
verification_code
|
string | The 6-digit code from the verification email body, OR the long token from the verification link URL. |
{
"verification_code": "123456"
}
{
"status": "success",
"message": "Email verified. Account is now active.",
"api_token": "abc123...",
"user_id": 42,
"company_id": 7,
"name": "Alice Smith",
"email": "[email protected]"
}
Auth Login
Retrieve the api_token for an existing FlowyTeam account using email and password. Only call this tool when the user explicitly provides their credentials and requests a login — never proactively. Note: for new accounts use auth_register + auth_verify_email instead. Existing users can also copy their token directly from FlowyTeam Settings → MCP & AI Integration. Returns an api_token — reconfigure the MCP connection with it as the Bearer token.
| Name | Type | Description |
|---|---|---|
email
|
string | Registered email address. |
password
|
string | Account password. |
{
"email": "[email protected]",
"password": "••••••••"
}
{
"status": "success",
"message": "Login successful.",
"api_token": "abc123...",
"user_id": 42,
"name": "Alice Smith"
}
Available Tools
All workspace tools require Authorization: Bearer <api_token>.
Use the authenticated RPC endpoint
(https://flowyteam.com/api/v2/mcp/rpc) or the Gateway endpoint with your token.
Tasks
Manage tasks: list, create, update, delete, mark complete. Filter by status, priority, project, assignee, due date; archived=true lists archived tasks. A new task needs the person it is assigned to (assigned_to), as on the web form. DELETE archives a task: dry_run=true previews it, permanent=true removes an archived one for good, and PUT with restore=true brings it back.
| Name | Type | Description |
|---|---|---|
id
|
string | Task ID — or use "heading"/"title" to look up by name |
heading
|
string | Task title (required for POST; also used to look up task by name for PUT/DELETE) |
title
|
string | Alias for heading |
description
|
string | — |
status
|
string | Filter (GET) or new status (PUT) |
priority
|
string | — |
project_id
|
string | Project ID to link or filter by |
assigned_to
|
string | POST: User to assign the task to: a user ID, or a name or email (partial match). Required, as on the web form, except for members who can only create tasks for themselves, whose tasks are their own. GET: only tasks assigned to this user (admins). PUT: reassign (admins). |
user_id
|
string | Alias for assigned_to |
due_date
|
string | Due date (Y-m-d) |
overdue
|
boolean | string | GET: true returns only tasks that are past their due date AND not completed. Use this for any "overdue", "late" or "past due" question. Do NOT use status=incomplete for that: incomplete includes tasks whose due date has not arrived yet. |
start_date
|
string | Start date (Y-m-d). Defaults to today. |
task_category_id
|
string | Task category ID |
search
|
string | Search in task heading |
key_result_id
|
string | POST/PUT: ID of the key result this task works toward (list_key_results). Pass 0 to unlink. A key result can have several tasks. |
employee_access
|
array | Array of user IDs to grant view/edit access to this task (in addition to creator and assignee). |
dry_run
|
boolean | DELETE: true shows what would be archived, unlinked or removed, and how progress would change, and changes nothing. |
permanent
|
boolean | DELETE: true removes an item that is already archived, for good, with what was archived with it. Admins only; archive it first. Takes its sub-tasks, comments, files and time logs with it. Needs the id. |
restore
|
boolean | PUT with id: true brings back an archived task. |
archived
|
boolean | GET: true lists archived tasks instead, with when and by whom: every task for admins and those with the delete tasks permission, else your own. Takes project_id, key_result_id and search. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"status": "incomplete",
"project_id": 12
}
{
"status": "success",
"data": [
{
"id": 101,
"heading": "Design landing page",
"status": "incomplete",
"priority": "high",
"due_date": "2026-05-01",
"assigned_to": 7
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
403 |
This action is unauthorized. |
{
"heading": "Write API docs",
"project_id": 12,
"due_date": "2026-05-10",
"priority": "medium",
"assigned_to": 7
}
{
"status": "success",
"message": "Task created successfully.",
"id": 102
}
| HTTP Code | Message |
|---|---|
422 |
The heading field is required. |
422 |
assigned_to is required: name the person to assign the task to (a user ID, name or email), as the web form asks for one. |
401 |
Unauthenticated. |
{
"id": 101,
"status": "complete",
"priority": "low"
}
{
"status": "success",
"message": "Task updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Task not found. |
422 |
The id field is required. |
{
"id": 101
}
{
"success": true,
"message": "Task #101 'Design landing page' archived. PUT tasks with its id and restore=true brings it back."
}
| HTTP Code | Message |
|---|---|
404 |
Task not found. |
Projects
Manage projects and their sub-resources (milestones, members, categories, files). Set resource=project (default), milestone, member, category, or file. All lookups are name-based — no IDs needed. Admins have full access. Members can view/update their own projects, and perform full CRUD on milestones, members, and files. POST requires project_name, start_date, and either deadline (Y-m-d) or without_deadline=true. If the user has not given a deadline, ask which they want before calling this tool, do not call it and wait to be refused.
| Name | Type | Description |
|---|---|---|
action
|
string | Special action on the project resource. "duplicate" — clones an existing project (members + milestones + tasks all copied, tasks reset to incomplete). "stats" — returns project statistics: total count, by-status breakdown, overdue count, budget total. When action=duplicate: provide project_name to identify the project to clone. When action=stats: no extra params needed (use method=GET). |
resource
|
string | Sub-resource to manage. Defaults to "project". Use "milestone" for milestones, "member" to add/remove members, "category" to manage categories (admin only for create/update/delete), "file" to list or delete project files. |
project_name
|
string | Project name. Used as lookup key for GET (single), PUT, DELETE, and to scope milestones/members. Partial match. |
new_project_name
|
string | New project name — only used when renaming a project via PUT. |
search
|
string | Partial name search for list (GET) operations. |
status
|
string | Project status. Defaults to "not started" on create. |
start_date
|
string | Project start date (Y-m-d). Required for POST. |
deadline
|
string | Project deadline (Y-m-d). Required for POST unless without_deadline=true. |
without_deadline
|
boolean | Set true to create/update a project with no deadline. |
project_summary
|
string | Short project description / summary. |
notes
|
string | Internal project notes. |
feedback
|
string | Client feedback field. Used on update only. |
client_name
|
string | Client company or user name to assign to the project. Looked up by partial match. |
category_name
|
string | The CURRENT/EXISTING category name — used as a lookup key (partial match). When assigning a project to a category, provide the existing category name here. When resource=category: PUT (rename) → provide the CURRENT name here and the NEW name in new_category_name. POST (create) → provide the new category name here. DELETE → provide the name of the category to delete here. NEVER put the new/desired name in this field for rename operations. |
new_category_name
|
string | New category name — only used when renaming a category via PUT (resource=category). |
project_budget
|
number | Project budget amount. |
hours_allocated
|
number | Total hours budget allocated to this project. |
completion_percent
|
integer | Manual completion percentage (0–100). Update only. |
client_view_task
|
string | Allow client to view tasks. |
allow_client_notification
|
string | Send client notifications for task updates. Only effective when client_view_task=enable. |
manual_timelog
|
string | Allow manual time log entries on this project. |
milestone_title
|
string | Milestone title. Required for POST (resource=milestone). Used as lookup key for PUT/DELETE. |
new_milestone_title
|
string | New milestone title — only used when renaming a milestone via PUT. |
milestone_summary
|
string | Milestone description/summary. Required for POST (resource=milestone). |
milestone_status
|
string | Milestone completion status. Defaults to "incomplete" on create. |
milestone_cost
|
number | Milestone cost amount. Defaults to 0. |
member_name
|
string | Employee name to add or remove as a project member. Partial match. |
file_name
|
string | Filename (partial match) for file operations (resource=file). Used for DELETE. |
filter_status
|
string | Filter project list by status. |
filter_client
|
string | Filter project list by client name (partial match). |
filter_category
|
string | Filter project list by category name (partial match). |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"status": "not started",
"search": "website"
}
{
"status": "success",
"data": [
{
"id": 12,
"project_name": "Website Redesign",
"status": "not started",
"start_date": "2026-05-01",
"deadline": "2026-07-31",
"client_id": 3
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"project_name": "Mobile App v2",
"start_date": "2026-06-01",
"deadline": "2026-09-30",
"client_id": 3
}
{
"status": "success",
"message": "Project created successfully.",
"id": 13
}
| HTTP Code | Message |
|---|---|
422 |
The project name field is required. |
{
"id": 12,
"status": "in progress"
}
{
"status": "success",
"message": "Project updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Project not found. |
{
"id": 12
}
{
"status": "success",
"message": "Project deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Project not found. |
Task Category
Manage task categories: list, create, update, delete. Lookup by ID or category name.
| Name | Type | Description |
|---|---|---|
id
|
string | Category ID. For PUT/DELETE you may also supply category_name instead of id for name-based lookup. |
category_name
|
string | Category name (required for POST; used as lookup key for PUT/DELETE when id is omitted). |
search
|
string | Partial name search for GET. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"search": "bug"
}
{
"status": "success",
"data": [
{
"id": 1,
"category_name": "Bug Fix"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"category_name": "Feature Request"
}
{
"status": "success",
"message": "Category created successfully.",
"id": 2
}
| HTTP Code | Message |
|---|---|
422 |
The category name field is required. |
{
"id": 1,
"category_name": "Critical Bug Fix"
}
{
"status": "success",
"message": "Category updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Category not found. |
{
"id": 1
}
{
"status": "success",
"message": "Category deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Category not found. |
Leave
Manage employee leave requests: list, create, approve, reject, cancel. Filter by status, employee, date range. Approving or rejecting recalculates the allowance and notifies the employee, and settles every date of a multi-date request at once. Approval authority follows the company leave approval setting: an admin caller may decide any request, a reporting manager may decide their own team when the setting allows managers, and nobody but an admin decides their own request. Every write returns the employee remaining days, used days and allowance, so no follow-up call is needed to read them. Set reject_reason when rejecting.
| Name | Type | Description |
|---|---|---|
id
|
string | Leave ID — or use "name" + "leave_date" to look up by employee name and date instead |
name
|
string | Employee name — used together with "leave_date" to look up a leave request without an id |
leave_type_id
|
string | Leave type ID (use leave_type_name instead if you do not know the ID) |
leave_type_name
|
string | Leave type name, resolved to leave_type_id automatically. Companies name types tersely ("Annual", "Sick"), and either wording matches, so "annual" and "Annual Leave" both work |
employee_name
|
string | Employee name — admin can set this to create a leave on behalf of that employee (resolved to user_id) |
user_id
|
string | Employee user ID (admin only; use employee_name if ID is unknown) |
userId
|
string | Filter by employee (GET) |
leave_date
|
string | Leave start date (Y-m-d, required for POST; also used as a lookup key with "name") |
duration
|
string | "full day", "half day", or "multiple" |
status
|
string | — |
reason
|
string | Why the leave is being taken |
reject_reason
|
string | Why the request was rejected (PUT with status "rejected") |
multi_date
|
string | Comma-separated dates for multiple duration |
startDate
|
string | Filter from date (Y-m-d) |
endDate
|
string | Filter to date (Y-m-d) |
sort_direction
|
string | — |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"userId": 7,
"status": "pending"
}
{
"status": "success",
"data": [
{
"id": 88,
"user_id": 7,
"leave_type_id": 1,
"leave_date": "2026-05-15",
"duration": "full day",
"status": "pending",
"reason": "Family event"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"user_id": 7,
"leave_type_id": 1,
"leave_date": "2026-05-15",
"duration": "full day",
"reason": "Family event"
}
{
"status": "success",
"message": "Leave request submitted.",
"id": 89
}
| HTTP Code | Message |
|---|---|
422 |
The leave date field is required. |
{
"id": 88,
"status": "approved"
}
{
"status": "success",
"message": "Leave updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Leave record not found. |
{
"id": 88
}
{
"status": "success",
"message": "Leave deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Leave record not found. |
Leave Type
Manage leave types (annual, sick, etc.): list, create, update, delete. Admin-only for write operations. POST needs type_name and no_of_leaves, the days each employee is entitled to per year.
| Name | Type | Description |
|---|---|---|
id
|
string | Leave type ID — or use type_name to look up by name |
type_name
|
string | Leave type name (e.g. Annual Leave, Sick Leave) — required for POST, or use to look up for PUT/DELETE |
name
|
string | Alias for `type_name`. |
leave_type
|
string | Alias for `type_name`. |
title
|
string | Alias for `type_name`. |
color
|
string | Color code (e.g. #FF0000) |
no_of_leaves
|
number | Days each employee is entitled to per year, a whole number from 0 to 365. Required for POST. A change on PUT reaches employee balances at the next monthly calculation; PUT leave_entitlement with all=true applies it now. |
days
|
number | Alias for `no_of_leaves`. |
status
|
string | Whether employees can take this leave type. Default active on POST. Making a type active works out every employee's entitlement for it. |
search
|
string | Search by type name |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
[]
{
"status": "success",
"data": [
{
"id": 1,
"type_name": "Annual Leave",
"color": "#4CAF50",
"no_of_leaves": 14,
"status": "active"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"type_name": "Maternity Leave",
"no_of_leaves": 90
}
{
"status": "success",
"message": "Leave type 'Maternity Leave' created.",
"data": {
"id": 4,
"type_name": "Maternity Leave",
"color": "#6c757d",
"no_of_leaves": 90,
"status": "active"
}
}
| HTTP Code | Message |
|---|---|
422 |
"no_of_leaves" is required to create a leave type: the days each employee is entitled to per year (0 for a type with no fixed allowance, such as unpaid leave). |
{
"type_name": "Annual Leave",
"no_of_leaves": 16
}
{
"status": "success",
"message": "Leave type 'Annual Leave' updated."
}
| HTTP Code | Message |
|---|---|
404 |
Leave type not found. |
{
"id": "1"
}
{
"status": "success",
"message": "Leave type 'Annual Leave' deleted."
}
| HTTP Code | Message |
|---|---|
404 |
Leave type not found. |
Leave Entitlement
Read how many leave days an employee is entitled to, has used, and has left, per leave type. Name the person with employee_name. PUT recalculates the figures, the same recalculation the admin leave pages run; it needs employee_name, or all set to true for every employee. Approving leave already recalculates on its own, so PUT is only for a manual refresh.
| Name | Type | Description |
|---|---|---|
employee_name
|
string | Employee name, resolved to a user (admins only; employees always see their own figures) |
user_id
|
string | Employee user ID (admin only; use employee_name if the ID is unknown) |
leave_type_name
|
string | Only report this leave type. Companies name types tersely ("Annual", "Sick"), and either wording matches, so "annual" and "Annual Leave" both work |
all
|
boolean | PUT only: recalculate every employee in the company instead of one |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"employee_name": "Aqilah"
}
{
"status": "success",
"data": [
{
"leave_type": "Annual Leave",
"entitlement": 14,
"used": 4,
"balance": 10
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"employee_name": "Aqilah"
}
{
"status": "success",
"message": "Leave entitlement recalculated for Aqilah Najwa."
}
| HTTP Code | Message |
|---|---|
403 |
Only admins can recalculate leave entitlement. |
Attendance
Manage attendance: clock-in, clock-out, list records. Filter by employee, date, month, year. A list returns at most 25 records with a total count; when total exceeds what was returned, say so in the answer.
| Name | Type | Description |
|---|---|---|
method
|
string | GET = list/view attendance records; POST = clock IN (start of shift, beginning of work day); PUT = clock OUT (end of shift, leaving work). Use PUT for any "clock out", "check out", or "end shift" request. |
id
|
string | Attendance record ID. GET: only that record. PUT: targets that record directly (admin) |
employee_id
|
string | User ID to filter (GET) or clock in/out for. Admin only for anyone other than the caller; a non-admin asking about another employee is refused. |
employee_name
|
string | Employee name to look up. Admin only for anyone other than the caller; a non-admin asking about another employee is refused. |
date
|
string | Specific date filter Y-m-d (GET) |
month
|
string | Month number 1-12 filter (GET) |
year
|
string | Year e.g. 2026 filter (GET) |
today
|
boolean | string | If true, return only today's records (GET) |
clock_in_datetime
|
string | Admin only: explicit clock-in datetime (Y-m-d H:i:s or H:i) |
clock_out_datetime
|
string | Admin only: explicit clock-out datetime (Y-m-d H:i:s or H:i) |
working_from
|
string | Where the employee is working from |
late
|
string | Admin only: override late flag |
half_day
|
string | Admin only: override half-day flag |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"employee_name": "Ana Shuhada",
"year": "2026"
}
{
"status": "success",
"data": [
{
"id": 300,
"employee_name": "Ana Shuhada",
"attendance_date": "2026-05-07",
"clock_in_datetime": "2026-05-07 08:55:00",
"clock_out_datetime": null,
"working_from": "office"
}
],
"returned": 25,
"total": 87,
"truncated": true,
"note": "Showing only the 25 most recent of 87 matching records. Say this in the answer, and narrow the filter by month or date to see the rest."
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
403 |
You can only view your own attendance. Ask an administrator for another employee's records. |
404 |
Employee 'Ana Shuhada' not found in your company. |
{
"employee_id": 7,
"working_from": "office"
}
{
"status": "success",
"message": "Clocked in successfully.",
"id": 300
}
| HTTP Code | Message |
|---|---|
422 |
Already clocked in today. |
{
"id": 300
}
{
"status": "success",
"message": "Clocked out successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Attendance record not found. |
Employees
Manage employees: list, show full profile, create, update (profile/department/designation/status/skills), deactivate/delete. Always resolve by name or email — never require an ID from the user. Members read and update their own record. With the view, add, edit or delete employees permission they also list other non-admin employees, create employees, update the basic record of other non-admin employees, or delete them, as in the web app. Salary, bank details, manager flag, status, login and reporting line changes need an admin. Pay, bank and identity fields are left out of every record unless include_private is true, for an admin or for your own record.
| Name | Type | Description |
|---|---|---|
name
|
string | Employee full name (partial match). GET: filters the list. PUT/DELETE: identifies the employee. |
email
|
string | Employee email (exact). GET: filters the list. PUT/DELETE: alternative to name. |
search
|
string | Search employees by name or email (GET list). |
status
|
string | Filter/set employee status. Use "active" for active employees, "deactive" for inactive/deactivated employees. The tool also accepts "inactive" or "deactivated" and normalizes them automatically. |
department_name
|
string | Primary department name (partial match). Resolved to department_id. |
designation_name
|
string | Designation/position name (partial match). Resolved to designation_id. |
with_details
|
boolean | Include full profile details in GET. Default false (summary only). Pay, bank and identity fields still need include_private. |
include_private
|
boolean | GET, and the record a write returns: true adds pay, bank and identity fields (salary, bank account, NRIC, passport, work permit). Admins, or your own record only. |
role
|
string | Filter list by role name (partial match, e.g. "ticket manager", "admin", "employee"). Leave blank to show all employees. |
new_name
|
string | Full name for new employee (POST) or rename existing (PUT). |
new_email
|
string | Email for new employee (POST) or change email (PUT). |
password
|
string | Password (POST/PUT). Default: Welcome@123. |
mobile
|
string | Mobile / phone number. |
gender
|
string | Gender. |
timezone
|
string | User timezone (e.g. Asia/Jakarta, UTC, America/New_York). Stored on the user account. |
login
|
string | Whether the employee can log in. Default enable. |
employee_id
|
string | Custom employee/staff ID (e.g. EMP-001). |
preferred_name
|
string | Preferred display name. |
joining_date
|
string | Joining date (Y-m-d or natural language like "today"). |
last_date
|
string | Last working date (Y-m-d). Set when offboarding. |
birthday
|
string | Date of birth (Y-m-d). |
address
|
string | Home address. |
hourly_rate
|
string | Hourly rate (numeric). |
slack_username
|
string | Slack username. |
manager
|
string | Whether this employee is a manager. |
marital_status
|
string | Marital status. |
nationality
|
string | Nationality (e.g. Indonesia, Malaysian, Filipino). |
location
|
string | Work location / branch. |
employment_type
|
string | Employment type (e.g. full-time, part-time, contract). |
attendance
|
string | Attendance tracking mode. "active" = tracking enabled, "deactive" = disabled. |
subsidiary
|
string | Subsidiary / branch name. |
highest_qualification
|
string | Highest educational qualification. |
show_organizational_chart
|
string | Show in org chart. |
nric
|
string | National ID / NRIC number. |
passport
|
string | Passport number. |
resident
|
string | Residency status. |
work_permit_no
|
string | Work permit number. |
work_permit_expiry_date
|
string | Work permit expiry date (Y-m-d). |
amount_of_children
|
string | Number of children. |
spouse_working
|
string | Whether spouse is working. |
disabled_person
|
string | Employee is a disabled person. |
disabled_spouse
|
string | Spouse is a disabled person. |
disabled_children
|
string | Has disabled children. |
next_of_kin_full_name
|
string | Next of kin full name. |
next_of_kin_phone_number
|
string | Next of kin phone number. |
next_of_kin_relationship
|
string | Relationship to next of kin (e.g. spouse, parent). |
salary
|
string | Salary amount (numeric). |
salary_type
|
string | Salary type. |
salary_currency
|
string | Currency code for salary (e.g. IDR, MYR, USD). Must be one of the workspace currencies. |
payment_method
|
string | Payment method (e.g. bank transfer, cash). |
bank_name
|
string | Bank name. |
bank_account_no
|
string | Bank account number. |
bank_account_name
|
string | Bank account holder name. |
bank_account_type
|
string | Bank account type (e.g. savings, current). |
employee_type_name
|
string | Employee type name (partial match, e.g. Full Time, Part Time, Intern). Resolved to employee_type_id. |
reports_to_name
|
string | Manager/supervisor name (partial match). Resolved to reports_to field. |
skills
|
string | Comma-separated skill names to assign (replaces existing). |
reports_to_since
|
string | Date since employee reports to current manager (Y-m-d). |
employment_status_effective_from
|
string | Employment status effective from date (Y-m-d). |
employment_status_effective_to
|
string | Employment status effective to date (Y-m-d). |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"department_id": 4,
"status": "active"
}
{
"status": "success",
"data": [
{
"id": 7,
"name": "Sarah Lee",
"email": "[email protected]",
"department_id": 4,
"designation_id": 2,
"status": "active",
"joining_date": "2024-01-15"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"name": "John Doe",
"email": "[email protected]",
"password": "secret123",
"department_id": 4,
"designation_id": 2,
"joining_date": "2026-06-01"
}
{
"status": "success",
"message": "Employee created successfully.",
"id": 8
}
| HTTP Code | Message |
|---|---|
422 |
The email has already been taken. |
{
"id": 7,
"status": "inactive"
}
{
"status": "success",
"message": "Employee updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Employee not found. |
{
"id": 7
}
{
"status": "success",
"message": "Employee deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Employee not found. |
Department
Manage departments/teams: list, show details with members, create, update, delete, add/remove members. Always resolve by name — never require an ID from the user.
| Name | Type | Description |
|---|---|---|
name
|
string | Department name (partial match for lookup on GET/PUT/DELETE). Never ask the user for an ID — use this. |
team_name
|
string | Alias for name. New name when creating/renaming. |
new_name
|
string | New department name when renaming an existing department (PUT). |
description
|
string | Department description. |
parent_name
|
string | Parent department name (partial match). Used for hierarchical setup. |
leader_name
|
string | Full name of the department leader/manager (partial match on employee name). |
action
|
string | Member action: "add_member", "remove_member", "add_associate", "remove_associate". Use with employee_name. |
employee_name
|
string | Employee full name (partial match) for member actions. |
search
|
string | Search departments by name (GET). |
with_members
|
boolean | Include members list in GET response. Default false. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"search": "marketing"
}
{
"status": "success",
"data": [
{
"id": 4,
"team_name": "Digital Marketing",
"name": "Digital Marketing",
"description": "Handles all digital channels",
"parent_id": null
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"team_name": "Product Design",
"description": "UI\/UX and product design team"
}
{
"status": "success",
"message": "Department created successfully.",
"id": 5
}
| HTTP Code | Message |
|---|---|
422 |
The team name field is required. |
{
"id": 4,
"team_name": "Digital Marketing & SEO"
}
{
"status": "success",
"message": "Department updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Department not found. |
{
"id": 4
}
{
"status": "success",
"message": "Department deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Department not found. |
Designation
Manage job designations/positions: list (with member counts), create, update, delete. Always resolve by name — never require an ID from the user.
| Name | Type | Description |
|---|---|---|
name
|
string | Designation name for lookup (partial match). Used for GET detail, PUT, DELETE. Never ask the user for an ID. |
search
|
string | Partial name search for GET list. |
new_name
|
string | New designation name when renaming an existing one (PUT). Use `name` to identify which designation to rename. |
description
|
string | Optional description of the designation/position. |
with_members
|
boolean | Include list of members holding this designation in GET response. Default false. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"search": "engineer"
}
{
"status": "success",
"data": [
{
"id": 2,
"name": "Senior Software Engineer"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"name": "DevOps Engineer"
}
{
"status": "success",
"message": "Designation created successfully.",
"id": 3
}
| HTTP Code | Message |
|---|---|
422 |
The name field is required. |
{
"id": 2,
"name": "Principal Software Engineer"
}
{
"status": "success",
"message": "Designation updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Designation not found. |
{
"id": 2
}
{
"status": "success",
"message": "Designation deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Designation not found. |
Clients
Manage clients: list, create, update, delete. Filter by name, company. POST requires email as well as the company or contact name. If the user has not given an email, ask for it before calling this tool rather than calling it and waiting to be refused.
| Name | Type | Description |
|---|---|---|
id
|
string | Client user ID — or use "company_name" or "name" to look up by name instead |
search
|
string | Search by name or company |
company_name
|
string | Company name (also used to look up client for PUT/DELETE) |
name
|
string | Contact person name (also used to look up client for PUT/DELETE) |
email
|
string | — |
mobile
|
string | — |
website
|
string | — |
address
|
string | — |
note
|
string | — |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"search": "acme"
}
{
"status": "success",
"data": [
{
"id": 3,
"name": "Bob Smith",
"company_name": "Acme Corp",
"email": "[email protected]",
"mobile": "+1234567890",
"website": "https:\/\/acme.com"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"name": "Alice Johnson",
"email": "[email protected]",
"company_name": "New Client Ltd",
"mobile": "+0987654321",
"send_email": "yes"
}
{
"status": "success",
"message": "Client created successfully.",
"id": 4
}
| HTTP Code | Message |
|---|---|
422 |
The email has already been taken. |
{
"id": 3,
"website": "https:\/\/acme-new.com"
}
{
"status": "success",
"message": "Client updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Client not found. |
{
"id": 3
}
{
"status": "success",
"message": "Client deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Client not found. |
Tickets
Manage support tickets and their conversation replies. Supports full CRUD (list, create, update, delete), reply/conversation management (resource=reply), ticket statistics (action=stats), and close/reopen actions. Name-based lookups for agent, requester, type, and channel. Admins and privileged members have full access. Regular members can manage their own tickets (create, view, reply, close, reopen).
| Name | Type | Description |
|---|---|---|
resource
|
string | Sub-resource to manage. Defaults to "ticket". Use "reply" to list (GET) or add (POST) replies to a ticket conversation. |
action
|
string | Special action on a ticket. "close" — set status to closed and add a system reply. Provide id or subject to identify the ticket. "reopen" — reopen a closed ticket (open if no agent, pending if agent assigned). "stats" — return ticket counts by status (open, pending, resolved, closed, total). No other params needed. |
id
|
string | Ticket ID (numeric). Preferred for PUT/DELETE/reply to pinpoint a specific ticket. |
subject
|
string | Ticket subject. Required for POST (create ticket). Also used as a partial-match fallback lookup when id is not provided (PUT/DELETE). |
search
|
string | Search/filter ticket list by subject substring (GET only). |
description
|
string | Ticket body — the initial message. Required for POST (create). Stored as the first reply in the conversation thread (not on the ticket record itself). |
message
|
string | Reply text to append to the ticket conversation. Use on PUT (update) to add a reply while also updating other fields. Use with resource=reply, method=POST to add a standalone reply. |
status
|
string | Ticket status. Defaults to "open" on create. Prefer action=close / action=reopen for status transitions. |
priority
|
string | Ticket priority. Defaults to "medium" on create. |
due_date
|
string | Expected resolution date (Y-m-d). Admin/privileged members only. |
tags
|
string | Comma-separated tags to attach to the ticket (e.g. "billing,urgent,vip"). Replaces existing tags. Admin/privileged members only. |
agent_name
|
string | Name of the support agent to assign (partial match). Resolved to agent_id automatically. When agent is reassigned, they are notified. Admin/privileged members only. |
requester_name
|
string | Name of the ticket requester/submitter (partial match). Resolved to user_id. Admin/privileged use only — creates ticket on behalf of that user. |
type_name
|
string | Ticket type name (partial match, e.g. "bug", "feature request"). Resolved to type_id automatically. |
channel_name
|
string | Ticket channel name (partial match, e.g. "email", "chat", "phone"). Resolved to channel_id automatically. |
agent_id
|
integer | User ID of the support agent. Use instead of agent_name when known. |
user_id
|
integer | User ID of the requester. Use instead of requester_name when known. |
type_id
|
integer | Ticket type ID. Use instead of type_name when known; also filters GET. |
channel_id
|
integer | Ticket channel ID. Use instead of channel_name when known; also filters GET. |
filter_status
|
string | Filter ticket list by status (GET only). |
filter_priority
|
string | Filter ticket list by priority (GET only). |
filter_agent
|
string | Filter ticket list by agent name (partial match, GET only). |
filter_requester
|
string | Filter ticket list by requester/submitter name (partial match, GET only, admin only). |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"status": "open",
"priority": "high"
}
{
"status": "success",
"data": [
{
"id": 500,
"subject": "Login not working",
"status": "open",
"priority": "high",
"agent_id": 7,
"type_id": 1,
"channel_id": 2
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"subject": "Cannot export report",
"description": "Export button not responding",
"priority": "medium",
"type_id": 1,
"channel_id": 2,
"user_id": 7
}
{
"status": "success",
"message": "Ticket created successfully.",
"id": 501
}
| HTTP Code | Message |
|---|---|
422 |
The subject field is required. |
{
"id": 500,
"status": "resolved",
"agent_id": 8
}
{
"status": "success",
"message": "Ticket updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Ticket not found. |
{
"id": 500
}
{
"status": "success",
"message": "Ticket deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Ticket not found. |
Ticket Type
Manage ticket types/classifications: list, create, update, delete. Lookup by name or ID.
| Name | Type | Description |
|---|---|---|
id
|
string | Ticket type ID. For PUT/DELETE you may also supply `type` or `name` to look up by name instead. |
type
|
string | Ticket type name (required for POST; used as name-based lookup key for PUT/DELETE when id is omitted). |
name
|
string | Alias for `type`. Used as name-based lookup key for PUT/DELETE when id is omitted. |
search
|
string | Search ticket types by name (GET). |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"search": "bug"
}
{
"status": "success",
"data": [
{
"id": 1,
"type": "Bug Report"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"type": "Feature Request"
}
{
"status": "success",
"message": "Ticket type created successfully.",
"id": 2
}
| HTTP Code | Message |
|---|---|
422 |
The type field is required. |
{
"id": 1,
"type": "Critical Bug"
}
{
"status": "success",
"message": "Ticket type updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Ticket type not found. |
{
"id": 1
}
{
"status": "success",
"message": "Ticket type deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Ticket type not found. |
Ticket Channel
Manage ticket channels (e.g. email, phone, chat): list, create, update, delete. Requires the tickets module.
| Name | Type | Description |
|---|---|---|
id
|
string | Channel ID. For PUT/DELETE you may supply id OR channel_name/name instead. |
channel_name
|
string | Channel name (required for POST; used as lookup key for PUT/DELETE when id is omitted). |
name
|
string | Alias for channel_name — can be used as a lookup key for PUT/DELETE. |
search
|
string | Search substring within channel_name. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
[]
{
"status": "success",
"data": [
{
"id": 1,
"channel_name": "Email"
},
{
"id": 2,
"channel_name": "Live Chat"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"channel_name": "WhatsApp"
}
{
"status": "success",
"message": "Channel created successfully.",
"id": 3
}
| HTTP Code | Message |
|---|---|
422 |
The channel name field is required. |
{
"id": 1,
"channel_name": "Email Support"
}
{
"status": "success",
"message": "Channel updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Channel not found. |
{
"id": 1
}
{
"status": "success",
"message": "Channel deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Channel not found. |
Ticket Agent
List ticket agents and their assigned groups.
| Name | Type | Description |
|---|---|---|
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
[]
{
"status": "success",
"data": [
{
"id": 7,
"name": "Sarah Lee",
"email": "[email protected]",
"group": "Technical Support"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
Objectives
Manage OKR objectives: list, create, update, delete. Filter by cycle, type (company/team/personal), team, employee or leader; archived=true lists the archived ones. Writes return the objective as stored. POST with the action parameter copies one into another cycle (target_cycle_id). PUT with retro writes its retro notes once its cycle is being scored. GET by id adds its retro, and with history=true every change with who, when and how. PUT with dry_run=true previews an update, an alignment change included, with the progress it moves; PUT with items updates up to 50 objectives at once, all or none. DELETE archives: one with key results needs cascade=true, dry_run=true previews it, permanent=true removes an archived one for good, and PUT with restore=true brings it back.
| Name | Type | Description |
|---|---|---|
id
|
string | Objective ID. GET: returns only that objective, in whichever cycle it is. For PUT/DELETE you may omit and supply title for name-based lookup. |
title
|
string | Objective title (required for POST; used as lookup key for PUT/DELETE when id omitted). POST with the title of an objective the same owner already has in that cycle creates nothing: the existing one is returned unchanged with created false. |
heading
|
string | Alias for `title`. |
description
|
string | — |
objective_type
|
string | "company", "team", or "personal". GET: omit to list every type the caller may read. POST: defaults to "personal" for members, required for admins. PUT cannot change it: it follows who owns the objective. |
cycle_id
|
string | Performance cycle ID. Uses active cycle if omitted. |
cycle_name
|
string | Filter by cycle name (partial match). Used when cycle_id is unknown. |
team_id
|
string | Team/department ID. Use to filter team objectives by exact ID. |
team_name
|
string | Team/department name (partial match). Used when team_id is unknown. |
employee_id
|
string | EmployeeDetails ID. Use to filter personal objectives by exact ID. |
employee_name
|
string | Employee full name (partial match). Used when employee_id is unknown. |
started_at
|
string | Start date (Y-m-d). Uses cycle start if omitted. |
finished_at
|
string | End date (Y-m-d). Uses cycle end if omitted. |
weight
|
string | Objective weight 1-100. Default 1. A value outside 1-100 is refused, not clamped. |
confidence
|
string | Confidence score, a whole number 0-10. Default 0. Anything else is refused. |
search
|
string | Search in objective title. |
leader_id
|
string | User ID of the objective leader (owner), as the employees tool returns it. GET: only objectives this user leads; "0" lists objectives with no leader. POST/PUT: sets the leader; "0" removes it. Admins: any objective. Members: own personal objectives only. |
leader_name
|
string | Leader (owner) full name (partial match). Used when leader_id is unknown. GET: only objectives led by a matching employee. POST/PUT: sets the leader. |
parent_objective_id
|
string | POST/PUT: ID of the parent objective this objective aligns to. Team objectives align to company or parent-team objectives; personal objectives to company, own-department or other-department objectives. Pass 0 to remove the alignment. Company objectives cannot have a parent. |
parent_key_result_id
|
string | POST/PUT: ID of the parent key result this objective contributes to, instead of a parent objective. Same rules as parent_objective_id. Pass 0 to remove the alignment. The key result then takes its progress from the objectives aligned to it instead of its own value, so a new objective with no progress yet can take it to 0%; the result warns with the before and after. Its own value comes back when no objective is aligned to it. |
collaborator_ids
|
string | POST/PUT: comma-separated user IDs of the collaborators (contributors) with edit access. Replaces the current list; "" removes all. Same permission rule as leader_id. |
dry_run
|
boolean | DELETE: true shows what would be archived, unlinked or removed, and how progress would change, and changes nothing. PUT: true shows the objective as the update would leave it and the progress it would move (an alignment change included), and changes nothing. |
archived
|
boolean | GET: true lists archived objectives instead, with when and by whom. Takes cycle_id and search. |
items
|
array | PUT: Up to 50 items, all saved or none: each an object with the id and the fields to change. Objectives take title, description, weight and leader_id; key results also take initial_value, target_value and unit_value. Values and confidence are not taken: record those as check-ins. dry_run=true shows the new progress and weight totals first. |
retro
|
object | PUT: Retro notes: an object with any of went_well, didnt_go_well, learned and carry_over, each text ("" clears it). Written while the cycle is in scoring or closed; returned in the scorecard. |
status
|
string | POST/PUT: Objective status: draft (seen only by its owner, its leader and admins, and left out of reminders, alignment, the health check and scorecards), approved or committed (unset reads as committed). Leaving draft is an approval, by an admin or the owner or leader of the objective it is aligned to. GET: only objectives with this status. |
history
|
boolean | GET by id: true adds history, newest first: each field change to the title, description, weight, targets, owner, leader, cycle, alignment, objective or final score, with when, by whom and through what (via web, api, mcp, assistant or cli), and when it was archived or restored. |
action
|
string | POST: "copy" with id (or source_objective_id). Copy an objective into another cycle (target_cycle_id), with all its key results or those in key_result_ids. Progress starts again; baseline_from_current=true starts each key result from where it ended. Check-ins, comments, tasks and alignment are not copied. The copy links back to the original, and a second copy into the same cycle is refused. dry_run=true previews it. |
source_objective_id
|
string | POST with action=copy: the objective to copy, when id is not given. |
target_cycle_id
|
string | POST with action=copy: the cycle to copy into. |
key_result_ids
|
array | POST with action=copy: only these key results. Default: all of them. |
baseline_from_current
|
boolean | POST with action=copy: true starts each copied key result from the value it ended at. |
carry_over_note
|
string | POST with action=copy: a note put at the top of the copy's description, saying why it carries over. |
cascade
|
boolean | DELETE: true archives the objective's key results with it. Required when it has key results or tasks linked to them; those tasks stay in their projects, unlinked. |
permanent
|
boolean | DELETE: true removes an item that is already archived, for good, with what was archived with it. Admins only; archive it first. Needs the id. |
restore
|
boolean | PUT with id: true brings back an archived objective, with the key results archived with it, and links back what archiving unlinked. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"cycle_id": 3,
"level": "company"
}
{
"status": "success",
"data": [
{
"id": 20,
"heading": "Grow ARR by 40%",
"level": "company",
"status": "on track",
"cycle_id": 3,
"description": "Increase annual recurring revenue"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"heading": "Improve NPS Score",
"level": "company",
"cycle_id": 3,
"type": "qualitative"
}
{
"status": "success",
"message": "Objective created successfully.",
"id": 21
}
| HTTP Code | Message |
|---|---|
422 |
The heading field is required. |
{
"id": 20,
"status": "at risk"
}
{
"status": "success",
"message": "Objective updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Objective not found. |
{
"id": 20,
"cascade": true
}
{
"success": true,
"message": "Objective #20 'Grow ARR by 40%' archived, with 2 key results; unlinked 1 task. PUT objectives with its id and restore=true brings it back.",
"data": {
"action": "archive",
"items": {
"key_results": [
{
"id": 30,
"title": "Sign 50 new enterprise clients"
}
]
}
}
}
| HTTP Code | Message |
|---|---|
409 |
Objective #20 'Grow ARR by 40%' still has 2 key results (#30 'Sign 50 new enterprise clients', #31 'Cut churn'). Pass cascade=true to archive the key results with it; the tasks stay in their projects, unlinked. dry_run=true shows the full effect first. |
Key Result
Manage OKR key results: list, create, update, delete, update progress. Link to objectives. Each key result lists the tasks working toward it and its linked KPIs (indicator_ids); GET by id adds the confidence history. Use the checkins tool for check-in history and who has not checked in. GET finds key results at risk with confidence_max, status and stale_weeks, and archived=true lists the archived ones. In a cycle being scored, PUT sets final_score and score_note; contributor_ids names who helps (no rights); GET by id with history=true lists every change with who, when and how. PUT with move_to_objective_id moves one to another objective in its cycle; PUT with items updates up to 50 at once, all or none; POST with the action parameter turns one into a task under target_key_result_id and archives it. Each takes dry_run=true to preview. DELETE archives it and unlinks its tasks: dry_run=true previews that with the new weight total, permanent=true removes an archived one for good, and PUT with restore=true brings it back.
| Name | Type | Description |
|---|---|---|
id
|
string | Key result ID. GET: returns only that key result. PUT/DELETE: if omitted, lookup is attempted by title. |
objective_id
|
string | Parent objective ID. Use this when you know the exact ID, otherwise use objective_title + context filters. |
objective_title
|
string | Parent objective title (partial match). Combine with team_name or employee_name to narrow down. |
objective_type
|
string | Objective owner type: "company", "team", or "personal". Helps narrow lookup. |
team_name
|
string | Team name (partial match). Used to find the objective when objective_type=team. |
employee_name
|
string | Employee full name (partial match). Used to find the objective when objective_type=personal. |
cycle_name
|
string | Performance cycle name (partial match, e.g. "Q2 2026"). GET: key results of every objective in that cycle. POST: narrows the objective lookup. |
cycle_id
|
string | Performance cycle ID. Alternative to cycle_name. |
title
|
string | Key result title (required for POST, used as name-based lookup for PUT/DELETE) |
description
|
string | — |
initial_value
|
string | Start value. Default 0. Up to 4 decimal places. |
target_value
|
string | Target value. Default 100. Up to 4 decimal places. |
current_value
|
string | Current progress value. Default equals initial_value. Up to 4 decimal places. |
measurement_type
|
string | POST/PUT: How progress is measured: omit (or "") for start to target, whichever way it runs; "maintain" keeps the value within threshold_min..threshold_max, 100% inside the range and 0% outside it. A milestone is a key result from 0 to 1, set to 1 when done. |
threshold_min
|
string | POST/PUT with measurement_type maintain: the lowest acceptable value. |
threshold_max
|
string | POST/PUT with measurement_type maintain: the highest acceptable value. |
progress_source
|
string | PUT: "indicator" makes the key result take its value from one KPI (indicator_id): the KPI's own total (sum, average, max, min or count, as it is set up) of its records dated within the cycle. Each new or changed record updates the value and today's check-in, noted with the KPI. "own" ends it. Refused while objectives are aligned to the key result. |
indicator_id
|
string | PUT with progress_source=indicator: the KPI (indicators) the value comes from. It is linked to the key result too. |
weight
|
string | Weight 1–100. Default 100. A value outside 1–100 is refused, not clamped. |
unit_value
|
string | Unit label (e.g. %, $, units). Default: %. |
confidence
|
string | Confidence level, a whole number 0–10. Anything else is refused. |
leader_id
|
string | User ID of the key result leader (owner), as the employees tool returns it. GET: only key results this user leads; "0" lists key results with no leader. POST/PUT: sets the leader; "0" removes it. Admins: any key result. Members: key results on their own personal objectives only. |
leader_name
|
string | Key result leader (owner) full name (partial match). Used when leader_id is unknown. GET: only key results led by a matching employee. POST/PUT: sets the leader. |
remarks
|
string | Check-in remarks / notes for this update. PUT saves today's check-in only when it sets current_value, confidence, remarks, status, blockers, source or category; a title, target, weight or leader change alone does not. |
status
|
string | PUT: the owner's status for today's check-in, separate from confidence. GET: only key results whose latest check-in has this status. |
confidence_max
|
string | GET: only key results whose confidence is at most this (1-10). The web calls 1 not started and 2-3 at risk, so 3 lists both; key results with no confidence yet (0) are left out. |
stale_weeks
|
string | GET: only key results with no check-in for at least this many weeks (1-52), including ones never checked in. |
archived
|
boolean | GET: true lists archived key results instead, with when and by whom, including those archived with their objective. Takes objective_id and cycle_id. |
indicator_ids
|
array | POST/PUT: IDs of the KPIs (indicators) to associate with the key result, as the web form's KPI picker does. Replaces the current list; an empty list removes them all. Members can link the KPIs they own or have access to. Linking does not change the key result's value. |
move_to_objective_id
|
string | PUT: ID of the objective to move the key result to, in the same cycle. Its check-ins, comments, tasks and the objectives aligned to it go with it, and both objectives are recalculated. Needs edit rights on both objectives. Send it on its own, with dry_run=true to preview the progress change. |
final_score
|
string | PUT: Final score from 0.0 to 1.0 in steps of 0.1, kept apart from live progress. Set while the cycle is in scoring, by the key result's leader, the objective's owner or leader, or an admin. null clears it. |
score_note
|
string | PUT: Why the key result got its final score. Same rules as final_score. |
contributor_ids
|
array | POST/PUT: User IDs of the people who help with this key result (list_employees). Informational: it gives no rights, which come from the objective's collaborators. Replaces the list; an empty list removes them all. |
history
|
boolean | GET by id: true adds history, newest first: each field change to the title, description, weight, targets, owner, leader, cycle, alignment, objective or final score, with when, by whom and through what (via web, api, mcp, assistant or cli), and when it was archived or restored. |
items
|
array | PUT: Up to 50 items, all saved or none: each an object with the id and the fields to change. Objectives take title, description, weight and leader_id; key results also take initial_value, target_value and unit_value. Values and confidence are not taken: record those as check-ins. dry_run=true shows the new progress and weight totals first. |
action
|
string | POST with id: "convert_to_task" turns the key result into a task working toward target_key_result_id, moves its tasks there and archives it. Its check-ins and comments stay with it, and restoring it brings it back. Refused while objectives are aligned to it. |
target_key_result_id
|
string | action=convert_to_task: ID of the key result the new task works toward, in the same cycle and one you can edit. |
assigned_to
|
string | action=convert_to_task: who the task is assigned to (user ID, name or email). Defaults to the key result's leader; required when it has none. |
due_date
|
string | action=convert_to_task: the task's due date (Y-m-d). Defaults to the end of the cycle. |
project_id
|
string | action=convert_to_task: project to put the task in. Optional. |
blockers
|
string | PUT: what is blocking progress, saved on today's check-in. |
source
|
string | PUT: where the current value came from (a report, a system), saved on today's check-in. |
category
|
string | PUT: tag for the check-in remark, as the web form offers. |
dry_run
|
boolean | DELETE: true shows what would be archived, unlinked or removed, and how progress would change, and changes nothing. Also previews a move, a convert or a PUT with items. |
permanent
|
boolean | DELETE: true removes an item that is already archived, for good, with what was archived with it. Admins only; archive it first. Needs the id. |
restore
|
boolean | PUT with id: true brings back an archived key result and links back what archiving unlinked. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"objective_id": 20
}
{
"status": "success",
"data": [
{
"id": 30,
"krs_title": "Sign 50 new enterprise clients",
"krs_init": 0,
"krs_tar": 50,
"krs_now": 18,
"krs_weight": 100,
"krs_unit": "%",
"objective_id": 20
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"krs_owner": 20,
"krs_title": "Reduce churn to < 5%",
"krs_init": 8,
"krs_tar": 5,
"krs_now": 8,
"krs_weight": 100,
"krs_unit": "%"
}
{
"status": "success",
"message": "Key result created successfully.",
"id": 31
}
| HTTP Code | Message |
|---|---|
422 |
The krs owner field is required. |
{
"id": 30,
"krs_now": 24
}
{
"status": "success",
"message": "Key result updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Key result not found. |
{
"id": 30,
"dry_run": true
}
{
"success": true,
"message": "Dry run, nothing changed. Archiving key result #30 'Sign 50 new enterprise clients' would archive it, and unlink 1 task. Its 4 check-ins would be kept with it. Objective #20 'Grow ARR by 40%': 40% to 55%, weight total 200 to 100."
}
| HTTP Code | Message |
|---|---|
404 |
Key result not found. |
Checkins
Key result check-ins. GET lists the check-in history, newest first; with missing=true it lists the key results in a cycle nobody checked in on during a week, with their leader and the owner a reminder goes to. POST records a check-in on a key result: its current value, confidence, status, remarks, blockers and the source of the value. The key result and objective scores update as the web form updates them. DELETE with id archives a blank or mistaken check-in (the key result keeps its value; dry_run=true previews, permanent=true removes an archived one for good), and PUT with id and restore=true brings it back.
| Name | Type | Description |
|---|---|---|
key_result_id
|
string | Key result ID. GET: only its check-ins. POST: required, the key result checked in on. |
objective_id
|
string | GET: only check-ins on this objective's key results. |
cycle_id
|
string | GET: only key results in this performance cycle. With missing=true it defaults to the current cycle. |
team_id
|
string | GET: only key results of this team's objectives. |
leader_id
|
string | GET: only key results led by this user ID; "0" for key results with no leader. |
leader_name
|
string | GET: only key results led by an employee whose name contains this, when leader_id is unknown. |
from
|
string | GET history: check-ins on or after this date (Y-m-d). |
to
|
string | GET history: check-ins on or before this date (Y-m-d). |
missing
|
boolean | GET: true lists the key results with no check-in in the week instead of the history. |
week
|
string | GET with missing=true: ISO week, as YYYY-Www (e.g. 2026-W44). Default: the current week. A cycle checked in every two weeks or every month counts the fortnight or month that week falls in. |
current_value
|
string | POST: the key result's new current value. Omit to check in confidence or remarks only. Refused on a key result whose progress comes from aligned objectives. |
confidence
|
string | POST: confidence 0-10. Label for the 0-10 confidence, as the web shows it: not_started (1), at_risk (2-3), in_progress (4-6), on_track (7-9), completed (10). Null when no confidence was given. |
status
|
string | POST: the owner's status, separate from confidence. |
remarks
|
string | POST: the check-in note. |
blockers
|
string | POST: what is blocking progress. |
source
|
string | POST: where the value came from, such as a report or a system. At most 255 characters. |
category
|
string | POST: tag for the remark, as the web form offers. |
id
|
string | DELETE/PUT: the check-in ID, as GET lists it. |
dry_run
|
boolean | DELETE: true shows what would be archived, unlinked or removed, and how progress would change, and changes nothing. |
permanent
|
boolean | DELETE: true removes an item that is already archived, for good, with what was archived with it. Admins only; archive it first. |
restore
|
boolean | PUT: true brings back an archived check-in. PUT does nothing else; record a new check-in with POST. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"missing": true,
"week": "2026-W44"
}
{
"success": true,
"data": [
{
"key_result_id": 177258,
"title": "Close 10 deals",
"leader_id": 7,
"leader_name": "Jane Doe",
"owner_id": 7,
"owner_name": "Jane Doe",
"owner_source": "key_result_leader",
"last_checkin_date": "2026-10-19"
}
]
}
| HTTP Code | Message |
|---|---|
422 |
week must be an ISO week such as 2026-W44. |
{
"key_result_id": 177258,
"current_value": 4,
"confidence": 7,
"status": "on_track",
"remarks": "Two deals signed"
}
{
"success": true,
"message": "Checked in on 'Close 10 deals': 40% progress."
}
| HTTP Code | Message |
|---|---|
403 |
You do not have permission to check in on this key result. |
No sample available for this method.
No sample available for this method.
Okr Comments
Discussion threads on OKR objectives and key results, the same threads the web pages show. GET lists the comments on one objective or key result, oldest first, each with its replies. POST adds a comment, or a reply with parent_id. PUT edits your own comment. DELETE archives a comment with its replies (dry_run=true previews, permanent=true removes an archived one for good), and PUT with restore=true brings it back; the author, admins and the objective's owner or leader may. Anyone who can read the objective may comment.
| Name | Type | Description |
|---|---|---|
target_type
|
string | What the thread is on: "objective" or "key_result". Required. |
target_id
|
string | ID of the objective or key result. Required. |
id
|
string | The comment ID. GET: only the thread holding that comment. PUT/DELETE: the comment to change. |
message
|
string | POST/PUT: the comment text. |
parent_id
|
string | POST: reply to this top-level comment. Replies go one level deep. |
mention_ids
|
array | POST: User IDs of the people to mention (list_employees). Each gets a notification in the app linking to the objective or key result; everyone mentioned must be able to read it. |
dry_run
|
boolean | DELETE: true shows what would be archived, unlinked or removed, and how progress would change, and changes nothing. |
permanent
|
boolean | DELETE: true removes an item that is already archived, for good, with what was archived with it. Admins only; archive it first. |
restore
|
boolean | PUT with id: true brings back an archived comment with the replies archived with it. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"target_type": "key_result",
"target_id": 177258
}
{
"success": true,
"data": [
{
"id": 12,
"message": "Pipeline looks thin for November",
"author": {
"id": 7,
"name": "Jane Doe"
},
"replies": []
}
]
}
| HTTP Code | Message |
|---|---|
404 |
Key result #177258 not found. |
{
"target_type": "objective",
"target_id": 81148,
"message": "Agreed at the Monday review."
}
{
"success": true,
"message": "Comment added."
}
| HTTP Code | Message |
|---|---|
422 |
message is required. |
No sample available for this method.
No sample available for this method.
Okr Alignment
How a performance cycle's OKRs align. Returns the objectives in tree order from the company objectives down, each with its depth, its key results and which objectives support each key result; then the team objectives (and with include_personal the personal ones) aligned to nothing, the objectives aligned to something the tree does not reach, and the company key results nothing contributes to. Defaults to the current cycle. Personal objectives are left out unless include_personal is true.
| Name | Type | Description |
|---|---|---|
cycle_id
|
string | Performance cycle ID. Default: the current cycle. |
include_personal
|
boolean | Also walk personal objectives. Default false, which keeps the tree to company and team objectives. |
{
"cycle_id": 21935
}
{
"success": true,
"data": {
"cycle": {
"id": 21935,
"name": "Q4 2026"
},
"objectives": [
{
"id": 81148,
"title": "Grow ARR",
"depth": 0,
"key_results": []
}
],
"unlinked_team_objectives": [],
"unlinked_personal_objectives": [],
"unreached_objectives": [],
"company_key_results_without_support": []
}
}
| HTTP Code | Message |
|---|---|
404 |
Performance cycle #1 not found. |
Okr Health
OKR health check for a performance cycle, in one call: objectives and key results with no owner (a key result counts as owned when someone would get its check-in reminder, and those owned through their objective are listed apart), objectives whose key result weights do not total 100 (a notice: scores are weighted averages), key results with no check-in for stale_weeks (default 2), key results off track or at confidence 3 or below for their last two check-ins, objectives with fewer than 2 key results or more than the workspace allows, team and personal objectives aligned to nothing, company key results nothing supports, and KPIs linked to no key result. Each check gives a count and up to 50 items. Defaults to the current cycle; narrow with objective_type or team_id. Only what you can read is counted.
| Name | Type | Description |
|---|---|---|
cycle_id
|
string | Performance cycle ID. Default: the current cycle. |
objective_type
|
string | Only objectives of this type, and their key results. |
team_id
|
string | Only this team's objectives. |
stale_weeks
|
string | Weeks without a check-in that count as stale, 1-52. Default 2. |
{
"cycle_id": 21935
}
{
"success": true,
"data": {
"cycle": {
"id": 21935,
"name": "Q4 2026",
"status": "active"
},
"company_weight_total": 100,
"unowned_key_results": {
"count": 0,
"items": []
},
"stale_key_results": {
"count": 3,
"items": [
{
"id": 177258,
"title": "Close 10 deals",
"owner_name": "Jane Doe",
"last_checkin": "2026-10-05"
}
]
}
}
}
| HTTP Code | Message |
|---|---|
404 |
Performance cycle #1 not found. |
Okr Webhooks
Webhooks for OKR events, admins only: a check-in saved, a key result changing status or going off track, an objective archived, a cycle going into scoring or closing (the event parameter lists them). GET lists them; POST subscribes a URL (target_url) to an event, optionally with a secret that signs each delivery (X-FlowyTeam-Signature: sha256 HMAC of the body); DELETE removes one by id. A delivery is JSON with the event, the company, when it was sent and the data; it goes out within a minute and is retried three times.
| Name | Type | Description |
|---|---|---|
id
|
string | DELETE: the webhook to remove. |
event
|
string | POST: the event to send: checkin.created (a check-in was saved), kr.status_changed (a check-in changed the key result's status), kr.off_track (it went off track), objective.archived, cycle.closing (a cycle went into scoring) and cycle.closed. GET: only this event. |
target_url
|
string | POST: the https URL deliveries are posted to. |
secret
|
string | POST: optional; signs each delivery so the receiver can check it came from FlowyTeam. Not shown again. |
No sample available for this method.
{
"event": "kr.off_track",
"target_url": "https:\/\/hooks.example.com\/okr",
"secret": "s3cret"
}
{
"success": true,
"data": {
"id": 12,
"event": "kr.off_track",
"target_url": "https:\/\/hooks.example.com\/okr",
"signed": true
}
}
| HTTP Code | Message |
|---|---|
403 |
Only an admin can manage OKR webhooks. |
No sample available for this method.
Okr Reminders
OKR reminders that FlowyTeam sends in the app, and by email when asked, under the admin's name. GET with preview=true shows who a reminder would reach and who it would leave out, without sending; show it to the user before sending. POST sends it. GET without preview lists the reminders sent. Audiences: the owners of key results with no check-in in a week, or everyone leading an objective or key result in a cycle. A person gets one reminder per audience per week. Admins only.
| Name | Type | Description |
|---|---|---|
preview
|
boolean | GET: true shows who the reminder described by the other arguments would reach, without sending. |
audience
|
string | Preview/POST: required. "missing_checkin": the owners of key results with no check-in in the week. "owners": everyone leading an objective or key result in the cycle. GET history: filter. |
cycle_id
|
string | Performance cycle. Default: the current cycle. GET history: filter. |
week
|
string | For missing_checkin: ISO week, as YYYY-Www (e.g. 2026-W44). Default: the current week. A cycle checked in every two weeks or every month counts the fortnight or month that week falls in. GET history: filter. |
team_id
|
string | Preview/POST: only key results of this team's objectives. |
objective_id
|
string | Preview/POST: only this objective and its key results. |
leader_id
|
string | Preview/POST, missing_checkin: only key results led by this user ID. |
email
|
boolean | POST: also email the reminder. Default false: in the app only. |
note
|
string | POST: optional note from you, shown under your name. At most 500 characters. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"preview": true,
"audience": "missing_checkin",
"week": "2026-W44"
}
{
"success": true,
"data": {
"recipients": [
{
"user_id": 7,
"name": "Jane Doe",
"key_results": [
{
"id": 177258,
"title": "Close 10 deals"
}
]
}
],
"skipped": [],
"unowned_key_results": []
}
}
| HTTP Code | Message |
|---|---|
403 |
Only admins can send OKR reminders. |
{
"audience": "missing_checkin",
"email": true,
"note": "Review is on Monday."
}
{
"success": true,
"message": "Reminder sent to 3 people."
}
| HTTP Code | Message |
|---|---|
422 |
Nobody to remind: everyone in this audience has been reminded this week. |
Indicators
Manage KPI indicators: list, create, update, delete. Filter by category, employee. Lookup by title or ID. GET returns each KPI's owner, red and green thresholds, latest recorded value and the key results it is linked to.
| Name | Type | Description |
|---|---|---|
id
|
string | Indicator ID. GET: returns only that indicator. For PUT/DELETE, name-based lookup also supported via `title`. |
title
|
string | Indicator title (required for POST; used for name-based lookup on PUT/DELETE) |
category_id
|
string | Indicator type/category ID (use category_name if ID unknown) |
category_name
|
string | Indicator category name — resolved to category_id automatically |
employee_id
|
string | Employee user ID to filter or assign (use employee_name if ID unknown) |
employee_name
|
string | Employee name — resolved to employee_id automatically for filter or assign |
target_value
|
number | string | Target value for the indicator |
unit_value
|
string | Unit of measurement (e.g. %, $, units) |
occurance
|
string | "daily", "weekly", "monthly", "quarterly", or "yearly" |
description
|
string | Description or notes for the indicator |
search
|
string | Search indicators by title |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"employee_id": 7,
"frequency": "monthly"
}
{
"status": "success",
"data": [
{
"id": 40,
"name": "Monthly Sales Revenue",
"target": 100000,
"unit": "USD",
"frequency": "monthly",
"employee_id": 7,
"category_id": 2
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"name": "Customer Satisfaction Score",
"target": 90,
"unit": "%",
"frequency": "monthly",
"employee_id": 7,
"category_id": 2
}
{
"status": "success",
"message": "KPI created successfully.",
"id": 41
}
| HTTP Code | Message |
|---|---|
422 |
The name field is required. |
{
"id": 40,
"target": 120000
}
{
"status": "success",
"message": "KPI updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
KPI not found. |
{
"id": 40
}
{
"status": "success",
"message": "KPI deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
KPI not found. |
Indicator Category
Manage KPI indicator categories/types: list, create, update, delete. Lookup by name or ID.
| Name | Type | Description |
|---|---|---|
id
|
string | Category ID. GET: only that category. PUT/DELETE: if omitted, lookup falls back to indicator_type_name or name. |
indicator_type_name
|
string | Category name (required for POST; used as name-based lookup key for PUT/DELETE). |
name
|
string | Alias for indicator_type_name. Can be used for lookup in PUT/DELETE. |
search
|
string | Partial name search for GET. |
status
|
string | Optional status filter. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"all": true
}
{
"status": "success",
"data": [
{
"id": 1,
"indicator_type_name": "Sales"
},
{
"id": 2,
"indicator_type_name": "Customer Success"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"indicator_type_name": "Engineering"
}
{
"status": "success",
"message": "Category created successfully.",
"id": 3
}
| HTTP Code | Message |
|---|---|
422 |
The indicator type name field is required. |
{
"id": 1,
"indicator_type_name": "Revenue & Sales"
}
{
"status": "success",
"message": "Category updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Category not found. |
{
"id": 1
}
{
"status": "success",
"message": "Category deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Category not found. |
Indicator Record
Manage KPI indicator records (actual values per period): list, add, delete. Lookup by indicator_id + period_key.
| Name | Type | Description |
|---|---|---|
id
|
string | Record ID (for single GET or DELETE) |
indicator_id
|
string | KPI indicator ID (required for GET list and POST) |
period_key
|
string | Period date in Y-m-d format (required for POST; used with indicator_id as unique lookup key) |
current_value
|
number | string | Actual value for the period |
target_value
|
number | string | Target value for the period |
remark
|
string | Optional remark / notes for the record |
updated_by
|
string | User ID of who updated this record |
start_date
|
string | Filter records from this date (Y-m-d) |
end_date
|
string | Filter records to this date (Y-m-d) |
month
|
string | Filter by month number (1-12) |
year
|
string | Filter by 4-digit year |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"indicator_id": 40,
"month": 5,
"year": 2026
}
{
"status": "success",
"data": [
{
"id": 200,
"indicator_id": 40,
"period_key": "01-5-2026",
"current_value": 95000,
"target_value": 100000,
"score": 95,
"remark": "On track"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"indicator_id": 40,
"period_key": "01-6-2026",
"current_value": 112000,
"target_value": 100000,
"remark": "Exceeded target"
}
{
"status": "success",
"message": "Record updated successfully."
}
| HTTP Code | Message |
|---|---|
422 |
The indicator id field is required. |
{
"id": 200
}
{
"status": "success",
"message": "Record deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Record not found. |
Performance Cycle
Manage OKR performance cycles: list, create, update, delete. Lookup by name or ID. GET flags the cycle the workspace is in today as current and gives each status. End of cycle: PUT status moves a cycle to scoring (final scores on key results open) and to closed, which freezes the scores and locks it; dry_run=true lists what is missing first and fill_missing=true fills unscored key results. GET by id with scorecard=true returns each objective and key result with its baseline, target, final value, score and retro, and averages per objective type and team; history=true lists who changed the cycle.
| Name | Type | Description |
|---|---|---|
id
|
string | Cycle ID. GET: returns only that cycle. PUT/DELETE: if omitted, lookup is attempted by name. |
name
|
string | Cycle name (required for POST; used as fallback lookup key for PUT/DELETE). |
cycle_type
|
string | Cycle type: annually, semiannually, quarterly, monthly, biweekly, weekly. POST default quarterly. |
started_at
|
string | Start date (Y-m-d). |
finished_at
|
string | End date (Y-m-d). |
search
|
string | Search cycles by name (GET). |
show_employee_status
|
boolean | When true (GET), returns each employee's archived OKR totals instead of listing cycles. A cycle has rows only after it has ended and been archived, when the next cycle begins, so the current cycle returns none. Use cycle_id to filter by a specific cycle. The counts are confidence bands, as the web report groups them, not progress: complete is confidence 9-10, ongoing 5-8, at risk 1-4 and no track no confidence given; completion_pct is the share in the complete band. They are for learning reviews, not for rating people. |
cycle_id
|
string | Filter employee performance status by this cycle ID (used with show_employee_status). |
status
|
string | PUT: Cycle status, set by admins: planning, active, scoring or closed (unset reads as active). scoring opens final scores on its key results. closed freezes the objective scores and locks its objectives, key results and check-ins everywhere, the web included; it needs every key result scored and checked in within the last two weeks, unless fill_missing=true stores the suggested score for the unscored ones. dry_run=true with closed lists what is missing. A closed cycle reopens by setting scoring. |
dry_run
|
boolean | PUT with status: true reports what the change would do, and with closed the key results that are unscored or have no check-in in two weeks, and changes nothing. |
fill_missing
|
boolean | PUT with status closed: true gives every unscored key result its suggested score (live progress rounded to 0.1), marked automatic, and closes the cycle. |
scorecard
|
boolean | GET with id: true returns the cycle's scorecard instead: each objective (company and team; personal ones with include_personal) with its key results' baseline, target, final value, live progress, final and suggested score, note and retro, plus averages per objective type and per team. Paged by objective. |
include_personal
|
boolean | GET with scorecard: also list personal objectives. Default false. |
history
|
boolean | GET with id: true adds the cycle's change history (status, name, dates) with who and when. |
checkin_cadence
|
string | POST/PUT: How often key results in the cycle are checked in: weekly (the default), biweekly (fortnights from the week the cycle starts) or monthly. Missing check-ins and reminders follow it. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"per_page": 10,
"page": 1
}
{
"status": "success",
"data": [
{
"id": 3,
"name": "Q2 2026",
"cycle_type": "quarterly",
"started_at": "2026-04-01",
"finished_at": "2026-06-30"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"name": "Q3 2026",
"cycle_type": "quarterly",
"started_at": "2026-07-01",
"finished_at": "2026-09-30"
}
{
"status": "success",
"message": "Performance cycle created successfully.",
"id": 4
}
| HTTP Code | Message |
|---|---|
422 |
The name field is required. |
{
"id": 3,
"name": "Q2 2026 Updated"
}
{
"status": "success",
"message": "Cycle updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Cycle not found. |
{
"id": 3
}
{
"status": "success",
"message": "Cycle deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Cycle not found. |
Holiday
Manage company holidays: list, create, update, delete. Filter by year, upcoming/past. Lookup by ID or holiday name.
| Name | Type | Description |
|---|---|---|
id
|
string | Holiday ID. For PUT/DELETE you may also use `occassion` or `name` for name-based lookup instead. |
occassion
|
string | Holiday occasion/name (required for POST; used as name-based lookup key for PUT/DELETE). |
name
|
string | Alias for `occassion`. Used for name-based lookup on PUT/DELETE. |
holiday_name
|
string | Alias for `occassion`. |
title
|
string | Alias for `occassion`. |
date
|
string | Holiday date in Y-m-d format (required for POST; optional for PUT). |
year
|
string | Filter list results by year (GET only). |
status
|
string | "upcoming" returns holidays on or after today; "past" returns holidays before today (GET only). |
search
|
string | Partial name search against the occasion field (GET only). |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"year": 2026,
"status": "upcoming"
}
{
"status": "success",
"data": [
{
"id": 10,
"occasion": "Independence Day",
"date": "17\/08\/2026"
},
{
"id": 11,
"occasion": "Christmas Day",
"date": "25\/12\/2026"
}
]
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"occasion": "New Year's Day",
"date": "01\/01\/2027"
}
{
"status": "success",
"message": "Holiday created successfully.",
"id": 12
}
| HTTP Code | Message |
|---|---|
422 |
The date field is required. |
{
"id": 10,
"occasion": "National Independence Day"
}
{
"status": "success",
"message": "Holiday updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Holiday not found. |
{
"id": 10
}
{
"status": "success",
"message": "Holiday deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Holiday not found. |
Leads
Manage sales leads: list, create, update, delete. Filter by status, agent, date range.
| Name | Type | Description |
|---|---|---|
id
|
string | Lead ID — or use "client_name" to look up by name instead |
client_name
|
string | Lead contact name (required for POST; also used to look up lead for PUT/DELETE) |
company_name
|
string | — |
email
|
string | — |
mobile
|
string | — |
website
|
string | — |
address
|
string | — |
note
|
string | — |
agent_id
|
string | Agent user ID (use agent_name if ID unknown) |
agent_name
|
string | Agent name — resolved to agent_id automatically |
status_id
|
string | Lead status ID (use status_name if ID unknown) |
status_name
|
string | Lead status name (e.g. "New", "In Progress") — resolved to status_id automatically |
source_id
|
string | Lead source ID (use source_name if ID unknown) |
source_name
|
string | Lead source name (e.g. "Website", "Referral") — resolved to source_id automatically |
next_follow_up
|
string | Whether a follow-up is scheduled (yes/no). Default: yes |
next_follow_up_date
|
string | Follow-up date (Y-m-d) — stored as a follow-up record, not on the lead directly |
startDate
|
string | Filter from date |
endDate
|
string | Filter to date |
search
|
string | — |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"client": "lead",
"per_page": 20,
"page": 1
}
{
"status": "success",
"data": [
{
"id": 55,
"client_name": "PT Maju Jaya",
"email": "[email protected]",
"mobile": "+6281234567890",
"status_id": 2,
"agent_id": 7
}
],
"total": 1
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"client_name": "PT Maju Jaya",
"email": "[email protected]",
"mobile": "+6281234567890",
"status_id": 1,
"agent_id": 7
}
{
"status": "success",
"message": "Lead created successfully.",
"id": 56
}
| HTTP Code | Message |
|---|---|
422 |
The client name field is required. |
{
"id": 55,
"status": 3,
"note": "Proposal sent"
}
{
"status": "success",
"message": "Lead updated successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Lead not found. |
{
"id": 55
}
{
"status": "success",
"message": "Lead deleted successfully."
}
| HTTP Code | Message |
|---|---|
404 |
Lead not found. |
Invoices
Manage client invoices: list, create, update status, delete. Filter by client, status, date range.
| Name | Type | Description |
|---|---|---|
id
|
string | Invoice ID — or use invoice_number to look up |
invoice_number
|
string | Invoice number to look up (e.g. INV#0001) for PUT/DELETE |
client_name
|
string | Client company name or contact name to filter or use for create |
client_id
|
string | — |
project_id
|
string | — |
issue_date
|
string | Issue date (Y-m-d, defaults to today) |
due_date
|
string | Due date (Y-m-d) |
sub_total
|
number | Subtotal amount |
total
|
number | Total amount (required for POST) |
currency_id
|
string | — |
note
|
string | — |
status
|
string | Filter invoices by status (GET) or update status (PUT) |
search
|
string | Search in invoice number or client name |
startDate
|
string | — |
endDate
|
string | — |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"client_name": "Acme Corp"
}
{
"success": true,
"data": [
{
"id": 1,
"invoice_number": 1,
"client_id": 3,
"total": 5000,
"issue_date": "2026-05-01",
"due_date": "2026-05-31"
}
],
"total": 1
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"client_name": "Acme Corp",
"total": 5000,
"issue_date": "2026-05-01",
"due_date": "2026-05-31"
}
{
"success": true,
"message": "Invoice created for client #3 with total 5000."
}
| HTTP Code | Message |
|---|---|
422 |
"total" is required to create an invoice. |
{
"invoice_number": "INV#0001",
"due_date": "2026-06-15"
}
{
"success": true,
"message": "Invoice #1 updated."
}
| HTTP Code | Message |
|---|---|
404 |
Invoice not found. |
{
"invoice_number": "INV#0001"
}
{
"success": true,
"message": "Invoice #1 deleted."
}
| HTTP Code | Message |
|---|---|
404 |
Invoice not found. |
Estimates
Manage client estimates/quotes: list, create, update, delete. Filter by client, status, date.
| Name | Type | Description |
|---|---|---|
id
|
string | Estimate ID — or use estimate_number to look up |
estimate_number
|
string | Estimate number for lookup (PUT/DELETE), e.g. EST#0001 |
client_name
|
string | Client company name or contact name for create/filter |
client_id
|
string | — |
valid_till
|
string | Expiry date (Y-m-d) |
sub_total
|
number | — |
total
|
number | Total amount (required for POST) |
discount
|
number | — |
discount_type
|
string | percent or fixed |
currency_id
|
string | — |
note
|
string | — |
status
|
string | draft/sent/declined/accepted — filter (GET) or update (PUT) |
search
|
string | — |
startDate
|
string | — |
endDate
|
string | — |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"status": "draft"
}
{
"success": true,
"data": [
{
"id": 1,
"estimate_number": 1,
"client_id": 3,
"total": 12000,
"valid_till": "2026-06-30",
"status": "draft"
}
],
"total": 1
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"client_name": "Acme Corp",
"total": 12000,
"valid_till": "2026-06-30",
"status": "draft"
}
{
"success": true,
"message": "Estimate created with total 12000 for client #3."
}
| HTTP Code | Message |
|---|---|
422 |
"total" is required to create an estimate. |
{
"estimate_number": "EST#0001",
"status": "sent"
}
{
"success": true,
"message": "Estimate #1 updated."
}
| HTTP Code | Message |
|---|---|
404 |
Estimate not found. |
{
"estimate_number": "EST#0001"
}
{
"success": true,
"message": "Estimate #1 deleted."
}
| HTTP Code | Message |
|---|---|
404 |
Estimate not found. |
Events
Manage company events/calendar: list, create, update, delete. Filter by month, year, date range.
| Name | Type | Description |
|---|---|---|
id
|
string | Event ID — or use event_name to look up by name |
event_name
|
string | Event name (required for POST, or use to look up for PUT/DELETE) |
where
|
string | Event location |
description
|
string | — |
start_date_time
|
string | Start date/time (Y-m-d H:i or Y-m-d, required for POST) |
end_date_time
|
string | End date/time (Y-m-d H:i or Y-m-d, required for POST) |
repeat
|
string | Whether the event repeats. Default: no |
repeat_every
|
integer | Repeat interval (e.g. every 2 weeks) |
repeat_cycles
|
integer | Number of times the event repeats |
repeat_type
|
string | Repeat frequency unit |
label_color
|
string | Bootstrap color class or hex color (default: bg-info) |
month
|
string | Filter list by month number (1–12) |
year
|
string | Filter list by 4-digit year |
startDate
|
string | Filter events starting on or after this date (Y-m-d) |
endDate
|
string | Filter events starting on or before this date (Y-m-d) |
search
|
string | Partial name search against event_name |
upcoming
|
boolean | When true (default), only return events whose start_date_time is today or in the future. Pass false to include past events. |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"month": "6",
"year": "2026"
}
{
"success": true,
"data": [
{
"id": 15,
"event_name": "Q2 All-Hands Meeting",
"where": "Main Conference Room",
"start_date_time": "2026-06-15 09:00:00",
"end_date_time": "2026-06-15 11:00:00",
"repeat": "no"
}
],
"total": 1
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"event_name": "Q2 All-Hands Meeting",
"start_date_time": "2026-06-15 09:00",
"end_date_time": "2026-06-15 11:00",
"where": "Main Conference Room"
}
{
"success": true,
"message": "Event 'Q2 All-Hands Meeting' created on 2026-06-15."
}
| HTTP Code | Message |
|---|---|
422 |
"event_name" is required to create an event. |
{
"event_name": "Q2 All-Hands Meeting",
"where": "Zoom"
}
{
"success": true,
"message": "Event 'Q2 All-Hands Meeting' updated."
}
| HTTP Code | Message |
|---|---|
404 |
Event not found. |
{
"id": "15"
}
{
"success": true,
"message": "Event 'Q2 All-Hands Meeting' deleted."
}
| HTTP Code | Message |
|---|---|
404 |
Event not found. |
Expenses
Manage expenses and claims: list, create, update status (approve/reject), delete. Filter by employee, category, status, date range.
| Name | Type | Description |
|---|---|---|
id
|
string | Expense ID — or use item_name to look up by name |
item_name
|
string | Expense item name (required for POST, or use to look up for PUT/DELETE) |
purchase_date
|
string | Purchase date (Y-m-d, required for POST) |
purchase_from
|
string | Where it was purchased from |
price
|
number | Expense amount (required for POST) |
currency_id
|
string | Currency ID (defaults to company currency if omitted) |
expense_category_id
|
string | Expense category ID (use expense_category_name if ID unknown) |
expense_category_name
|
string | Expense category name — resolved to expense_category_id automatically |
employee_name
|
string | Filter by employee name (GET) or look up user to assign (POST) |
user_id
|
string | Employee user ID (admin can specify; defaults to self) |
project_id
|
string | Link expense to a project (optional) |
description
|
string | Additional notes about the expense |
status
|
string | Filter (GET) or update status (PUT) |
can_claim
|
integer | boolean | 1 = claim reimbursement request, 0 = regular expense |
startDate
|
string | Filter expenses from this purchase date (Y-m-d) |
endDate
|
string | Filter expenses up to this purchase date (Y-m-d) |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"status": "pending"
}
{
"success": true,
"data": [
{
"id": 99,
"item_name": "Team Lunch",
"purchase_date": "2026-05-10",
"price": 250,
"status": "pending",
"can_claim": 1,
"user_id": 7
}
],
"total": 1
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"item_name": "Team Lunch",
"purchase_date": "2026-05-10",
"price": 250,
"can_claim": 1
}
{
"success": true,
"message": "Expense 'Team Lunch' created (ID #99)."
}
| HTTP Code | Message |
|---|---|
422 |
'item_name' is required. |
{
"id": "99",
"status": "approved"
}
{
"success": true,
"message": "Expense 'Team Lunch' updated."
}
| HTTP Code | Message |
|---|---|
404 |
Expense not found. |
{
"id": "99"
}
{
"success": true,
"message": "Expense 'Team Lunch' (ID #99) deleted."
}
| HTTP Code | Message |
|---|---|
403 |
Only admins can delete expenses. |
Expense Category
Manage expense categories: list, create, update, delete. Admin-only for write operations.
| Name | Type | Description |
|---|---|---|
id
|
string | Category ID — or use category_name to look up by name instead |
category_name
|
string | Category name (required for POST; also used to look up for PUT/DELETE) |
search
|
string | Partial name search against category_name (GET only) |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
[]
{
"success": true,
"data": [
{
"id": 1,
"category_name": "Travel & Accommodation"
},
{
"id": 2,
"category_name": "Office Supplies"
}
],
"total": 2
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"category_name": "Marketing"
}
{
"success": true,
"message": "Expense category 'Marketing' created."
}
| HTTP Code | Message |
|---|---|
422 |
"category_name" is required. |
{
"category_name": "Travel & Accommodation"
}
{
"success": true,
"message": "Expense category 'Travel & Accommodation' updated."
}
| HTTP Code | Message |
|---|---|
404 |
Expense category not found. |
{
"id": "1"
}
{
"success": true,
"message": "Expense category 'Travel & Accommodation' deleted."
}
| HTTP Code | Message |
|---|---|
403 |
Only admins can delete expense categories. |
Notices
Manage notice board: list, create, update, delete. Filter by date range or audience (all/employee/client).
| Name | Type | Description |
|---|---|---|
id
|
string | Notice ID — or use heading to look up by title |
heading
|
string | Notice title (required for POST, or use to look up for PUT/DELETE) |
title
|
string | Alias for `heading`. |
name
|
string | Alias for `heading`. |
description
|
string | Notice content body (required for POST) |
to
|
string | Target audience (default: all) |
search
|
string | Search keyword matched against notice heading |
startDate
|
string | Filter notices created on or after this date (Y-m-d) |
endDate
|
string | Filter notices created on or before this date (Y-m-d) |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"to": "employee"
}
{
"success": true,
"data": [
{
"id": 12,
"heading": "Office Closed on Friday",
"description": "The office will be closed this Friday for maintenance.",
"to": "all",
"created_at": "2026-05-08T10:00:00Z"
}
],
"total": 1
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"heading": "Office Closed on Friday",
"description": "The office will be closed this Friday for maintenance.",
"to": "all"
}
{
"success": true,
"message": "Notice 'Office Closed on Friday' created (ID #12)."
}
| HTTP Code | Message |
|---|---|
422 |
'heading' is required. |
{
"heading": "Office Closed on Friday",
"description": "Updated: Office closed Friday AND Monday."
}
{
"success": true,
"message": "Notice 'Office Closed on Friday' updated."
}
| HTTP Code | Message |
|---|---|
404 |
Notice not found. |
{
"id": "12"
}
{
"success": true,
"message": "Notice 'Office Closed on Friday' (ID #12) deleted."
}
| HTTP Code | Message |
|---|---|
403 |
Only admins can delete notices. |
Contracts
Manage client contracts: list, create, update, delete. Filter by client, status, date range.
| Name | Type | Description |
|---|---|---|
id
|
string | Contract ID — or use subject to look up by title |
subject
|
string | Contract title (required for POST, or use to look up for PUT/DELETE) |
client_name
|
string | Client company name to find client user ID (used for create or filter) |
client_id
|
string | Client user ID (alternative to client_name) |
contract_type_id
|
string | Contract type ID (auto-resolved from contract_type_name if omitted) |
contract_type_name
|
string | Contract type name (e.g. "Service Agreement") — resolved to contract_type_id |
start_date
|
string | Contract start date (Y-m-d) |
end_date
|
string | Contract end date (Y-m-d) |
description
|
string | Contract description or notes |
amount
|
number | Contract monetary value |
search
|
string | Search keyword matched against contract subject |
startDate
|
string | Filter contracts with start_date on or after this date (Y-m-d) |
endDate
|
string | Filter contracts with end_date on or before this date (Y-m-d) |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"client_name": "Acme Corp"
}
{
"success": true,
"data": [
{
"id": 7,
"subject": "Annual Support Contract",
"client_id": 3,
"amount": 24000,
"start_date": "2026-01-01",
"end_date": "2026-12-31"
}
],
"total": 1
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"subject": "Annual Support Contract",
"client_name": "Acme Corp",
"start_date": "2026-01-01",
"end_date": "2026-12-31",
"amount": 24000
}
{
"success": true,
"message": "Contract 'Annual Support Contract' created (ID #7)."
}
| HTTP Code | Message |
|---|---|
422 |
'subject' is required. |
{
"subject": "Annual Support Contract",
"amount": 30000
}
{
"success": true,
"message": "Contract 'Annual Support Contract' updated."
}
| HTTP Code | Message |
|---|---|
404 |
Contract not found. |
{
"id": "7"
}
{
"success": true,
"message": "Contract 'Annual Support Contract' (ID #7) deleted."
}
| HTTP Code | Message |
|---|---|
404 |
Contract not found. |
Timelogs
Manage project/task time logs: list, create (start timer), update (stop timer/edit), delete. Filter by project, employee, date.
| Name | Type | Description |
|---|---|---|
id
|
string | Time log ID. GET: only that time log. Required for DELETE; used for PUT when provided. |
project_name
|
string | Project name — resolved to project_id automatically |
project_id
|
string | Project ID (alternative to project_name) |
task_name
|
string | Task heading — resolved to task_id automatically |
task_id
|
string | Task ID (alternative to task_name) |
employee_name
|
string | Employee name — resolved to user_id for filtering or creating |
user_id
|
string | Employee user ID (admin can specify; defaults to self) |
start_time
|
string | Start datetime (Y-m-d H:i or Y-m-d H:i:s). Defaults to now() on POST. |
end_time
|
string | End datetime — provide on PUT to stop a running timer; omit to keep timer running |
memo
|
string | Optional note or description for the time log entry |
date
|
string | Filter by specific date (Y-m-d) |
month
|
string | Filter by month number (1–12) |
year
|
string | Filter by year (e.g. 2025) |
active_only
|
boolean | string | If true, return only running timers (no end_time) |
page
|
integer | Page of a list, starting at 1. Default 1. A list result gives next_page while more rows remain. |
per_page
|
integer | Rows per page of a list, 1-100. Default 25. |
{
"month": "5",
"year": "2026"
}
{
"success": true,
"data": [
{
"id": 88,
"user_id": 7,
"project_id": 12,
"task_id": 101,
"start_time": "2026-05-10 09:00:00",
"end_time": "2026-05-10 17:30:00",
"total_hours": 8.5,
"total_minutes": 510,
"memo": "Working on hero section"
}
],
"total": 1
}
| HTTP Code | Message |
|---|---|
401 |
Unauthenticated. |
{
"project_name": "Mobile App v2",
"task_name": "Design landing page",
"memo": "Working on hero section"
}
{
"success": true,
"message": "Timer started for project ID #12, task ID #101 (log ID #88)."
}
| HTTP Code | Message |
|---|---|
422 |
Failed to start timer. |
{
"id": "88",
"end_time": "2026-05-10 17:30"
}
{
"success": true,
"message": "Time log #88 updated (timer stopped). Total logged: 8h 30m."
}
| HTTP Code | Message |
|---|---|
404 |
Time log not found or no active timer running. |
{
"id": "88"
}
{
"success": true,
"message": "Time log #88 deleted."
}
| HTTP Code | Message |
|---|---|
403 |
Only admins can delete time logs. |
Start building with the FlowyTeam OKR MCP server
Create your free account and connect your AI agent to FlowyTeam in minutes.
Start for Free