HACCP
Food-safety journals (HACCP): today's checks per location, journal entries with automatic limit checks, deviations with corrective actions, registries (equipment, suppliers, verification, medical books) and the HACCP plan. Locations follow haccp.view; medical books require the sensitive haccp.medical permission.
GETList HACCP locations
HACCP status of the company (enabled, country profile, timezone) and the HACCP locations visible to the token.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| MCP tool | haccp_sites · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/haccp/sites' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/sites', {
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/haccp/sites",
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', 'haccp/sites', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_sites",
"arguments": {}
}
}{
"data": {
"enabled": true,
"profile": "kz",
"timezone": "Asia/Almaty",
"sites": [
{
"id": 4609,
"name": "Abay Ave",
"address": "10 Abay Ave",
"kind": "cafe"
}
]
}
}GETGet today's HACCP checks
What must be recorded today: journals with time slots (status pending, overdue, done, late, missed, skipped), objects with limits, today's entries, staff on shift and open deviations. Without location_id — all HACCP locations visible to the token.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| MCP tool | haccp_today · 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 haccp.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/haccp/today?location_id=12' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/today?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/haccp/today",
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', 'haccp/today', [
'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": "haccp_today",
"arguments": {
"location_id": 12
}
}
}{
"data": {
"day": "2026-11-03",
"timezone": "Asia/Almaty",
"late_minutes": 60,
"sites": [
{
"location": {
"id": 4609,
"name": "Abay Ave"
},
"can_resolve": false,
"summary": {
"total": 9,
"done": 4,
"pending": 5,
"overdue": 1,
"deviations": 1
},
"journals": [
{
"id": 14,
"uuid": "4c3b2a1f-0e9d-4c8b-9a7f-6e5d4c3b2a1f",
"title": "Storage temperature",
"category": "monitoring",
"subject": "object",
"event_based": false,
"schedule_text": "Daily at 09:00, 17:00",
"fields": [
{
"key": "temperature",
"label": "Temperature",
"type": "temperature",
"unit": "°C",
"required": true,
"min": 2,
"max": 6
}
],
"can_fill": true,
"objects": [
{
"id": 301,
"name": "Fridge #1",
"type": "fridge",
"type_label": "Fridge",
"limits": {
"temperature": {
"min": 2,
"max": 6
}
}
}
],
"slots": [
{
"id": 99120,
"object_id": 301,
"opens_at": "2026-11-03T08:00:00+05:00",
"due_at": "2026-11-03T09:00:00+05:00",
"status": "done",
"open": true,
"entry_id": 88231,
"skip_reason": null
}
],
"entries": [
{
"id": 88231,
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"object_id": 301,
"subject_user": null,
"recorded_at": "2026-11-03T09:02:00+05:00",
"values": {
"temperature": 8.4
},
"status": "deviation",
"late": false
}
],
"done": 1,
"pending": 1,
"total": 2
}
],
"staff": [
{
"id": 311,
"name": "Aigerim Sadykova",
"on_shift": true
}
],
"deviations": [
{
"uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"journal": {
"id": 14,
"title": "Storage temperature"
},
"object_id": 301,
"severity": "major",
"status": "open",
"due_at": "2026-11-04T09:02:00+05:00",
"overdue": false,
"recheck": false
}
]
}
]
}
}GETList HACCP journals
Journals of the company with their field structure and schedule.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_journals_list · read-only |
Query parameters
active
boolean
true) or disabled (false) journals.
location_id
integer
category
string
monitoringprpreceivingpersonnelcleaningotherResponses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view, or the plan has no haccp
|
| 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/haccp/journals?active=true' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/journals?active=true', {
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/haccp/journals",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"active":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('GET', 'haccp/journals', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'active' => true,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_journals_list",
"arguments": {
"active": true
}
}
}{
"data": [
{
"id": 14,
"uuid": "4c3b2a1f-0e9d-4c8b-9a7f-6e5d4c3b2a1f",
"title": "Storage temperature",
"description": "Fridges and freezers, twice per shift",
"category": "monitoring",
"icon": "kitchen",
"color": "#0e9aa7",
"active": true,
"subject": "object",
"event_based": false,
"schedule": {
"type": "times",
"times": [
"09:00",
"17:00"
]
},
"schedule_text": "Daily at 09:00, 17:00",
"fields": [
{
"key": "temperature",
"label": "Temperature",
"type": "temperature",
"unit": "°C",
"required": true,
"min": 2,
"max": 6
}
],
"object_types": [
"fridge",
"freezer"
],
"location_ids": null,
"position_ids": [],
"ccp_id": null,
"requires_photo_on_deviation": false,
"template_key": "storage_temperature",
"updated_at": "2026-10-20T10:00:00+05:00"
}
]
}GETRetrieve a HACCP journal
Journal structure: fields (types, units, limits), schedule, subject (site, object, employee), object types, locations and positions, plus objects at the visible locations.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_journals_get · read-only |
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 haccp.view, or the plan has no haccp
|
| 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 'https://app.jecheck.io/api/ext/v1/haccp/journals/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/journals/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/haccp/journals/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('GET', 'haccp/journals/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_journals_get",
"arguments": {
"id": 42
}
}
}{
"data": {
"id": 14,
"uuid": "4c3b2a1f-0e9d-4c8b-9a7f-6e5d4c3b2a1f",
"title": "Storage temperature",
"description": "Fridges and freezers, twice per shift",
"category": "monitoring",
"icon": "kitchen",
"color": "#0e9aa7",
"active": true,
"subject": "object",
"event_based": false,
"schedule": {
"type": "times",
"times": [
"09:00",
"17:00"
]
},
"schedule_text": "Daily at 09:00, 17:00",
"fields": [
{
"key": "temperature",
"label": "Temperature",
"type": "temperature",
"unit": "°C",
"required": true,
"min": 2,
"max": 6
}
],
"object_types": [
"fridge",
"freezer"
],
"location_ids": null,
"position_ids": [],
"ccp_id": null,
"requires_photo_on_deviation": false,
"template_key": "storage_temperature",
"updated_at": "2026-10-20T10:00:00+05:00",
"objects": [
{
"id": 301,
"name": "Fridge #1",
"type": "fridge",
"location": {
"id": 4609,
"name": "Abay Ave"
},
"limits": {
"temperature": {
"min": 2,
"max": 6
}
},
"active": true
}
]
}
}GETList HACCP journal entries
Entries at the visible locations, newest first. Archived entries (past the retention period) are not returned; voided entries are included only with voided=true.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_entries_list · read-only |
Query parameters
page
integer
1. Minimum 1.
per_page
integer
25. Value 1–100.
journal_id
integer
location_ids
array<integer>
object_id
integer
status
string
okwarningdeviationlate
boolean
voided
boolean
true — only voided entries.
recorded_from
date-time
recorded_to
date-time
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 haccp.view, or the plan has no haccp
|
| 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/haccp/entries?journal_id=42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/entries?journal_id=42', {
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/haccp/entries",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"journal_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', 'haccp/entries', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'journal_id' => 42,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_entries_list",
"arguments": {
"journal_id": 42
}
}
}{
"data": [
{
"id": 88231,
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"journal_id": 14,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object_id": 301,
"subject_user": null,
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"values": {
"temperature": 8.4
},
"status": "deviation",
"flags": {
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"warnings": []
},
"late": false,
"late_reason": null,
"comment": "Door was left open",
"photos": [
{
"path": "haccp/761/photos/2026/11/a1b2.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/a1b2.jpg?X-Amz-Signature=…"
}
],
"deviation_uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"source": "app",
"revision": 0,
"voided": false,
"void_reason": null,
"updated_at": "2026-11-03T09:02:05+05:00"
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 1,
"has_more": false
}
}POSTRecord a HACCP journal entry
Records a measurement/check. Values are checked against the field and object limits: out of limits — the entry gets status deviation and a deviation is opened (notifications as in the app). Idempotent with uuid: repeating the same uuid returns the existing entry. With deviation_id — a re-check after correction.
| Token scope | write |
|---|---|
| Permission |
haccp.fill |
| MCP tool | haccp_entries_create |
Path parameters
id
integer
Required Request body
location_id
integer
Required values
object
Required key, e.g. {"temperature": 4.2}.
object_id
integer
object).
subject_user_id
integer
employee, e.g. health check).
recorded_at
date-time
uuid
uuid
comment
string
photos
array<string>
haccp.media.upload.
Maximum items 10. deviation_id
integer
late_reason
string
latitude
number
longitude
number
Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.fill, 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/haccp/journals/42/entries' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"values": [],
"object_id": 42,
"subject_user_id": 42
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/journals/42/entries', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"values": [],
"object_id": 42,
"subject_user_id": 42
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/haccp/journals/42/entries",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"values":[],"object_id":42,"subject_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', 'haccp/journals/42/entries', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'values' => [
],
'object_id' => 42,
'subject_user_id' => 42,
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_entries_create",
"arguments": {
"id": 42,
"location_id": 12,
"values": [],
"object_id": 42,
"subject_user_id": 42
}
}
}{
"data": {
"id": 88231,
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"journal_id": 14,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object_id": 301,
"subject_user": null,
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"values": {
"temperature": 8.4
},
"status": "deviation",
"flags": {
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"warnings": []
},
"late": false,
"late_reason": null,
"comment": "Door was left open",
"photos": [
{
"path": "haccp/761/photos/2026/11/a1b2.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/a1b2.jpg?X-Amz-Signature=…"
}
],
"deviation_uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"source": "app",
"revision": 0,
"voided": false,
"void_reason": null,
"updated_at": "2026-11-03T09:02:05+05:00"
}
}POSTCorrect a HACCP journal entry
Corrects values of an entry with a reason; previous values stay in the revision history. Allowed to the author or with haccp.resolve at the location.
| Token scope | write |
|---|---|
| Permission |
haccp.fill or haccp.resolve |
| Plan feature | haccp |
| MCP tool | haccp_entries_revise |
Path parameters
id
integer
Required Request body
values
object
Required reason
string
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.fill / haccp.resolve, is read-only, or the plan has no haccp
|
| 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/haccp/entries/42/revise' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"values": [],
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/entries/42/revise', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"values": [],
"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/haccp/entries/42/revise",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"values":[],"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', 'haccp/entries/42/revise', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'values' => [
],
'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": "haccp_entries_revise",
"arguments": {
"id": 42,
"values": [],
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"id": 88231,
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"journal_id": 14,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object_id": 301,
"subject_user": null,
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"values": {
"temperature": 8.4
},
"status": "deviation",
"flags": {
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"warnings": []
},
"late": false,
"late_reason": null,
"comment": "Door was left open",
"photos": [
{
"path": "haccp/761/photos/2026/11/a1b2.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/a1b2.jpg?X-Amz-Signature=…"
}
],
"deviation_uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"source": "app",
"revision": 0,
"voided": false,
"void_reason": null,
"updated_at": "2026-11-03T09:02:05+05:00"
}
}POSTVoid a HACCP journal entry
Marks an entry as void with a reason (haccp.resolve at the location). The entry stays in the journal for the record.
| Token scope | write |
|---|---|
| Permission |
haccp.resolve |
| Plan feature | haccp |
| MCP tool | haccp_entries_void |
Path parameters
id
integer
Required Request body
reason
string
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.resolve, is read-only, or the plan has no haccp
|
| 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/haccp/entries/42/void' \
-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/haccp/entries/42/void', {
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/haccp/entries/42/void",
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', 'haccp/entries/42/void', [
'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": "haccp_entries_void",
"arguments": {
"id": 42,
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"id": 88231,
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"journal_id": 14,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object_id": 301,
"subject_user": null,
"user": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"values": {
"temperature": 8.4
},
"status": "deviation",
"flags": {
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"warnings": []
},
"late": false,
"late_reason": null,
"comment": "Door was left open",
"photos": [
{
"path": "haccp/761/photos/2026/11/a1b2.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/a1b2.jpg?X-Amz-Signature=…"
}
],
"deviation_uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"source": "app",
"revision": 0,
"voided": false,
"void_reason": null,
"updated_at": "2026-11-03T09:02:05+05:00"
}
}POSTSkip a scheduled HACCP check
Marks a time slot as not required today (location closed, equipment off) with a reason. Requires haccp.resolve at the location.
| Token scope | write |
|---|---|
| Permission |
haccp.resolve |
| Plan feature | haccp |
| MCP tool | haccp_slots_skip |
Path parameters
id
integer
Required haccp.today.
Request body
reason
string
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.resolve, is read-only, or the plan has no haccp
|
| 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/haccp/slots/42/skip' \
-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/haccp/slots/42/skip', {
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/haccp/slots/42/skip",
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', 'haccp/slots/42/skip', [
'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": "haccp_slots_skip",
"arguments": {
"id": 42,
"reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"skipped": true
}
}POSTUpload a HACCP photo or document
Uploads an image (jpg, png, webp, heic) or PDF and returns its path for photos of an entry or deviation, photo fields, staff documents and verification records.
| Token scope | write |
|---|---|
| Permission |
haccp.view |
| MCP tool | haccp_media_upload |
| Content type | multipart/form-data application/json |
Request body
file
file
Required name and content_base64).
folder
string
photos. photosdocsResponses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view, 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/haccp/media' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-F 'folder=photos' \
-F '[email protected]'const form = new FormData();
form.append('folder', "photos");
form.append('file', fileInput.files[0]);
const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/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/haccp/media",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
data={"folder":"photos"},
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', 'haccp/media', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'multipart' => [
['name' => 'folder', 'contents' => 'photos'],
['name' => 'file', 'contents' => fopen('photo.jpg', 'r')],
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_media_upload",
"arguments": {
"folder": "photos"
}
}
}{
"data": {
"path": "haccp/761/photos/2026/11/a1b2.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/a1b2.jpg?X-Amz-Signature=…"
}
}GETList HACCP deviations
Deviations at the visible locations: reviews first, then newest. status=open (default) — all unresolved (stages open, correcting, review); closed; all. Use stage for an exact stage.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_deviations_list · read-only |
Query parameters
page
integer
1. Minimum 1.
per_page
integer
25. Value 1–100.
status
string
open. openclosedallstage
string
opencorrectingreviewclosedseverity
string
criticalmajorminorjournal_id
integer
location_ids
array<integer>
overdue
boolean
date_from
date
date_to
date
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 haccp.view, or the plan has no haccp
|
| 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/haccp/deviations?status=open' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/deviations?status=open', {
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/haccp/deviations",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
params={"status":"open"},
)
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', 'haccp/deviations', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'query' => [
'status' => 'open',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_deviations_list",
"arguments": {
"status": "open"
}
}
}{
"data": [
{
"id": 5120,
"uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"journal": {
"id": 14,
"title": "Storage temperature"
},
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object": {
"id": 301,
"name": "Fridge #1"
},
"severity": "major",
"status": "correcting",
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"responsible": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"due_at": "2026-11-04T09:02:00+05:00",
"overdue": false,
"created_at": "2026-11-03T09:02:05+05:00",
"closed_at": null,
"updated_at": "2026-11-03T09:40:00+05:00",
"url": "https://app.jecheck.io/ui/haccp/deviations?open=0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d"
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 1,
"has_more": false
}
}GETRetrieve a HACCP deviation
Deviation card: breached limits, the original entry and the re-check, corrective actions, required fields before submitting, the follow-up task and whether the token can work on / verify it.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_deviations_get · read-only |
Path parameters
uuid
uuid
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view, or the plan has no haccp
|
| 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 'https://app.jecheck.io/api/ext/v1/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
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', 'haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_deviations_get",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}
}
}{
"data": {
"id": 5120,
"uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"journal": {
"id": 14,
"title": "Storage temperature"
},
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object": {
"id": 301,
"name": "Fridge #1"
},
"severity": "major",
"status": "correcting",
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"responsible": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"due_at": "2026-11-04T09:02:00+05:00",
"overdue": false,
"created_at": "2026-11-03T09:02:05+05:00",
"closed_at": null,
"updated_at": "2026-11-03T09:40:00+05:00",
"url": "https://app.jecheck.io/ui/haccp/deviations?open=0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"correction": "Products moved to the backup fridge, door seal replaced",
"product_disposition": "released",
"product_note": null,
"root_cause": null,
"preventive_action": null,
"photos": [
{
"path": "haccp/761/photos/2026/11/c3d4.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/c3d4.jpg?X-Amz-Signature=…"
}
],
"required": [
"correction",
"product_disposition"
],
"entry": {
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"values": {
"temperature": 8.4
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"comment": "Door was left open"
},
"recheck": {
"uuid": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b",
"status": "ok",
"values": {
"temperature": 4.1
},
"recorded_at": "2026-11-03T11:00:00+05:00"
},
"task": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"title": "Fix the deviation: Storage temperature",
"status": "in_progress"
},
"closed_by": null,
"verified_by": null,
"verified_at": null,
"verify_note": null,
"can_work": true,
"can_verify": false
}
}PATCHUpdate corrective actions
Saves corrective actions as a draft (stage moves to correcting). Allowed to the responsible person or with haccp.resolve at the location; responsible_user_id — with haccp.resolve only.
| Token scope | write |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_deviations_update |
Path parameters
uuid
uuid
Required Request body
correction
string | null
product_disposition
string | null
releasedreworkeddestroyedreturnedquarantinenot_applicableproduct_note
string | null
root_cause
string | null
preventive_action
string | null
photos
array<string>
haccp.media.upload.
Maximum items 10. responsible_user_id
integer | null
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view, is read-only, or the plan has no haccp
|
| 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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"correction": "string",
"product_disposition": "released",
"product_note": "string",
"root_cause": "string"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"correction": "string",
"product_disposition": "released",
"product_note": "string",
"root_cause": "string"
}),
});
const { data } = await res.json();import os, requests
res = requests.patch(
"https://app.jecheck.io/api/ext/v1/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"correction":"string","product_disposition":"released","product_note":"string","root_cause":"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('PATCH', 'haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'correction' => 'string',
'product_disposition' => 'released',
'product_note' => 'string',
'root_cause' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_deviations_update",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"correction": "string",
"product_disposition": "released",
"product_note": "string",
"root_cause": "string"
}
}
}{
"data": {
"id": 5120,
"uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"journal": {
"id": 14,
"title": "Storage temperature"
},
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object": {
"id": 301,
"name": "Fridge #1"
},
"severity": "major",
"status": "correcting",
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"responsible": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"due_at": "2026-11-04T09:02:00+05:00",
"overdue": false,
"created_at": "2026-11-03T09:02:05+05:00",
"closed_at": null,
"updated_at": "2026-11-03T09:40:00+05:00",
"url": "https://app.jecheck.io/ui/haccp/deviations?open=0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"correction": "Products moved to the backup fridge, door seal replaced",
"product_disposition": "released",
"product_note": null,
"root_cause": null,
"preventive_action": null,
"photos": [
{
"path": "haccp/761/photos/2026/11/c3d4.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/c3d4.jpg?X-Amz-Signature=…"
}
],
"required": [
"correction",
"product_disposition"
],
"entry": {
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"values": {
"temperature": 8.4
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"comment": "Door was left open"
},
"recheck": {
"uuid": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b",
"status": "ok",
"values": {
"temperature": 4.1
},
"recorded_at": "2026-11-03T11:00:00+05:00"
},
"task": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"title": "Fix the deviation: Storage temperature",
"status": "in_progress"
},
"closed_by": null,
"verified_by": null,
"verified_at": null,
"verify_note": null,
"can_work": true,
"can_verify": false
}
}POSTSubmit corrective actions for review
Sends the deviation to review when the required fields are filled. If the token itself may close it (haccp.resolve, or haccp.close_critical for critical ones), it is closed right away.
| Token scope | write |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_deviations_submit |
Path parameters
uuid
uuid
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view, is read-only, or the plan has no haccp
|
| 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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit', {
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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit",
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', 'haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_deviations_submit",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}
}
}{
"data": {
"id": 5120,
"uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"journal": {
"id": 14,
"title": "Storage temperature"
},
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object": {
"id": 301,
"name": "Fridge #1"
},
"severity": "major",
"status": "correcting",
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"responsible": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"due_at": "2026-11-04T09:02:00+05:00",
"overdue": false,
"created_at": "2026-11-03T09:02:05+05:00",
"closed_at": null,
"updated_at": "2026-11-03T09:40:00+05:00",
"url": "https://app.jecheck.io/ui/haccp/deviations?open=0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"correction": "Products moved to the backup fridge, door seal replaced",
"product_disposition": "released",
"product_note": null,
"root_cause": null,
"preventive_action": null,
"photos": [
{
"path": "haccp/761/photos/2026/11/c3d4.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/c3d4.jpg?X-Amz-Signature=…"
}
],
"required": [
"correction",
"product_disposition"
],
"entry": {
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"values": {
"temperature": 8.4
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"comment": "Door was left open"
},
"recheck": {
"uuid": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b",
"status": "ok",
"values": {
"temperature": 4.1
},
"recorded_at": "2026-11-03T11:00:00+05:00"
},
"task": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"title": "Fix the deviation: Storage temperature",
"status": "in_progress"
},
"closed_by": null,
"verified_by": null,
"verified_at": null,
"verify_note": null,
"can_work": true,
"can_verify": false
}
}POSTClose a HACCP deviation
Verifies corrective actions and closes the deviation. Requires haccp.resolve at the location; critical deviations — haccp.close_critical.
| Token scope | write |
|---|---|
| Permission |
haccp.resolve or haccp.close_critical |
| Plan feature | haccp |
| MCP tool | haccp_deviations_close |
Path parameters
uuid
uuid
Required Request body
note
string | null
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.resolve / haccp.close_critical, is read-only, or the plan has no haccp
|
| 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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/close' \
-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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/close', {
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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/close",
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', 'haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/close', [
'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": "haccp_deviations_close",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"id": 5120,
"uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"journal": {
"id": 14,
"title": "Storage temperature"
},
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object": {
"id": 301,
"name": "Fridge #1"
},
"severity": "major",
"status": "correcting",
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"responsible": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"due_at": "2026-11-04T09:02:00+05:00",
"overdue": false,
"created_at": "2026-11-03T09:02:05+05:00",
"closed_at": null,
"updated_at": "2026-11-03T09:40:00+05:00",
"url": "https://app.jecheck.io/ui/haccp/deviations?open=0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"correction": "Products moved to the backup fridge, door seal replaced",
"product_disposition": "released",
"product_note": null,
"root_cause": null,
"preventive_action": null,
"photos": [
{
"path": "haccp/761/photos/2026/11/c3d4.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/c3d4.jpg?X-Amz-Signature=…"
}
],
"required": [
"correction",
"product_disposition"
],
"entry": {
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"values": {
"temperature": 8.4
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"comment": "Door was left open"
},
"recheck": {
"uuid": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b",
"status": "ok",
"values": {
"temperature": 4.1
},
"recorded_at": "2026-11-03T11:00:00+05:00"
},
"task": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"title": "Fix the deviation: Storage temperature",
"status": "in_progress"
},
"closed_by": null,
"verified_by": null,
"verified_at": null,
"verify_note": null,
"can_work": true,
"can_verify": false
}
}POSTReturn a HACCP deviation for rework
Returns a deviation in review back to correction with a note. Same permissions as closing.
| Token scope | write |
|---|---|
| Permission |
haccp.resolve or haccp.close_critical |
| Plan feature | haccp |
| MCP tool | haccp_deviations_reject |
Path parameters
uuid
uuid
Required Request body
note
string
Required Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.resolve / haccp.close_critical, is read-only, or the plan has no haccp
|
| 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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/reject' \
-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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/reject', {
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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/reject",
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', 'haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/reject', [
'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": "haccp_deviations_reject",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"id": 5120,
"uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"journal": {
"id": 14,
"title": "Storage temperature"
},
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object": {
"id": 301,
"name": "Fridge #1"
},
"severity": "major",
"status": "correcting",
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"responsible": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"due_at": "2026-11-04T09:02:00+05:00",
"overdue": false,
"created_at": "2026-11-03T09:02:05+05:00",
"closed_at": null,
"updated_at": "2026-11-03T09:40:00+05:00",
"url": "https://app.jecheck.io/ui/haccp/deviations?open=0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"correction": "Products moved to the backup fridge, door seal replaced",
"product_disposition": "released",
"product_note": null,
"root_cause": null,
"preventive_action": null,
"photos": [
{
"path": "haccp/761/photos/2026/11/c3d4.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/c3d4.jpg?X-Amz-Signature=…"
}
],
"required": [
"correction",
"product_disposition"
],
"entry": {
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"values": {
"temperature": 8.4
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"comment": "Door was left open"
},
"recheck": {
"uuid": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b",
"status": "ok",
"values": {
"temperature": 4.1
},
"recorded_at": "2026-11-03T11:00:00+05:00"
},
"task": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"title": "Fix the deviation: Storage temperature",
"status": "in_progress"
},
"closed_by": null,
"verified_by": null,
"verified_at": null,
"verify_note": null,
"can_work": true,
"can_verify": false
}
}POSTCreate a task for a HACCP deviation
Creates a task when the fix needs work (repair, deep cleaning). Assign to a user (assignee_id) or a team queue (team_id). Requires haccp.resolve at the location.
| Token scope | write |
|---|---|
| Permission |
haccp.resolve |
| Plan feature | haccp |
| MCP tool | haccp_deviations_task |
Path parameters
uuid
uuid
Required Request body
assignee_id
integer
team_id
integer
title
string
description
string
due_at
date-time
Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.resolve, is read-only, or the plan has no haccp
|
| 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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/task' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"assignee_id": 42,
"team_id": 42,
"title": "Replace the broken fridge seal",
"description": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/task', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"assignee_id": 42,
"team_id": 42,
"title": "Replace the broken fridge seal",
"description": "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/haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/task",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"assignee_id":42,"team_id":42,"title":"Replace the broken fridge seal","description":"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', 'haccp/deviations/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/task', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'assignee_id' => 42,
'team_id' => 42,
'title' => 'Replace the broken fridge seal',
'description' => '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": "haccp_deviations_task",
"arguments": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"assignee_id": 42,
"team_id": 42,
"title": "Replace the broken fridge seal",
"description": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}
}
}{
"data": {
"id": 5120,
"uuid": "0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"journal": {
"id": 14,
"title": "Storage temperature"
},
"location": {
"id": 4609,
"name": "Abay Ave"
},
"object": {
"id": 301,
"name": "Fridge #1"
},
"severity": "major",
"status": "correcting",
"breaches": [
{
"key": "temperature",
"label": "Temperature",
"value": 8.4,
"unit": "°C",
"min": 2,
"max": 6
}
],
"responsible": {
"id": 311,
"uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
"name": "Aigerim Sadykova"
},
"due_at": "2026-11-04T09:02:00+05:00",
"overdue": false,
"created_at": "2026-11-03T09:02:05+05:00",
"closed_at": null,
"updated_at": "2026-11-03T09:40:00+05:00",
"url": "https://app.jecheck.io/ui/haccp/deviations?open=0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d",
"correction": "Products moved to the backup fridge, door seal replaced",
"product_disposition": "released",
"product_note": null,
"root_cause": null,
"preventive_action": null,
"photos": [
{
"path": "haccp/761/photos/2026/11/c3d4.jpg",
"url": "https://storage.example.com/haccp/761/photos/2026/11/c3d4.jpg?X-Amz-Signature=…"
}
],
"required": [
"correction",
"product_disposition"
],
"entry": {
"uuid": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"values": {
"temperature": 8.4
},
"recorded_at": "2026-11-03T09:02:00+05:00",
"comment": "Door was left open"
},
"recheck": {
"uuid": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b",
"status": "ok",
"values": {
"temperature": 4.1
},
"recorded_at": "2026-11-03T11:00:00+05:00"
},
"task": {
"uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
"title": "Fix the deviation: Storage temperature",
"status": "in_progress"
},
"closed_by": null,
"verified_by": null,
"verified_at": null,
"verify_note": null,
"can_work": true,
"can_verify": false
}
}GETList HACCP plans
HACCP plans with status, version, review date and completion of the 12 Codex steps.
| Token scope | read |
|---|---|
| Permission |
haccp.plan |
| Plan feature | haccp |
| MCP tool | haccp_plans_list · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.plan, or the plan has no haccp
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/haccp/plans' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/plans', {
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/haccp/plans",
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', 'haccp/plans', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_plans_list",
"arguments": {}
}
}{
"data": [
{
"id": 3,
"uuid": "8b7a6c5d-4e3f-4a2b-9c1d-0e9f8a7b6c5d",
"title": "Hot kitchen",
"status": "approved",
"version": 2,
"review_due_at": "2027-05-01",
"location_ids": [
4609
],
"progress": {
"1": true,
"2": true,
"3": true,
"4": true,
"5": true,
"6": true,
"7": true,
"8": true,
"9": true,
"10": true,
"11": false,
"12": true
},
"problems": 1,
"has_changes": false,
"counts": {
"steps": 9,
"hazards": 14,
"ccps": 2
},
"updated_at": "2026-10-28T15:00:00+05:00"
}
]
}GETRetrieve a HACCP plan
Full plan by the 12 Codex steps: team, products, process flow steps, hazard analysis, critical control points with limits, monitoring and corrective actions; open problems per step.
| Token scope | read |
|---|---|
| Permission |
haccp.plan |
| Plan feature | haccp |
| MCP tool | haccp_plans_get · read-only |
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 haccp.plan, or the plan has no haccp
|
| 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 'https://app.jecheck.io/api/ext/v1/haccp/plans/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/plans/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/haccp/plans/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('GET', 'haccp/plans/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_plans_get",
"arguments": {
"id": 42
}
}
}{
"data": {
"id": 3,
"uuid": "8b7a6c5d-4e3f-4a2b-9c1d-0e9f8a7b6c5d",
"title": "Hot kitchen",
"scope": "Hot dishes of the main menu",
"location_ids": [
4609
],
"status": "approved",
"intended_use": "Served hot within 2 hours",
"consumer_groups": [
"general"
],
"prerequisites": [],
"flow_confirmed_at": "2026-10-10",
"flow_confirmed_by": 120,
"documentation": {
"records": []
},
"validation": null,
"risk_threshold": 6,
"version": 2,
"review_due_at": "2027-05-01",
"updated_at": "2026-10-28T15:00:00+05:00",
"team": [
{
"id": 1,
"user_id": 120,
"name": "Daniyar Omarov",
"role": "leader",
"job_title": "Chef",
"competence": "HACCP course 2025",
"responsibilities": "Plan owner"
}
],
"products": [
{
"id": 4,
"name": "Chicken soup",
"group": "hot",
"composition": "Chicken, vegetables",
"characteristics": null,
"processing": "Boiling",
"packaging": null,
"storage": "+2…+6 °C",
"shelf_life": "24 h",
"distribution": null,
"allergens": [
"celery"
],
"labeling": null,
"intended_use": null
}
],
"steps": [
{
"id": 21,
"name": "Cooking",
"type": "process",
"description": null,
"parameters": "≥ 75 °C in the core",
"inputs": null,
"outsourced": false,
"rework": false,
"analyzed": true,
"next": [
22
],
"products": [
4
],
"position": 3
}
],
"hazards": [
{
"id": 51,
"step_id": 21,
"type": "biological",
"name": "Salmonella survival",
"source": "Raw chicken",
"likelihood": 3,
"severity": 3,
"significant": true,
"justification": null,
"control_measures": "Core temperature control",
"decision": {
"q1": true
},
"outcome": "ccp"
}
],
"ccps": [
{
"id": 7,
"step_id": 21,
"hazard_id": 51,
"kind": "ccp",
"code": "CCP-1",
"name": "Core temperature",
"limits": [
{
"field": "temperature",
"min": 75
}
],
"limits_justification": null,
"monitoring_what": "Core temperature",
"monitoring_how": "Probe thermometer",
"monitoring_frequency": "Each batch",
"monitoring_positions": [],
"journal_id": 15,
"corrective_actions": "Continue cooking",
"corrective_positions": [],
"verification": "Weekly journal review",
"verification_frequency": "weekly",
"validation": null,
"records": null
}
],
"problems": [
{
"step": 11,
"message": "Describe verification procedures"
}
]
}
}GETList HACCP equipment
Controlled objects (fridges, freezers, thermometers, …) at the visible locations with limits, calibration dates and the last measurement.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_equipment_list · read-only |
Query parameters
location_id
integer
type
string
fridge.
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view, or the plan has no haccp
|
| 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/haccp/equipment?location_id=12' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/equipment?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/haccp/equipment",
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', 'haccp/equipment', [
'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": "haccp_equipment_list",
"arguments": {
"location_id": 12
}
}
}{
"data": [
{
"id": 301,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"type": "fridge",
"type_label": "Fridge",
"name": "Fridge #1",
"serial": "LG-2231",
"limits": {
"temperature": {
"min": 2,
"max": 6
}
},
"calibrated_at": null,
"calibration_due_at": null,
"calibration_overdue": false,
"note": null,
"active": true,
"last": {
"values": {
"temperature": 4.1
},
"status": "ok",
"recorded_at": "2026-11-03T11:00:00+05:00"
}
}
]
}POSTAdd HACCP equipment
Adds a controlled object at a HACCP location. Without limits the country profile defaults for the type are used. Requires haccp.registries.
| Token scope | write |
|---|---|
| Permission |
haccp.registries |
| Plan feature | haccp |
| MCP tool | haccp_equipment_create |
Request body
location_id
integer
Required type
string
fridge, freezer, …); unknown — other.
Maximum length 60. name
string
Required serial
string | null
limits
object
{"temperature": {"min": 2, "max": 6}, "humidity": {"min": 40, "max": 70}}.
calibrated_at
date | null
calibration_due_at
date | null
note
string | null
active
boolean
Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.registries, is read-only, or the plan has no haccp
|
| 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/haccp/equipment' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"type": "string",
"name": "Replace the broken fridge seal",
"serial": "string"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/equipment', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"type": "string",
"name": "Replace the broken fridge seal",
"serial": "string"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/haccp/equipment",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"type":"string","name":"Replace the broken fridge seal","serial":"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', 'haccp/equipment', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'type' => 'string',
'name' => 'Replace the broken fridge seal',
'serial' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_equipment_create",
"arguments": {
"location_id": 12,
"type": "string",
"name": "Replace the broken fridge seal",
"serial": "string"
}
}
}{
"data": {
"id": 301,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"type": "fridge",
"type_label": "Fridge",
"name": "Fridge #1",
"serial": "LG-2231",
"limits": {
"temperature": {
"min": 2,
"max": 6
}
},
"calibrated_at": null,
"calibration_due_at": null,
"calibration_overdue": false,
"note": null,
"active": true,
"last": {
"values": {
"temperature": 4.1
},
"status": "ok",
"recorded_at": "2026-11-03T11:00:00+05:00"
}
}
}PATCHUpdate HACCP equipment
Partial update of a controlled object. Requires haccp.registries.
| Token scope | write |
|---|---|
| Permission |
haccp.registries |
| Plan feature | haccp |
| MCP tool | haccp_equipment_update |
Path parameters
id
integer
Required Request body
location_id
integer
type
string
fridge, freezer, …); unknown — other.
Maximum length 60. name
string
serial
string | null
limits
object
{"temperature": {"min": 2, "max": 6}, "humidity": {"min": 40, "max": 70}}.
calibrated_at
date | null
calibration_due_at
date | null
note
string | null
active
boolean
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.registries, is read-only, or the plan has no haccp
|
| 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/haccp/equipment/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"location_id": 12,
"type": "string",
"name": "Replace the broken fridge seal",
"serial": "string"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/equipment/42', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"location_id": 12,
"type": "string",
"name": "Replace the broken fridge seal",
"serial": "string"
}),
});
const { data } = await res.json();import os, requests
res = requests.patch(
"https://app.jecheck.io/api/ext/v1/haccp/equipment/42",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"location_id":12,"type":"string","name":"Replace the broken fridge seal","serial":"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('PATCH', 'haccp/equipment/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'location_id' => 12,
'type' => 'string',
'name' => 'Replace the broken fridge seal',
'serial' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_equipment_update",
"arguments": {
"id": 42,
"location_id": 12,
"type": "string",
"name": "Replace the broken fridge seal",
"serial": "string"
}
}
}{
"data": {
"id": 301,
"location": {
"id": 4609,
"name": "Abay Ave"
},
"type": "fridge",
"type_label": "Fridge",
"name": "Fridge #1",
"serial": "LG-2231",
"limits": {
"temperature": {
"min": 2,
"max": 6
}
},
"calibrated_at": null,
"calibration_due_at": null,
"calibration_overdue": false,
"note": null,
"active": true,
"last": {
"values": {
"temperature": 4.1
},
"status": "ok",
"recorded_at": "2026-11-03T11:00:00+05:00"
}
}
}DELDelete HACCP equipment
Removes a controlled object; its entries are kept. Requires haccp.registries.
| Token scope | write |
|---|---|
| Permission |
haccp.registries |
| Plan feature | haccp |
| MCP tool | haccp_equipment_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 haccp.registries, is read-only, or the plan has no haccp
|
| 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/haccp/equipment/42' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/equipment/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/haccp/equipment/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', 'haccp/equipment/42', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_equipment_delete",
"arguments": {
"id": 42
}
}
}{
"data": {
"deleted": true
}
}GETList staff medical books
Employees of locations where the token has the sensitive haccp.medical permission, with the latest document of each type (medical book, hygiene training, …) and its status (valid, expiring, expired, missing). Each call is written to the access log.
| Token scope | read |
|---|---|
| Permission |
haccp.medical |
| Plan feature | haccp |
| MCP tool | haccp_medical_list · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.medical, or the plan has no haccp
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/haccp/medical-books' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/medical-books', {
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/haccp/medical-books",
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', 'haccp/medical-books', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_medical_list",
"arguments": {}
}
}{
"data": [
{
"id": 311,
"name": "Aigerim Sadykova",
"position": "Cook",
"location_ids": [
4609
],
"problems": 0,
"docs": {
"medical_book": {
"doc_id": 77,
"number": "MB-1022",
"issued_at": "2026-03-01",
"expires_at": "2027-03-01",
"status": "valid"
}
},
"history": [
{
"id": 77,
"type": "medical_book",
"number": "MB-1022",
"issued_at": "2026-03-01",
"expires_at": "2027-03-01",
"issuer": "City clinic #4",
"note": null,
"file": {
"path": "haccp/761/docs/2026/03/mb.pdf",
"url": "https://storage.example.com/haccp/761/docs/2026/03/mb.pdf?X-Amz-Signature=…"
}
}
]
}
]
}POSTAdd a staff medical document
Records a medical book / health document of an employee. Requires haccp.registries and haccp.medical for the employee's location. expires_at defaults from the document type validity.
| Token scope | write |
|---|---|
| Permission |
haccp.registries |
| Plan feature | haccp |
| MCP tool | haccp_medical_create |
Request body
user_id
integer
Required type
string
Required medical_book.
Maximum length 60. number
string
issued_at
date
expires_at
date
issuer
string
note
string
file
string
haccp.media.upload (folder docs).
Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.registries, is read-only, or the plan has no haccp
|
| 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/haccp/medical-books' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"user_id": 42,
"type": "string",
"number": "string",
"issued_at": "2026-11-01"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/medical-books', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"user_id": 42,
"type": "string",
"number": "string",
"issued_at": "2026-11-01"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/haccp/medical-books",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"user_id":42,"type":"string","number":"string","issued_at":"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', 'haccp/medical-books', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'user_id' => 42,
'type' => 'string',
'number' => 'string',
'issued_at' => '2026-11-01',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_medical_create",
"arguments": {
"user_id": 42,
"type": "string",
"number": "string",
"issued_at": "2026-11-01"
}
}
}{
"data": {
"id": 77,
"user_id": 311,
"type": "medical_book",
"number": "MB-1022",
"issued_at": "2026-03-01",
"expires_at": "2027-03-01",
"issuer": "City clinic #4",
"note": null
}
}GETList HACCP suppliers
Approved suppliers with their documents (certificates, declarations) and expiry status.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_suppliers_list · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view, or the plan has no haccp
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/haccp/suppliers' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/suppliers', {
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/haccp/suppliers",
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', 'haccp/suppliers', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_suppliers_list",
"arguments": {}
}
}{
"data": [
{
"id": 12,
"name": "Fresh Farm LLP",
"tax_id": "123456789012",
"contact": "Arman",
"phone": "+7 701 000 00 00",
"email": "[email protected]",
"products": "Poultry",
"status": "approved",
"rating": 5,
"note": null,
"evaluated_at": "2026-09-01",
"expired": 0,
"expiring": 1,
"docs": [
{
"id": 30,
"type": "certificate",
"number": "KZ-77",
"product": "Chicken",
"issued_at": "2026-01-10",
"valid_until": "2026-11-20",
"status": "expiring",
"file": null
}
]
}
]
}GETList HACCP verification activities
Scheduled verification activities (internal audits, lab tests, swabs, calibration) with next due date and the latest records.
| Token scope | read |
|---|---|
| Permission |
haccp.view |
| Plan feature | haccp |
| MCP tool | haccp_verifications_list · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.view, or the plan has no haccp
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/haccp/verifications' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/verifications', {
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/haccp/verifications",
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', 'haccp/verifications', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_verifications_list",
"arguments": {}
}
}{
"data": [
{
"id": 5,
"type": "internal_audit",
"title": "Quarterly HACCP audit",
"description": null,
"every_days": 90,
"next_due_at": "2026-12-01",
"last_done_at": "2026-09-01",
"responsible": {
"id": 120,
"uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
"name": "Daniyar Omarov"
},
"plan_id": 3,
"active": true,
"overdue": false,
"records": [
{
"id": 41,
"done_at": "2026-09-01",
"user": "Daniyar Omarov",
"result": "ok",
"findings": null,
"actions": null,
"files": []
}
]
}
]
}POSTRecord a verification result
Records that a verification activity was done; the next due date moves by its interval. Findings are required when result is issues. Requires haccp.registries.
| Token scope | write |
|---|---|
| Permission |
haccp.registries |
| Plan feature | haccp |
| MCP tool | haccp_verifications_record |
Path parameters
id
integer
Required Request body
done_at
date
result
string
ok. okissuesfindings
string
actions
string
files
array<string>
haccp.media.upload.
Maximum items 10. Responses
| Status | Description |
|---|---|
| 201 | Created |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.registries, is read-only, or the plan has no haccp
|
| 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/haccp/verifications/42/records' \
-H 'Authorization: Bearer $JECHECK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"done_at": "2026-11-01",
"result": "ok",
"findings": "string",
"actions": "string"
}'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/verifications/42/records', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"done_at": "2026-11-01",
"result": "ok",
"findings": "string",
"actions": "string"
}),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.jecheck.io/api/ext/v1/haccp/verifications/42/records",
headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
json={"done_at":"2026-11-01","result":"ok","findings":"string","actions":"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', 'haccp/verifications/42/records', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
'json' => [
'done_at' => '2026-11-01',
'result' => 'ok',
'findings' => 'string',
'actions' => 'string',
],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_verifications_record",
"arguments": {
"id": 42,
"done_at": "2026-11-01",
"result": "ok",
"findings": "string",
"actions": "string"
}
}
}{
"data": {
"id": 42,
"verification_id": 5,
"done_at": "2026-11-03",
"result": "ok",
"findings": null,
"actions": null
}
}GETGet HACCP settings
Module settings: country profile of norms, timezone, shift start, HACCP leader, retention and grace periods, options and HACCP locations. Requires haccp.settings.
| Token scope | read |
|---|---|
| Permission |
haccp.settings |
| Plan feature | haccp |
| MCP tool | haccp_settings_get · read-only |
Responses
| Status | Description |
|---|---|
| 200 | OK |
| 401 | unauthorized — Missing, invalid, revoked or expired token
|
| 403 | forbidden · insufficient_scope · plan_required — Token lacks haccp.settings, or the plan has no haccp
|
| 429 | rate_limited — Too many requests — retry after Retry-After seconds
|
curl 'https://app.jecheck.io/api/ext/v1/haccp/settings' \
-H 'Authorization: Bearer $JECHECK_TOKEN'const res = await fetch('https://app.jecheck.io/api/ext/v1/haccp/settings', {
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/haccp/settings",
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', 'haccp/settings', [
'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "haccp_settings_get",
"arguments": {}
}
}{
"data": {
"profile": "kz",
"profile_name": "Kazakhstan",
"timezone": "Asia/Almaty",
"shift_start": "08:00",
"leader": {
"id": 120,
"uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
"name": "Daniyar Omarov"
},
"retention_months": 12,
"grace_minutes": 30,
"options": {
"late_minutes": 60,
"expiry_warning_days": 30
},
"onboarded_at": "2026-10-17T12:00:00+05:00",
"sites": [
{
"location": {
"id": 4609,
"name": "Abay Ave"
},
"kind": "cafe",
"seats": 40,
"shift_start": null,
"work_days": [
1,
2,
3,
4,
5,
6,
7
],
"active": true
}
]
}
}