The AI dispatcher follows work through to the end in Telegram: it asks assignees about stuck and overdue tasks and audit findings, takes answers (buttons, short phrases, photos) and applies them in jeCheck, escalates only what was not resolved, sends urgent HACCP alerts and turns employee messages into requests. Settings, weekly metrics, conversations and Telegram connection of employees.
Whether the dispatcher is on, which duties it performs and its limits. Duties: task_flow — asks about stuck/overdue tasks; audit_triage — makes sure every audit finding has an owner who accepted it; fix_verification — asks reviewers to accept or return fixes, closes tasks when the item passes in a new audit (close_on_pass); haccp_urgent — critical HACCP deviations; plan_discipline — overdue planned audits; morning_digest — manager digest; intake — requests written to the bot.
Token scope
read
Permission
dispatcher.view or dispatcher.manage
MCP tool
dispatcher_settings_get · read-only
Responses
Status
Description
200
OK
401
unauthorized — Missing, invalid, revoked or expired token
Last 7 days and the 7 days before: reaction_minutes — median time from a question about audit findings to the answer; accepted_share — % of findings accepted within accept_hours; on_time_share — % of tasks closed on time; without_manager_share — % of task questions resolved without a manager; answered_share — % of questions answered; messages — messages sent. stale_now — open tasks without movement for over 7 days; coverage — employees with Telegram connected.
Token scope
read
Permission
dispatcher.view or dispatcher.manage
MCP tool
dispatcher_metrics · read-only
Responses
Status
Description
200
OK
401
unauthorized — Missing, invalid, revoked or expired token
Conversations of the dispatcher about one item each (a task, an audit, a HACCP deviation, a planned audit, a request), newest first. reason (why the assignee is blocked or says the task is not theirs) is returned only with the dispatcher.messages permission.
Token scope
read
Permission
dispatcher.view or dispatcher.manage
MCP tool
dispatcher_threads_list · read-only
Query parameters
pageinteger
Page number, starting from 1.
Default 1. Minimum 1.
per_pageinteger
Items per page (max 100).
Default 25. Value 1–100.
A conversation with its messages. Message texts are returned only with the dispatcher.messages permission (each view is recorded in the access log); otherwise messages is null.
Token scope
read
Permission
dispatcher.view or dispatcher.manage
MCP tool
dispatcher_threads_get · read-only
Path parameters
idintegerRequired
Conversation id.
Responses
Status
Description
200
OK
401
unauthorized — Missing, invalid, revoked or expired token
GETList employees and their Telegram and Slack connection
GET /dispatcher/people
Active employees of the company: whether Telegram is connected (linked), stopped the bot (blocked), whether a phone is set in the profile (needed to connect by sharing the phone number in the bot), and whether the employee was found in the connected Slack workspace (slack) — then the dispatcher writes to them in Slack.
Token scope
read
Permission
dispatcher.manage
MCP tool
dispatcher_people · read-only
Query parameters
pageinteger
Page number, starting from 1.
Default 1. Minimum 1.
per_pageinteger
Items per page (max 100).
Default 25. Value 1–100.
linkedboolean
Only connected (true) or not connected (false) employees.
Responses
Status
Description
200
OK — a page of results with meta
401
unauthorized — Missing, invalid, revoked or expired token
Whether the company connected its Slack workspace to the dispatcher. Employees are matched by email; matched employees get dispatcher messages in Slack instead of Telegram. Connecting needs the Slack sign-in in a browser — open install_url while signed in to jeCheck.
Token scope
read
Permission
dispatcher.view
MCP tool
dispatcher_slack_get · read-only
Responses
Status
Description
200
OK
401
unauthorized — Missing, invalid, revoked or expired token
One-time links (valid 7 days) that connect each employee to the dispatcher bot. Send them by your own channel, or connect employees by a QR code with the bot link: an employee shares the phone number and the bot finds them by the profile phone.
Token scope
write
Permission
dispatcher.manage
MCP tool
dispatcher_invites_create
Request body
user_idsarray<integer>Required
Employee ids.
Maximum items 500.
Responses
Status
Description
200
OK
401
unauthorized — Missing, invalid, revoked or expired token
Questions the dispatcher is waiting for the acting user to answer (the same as in Telegram and its mini app): stuck or overdue tasks, audit findings to accept, fixes to confirm, HACCP alerts, overdue planned audits, due date approvals. text is the question in HTML (Telegram subset); buttons are the possible answers — pass action and arg of a button to the answer operation. input tells what else is accepted: text (free answer), date, reason, who (free text) or photo.
Token scope
read
Permission
Any employee; results follow the token's visibility
MCP tool
dispatcher_me_questions · 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
Answers as the acting user, exactly like a button, a short message or a photo in Telegram: the dispatcher applies it in jeCheck (completes the task, moves the due date within the company norm or asks the manager, records the reason, attaches the after photo). Pass one of: action (+ arg) from the question buttons, text, or photo. Returns the dispatcher replies and the question state after the answer (open: false — nothing more is needed).
Token scope
write
Permission
Any employee; results follow the token's visibility
MCP tool
dispatcher_me_questions_answer
Content type
multipart/form-dataapplication/json
Path parameters
idintegerRequired
Question (conversation) id.
Query parameters
photofile
Photo (after photo for a task, photo for a request).
Request body
actionstring
Button action, e.g. done, time, date, blocked, not_mine, accept, return, approve, deny.
Maximum length 32.
argstring
Button argument, e.g. a date 2026-11-11 for date.
Maximum length 40.
textstring
Free-text answer (a reason, a new date in words, a comment).
Maximum length 2000.
Responses
Status
Description
200
OK
401
unauthorized — Missing, invalid, revoked or expired token
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 -X POST 'https://app.jecheck.io/api/ext/v1/me/dispatcher/questions/42/answer' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"action": "string",
"arg": "string",
"text": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/me/dispatcher/questions/42/answer', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"action": "string",
"arg": "string",
"text": "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/me/dispatcher/questions/42/answer",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"action":"string","arg":"string","text":"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', 'me/dispatcher/questions/42/answer', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'action' => 'string',
'arg' => 'string',
'text' => '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": "dispatcher_me_questions_answer",
"arguments": {
"id": 42,
"action": "string",
"arg": "string",
"text": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}
Response 200
{
"data": {
"replies": [
{
"html": "Moved the due date to 11.11. I will write if the task is not closed by then.",
"buttons": []
}
],
"question": {
"id": 512,
"duty": "task_flow",
"role": "executor",
"open": false,
"state": "waiting",
"awaiting": null,
"input": null,
"photo": true,
"text": "The task is overdue by 3 days.\n<b>Replace the shower seal</b> · Downtown club · due 01.11\n\nWhat is its status?",
"buttons": [],
"asked_at": "2026-11-04T11:02:00+05:00",
"task": {
"id": 88231,
"key": "6f1c2a9e-3b7d-4c11-9a52-0d8e7f6b1a23",
"title": "Replace the shower seal",
"status": "in_progress",
"location": "Downtown club",
"due": "01.11.2026",
"overdue": true,
"needs_photo": true
}
}
}
}
POSTSend a request to the dispatcher
POST /me/dispatcher/requests
What broke or what is needed, in free words (as a message to the bot). The dispatcher works out the location and the department and asks to confirm: the reply contains the question with the confirmation buttons — answer it with the answer operation. Needs the dispatcher and its «Requests» duty to be on.
Token scope
write
Permission
Any employee; results follow the token's visibility
MCP tool
dispatcher_me_requests_create
Content type
multipart/form-dataapplication/json
Query parameters
photofile
Photo of the problem.
Request body
textstringRequired
What happened or what is needed.
Length 3–2000.
Responses
Status
Description
200
OK
401
unauthorized — Missing, invalid, revoked or expired token
validation_failed — Invalid parameters — see error.fields
429
rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/me/dispatcher/requests' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"text": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/me/dispatcher/requests', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"text": "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/me/dispatcher/requests",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"text":"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', 'me/dispatcher/requests', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'text' => '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": "dispatcher_me_requests_create",
"arguments": {
"text": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}