Getting started
The setup checklist that guides a new company through jeCheck (locations, employees, checklists, the mobile app, the first audit, who fixes violations, the audit plan) and booking a free call with a jeCheck manager. Steps are marked done automatically from company data; optional steps can be skipped.
GET Get setup progress
GET /onboarding
Steps in order with done (detected from company data — the demo location and demo checklist created at sign-up do not count), skipped and optional; done/total progress (skipped steps are excluded) and whether the Getting started screen is hidden (dismissed_at). Step keys: company, locations, people, checklist, app, audit, fixes, planner, call.
Token scope read
Permission
start.view
MCP tool onboarding_get · read-only
Responses
Status Description
200 OK
401 unauthorized — Missing, invalid, revoked or expired token
403 forbidden · insufficient_scope · plan_required — Token lacks start.view
429 rate_limited — Too many requests — retry after Retry-After seconds
Request
cURL
Node.js
Python
PHP
MCP
Copy
curl 'https://app.jecheck.io/api/ext/v1/onboarding' \
-H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/onboarding', {
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/onboarding",
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', 'onboarding', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "onboarding_get",
"arguments": {}
}
}
{
"data": {
"steps": [
{
"key": "locations",
"optional": false,
"done": true,
"skipped": false,
"facts": 3
}
],
"done": 7,
"total": 9,
"complete": false,
"dismissed_at": null,
"active": true
}
}
POST Skip or restore an optional step
POST /onboarding/steps/{step}/skip
Only optional steps (planner, call) can be skipped. Returns the updated progress.
Token scope write
Permission
start.view
MCP tool onboarding_steps_skip
Path parameters
step
string
Required
Step key.
plannercall
Request body
skip
boolean
false — bring the step back.
Default true.
Responses
Status Description
200 OK
401 unauthorized — Missing, invalid, revoked or expired token
403 forbidden · insufficient_scope · plan_required — Token lacks start.view, 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
Request
cURL
Node.js
Python
PHP
MCP
Copy
curl -X POST 'https://app.jecheck.io/api/ext/v1/onboarding/steps/planner/skip' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"skip": true
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/onboarding/steps/planner/skip', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"skip": true
}),
});
const { data } = await res.json();
import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/onboarding/steps/planner/skip",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"skip":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', 'onboarding/steps/planner/skip', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'skip' => true,
],
]);
$data = json_decode($res->getBody(), true)['data'];
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "onboarding_steps_skip",
"arguments": {
"step": "planner",
"skip": true
}
}
}
{
"data": {
"steps": [
{
"key": "locations",
"optional": false,
"done": true,
"skipped": false,
"facts": 3
}
],
"done": 7,
"total": 8,
"complete": false,
"dismissed_at": null,
"active": true
}
}
POST Hide the Getting started screen
POST /onboarding/dismiss
The home page shows the AI control brief instead. The screen stays available at /ui/start.
Token scope write
Permission
start.view
MCP tool onboarding_dismiss
Responses
Status Description
200 OK
401 unauthorized — Missing, invalid, revoked or expired token
403 forbidden · insufficient_scope · plan_required — Token lacks start.view, is read-only
429 rate_limited — Too many requests — retry after Retry-After seconds
Request
cURL
Node.js
Python
PHP
MCP
Copy
curl -X POST 'https://app.jecheck.io/api/ext/v1/onboarding/dismiss' \
-H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/onboarding/dismiss', {
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/onboarding/dismiss",
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', 'onboarding/dismiss', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "onboarding_dismiss",
"arguments": {}
}
}
{
"data": {
"ok": true
}
}
POST Show the Getting started screen again
POST /onboarding/restore
Token scope write
Permission
start.view
MCP tool onboarding_restore
Responses
Status Description
200 OK
401 unauthorized — Missing, invalid, revoked or expired token
403 forbidden · insufficient_scope · plan_required — Token lacks start.view, is read-only
429 rate_limited — Too many requests — retry after Retry-After seconds
Request
cURL
Node.js
Python
PHP
MCP
Copy
curl -X POST 'https://app.jecheck.io/api/ext/v1/onboarding/restore' \
-H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/onboarding/restore', {
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/onboarding/restore",
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', 'onboarding/restore', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "onboarding_restore",
"arguments": {}
}
}
{
"data": {
"ok": true
}
}
GET Free time slots for a call with a manager
GET /onboarding/slots
Days ahead with free slots in the company time zone. Each slot lists the managers free at that time; pass start (and optionally manager_id) to book it.
Token scope read
Permission
start.view
MCP tool onboarding_slots · read-only
Query parameters
manager_id
integer
Only this manager.
Responses
Status Description
200 OK
401 unauthorized — Missing, invalid, revoked or expired token
403 forbidden · insufficient_scope · plan_required — Token lacks start.view
422 validation_failed — Invalid parameters — see error.fields
429 rate_limited — Too many requests — retry after Retry-After seconds
Request
cURL
Node.js
Python
PHP
MCP
Copy
curl 'https://app.jecheck.io/api/ext/v1/onboarding/slots?manager_id=42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/onboarding/slots?manager_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/onboarding/slots",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"manager_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', 'onboarding/slots', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'manager_id' => 42,
],
]);
$data = json_decode($res->getBody(), true)['data'];
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "onboarding_slots",
"arguments": {
"manager_id": 42
}
}
}
{
"data": {
"tz": "Asia/Almaty",
"days": [
{
"date": "2026-11-12",
"slots": [
{
"start": "2026-11-12T11:00:00+05:00",
"time": "11:00",
"managers": [
1
]
}
]
}
]
}
}
GET List the company's calls with managers
GET /onboarding/meetings
Upcoming and past calls (newest first), with the room link and status: booked, cancelled, done, no_show.
Token scope read
Permission
start.view
MCP tool onboarding_meetings_list · read-only
Responses
Status Description
200 OK
401 unauthorized — Missing, invalid, revoked or expired token
403 forbidden · insufficient_scope · plan_required — Token lacks start.view
429 rate_limited — Too many requests — retry after Retry-After seconds
Request
cURL
Node.js
Python
PHP
MCP
Copy
curl 'https://app.jecheck.io/api/ext/v1/onboarding/meetings' \
-H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/onboarding/meetings', {
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/onboarding/meetings",
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', 'onboarding/meetings', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "onboarding_meetings_list",
"arguments": {}
}
}
{
"data": {
"data": [
{
"uuid": "0b9e6c2a-6f1e-4c55-9a0b-1f2e3d4c5b6a",
"status": "booked",
"topic": "setup",
"comment": "We have 5 coffee shops",
"phone": null,
"starts_at": "2026-11-12T11:00:00+05:00",
"ends_at": "2026-11-12T11:30:00+05:00",
"provider": "zoom",
"url": "https://zoom.us/j/1234567890",
"manager": {
"id": 1,
"name": "Aliya Seitova",
"title": "Customer success manager",
"photo": null
},
"ics_url": "/ui/start/meetings/0b9e6c2a-6f1e-4c55-9a0b-1f2e3d4c5b6a/ics"
}
]
}
}
POST Book a call with a manager
POST /onboarding/meetings
Books a free slot from GET /onboarding/slots. The room is the manager's permanent Zoom room; both sides get an email with a calendar invite, the client gets a reminder an hour before. A company has one active booking — booking again reschedules it.
Token scope write
Permission
start.view
MCP tool onboarding_meetings_book
Request body
start
date-time
Required
Slot start from GET /onboarding/slots (ISO 8601 with offset).
manager_id
integer
Preferred manager; by default the least busy free one.
provider
string
Video service — currently only zoom.
zoom
topic
string
Default setup.
setupdemopricingother
comment
string
What to discuss.
Maximum length 2000.
phone
string
Maximum length 40.
Responses
Status Description
201 Created
401 unauthorized — Missing, invalid, revoked or expired token
403 forbidden · insufficient_scope · plan_required — Token lacks start.view, is read-only
422 422 — The slot is already taken
429 rate_limited — Too many requests — retry after Retry-After seconds
Request
cURL
Node.js
Python
PHP
MCP
Copy
curl -X POST 'https://app.jecheck.io/api/ext/v1/onboarding/meetings' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"start": "2026-11-03T09:00:00+05:00",
"manager_id": 42,
"provider": "zoom",
"topic": "setup"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/onboarding/meetings', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"start": "2026-11-03T09:00:00+05:00",
"manager_id": 42,
"provider": "zoom",
"topic": "setup"
}),
});
const { data } = await res.json();
import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/onboarding/meetings",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"start":"2026-11-03T09:00:00+05:00","manager_id":42,"provider":"zoom","topic":"setup"},
)
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', 'onboarding/meetings', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'start' => '2026-11-03T09:00:00+05:00',
'manager_id' => 42,
'provider' => 'zoom',
'topic' => 'setup',
],
]);
$data = json_decode($res->getBody(), true)['data'];
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "onboarding_meetings_book",
"arguments": {
"start": "2026-11-03T09:00:00+05:00",
"manager_id": 42,
"provider": "zoom",
"topic": "setup"
}
}
}
{
"data": {
"meeting": {
"uuid": "0b9e6c2a-6f1e-4c55-9a0b-1f2e3d4c5b6a",
"status": "booked",
"topic": "setup",
"comment": "We have 5 coffee shops",
"phone": null,
"starts_at": "2026-11-12T11:00:00+05:00",
"ends_at": "2026-11-12T11:30:00+05:00",
"provider": "zoom",
"url": "https://zoom.us/j/1234567890",
"manager": {
"id": 1,
"name": "Aliya Seitova",
"title": "Customer success manager",
"photo": null
},
"ics_url": "/ui/start/meetings/0b9e6c2a-6f1e-4c55-9a0b-1f2e3d4c5b6a/ics"
}
}
}
POST Cancel a call
POST /onboarding/meetings/{uuid}/cancel
The manager gets an email; the calendar event is cancelled.
Token scope write
Permission
start.view
MCP tool onboarding_meetings_cancel · destructive
Path parameters
Request body
reason
string
Maximum length 500.
Responses
Status Description
200 OK
401 unauthorized — Missing, invalid, revoked or expired token
403 forbidden · insufficient_scope · plan_required — Token lacks start.view, 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
Request
cURL
Node.js
Python
PHP
MCP
Copy
curl -X POST 'https://app.jecheck.io/api/ext/v1/onboarding/meetings/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/onboarding/meetings/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"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/onboarding/meetings/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"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', 'onboarding/meetings/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'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": "onboarding_meetings_cancel",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}
{
"data": {
"ok": true
}
}