Schedule & attendance
Staff schedule: shifts, publishing, open shifts, clock-in/out with breaks, who is on shift now, attendance records, timesheet, absences and shift swaps, availability, KPI and payroll. Location permissions follow schedule.*; pay rates and payroll require pay.* and are omitted otherwise.
GETList shifts
Shifts at locations where the token has schedule.view, by date. Drafts and unpublished changes are included (status: draft, published, changed; removed — deleted but not yet published). Default period — the current week, max 62 days.
| Token scope | read |
|---|---|
| Permission |
schedule.view |
| MCP tool | schedule_shifts_list · read-only |
Query parameters
page
integer
1. Minimum 1.
per_page
integer
25. Value 1–100.
date_from
date
date_to
date
location_ids
array<integer>
user_ids
array<integer>
status
string
draftpublishedchangedopen
boolean
true — only open (unassigned) shifts.
include_removed
boolean
updated_since
date-time
Responses
| Status | Description |
|---|---|
| 200 | OK — a page of results with meta |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.view
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/shifts?date_from=2026-11-01' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/shifts?date_from=2026-11-01', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data, meta } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/shifts",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"date_from":"2026-11-01"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/shifts', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'date_from' => '2026-11-01',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_shifts_list",
"arguments": {
"date_from": "2026-11-01"
}
}
}{
"data": [
{
"id": 70211,
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"position_id": 12,
"template_id": 3,
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"overnight": false,
"break_minutes": 60,
"minutes": 480,
"status": "published",
"published": true,
"removed": false,
"open": false,
"note": null,
"color": null,
"updated_at": "2026-11-01T12:00:00+05:00"
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 1,
"has_more": false
}
}GETGet the schedule board of a location
What the schedule grid shows for one location: employees with hour totals, shifts with warnings and attendance, absences, availability, demand coverage and totals. Shift cost, employee rates and the budget are included only with pay.rates.view at the location.
| Token scope | read |
|---|---|
| Permission |
schedule.view |
| MCP tool | schedule_board · read-only |
Query parameters
location_id
integer
Required date_from
date
date_to
date
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.view
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/board?location_id=12' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/board?location_id=12', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/board",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"location_id":12},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/board', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'location_id' => 12,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_board",
"arguments": {
"location_id": 12
}
}
}{
"data": {
"location": {
"id": 4609,
"name": "Abay Ave"
},
"date_from": "2026-11-02",
"date_to": "2026-11-08",
"days": [
{
"date": "2026-11-02",
"weekday": 1,
"holiday": null
}
],
"employees": [
{
"id": 311,
"name": "Aigerim Sadykova",
"position_id": 12,
"position": "Barista",
"pay_type": "hourly",
"rate": 1500,
"totals": {
"minutes": 2400,
"shifts": 5,
"cost": 60000
}
}
],
"shifts": [
{
"id": 70211,
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"position_id": 12,
"template_id": 3,
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"overnight": false,
"break_minutes": 60,
"minutes": 480,
"status": "published",
"published": true,
"removed": false,
"open": false,
"note": null,
"color": null,
"updated_at": "2026-11-01T12:00:00+05:00",
"warnings": [],
"cost": 12000,
"attendance": null
}
],
"absences": [
{
"id": 912,
"user_id": 311,
"type": "vacation",
"from": "2026-12-01",
"to": "2026-12-14",
"status": "approved"
}
],
"availability": [
{
"user_id": 311,
"date": "2026-11-06",
"kind": "unavailable",
"start": null,
"end": null,
"note": "University"
}
],
"coverage": {
"2026-11-02": [
{
"position_id": 12,
"start": "08:00",
"end": "12:00",
"need": 2,
"have": 1
}
]
},
"totals": {
"minutes": 9600,
"cost": 240000,
"budget": 300000
},
"unpublished": 3,
"warnings_count": 1,
"can_pay": true,
"can_edit": true,
"can_publish": true,
"currency": "KZT"
}
}POSTCreate a shift
Creates a draft shift (employees see it after schedule.publish). Rejected in a closed month.
| Token scope | write |
|---|---|
| Permission |
schedule.edit |
| MCP tool | schedule_shifts_create |
Request body
location_id
integer
Required schedule.edit there).
user_id
integer | null
null — open shift anyone eligible can claim.
position_id
integer | null
template_id
integer | null
date
date
Required start
string
HH:MM (local). Required without a template.
end
string
HH:MM; earlier than start — ends next day.
break_minutes
integer
note
string | null
color
string | null
Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.edit, is read-only
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/shifts' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"user_id": 42,
"position_id": 42,
"template_id": 42,
"date": "2026-11-01"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/shifts', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"user_id": 42,
"position_id": 42,
"template_id": 42,
"date": "2026-11-01"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/shifts",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"user_id":42,"position_id":42,"template_id":42,"date":"2026-11-01"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/shifts', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'user_id' => 42,
'position_id' => 42,
'template_id' => 42,
'date' => '2026-11-01',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_shifts_create",
"arguments": {
"location_id": 12,
"user_id": 42,
"position_id": 42,
"template_id": 42,
"date": "2026-11-01"
}
}
}{
"data": {
"id": 70211,
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"position_id": 12,
"template_id": 3,
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"overnight": false,
"break_minutes": 60,
"minutes": 480,
"status": "published",
"published": true,
"removed": false,
"open": false,
"note": null,
"color": null,
"updated_at": "2026-11-01T12:00:00+05:00"
}
}PATCHUpdate a shift
Partial update. A published shift becomes changed until published again.
| Token scope | write |
|---|---|
| Permission |
schedule.edit |
| MCP tool | schedule_shifts_update |
Path parameters
id
integer
Required Request body
location_id
integer
schedule.edit there).
user_id
integer | null
null — open shift anyone eligible can claim.
position_id
integer | null
template_id
integer | null
date
date
start
string
HH:MM (local). Required without a template.
end
string
HH:MM; earlier than start — ends next day.
break_minutes
integer
note
string | null
color
string | null
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.edit, is read-only
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X PATCH 'https://app.jecheck.io/api/ext/v1/schedule/shifts/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"user_id": 42,
"position_id": 42,
"template_id": 42
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/shifts/42', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"user_id": 42,
"position_id": 42,
"template_id": 42
}),
});
const { data } = await res.json();import os, requests
res = requests.patch(
"https://app.jecheck.io/api/ext/v1/schedule/shifts/42",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"user_id":42,"position_id":42,"template_id":42},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('PATCH', 'schedule/shifts/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'user_id' => 42,
'position_id' => 42,
'template_id' => 42,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_shifts_update",
"arguments": {
"id": 42,
"location_id": 12,
"user_id": 42,
"position_id": 42,
"template_id": 42
}
}
}{
"data": {
"id": 70211,
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"position_id": 12,
"template_id": 3,
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"overnight": false,
"break_minutes": 60,
"minutes": 480,
"status": "published",
"published": true,
"removed": false,
"open": false,
"note": null,
"color": null,
"updated_at": "2026-11-01T12:00:00+05:00"
}
}DELDelete a shift
Deletes a draft shift; a published one is marked removed until the schedule is published again.
| Token scope | write |
|---|---|
| Permission |
schedule.edit |
| MCP tool | schedule_shifts_delete · destructive |
Path parameters
id
integer
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.edit, is read-only
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X DELETE 'https://app.jecheck.io/api/ext/v1/schedule/shifts/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/shifts/42', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.delete(
"https://app.jecheck.io/api/ext/v1/schedule/shifts/42",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('DELETE', 'schedule/shifts/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_shifts_delete",
"arguments": {
"id": 42
}
}
}{
"data": {
"deleted": true
}
}POSTPublish the schedule
Publishes drafts and changes of a location for a period; affected employees are notified (unless notify=false).
| Token scope | write |
|---|---|
| Permission |
schedule.publish |
| MCP tool | schedule_publish |
Request body
location_id
integer
Required date_from
date
Required date_to
date
Required notify
boolean
true.
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.publish, is read-only
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/publish' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30",
"notify": true
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/publish', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30",
"notify": true
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/publish",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"date_from":"2026-11-01","date_to":"2026-11-30","notify":True},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/publish', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'date_from' => '2026-11-01',
'date_to' => '2026-11-30',
'notify' => true,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_publish",
"arguments": {
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30",
"notify": true
}
}
}{
"data": {
"published": 24,
"removed": 1,
"notified": 9
}
}POSTCopy shifts to another period
Copies the shifts of days days starting at date_from to target_date, optionally repeated. Absent employees are skipped. Copies are drafts.
| Token scope | write |
|---|---|
| Permission |
schedule.edit |
| MCP tool | schedule_copy |
Request body
location_id
integer
Required date_from
date
Required days
integer
Required target_date
date
Required repeat
integer
replace
boolean
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.edit, is read-only
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/copy' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"date_from": "2026-11-01",
"days": 42,
"target_date": "2026-11-01"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/copy', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"date_from": "2026-11-01",
"days": 42,
"target_date": "2026-11-01"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/copy",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"date_from":"2026-11-01","days":42,"target_date":"2026-11-01"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/copy', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'date_from' => '2026-11-01',
'days' => 42,
'target_date' => '2026-11-01',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_copy",
"arguments": {
"location_id": 12,
"date_from": "2026-11-01",
"days": 42,
"target_date": "2026-11-01"
}
}
}{
"data": {
"created": 35
}
}POSTAuto-fill the schedule
Assigns employees to shifts by demand, availability, absences and rules; unfilled demand becomes open shifts (unless create_open=false).
| Token scope | write |
|---|---|
| Permission |
schedule.edit |
| MCP tool | schedule_autofill |
Request body
location_id
integer
Required date_from
date
Required date_to
date
Required create_open
boolean
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.edit, is read-only
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/autofill' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30",
"create_open": true
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/autofill', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30",
"create_open": true
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/autofill",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"date_from":"2026-11-01","date_to":"2026-11-30","create_open":True},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/autofill', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'date_from' => '2026-11-01',
'date_to' => '2026-11-30',
'create_open' => true,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_autofill",
"arguments": {
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30",
"create_open": true
}
}
}{
"data": {
"assigned": 18,
"open": 2,
"unfilled": 1
}
}POSTClear shifts of a period
Deletes the shifts of a location for a period (published ones are marked removed until publishing).
| Token scope | write |
|---|---|
| Permission |
schedule.edit |
| MCP tool | schedule_clear · destructive |
Request body
location_id
integer
Required date_from
date
Required date_to
date
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.edit, is read-only
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/clear' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/clear', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/clear",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"date_from":"2026-11-01","date_to":"2026-11-30"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/clear', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'date_from' => '2026-11-01',
'date_to' => '2026-11-30',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_clear",
"arguments": {
"location_id": 12,
"date_from": "2026-11-01",
"date_to": "2026-11-30"
}
}
}{
"data": {
"cleared": 12
}
}GETList shift templates
Company-wide shift templates and templates of the location.
| Token scope | read |
|---|---|
| Permission |
schedule.view |
| MCP tool | schedule_templates_list · read-only |
Query parameters
location_id
integer
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.view
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/templates?location_id=12' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/templates?location_id=12', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/templates",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"location_id":12},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/templates', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'location_id' => 12,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_templates_list",
"arguments": {
"location_id": 12
}
}
}{
"data": [
{
"id": 3,
"location_id": null,
"name": "Morning",
"code": "M",
"start": "08:00",
"end": "16:00",
"break_minutes": 30,
"position_id": null,
"color": "#fab005"
}
]
}GETList my shifts
Published shifts of the acting user with attendance and (if enabled by the company) colleagues on the same shift. Default — current week, max 62 days.
| Token scope | read |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_my_shifts · read-only |
Query parameters
date_from
date
date_to
date
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Plan without API access
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/my/shifts?date_from=2026-11-01' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/my/shifts?date_from=2026-11-01', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/my/shifts",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"date_from":"2026-11-01"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/my/shifts', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'date_from' => '2026-11-01',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_my_shifts",
"arguments": {
"date_from": "2026-11-01"
}
}
}{
"data": [
{
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"break_minutes": 60,
"hours": 8,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"position_id": 12,
"position": "Barista",
"note": null,
"open": false,
"pending_change": false,
"attendance": null,
"colleagues": [
{
"user_id": 312,
"name": "Arman Bekov",
"start": "09:00",
"end": "18:00",
"position": "Cashier",
"shift_uuid": "3d4e5f6a-7b8c-4d9e-8f0a-1b2c3d4e5f6a"
}
]
}
]
}GETGet my hours for a month
Timesheet of the acting user for a month: totals and day codes; accrued pay only if the company shows pay to staff.
| Token scope | read |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_my_month · read-only |
Query parameters
month
string
YYYY-MM, default — current.
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Plan without API access
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/my/month?month=string' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/my/month?month=string', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/my/month",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"month":"string"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/my/month', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'month' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_my_month",
"arguments": {
"month": "string"
}
}
}{
"data": {
"month": "2026-11",
"totals": {
"planned_hours": 168,
"hours": 52.5,
"night_hours": 0,
"holiday_hours": 0,
"overtime_hours": 0,
"days": 7,
"late_count": 1,
"late_minutes": 4,
"absences": []
},
"days": [
{
"date": "2026-11-03",
"code": "Я",
"planned": 480,
"hours": 7.5,
"late": 4
}
],
"pay": null
}
}GETList open shifts I can take
Published unassigned future shifts at locations where the acting user may work, matching the position and without overlaps.
| Token scope | read |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_open_shifts_list · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Plan without API access
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/open-shifts' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/open-shifts', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/open-shifts",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/open-shifts', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_open_shifts_list",
"arguments": {}
}
}{
"data": [
{
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"break_minutes": 60,
"hours": 8,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"position_id": 12,
"position": "Barista",
"note": null,
"open": false,
"requested": false,
"taken": false
}
]
}POSTClaim an open shift
Takes an open shift: assigned right away or sent to the manager, depending on company rules.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_open_shifts_claim |
Path parameters
uuid
uuid
Required Request body
note
string
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/open-shifts/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/claim' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/open-shifts/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/claim', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/open-shifts/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/claim",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"note":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/open-shifts/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/claim', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'note' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_open_shifts_claim",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"status": "approved"
}
}GETGet my clock state
Whether the acting user is on shift / on a break, the open attendance, current and next shift, clock-in locations with geofence and the company clock rules.
| Token scope | read |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_clock_state · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Plan without API access
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/clock' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/clock', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/clock",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/clock', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_clock_state",
"arguments": {}
}
}{
"data": {
"on_shift": true,
"on_break": false,
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"current_shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"break_minutes": 60,
"hours": 8,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"position_id": 12,
"position": "Barista",
"note": null,
"open": false
},
"next_shift": null,
"sites": [
{
"id": 4609,
"name": "Abay Ave",
"latitude": 43.2389,
"longitude": 76.8897,
"radius": 150
}
],
"rules": {
"require_geo": true,
"require_photo": false,
"block_out_of_zone": false,
"allow_without_shift": true,
"early_clock_in_minutes": 30,
"allow_web_clock": false
},
"server_time": "2026-11-03T09:05:00+05:00"
}
}POSTClock in
Starts attendance for the acting user (same rules as the mobile app: geofence, photo, early clock-in limit, matching shift). Repeating the same uuid returns the existing record. Errors carry a reason code: already_in, no_shift, too_early, geo_required, out_of_zone, photo_required, qr_invalid, department_required, bad_time.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_clock_in |
Request body
uuid
uuid
attendance_uuid
uuid
location_id
integer
shift_uuid
uuid
qr
string
/clock/{token} or the token).
Maximum length 500. lat
number
lng
number
photo
string
schedule.media.upload.
recorded_at
date-time
note
string
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/clock/in' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/clock/in', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/clock/in",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","attendance_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","location_id":12,"shift_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/clock/in', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'attendance_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'location_id' => 12,
'shift_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_clock_in",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}
}
}{
"data": {
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"clock": {
"on_shift": true,
"on_break": false,
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"current_shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"break_minutes": 60,
"hours": 8,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"position_id": 12,
"position": "Barista",
"note": null,
"open": false
},
"next_shift": null,
"sites": [
{
"id": 4609,
"name": "Abay Ave",
"latitude": 43.2389,
"longitude": 76.8897,
"radius": 150
}
],
"rules": {
"require_geo": true,
"require_photo": false,
"block_out_of_zone": false,
"allow_without_shift": true,
"early_clock_in_minutes": 30,
"allow_web_clock": false
},
"server_time": "2026-11-03T09:05:00+05:00"
}
}
}POSTClock out
Closes the open attendance of the acting user and computes worked minutes. Errors: not_in, break_open, bad_time.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_clock_out |
Request body
uuid
uuid
attendance_uuid
uuid
location_id
integer
shift_uuid
uuid
qr
string
/clock/{token} or the token).
Maximum length 500. lat
number
lng
number
photo
string
schedule.media.upload.
recorded_at
date-time
note
string
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/clock/out' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/clock/out', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/clock/out",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","attendance_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","location_id":12,"shift_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/clock/out', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'attendance_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'location_id' => 12,
'shift_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_clock_out",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}
}
}{
"data": {
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"clock": {
"on_shift": true,
"on_break": false,
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"current_shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"break_minutes": 60,
"hours": 8,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"position_id": 12,
"position": "Barista",
"note": null,
"open": false
},
"next_shift": null,
"sites": [
{
"id": 4609,
"name": "Abay Ave",
"latitude": 43.2389,
"longitude": 76.8897,
"radius": 150
}
],
"rules": {
"require_geo": true,
"require_photo": false,
"block_out_of_zone": false,
"allow_without_shift": true,
"early_clock_in_minutes": 30,
"allow_web_clock": false
},
"server_time": "2026-11-03T09:05:00+05:00"
}
}
}POSTStart a break
Starts a break in the open attendance. Errors: not_in, break_open.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_clock_break_start |
Request body
uuid
uuid
attendance_uuid
uuid
location_id
integer
shift_uuid
uuid
qr
string
/clock/{token} or the token).
Maximum length 500. lat
number
lng
number
photo
string
schedule.media.upload.
recorded_at
date-time
note
string
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/clock/break-start' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/clock/break-start', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/clock/break-start",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","attendance_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","location_id":12,"shift_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/clock/break-start', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'attendance_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'location_id' => 12,
'shift_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_clock_break_start",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}
}
}{
"data": {
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"clock": {
"on_shift": true,
"on_break": false,
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"current_shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"break_minutes": 60,
"hours": 8,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"position_id": 12,
"position": "Barista",
"note": null,
"open": false
},
"next_shift": null,
"sites": [
{
"id": 4609,
"name": "Abay Ave",
"latitude": 43.2389,
"longitude": 76.8897,
"radius": 150
}
],
"rules": {
"require_geo": true,
"require_photo": false,
"block_out_of_zone": false,
"allow_without_shift": true,
"early_clock_in_minutes": 30,
"allow_web_clock": false
},
"server_time": "2026-11-03T09:05:00+05:00"
}
}
}POSTEnd a break
Ends the current break. Errors: not_in, no_break.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_clock_break_end |
Request body
uuid
uuid
attendance_uuid
uuid
location_id
integer
shift_uuid
uuid
qr
string
/clock/{token} or the token).
Maximum length 500. lat
number
lng
number
photo
string
schedule.media.upload.
recorded_at
date-time
note
string
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/clock/break-end' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/clock/break-end', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/clock/break-end",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","attendance_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","location_id":12,"shift_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/clock/break-end', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'attendance_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'location_id' => 12,
'shift_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_clock_break_end",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"attendance_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"location_id": 12,
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}
}
}{
"data": {
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"clock": {
"on_shift": true,
"on_break": false,
"attendance": {
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"date": "2026-11-03",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"clock_in_at": "2026-11-03T09:04:00+05:00",
"clock_out_at": null,
"breaks": [
{
"start": "13:00",
"end": "13:30"
}
],
"status": "open",
"late_minutes": 4,
"early_minutes": 0,
"worked_minutes": 0,
"approved_minutes": null,
"flags": [
"late"
]
},
"current_shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"starts_at": "2026-11-04T09:00:00+05:00",
"ends_at": "2026-11-04T18:00:00+05:00",
"break_minutes": 60,
"hours": 8,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"position_id": 12,
"position": "Barista",
"note": null,
"open": false
},
"next_shift": null,
"sites": [
{
"id": 4609,
"name": "Abay Ave",
"latitude": 43.2389,
"longitude": 76.8897,
"radius": 150
}
],
"rules": {
"require_geo": true,
"require_photo": false,
"block_out_of_zone": false,
"allow_without_shift": true,
"early_clock_in_minutes": 30,
"allow_web_clock": false
},
"server_time": "2026-11-03T09:05:00+05:00"
}
}
}POSTUpload a clock photo or request file
kind=clock — selfie for clock-in/out (image up to 8 MB); kind=absence — document for an absence request (image or PDF up to 10 MB). Returns the path to pass as photo / file.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_media_upload |
| Content type | multipart/form-data application/json |
Request body
file
file
Required name and content_base64).
kind
string
clock. clockabsenceResponses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/media' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-F 'kind=clock' \
-F '[email protected]'const form = new FormData();
form.append('kind', "clock");
form.append('file', fileInput.files[0]);
const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/media', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.JECHECK_TOKEN}` },
body: form,
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/media",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
data={"kind":"clock"},
files=[("file", open("photo.jpg", "rb"))],
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/media', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'multipart' => [
['name' => 'kind', 'contents' => 'clock'],
['name' => 'file', 'contents' => fopen('photo.jpg', 'r')],
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_media_upload",
"arguments": {
"kind": "clock"
}
}
}{
"data": {
"path": "staff/761/clock/2026/11/0f1e.jpg"
}
}GETGet who is on shift
Attendance board by location: every published shift of the day with status (upcoming, late, working, break, done, absent, missed) and clock-ins without a shift. Phones only with users.contacts. Default — today at each location.
| Token scope | read |
|---|---|
| Permission |
schedule.attendance |
| MCP tool | schedule_attendance · read-only |
Query parameters
location_ids
array<integer>
schedule.attendance).
date
date
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.attendance
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/attendance?date=2026-11-01' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/attendance?date=2026-11-01', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/attendance",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"date":"2026-11-01"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/attendance', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'date' => '2026-11-01',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_attendance",
"arguments": {
"date": "2026-11-01"
}
}
}{
"data": [
{
"location": {
"id": 4609,
"name": "Abay Ave"
},
"date": "2026-11-03",
"now": "09:15",
"counts": {
"working": 3,
"late": 1,
"upcoming": 2,
"total": 6
},
"people": [
{
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"position": "Barista",
"phone": null,
"status": "working",
"shift": {
"id": 70211,
"start": "09:00",
"end": "18:00"
},
"attendance": {
"id": 55120,
"in": "09:04",
"out": null,
"late_minutes": 4,
"worked_minutes": 11,
"method": "app",
"flags": [
"late"
]
},
"late_now_minutes": null,
"absence": null
}
]
}
]
}GETList attendance records
Clock-in/out journal at locations with schedule.attendance, newest first (max 1000 per period). flag: late, early, out_of_zone, manual, offline, auto_closed, unapproved. Default period — last 7 days.
| Token scope | read |
|---|---|
| Permission |
schedule.attendance |
| MCP tool | schedule_attendance_list · read-only |
Query parameters
page
integer
1. Minimum 1.
per_page
integer
25. Value 1–100.
date_from
date
date_to
date
location_ids
array<integer>
user_id
integer
flag
string
Responses
| Status | Description |
|---|---|
| 200 | OK — a page of results with meta |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.attendance
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/attendance/records?date_from=2026-11-01' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/attendance/records?date_from=2026-11-01', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data, meta } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/attendance/records",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"date_from":"2026-11-01"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/attendance/records', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'date_from' => '2026-11-01',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_attendance_list",
"arguments": {
"date_from": "2026-11-01"
}
}
}{
"data": [
{
"id": 55120,
"uuid": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"location": {
"id": 4609,
"name": "Abay Ave"
},
"date": "2026-11-03",
"clock_in": "2026-11-03 09:04",
"clock_out": "2026-11-03 18:02",
"shift": {
"id": 70211,
"start": "09:00",
"end": "18:00",
"minutes": 480
},
"break_minutes": 30,
"worked_minutes": 508,
"approved_minutes": null,
"approved": false,
"status": "closed",
"in_method": "app",
"out_method": "app",
"in_distance": 12,
"out_distance": 20,
"in_photo": null,
"out_photo": null,
"late_minutes": 4,
"early_minutes": 0,
"flags": [
"late"
],
"note": null,
"edits": 0
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 1,
"has_more": false
}
}POSTAdd an attendance record
Manual attendance for an employee (forgot to clock in) with a reason; written to the edit history. Times are local wall time YYYY-MM-DD HH:MM.
| Token scope | write |
|---|---|
| Permission |
schedule.attendance |
| MCP tool | schedule_attendance_create |
Request body
user_id
integer
Required location_id
integer
Required shift_id
integer
clock_in
string
Required YYYY-MM-DD HH:MM, local.
clock_out
string | null
YYYY-MM-DD HH:MM, local.
break_minutes
integer
approved_minutes
integer | null
note
string
reason
string
Required Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.attendance, is read-only
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/attendance/records' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"user_id": 42,
"location_id": 12,
"shift_id": 42,
"clock_in": "string",
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/attendance/records', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"user_id": 42,
"location_id": 12,
"shift_id": 42,
"clock_in": "string",
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/attendance/records",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"user_id":42,"location_id":12,"shift_id":42,"clock_in":"string","reason":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/attendance/records', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'user_id' => 42,
'location_id' => 12,
'shift_id' => 42,
'clock_in' => 'string',
'reason' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_attendance_create",
"arguments": {
"user_id": 42,
"location_id": 12,
"shift_id": 42,
"clock_in": "string",
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"id": 55121
}
}PATCHCorrect an attendance record
Corrects times of an attendance record with a reason (kept in history). Rejected in a closed month.
| Token scope | write |
|---|---|
| Permission |
schedule.attendance |
| MCP tool | schedule_attendance_update |
Path parameters
id
integer
Required Request body
clock_in
string
YYYY-MM-DD HH:MM, local.
clock_out
string | null
break_minutes
integer
approved_minutes
integer | null
note
string
reason
string
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.attendance, is read-only
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X PATCH 'https://app.jecheck.io/api/ext/v1/schedule/attendance/records/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"clock_in": "string",
"clock_out": "string",
"break_minutes": 42,
"approved_minutes": 42,
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/attendance/records/42', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"clock_in": "string",
"clock_out": "string",
"break_minutes": 42,
"approved_minutes": 42,
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}),
});
const { data } = await res.json();import os, requests
res = requests.patch(
"https://app.jecheck.io/api/ext/v1/schedule/attendance/records/42",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"clock_in":"string","clock_out":"string","break_minutes":42,"approved_minutes":42,"reason":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('PATCH', 'schedule/attendance/records/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'clock_in' => 'string',
'clock_out' => 'string',
'break_minutes' => 42,
'approved_minutes' => 42,
'reason' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_attendance_update",
"arguments": {
"id": 42,
"clock_in": "string",
"clock_out": "string",
"break_minutes": 42,
"approved_minutes": 42,
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"id": 55120,
"updated": true
}
}DELDelete an attendance record
| Token scope | write |
|---|---|
| Permission |
schedule.attendance |
| MCP tool | schedule_attendance_delete · destructive |
Path parameters
id
integer
Required Query parameters
reason
string
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.attendance, is read-only
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X DELETE 'https://app.jecheck.io/api/ext/v1/schedule/attendance/records/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/attendance/records/42', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.delete(
"https://app.jecheck.io/api/ext/v1/schedule/attendance/records/42",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('DELETE', 'schedule/attendance/records/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_attendance_delete",
"arguments": {
"id": 42
}
}
}{
"data": {
"deleted": true
}
}POSTApprove attendance records
Approves worked minutes of closed records (they go to the timesheet as approved). minutes overrides the value when approving one record.
| Token scope | write |
|---|---|
| Permission |
schedule.attendance |
| MCP tool | schedule_attendance_approve |
Request body
ids
array<integer>
Required minutes
integer
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.attendance, is read-only
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/attendance/records/approve' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ids": [
12,
14
],
"minutes": 42
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/attendance/records/approve', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"ids": [
12,
14
],
"minutes": 42
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/attendance/records/approve",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"ids":[12,14],"minutes":42},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/attendance/records/approve', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'ids' => [
12,
14,
],
'minutes' => 42,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_attendance_approve",
"arguments": {
"ids": [
12,
14
],
"minutes": 42
}
}
}{
"data": {
"approved": 12
}
}GETGet the timesheet
Planned and payable hours by employee and day (minutes), night/holiday/overtime, lateness, absences and day codes. A month, or date_from/date_to up to 62 days. closed — the month is locked.
| Token scope | read |
|---|---|
| Permission |
schedule.timesheet |
| MCP tool | schedule_timesheet · read-only |
Query parameters
month
string
YYYY-MM, default — current.
date_from
date
date_to
date
location_ids
array<integer>
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.timesheet
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/timesheet?month=string' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/timesheet?month=string', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/timesheet",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"month":"string"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/timesheet', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'month' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_timesheet",
"arguments": {
"month": "string"
}
}
}{
"data": {
"date_from": "2026-11-01",
"date_to": "2026-11-30",
"closed": false,
"holidays": [],
"rows": [
{
"user": {
"id": 311,
"name": "Aigerim Sadykova",
"position": "Barista",
"personnel_no": "A-17",
"location_ids": [
4609
]
},
"totals": {
"planned": 10080,
"worked": 3200,
"payable": 3150,
"night": 0,
"holiday": 0,
"overtime": 0,
"late_count": 1,
"late": 4,
"days": 7,
"shifts": 7,
"no_show": 0,
"absences": [],
"paid_absence_days": 0
},
"days": [
{
"date": "2026-11-03",
"code": "Я",
"planned": 480,
"payable": 450,
"night": 0,
"holiday": 0,
"overtime": 0,
"late": 4,
"absence": null,
"flags": [
"late"
],
"open": false
}
]
}
]
}
}GETGet the payroll
Payroll for a month by employee: hours, amounts (base, night, holiday, overtime, KPI, adjustments, deductions, payable) and status (draft, approved, paid). Sensitive: requires pay.payroll.view (locations of the employees); each call is written to the access log. rate / pay_type are omitted for employees of locations without pay.rates.view.
| Token scope | read |
|---|---|
| Permission |
pay.payroll.view |
| Plan feature | payroll |
| MCP tool | schedule_payroll · read-only |
Query parameters
month
string
YYYY-MM, default — current.
location_ids
array<integer>
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks pay.payroll.view, or the plan has no payroll
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/payroll?month=string' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/payroll?month=string', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/payroll",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"month":"string"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/payroll', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'month' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_payroll",
"arguments": {
"month": "string"
}
}
}{
"data": {
"month": "2026-10",
"currency": "KZT",
"status": "approved",
"approved_at": "2026-11-02T10:00:00+05:00",
"paid_at": null,
"lines": [
{
"user": {
"id": 311,
"name": "Aigerim Sadykova",
"position": "Barista",
"personnel_no": "A-17",
"location_ids": [
4609
]
},
"pay_type": "hourly",
"rate": 1500,
"hours": {
"planned": 168,
"payable": 170.5,
"night": 0,
"holiday": 8,
"overtime": 2.5
},
"days": 21,
"shifts": 21,
"late_count": 2,
"no_show": 0,
"paid_absence_days": 0,
"absences": [],
"kpi_score": 96.5,
"amounts": {
"base": 255750,
"night": 0,
"holiday": 12000,
"overtime": 5625,
"absence": 0,
"kpi": 20000,
"bonus": 0,
"compensation": 0,
"accrued": 293375,
"penalty": 0,
"deduction": 0,
"advance": 50000,
"percent": [
{
"name": "IIT",
"percent": 10,
"amount": 29337.5
}
],
"deducted": 79337.5,
"payable": 214037.5
},
"adjustments": [
{
"id": 81,
"kind": "advance",
"amount": 50000,
"comment": "Mid-month advance"
}
],
"norm_hours": 168
}
],
"totals": {
"base": 255750,
"night": 0,
"holiday": 12000,
"overtime": 5625,
"absence": 0,
"kpi": 20000,
"bonus": 0,
"compensation": 0,
"accrued": 293375,
"penalty": 0,
"deduction": 0,
"advance": 50000,
"deducted": 79337.5,
"payable": 214037.5,
"hours": 170.5,
"people": 1
}
}
}POSTApprove the payroll
Freezes the payroll of a month and closes the month for changes of shifts, attendance and absences. Requires pay.payroll.approve at all locations.
| Token scope | write |
|---|---|
| Permission |
pay.payroll.approve |
| Plan feature | payroll |
| MCP tool | schedule_payroll_approve |
Request body
month
string
Required YYYY-MM.
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks pay.payroll.approve, is read-only, or the plan has no payroll
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/payroll/approve' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"month": "string"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/payroll/approve', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"month": "string"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/payroll/approve",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"month":"string"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/payroll/approve', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'month' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_payroll_approve",
"arguments": {
"month": "string"
}
}
}{
"data": {
"month": "2026-10",
"status": "approved"
}
}POSTReopen the payroll
Reopens an approved month for corrections.
| Token scope | write |
|---|---|
| Permission |
pay.payroll.approve |
| Plan feature | payroll |
| MCP tool | schedule_payroll_reopen |
Request body
month
string
Required YYYY-MM.
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks pay.payroll.approve, is read-only, or the plan has no payroll
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/payroll/reopen' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"month": "string"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/payroll/reopen', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"month": "string"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/payroll/reopen",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"month":"string"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/payroll/reopen', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'month' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_payroll_reopen",
"arguments": {
"month": "string"
}
}
}{
"data": {
"month": "2026-10",
"status": "draft"
}
}POSTMark the payroll as paid
| Token scope | write |
|---|---|
| Permission |
pay.payroll.approve |
| Plan feature | payroll |
| MCP tool | schedule_payroll_paid |
Request body
month
string
Required YYYY-MM.
paid
boolean
false — undo, default true.
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks pay.payroll.approve, is read-only, or the plan has no payroll
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/payroll/paid' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"month": "string",
"paid": true
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/payroll/paid', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"month": "string",
"paid": true
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/payroll/paid",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"month":"string","paid":True},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/payroll/paid', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'month' => 'string',
'paid' => true,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_payroll_paid",
"arguments": {
"month": "string",
"paid": true
}
}
}{
"data": {
"month": "2026-10",
"paid": true
}
}POSTAdd a payroll adjustment
Bonus, compensation, penalty, deduction or advance for an employee in an open month.
| Token scope | write |
|---|---|
| Permission |
pay.payroll.approve |
| Plan feature | payroll |
| MCP tool | schedule_payroll_adjustments_create |
Request body
user_id
integer
Required month
string
Required YYYY-MM.
kind
string
Required bonuscompensationpenaltydeductionadvanceamount
number
Required comment
string
Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks pay.payroll.approve, is read-only, or the plan has no payroll
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/payroll/adjustments' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"user_id": 42,
"month": "string",
"kind": "bonus",
"amount": 4.5
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/payroll/adjustments', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"user_id": 42,
"month": "string",
"kind": "bonus",
"amount": 4.5
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/payroll/adjustments",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"user_id":42,"month":"string","kind":"bonus","amount":4.5},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/payroll/adjustments', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'user_id' => 42,
'month' => 'string',
'kind' => 'bonus',
'amount' => 4.5,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_payroll_adjustments_create",
"arguments": {
"user_id": 42,
"month": "string",
"kind": "bonus",
"amount": 4.5
}
}
}{
"data": {
"id": 81,
"user_id": 311,
"month": "2026-11",
"kind": "advance",
"amount": 50000,
"comment": "Mid-month advance"
}
}DELDelete a payroll adjustment
| Token scope | write |
|---|---|
| Permission |
pay.payroll.approve |
| Plan feature | payroll |
| MCP tool | schedule_payroll_adjustments_delete · destructive |
Path parameters
id
integer
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks pay.payroll.approve, is read-only, or the plan has no payroll
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X DELETE 'https://app.jecheck.io/api/ext/v1/schedule/payroll/adjustments/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/payroll/adjustments/42', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.delete(
"https://app.jecheck.io/api/ext/v1/schedule/payroll/adjustments/42",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('DELETE', 'schedule/payroll/adjustments/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_payroll_adjustments_delete",
"arguments": {
"id": 42
}
}
}{
"data": {
"deleted": true
}
}GETGet the KPI scorecard
KPI metrics and each employee's values, achievement and total score for a month. The KPI bonus base is included only with pay.rates.view.
| Token scope | read |
|---|---|
| Permission |
schedule.kpi |
| Plan feature | payroll |
| MCP tool | schedule_kpi · read-only |
Query parameters
month
string
YYYY-MM, default — current.
location_ids
array<integer>
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.kpi, or the plan has no payroll
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/kpi?month=string' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/kpi?month=string', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/kpi",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"month":"string"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/kpi', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'month' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_kpi",
"arguments": {
"month": "string"
}
}
}{
"data": {
"month": "2026-11",
"closed": false,
"threshold": 70,
"cap": 120,
"metrics": [
{
"id": 4,
"name": "Punctuality",
"description": null,
"unit": "%",
"source": "punctuality",
"direction": "up",
"target": 95,
"weight": 30,
"position_ids": [],
"active": true
}
],
"people": [
{
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"position": "Barista",
"kpi_bonus": 20000,
"kpi_bonus_type": "fixed",
"score": 96.5,
"metrics": [
{
"metric_id": 4,
"value": 98,
"auto": 98,
"manual": false,
"comment": null,
"achievement": 103.2
}
]
}
]
}
}POSTSet a KPI value
Sets (or overrides an automatic) value of a metric for an employee and month. value: null clears the manual value.
| Token scope | write |
|---|---|
| Permission |
schedule.kpi |
| Plan feature | payroll |
| MCP tool | schedule_kpi_values_set |
Request body
metric_id
integer
Required user_id
integer
Required month
string
Required YYYY-MM.
value
number | null
comment
string
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.kpi, is read-only, or the plan has no payroll
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/kpi/values' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"metric_id": 42,
"user_id": 42,
"month": "string",
"value": 4.5
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/kpi/values', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"metric_id": 42,
"user_id": 42,
"month": "string",
"value": 4.5
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/kpi/values",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"metric_id":42,"user_id":42,"month":"string","value":4.5},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/kpi/values', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'metric_id' => 42,
'user_id' => 42,
'month' => 'string',
'value' => 4.5,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_kpi_values_set",
"arguments": {
"metric_id": 42,
"user_id": 42,
"month": "string",
"value": 4.5
}
}
}{
"data": {
"saved": true
}
}GETList absence and swap requests
Requests of employees of locations with schedule.requests: pending (default) or history (decided in the last 90 days).
| Token scope | read |
|---|---|
| Permission |
schedule.requests |
| MCP tool | schedule_requests_list · read-only |
Query parameters
status
string
pending. pendinghistoryResponses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.requests
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/requests?status=pending' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/requests?status=pending', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/requests",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"status":"pending"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/requests', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'status' => 'pending',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_requests_list",
"arguments": {
"status": "pending"
}
}
}{
"data": {
"absences": [
{
"id": 912,
"uuid": "8c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"type": "vacation",
"date_from": "2026-12-01",
"date_to": "2026-12-14",
"days": 14,
"status": "pending",
"paid": true,
"comment": "Family trip",
"file": null,
"decided_by": null,
"decided_at": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
],
"swaps": [
{
"id": 340,
"uuid": "9d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"type": "give",
"status": "pending_peer",
"note": "Doctor appointment",
"from_user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"to_user": null,
"shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"location": {
"id": 4609,
"name": "Abay Ave"
}
},
"to_shift": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
]
}
}GETList my requests
Absences of the acting user (last 60 days and future) and swap requests — own and incoming (can_answer).
| Token scope | read |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_my_requests · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Plan without API access
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/my/requests' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/my/requests', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/my/requests",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/my/requests', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_my_requests",
"arguments": {}
}
}{
"data": {
"absences": [
{
"id": 912,
"uuid": "8c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"type": "vacation",
"date_from": "2026-12-01",
"date_to": "2026-12-14",
"days": 14,
"status": "pending",
"paid": true,
"comment": "Family trip",
"file": null,
"decided_by": null,
"decided_at": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
],
"swaps": [
{
"id": 340,
"uuid": "9d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"type": "give",
"status": "pending_peer",
"note": "Doctor appointment",
"from_user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"to_user": null,
"shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"location": {
"id": 4609,
"name": "Abay Ave"
}
},
"to_shift": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00",
"mine": true,
"can_answer": false
}
]
}
}POSTCreate an absence
Without user_id (or with the acting user) — a request of the acting user, goes to the manager. For another employee — recorded by the manager and approved right away (schedule.requests at the employee's location; release_shifts turns their shifts into open ones).
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_absences_create |
Request body
user_id
integer
type
string
Required vacationsickday_offunpaidbusiness_tripstudymaternitytruancyotherdate_from
date
Required date_to
date
comment
string
paid
boolean
file
string
schedule.media.upload (kind=absence).
release_shifts
boolean
Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/absences' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"user_id": 42,
"type": "vacation",
"date_from": "2026-11-01",
"date_to": "2026-11-30"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/absences', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"user_id": 42,
"type": "vacation",
"date_from": "2026-11-01",
"date_to": "2026-11-30"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/absences",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"user_id":42,"type":"vacation","date_from":"2026-11-01","date_to":"2026-11-30"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/absences', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'user_id' => 42,
'type' => 'vacation',
'date_from' => '2026-11-01',
'date_to' => '2026-11-30',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_absences_create",
"arguments": {
"user_id": 42,
"type": "vacation",
"date_from": "2026-11-01",
"date_to": "2026-11-30"
}
}
}{
"data": {
"id": 912,
"uuid": "8c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"type": "vacation",
"date_from": "2026-12-01",
"date_to": "2026-12-14",
"days": 14,
"status": "pending",
"paid": true,
"comment": "Family trip",
"file": null,
"decided_by": null,
"decided_at": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
}POSTCancel an absence
The employee cancels their pending request; a manager with schedule.requests can cancel any absence of their employees.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_absences_cancel |
Path parameters
uuid
uuid
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/absences/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/absences/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/absences/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/absences/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_absences_cancel",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}
}
}{
"data": {
"id": 912,
"uuid": "8c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"type": "vacation",
"date_from": "2026-12-01",
"date_to": "2026-12-14",
"days": 14,
"status": "pending",
"paid": true,
"comment": "Family trip",
"file": null,
"decided_by": null,
"decided_at": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
}POSTApprove or reject an absence request
Manager decision; the employee is notified. release_shifts makes their shifts in these days open.
| Token scope | write |
|---|---|
| Permission |
schedule.requests |
| MCP tool | schedule_absences_decide |
Path parameters
uuid
uuid
Required Request body
approve
boolean
Required note
string
release_shifts
boolean
paid
boolean
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.requests, is read-only
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/absences/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/decide' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"approve": true,
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
"release_shifts": true,
"paid": true
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/absences/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/decide', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"approve": true,
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
"release_shifts": true,
"paid": true
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/absences/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/decide",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"approve":True,"note":"Seal on fridge #2 is torn, temperature rises to +9 °C.","release_shifts":True,"paid":True},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/absences/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/decide', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'approve' => true,
'note' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
'release_shifts' => true,
'paid' => true,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_absences_decide",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"approve": true,
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
"release_shifts": true,
"paid": true
}
}
}{
"data": {
"id": 912,
"uuid": "8c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"type": "vacation",
"date_from": "2026-12-01",
"date_to": "2026-12-14",
"days": 14,
"status": "pending",
"paid": true,
"comment": "Family trip",
"file": null,
"decided_by": null,
"decided_at": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
}POSTRequest a shift swap
give — give away own shift (to to_user_id or anyone eligible); swap — exchange with a colleague's shift to_shift_uuid. The colleague answers, then the manager decides.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_swaps_create |
Request body
type
string
Required giveswapshift_uuid
uuid
Required to_shift_uuid
uuid
swap).
to_user_id
integer
give).
note
string
Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/swaps' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"type": "give",
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"to_shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"to_user_id": 42
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/swaps', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"type": "give",
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"to_shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"to_user_id": 42
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/swaps",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"type":"give","shift_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","to_shift_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","to_user_id":42},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/swaps', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'type' => 'give',
'shift_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'to_shift_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
'to_user_id' => 42,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_swaps_create",
"arguments": {
"type": "give",
"shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"to_shift_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"to_user_id": 42
}
}
}{
"data": {
"id": 340,
"uuid": "9d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"type": "give",
"status": "pending_peer",
"note": "Doctor appointment",
"from_user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"to_user": null,
"shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"location": {
"id": 4609,
"name": "Abay Ave"
}
},
"to_shift": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
}POSTAnswer a swap request
The colleague accepts or declines a swap / give-away addressed to them.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_swaps_answer |
Path parameters
uuid
uuid
Required Request body
accept
boolean
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/answer' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"accept": true
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/answer', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"accept": true
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/answer",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"accept":True},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/answer', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'accept' => true,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_swaps_answer",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"accept": true
}
}
}{
"data": {
"id": 340,
"uuid": "9d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"type": "give",
"status": "pending_peer",
"note": "Doctor appointment",
"from_user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"to_user": null,
"shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"location": {
"id": 4609,
"name": "Abay Ave"
}
},
"to_shift": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
}POSTCancel a swap request
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_swaps_cancel |
Path parameters
uuid
uuid
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_swaps_cancel",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}
}
}{
"data": {
"id": 340,
"uuid": "9d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"type": "give",
"status": "pending_peer",
"note": "Doctor appointment",
"from_user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"to_user": null,
"shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"location": {
"id": 4609,
"name": "Abay Ave"
}
},
"to_shift": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
}POSTApprove or reject a swap request
Manager decision (schedule.requests at the shift location). Approval reassigns the shifts and notifies both employees.
| Token scope | write |
|---|---|
| Permission |
schedule.requests |
| MCP tool | schedule_swaps_decide |
Path parameters
uuid
uuid
Required Request body
approve
boolean
Required note
string
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks schedule.requests, is read-only
|
| 404 | not_found — The record does not exist or is not visible to the token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X POST 'https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/decide' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"approve": true,
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/decide', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"approve": true,
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/decide",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"approve":True,"note":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('POST', 'schedule/swaps/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/decide', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'approve' => true,
'note' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_swaps_decide",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"approve": true,
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"id": 340,
"uuid": "9d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"type": "give",
"status": "pending_peer",
"note": "Doctor appointment",
"from_user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"to_user": null,
"shift": {
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"date": "2026-11-04",
"start": "09:00",
"end": "18:00",
"location": {
"id": 4609,
"name": "Abay Ave"
}
},
"to_shift": null,
"decision_note": null,
"created_at": "2026-11-03T10:00:00+05:00"
}
}GETGet availability
Weekly / dated availability preferences of the acting user, or of an employee the token manages (schedule.edit).
| Token scope | read |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_availability_get · read-only |
Query parameters
user_id
integer
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Plan without API access
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/schedule/availability?user_id=42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/availability?user_id=42', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
},
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://app.jecheck.io/api/ext/v1/schedule/availability",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"user_id":42},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('GET', 'schedule/availability', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'user_id' => 42,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_availability_get",
"arguments": {
"user_id": 42
}
}
}{
"data": [
{
"id": 15,
"weekday": 6,
"date": null,
"start_time": null,
"end_time": null,
"kind": "unavailable",
"note": "University"
}
]
}PUTReplace availability
Replaces all availability items of the acting user (or a managed employee). Each item: weekday (1–7) or date, optional start_time/end_time HH:MM, kind unavailable | preferred, note.
| Token scope | write |
|---|---|
| Permission | Any employee; results follow the token's visibility |
| MCP tool | schedule_availability_set |
Request body
user_id
integer
items
array<object>
Required weekday
integer
date
date
start_time
string
end_time
string
kind
string
unavailablepreferrednote
string
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Read-only token
|
| 422 | validation_failed — Invalid parameters — see error.fields
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl -X PUT 'https://app.jecheck.io/api/ext/v1/schedule/availability' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"user_id": 42,
"items": [
{
"weekday": 42,
"date": "2026-11-01",
"start_time": "string"
}
]
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/schedule/availability', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"user_id": 42,
"items": [
{
"weekday": 42,
"date": "2026-11-01",
"start_time": "string"
}
]
}),
});
const { data } = await res.json();import os, requests
res = requests.put(
"https://app.jecheck.io/api/ext/v1/schedule/availability",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"user_id":42,"items":[{"weekday":42,"date":"2026-11-01","start_time":"string"}]},
)
res.raise_for_status()
data = res.json()["data"]$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);
$res = $client->request('PUT', 'schedule/availability', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'user_id' => 42,
'items' => [
[
'weekday' => 42,
'date' => '2026-11-01',
'start_time' => 'string',
],
],
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "schedule_availability_set",
"arguments": {
"user_id": 42,
"items": [
{
"weekday": 42,
"date": "2026-11-01",
"start_time": "string"
}
]
}
}
}{
"data": [
{
"id": 15,
"weekday": 6,
"date": null,
"start_time": null,
"end_time": null,
"kind": "unavailable",
"note": "University"
}
]
}