jeCheckDevelopers
Get an API key

Dashboards

Custom dashboards of cards built from the metrics catalog (audits, tasks, HACCP, schedule…). List and read dashboards, get computed card data (always limited by the viewer's permissions and locations), create and edit dashboards and cards, share them with colleagues or the whole company (dashboards.company).

GETList dashboards

GET /dashboards

Dashboards visible to the acting user: own, shared with them and published to the whole company, in the user's tab order.

Token scoperead
Permission Any employee; results follow the token's visibility
MCP tooldashboards_list · read-only

Query parameters

include_hidden boolean
Include dashboards the user hid from tabs. Default false.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Plan without API access
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/dashboards?include_hidden=false' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards?include_hidden=false', {
  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/dashboards",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    params={"include_hidden":False},
)
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', 'dashboards', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'query' => [
        'include_hidden' => false,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_list",
        "arguments": {
            "include_hidden": false
        }
    }
}
Response 200
{
  "data": [
    {
      "uuid": "2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "title": "Location overview",
      "icon": "dashboard",
      "description": "Weekly picture of all coffee shops",
      "filters": {
        "period": "30d",
        "date_from": null,
        "date_to": null,
        "location_ids": [
          12,
          14
        ],
        "checklist_ids": [],
        "auditor_ids": []
      },
      "visibility": "company",
      "company_can_edit": false,
      "refresh_sec": 0,
      "template": "location",
      "owner": {
        "id": 120,
        "uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
        "name": "Daniyar Omarov"
      },
      "is_owner": false,
      "permissions": {
        "edit": false,
        "share": false,
        "delete": false
      },
      "updated_at": "2026-11-03T12:10:00+05:00",
      "url": "https://app.jecheck.io/ui/dashboards/2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "hidden": false
    }
  ]
}

GETRetrieve a dashboard

GET /dashboards/{uuid}

Dashboard settings, saved filters, the acting user's permissions and the cards with their layout on a 12-column grid. Card values — dashboards.data.

Token scoperead
Permission Any employee; results follow the token's visibility
MCP tooldashboards_get · read-only

Path parameters

uuid uuid Required
Dashboard UUID.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Plan without API access
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/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/dashboards/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', 'dashboards/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": "dashboards_get",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "uuid": "2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "title": "Location overview",
    "icon": "dashboard",
    "description": "Weekly picture of all coffee shops",
    "filters": {
      "period": "30d",
      "date_from": null,
      "date_to": null,
      "location_ids": [
        12,
        14
      ],
      "checklist_ids": [],
      "auditor_ids": []
    },
    "visibility": "company",
    "company_can_edit": false,
    "refresh_sec": 0,
    "template": "location",
    "owner": {
      "id": 120,
      "uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
      "name": "Daniyar Omarov"
    },
    "is_owner": false,
    "permissions": {
      "edit": false,
      "share": false,
      "delete": false
    },
    "updated_at": "2026-11-03T12:10:00+05:00",
    "url": "https://app.jecheck.io/ui/dashboards/2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "widgets": [
      {
        "uuid": "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
        "spec": {
          "v": 1,
          "type": "kpi",
          "metrics": [
            "audits.avg_result"
          ],
          "compare": true,
          "title": "Average score"
        },
        "x": 0,
        "y": 0,
        "w": 3,
        "h": 2,
        "source": "library",
        "preset": "kpi_avg"
      }
    ]
  }
}

GETGet computed data of all cards

GET /dashboards/{uuid}/data

Values of every card (or of widget_uuids), keyed by card UUID. Computed with the dashboard filters, or with the filters passed here (temporary, not saved). Data is always limited by the acting user's permissions; a card the user may not see returns {"error": "...", "message": "..."}.

Token scoperead
Permission Any employee; results follow the token's visibility
MCP tooldashboards_data · read-only

Path parameters

uuid uuid Required
Dashboard UUID.

Query parameters

widget_uuids array<string>
Only these cards. Maximum items 60.
period string
Period; custom needs date_from and date_to.
7d30d90d12mcustom
date_from date
Start of a custom period.
date_to date
End of a custom period.
location_ids array<integer>
Only these locations (within the viewer's permissions).
checklist_ids array<integer>
Only these checklists.
auditor_ids array<integer>
Only these auditors.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Plan without API access
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/data?period=7d' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/data?period=7d', {
  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/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/data",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    params={"period":"7d"},
)
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', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/data', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'query' => [
        'period' => '7d',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_data",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "period": "7d"
        }
    }
}
Response 200
{
  "data": {
    "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d": {
      "kind": "value",
      "value": 82.4,
      "previous": 79.1,
      "unit": "%",
      "meta": {
        "from": "2026-10-04",
        "to": "2026-11-03"
      }
    }
  }
}

GETGet computed data of a card

GET /dashboards/{uuid}/widgets/{widget_uuid}/data
Token scoperead
Permission Any employee; results follow the token's visibility
MCP tooldashboards_widgets_data · read-only

Path parameters

uuid uuid Required
Dashboard UUID.
widget_uuid uuid Required
Card UUID.

Query parameters

period string
Period; custom needs date_from and date_to.
7d30d90d12mcustom
date_from date
Start of a custom period.
date_to date
End of a custom period.
location_ids array<integer>
Only these locations (within the viewer's permissions).
checklist_ids array<integer>
Only these checklists.
auditor_ids array<integer>
Only these auditors.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Plan without API access
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/data?period=7d' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/data?period=7d', {
  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/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/data",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    params={"period":"7d"},
)
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', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/data', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'query' => [
        'period' => '7d',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_widgets_data",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "widget_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "period": "7d"
        }
    }
}
Response 200
{
  "data": {
    "kind": "value",
    "value": 82.4,
    "previous": 79.1,
    "unit": "%",
    "meta": {
      "from": "2026-10-04",
      "to": "2026-11-03"
    }
  }
}

POSTCompute a card without saving it

POST /dashboards/preview

Runs a card definition with the given filters — use it to try a spec before adding the card.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_preview

Request body

spec object Required
Card definition: {"type", "metrics", "dimension", "dimension2", "list", "period", "title", "target", "compare", "limit", "color", "text"} — see dashboards.catalog for metric, dimension, list and type keys.
period string
Period; custom needs date_from and date_to.
7d30d90d12mcustom
date_from date
Start of a custom period.
date_to date
End of a custom period.
location_ids array<integer>
Only these locations (within the viewer's permissions).
checklist_ids array<integer>
Only these checklists.
auditor_ids array<integer>
Only these auditors.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/dashboards/preview' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "spec": [],
    "period": "7d",
    "date_from": "2026-11-01",
    "date_to": "2026-11-30"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/preview', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "spec": [],
      "period": "7d",
      "date_from": "2026-11-01",
      "date_to": "2026-11-30"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/dashboards/preview",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"spec":[],"period":"7d","date_from":"2026-11-01","date_to":"2026-11-30"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'dashboards/preview', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'spec' => [
        ],
        'period' => '7d',
        'date_from' => '2026-11-01',
        'date_to' => '2026-11-30',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_preview",
        "arguments": {
            "spec": [],
            "period": "7d",
            "date_from": "2026-11-01",
            "date_to": "2026-11-30"
        }
    }
}
Response 200
{
  "data": {
    "kind": "value",
    "value": 82.4,
    "previous": 79.1,
    "unit": "%",
    "meta": {
      "from": "2026-10-04",
      "to": "2026-11-03"
    }
  }
}

GETGet the metrics catalog

GET /dashboards/catalog

Everything a card can be built from: metric groups, metrics (unit, which direction is better, allowed dimensions, availability for the acting user), dimensions, card types and their rules, lists, periods, ready-made cards (presets) and dashboard templates.

Token scoperead
Permission Any employee; results follow the token's visibility
MCP tooldashboards_catalog · read-only

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Plan without API access
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/dashboards/catalog' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/catalog', {
  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/dashboards/catalog",
    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', 'dashboards/catalog', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_catalog",
        "arguments": {}
    }
}
Response 200
{
  "data": {
    "groups": [
      {
        "key": "audits",
        "label": "Audits",
        "icon": "fact_check"
      }
    ],
    "dimensions": [
      {
        "key": "location",
        "label": "Location",
        "kind": "category"
      }
    ],
    "types": [
      {
        "key": "kpi",
        "label": "Number",
        "icon": "pin",
        "metrics": [
          1,
          1
        ],
        "dimension": false
      }
    ],
    "metrics": [
      {
        "key": "audits.avg_result",
        "group": "audits",
        "label": "Average score",
        "description": "Average audit result",
        "unit": "%",
        "better": "up",
        "dimensions": [
          "time",
          "location",
          "checklist"
        ],
        "snapshot": false,
        "available": true,
        "reason": null
      }
    ],
    "lists": [
      {
        "key": "tasks.overdue",
        "group": "tasks",
        "label": "Overdue tasks",
        "description": "Open tasks past due",
        "available": true,
        "reason": null
      }
    ],
    "periods": [
      {
        "key": "30d",
        "label": "30 days"
      }
    ],
    "presets": [
      {
        "key": "kpi_avg",
        "group": "audits",
        "label": "Average score",
        "description": "Average audit result with trend",
        "type": "kpi",
        "spec": {
          "v": 1,
          "type": "kpi",
          "metrics": [
            "audits.avg_result"
          ],
          "compare": true
        },
        "w": 3,
        "h": 2,
        "available": true,
        "reason": null
      }
    ],
    "templates": [
      {
        "key": "location",
        "label": "Location overview",
        "description": "Key figures of locations",
        "icon": "storefront",
        "presets": [
          "kpi_avg"
        ]
      }
    ]
  }
}

POSTCreate a dashboard

POST /dashboards

Creates a private dashboard of the acting user — empty, from a template (dashboards.catalog → templates) and/or with the given cards. Publish it with dashboards.share.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_create

Request body

title string
Title. Maximum length 120.
icon string
Material Symbols icon name. Maximum length 40.
description string
Maximum length 500.
template string
Template key.
networkqualityoperationslocationhaccptrainingmystery
widgets array<object>
Cards to add (layout is packed automatically when x/y are omitted). Maximum items 60.
spec object Required
Card definition.
x integer
y integer
w integer
h integer
preset string
Ready-made card key. Maximum length 60.

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/dashboards' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Replace the broken fridge seal",
    "icon": "string",
    "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
    "template": "network"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "title": "Replace the broken fridge seal",
      "icon": "string",
      "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
      "template": "network"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/dashboards",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"title":"Replace the broken fridge seal","icon":"string","description":"Seal on fridge #2 is torn, temperature rises to +9 °C.","template":"network"},
)
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', 'dashboards', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'title' => 'Replace the broken fridge seal',
        'icon' => 'string',
        'description' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
        'template' => 'network',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_create",
        "arguments": {
            "title": "Replace the broken fridge seal",
            "icon": "string",
            "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
            "template": "network"
        }
    }
}
Response 201
{
  "data": {
    "uuid": "2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "title": "Location overview",
    "icon": "dashboard",
    "description": "Weekly picture of all coffee shops",
    "filters": {
      "period": "30d",
      "date_from": null,
      "date_to": null,
      "location_ids": [
        12,
        14
      ],
      "checklist_ids": [],
      "auditor_ids": []
    },
    "visibility": "company",
    "company_can_edit": false,
    "refresh_sec": 0,
    "template": "location",
    "owner": {
      "id": 120,
      "uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
      "name": "Daniyar Omarov"
    },
    "is_owner": false,
    "permissions": {
      "edit": false,
      "share": false,
      "delete": false
    },
    "updated_at": "2026-11-03T12:10:00+05:00",
    "url": "https://app.jecheck.io/ui/dashboards/2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "widgets": [
      {
        "uuid": "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
        "spec": {
          "v": 1,
          "type": "kpi",
          "metrics": [
            "audits.avg_result"
          ],
          "compare": true,
          "title": "Average score"
        },
        "x": 0,
        "y": 0,
        "w": 3,
        "h": 2,
        "source": "library",
        "preset": "kpi_avg"
      }
    ]
  }
}

PATCHUpdate a dashboard

PATCH /dashboards/{uuid}

Title, icon, description, saved filters and auto-refresh. Requires edit access.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_update

Path parameters

uuid uuid Required
Dashboard UUID.

Request body

title string
Length 1–120.
icon string | null
Maximum length 40.
description string | null
Maximum length 500.
filters object
Saved filters: period, date_from, date_to, location_ids, checklist_ids, auditor_ids.
period string
Period; custom needs date_from and date_to.
7d30d90d12mcustom
date_from date
Start of a custom period.
date_to date
End of a custom period.
location_ids array<integer>
Only these locations (within the viewer's permissions).
checklist_ids array<integer>
Only these checklists.
auditor_ids array<integer>
Only these auditors.
refresh_sec integer
Auto-refresh in the web app, seconds (0 — off, minimum 60). Value 0–3600.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X PATCH 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Replace the broken fridge seal",
    "icon": "string",
    "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
    "filters": {
        "period": "7d",
        "date_from": "2026-11-01",
        "date_to": "2026-11-30"
    }
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "title": "Replace the broken fridge seal",
      "icon": "string",
      "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
      "filters": {
          "period": "7d",
          "date_from": "2026-11-01",
          "date_to": "2026-11-30"
      }
  }),
});
const { data } = await res.json();
import os, requests

res = requests.patch(
    "https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"title":"Replace the broken fridge seal","icon":"string","description":"Seal on fridge #2 is torn, temperature rises to +9 °C.","filters":{"period":"7d","date_from":"2026-11-01","date_to":"2026-11-30"}},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('PATCH', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'title' => 'Replace the broken fridge seal',
        'icon' => 'string',
        'description' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
        'filters' => [
            'period' => '7d',
            'date_from' => '2026-11-01',
            'date_to' => '2026-11-30',
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_update",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "title": "Replace the broken fridge seal",
            "icon": "string",
            "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
            "filters": {
                "period": "7d",
                "date_from": "2026-11-01",
                "date_to": "2026-11-30"
            }
        }
    }
}
Response 200
{
  "data": {
    "uuid": "2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "title": "Location overview",
    "icon": "dashboard",
    "description": "Weekly picture of all coffee shops",
    "filters": {
      "period": "30d",
      "date_from": null,
      "date_to": null,
      "location_ids": [
        12,
        14
      ],
      "checklist_ids": [],
      "auditor_ids": []
    },
    "visibility": "company",
    "company_can_edit": false,
    "refresh_sec": 0,
    "template": "location",
    "owner": {
      "id": 120,
      "uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
      "name": "Daniyar Omarov"
    },
    "is_owner": false,
    "permissions": {
      "edit": false,
      "share": false,
      "delete": false
    },
    "updated_at": "2026-11-03T12:10:00+05:00",
    "url": "https://app.jecheck.io/ui/dashboards/2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "widgets": [
      {
        "uuid": "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
        "spec": {
          "v": 1,
          "type": "kpi",
          "metrics": [
            "audits.avg_result"
          ],
          "compare": true,
          "title": "Average score"
        },
        "x": 0,
        "y": 0,
        "w": 3,
        "h": 2,
        "source": "library",
        "preset": "kpi_avg"
      }
    ]
  }
}

DELDelete a dashboard

DELETE /dashboards/{uuid}

Allowed to the owner; a company dashboard — also with dashboards.company.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_delete · destructive

Path parameters

uuid uuid Required
Dashboard UUID.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X DELETE 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
  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/dashboards/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('DELETE', 'dashboards/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": "dashboards_delete",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "deleted": true
  }
}

POSTDuplicate a dashboard

POST /dashboards/{uuid}/duplicate

Creates a private copy owned by the acting user.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_duplicate

Path parameters

uuid uuid Required
Dashboard UUID.

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/duplicate' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/duplicate', {
  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/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/duplicate",
    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', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/duplicate', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_duplicate",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 201
{
  "data": {
    "uuid": "2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "title": "Location overview",
    "icon": "dashboard",
    "description": "Weekly picture of all coffee shops",
    "filters": {
      "period": "30d",
      "date_from": null,
      "date_to": null,
      "location_ids": [
        12,
        14
      ],
      "checklist_ids": [],
      "auditor_ids": []
    },
    "visibility": "company",
    "company_can_edit": false,
    "refresh_sec": 0,
    "template": "location",
    "owner": {
      "id": 120,
      "uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
      "name": "Daniyar Omarov"
    },
    "is_owner": false,
    "permissions": {
      "edit": false,
      "share": false,
      "delete": false
    },
    "updated_at": "2026-11-03T12:10:00+05:00",
    "url": "https://app.jecheck.io/ui/dashboards/2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "widgets": [
      {
        "uuid": "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
        "spec": {
          "v": 1,
          "type": "kpi",
          "metrics": [
            "audits.avg_result"
          ],
          "compare": true,
          "title": "Average score"
        },
        "x": 0,
        "y": 0,
        "w": 3,
        "h": 2,
        "source": "library",
        "preset": "kpi_avg"
      }
    ]
  }
}

PUTReorder and hide dashboard tabs

PUT /dashboards/order

Personal tab order of the acting user; hidden — dashboards to hide from the tabs.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_reorder

Request body

uuids array<string> Required
Dashboard UUIDs in the new order. Maximum items 200.
hidden array<string>
Hidden dashboard UUIDs. Maximum items 200.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X PUT 'https://app.jecheck.io/api/ext/v1/dashboards/order' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "uuids": [
        "example"
    ],
    "hidden": [
        "example"
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/order', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "uuids": [
          "example"
      ],
      "hidden": [
          "example"
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.put(
    "https://app.jecheck.io/api/ext/v1/dashboards/order",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"uuids":["example"],"hidden":["example"]},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('PUT', 'dashboards/order', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'uuids' => [
            'example',
        ],
        'hidden' => [
            'example',
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_reorder",
        "arguments": {
            "uuids": [
                "example"
            ],
            "hidden": [
                "example"
            ]
        }
    }
}
Response 200
{
  "data": {
    "ok": true
  }
}

GETGet dashboard sharing

GET /dashboards/{uuid}/share

Who has access: visibility (private / company), whether everyone may edit a company dashboard, and colleagues with personal access. For the owner (and dashboards.company on company dashboards).

Token scoperead
Permission Any employee; results follow the token's visibility
MCP tooldashboards_sharing · read-only

Path parameters

uuid uuid Required
Dashboard UUID.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Plan without API access
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/share' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/share', {
  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/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/share",
    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', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/share', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_sharing",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "visibility": "private",
    "company_can_edit": false,
    "owner": {
      "id": 120,
      "uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
      "name": "Daniyar Omarov"
    },
    "shares": [
      {
        "user": {
          "id": 311,
          "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
          "name": "Aigerim Sadykova"
        },
        "can_edit": true
      }
    ],
    "can_publish_to_company": true
  }
}

PUTShare a dashboard

PUT /dashboards/{uuid}/share

Replaces the sharing settings. Publishing to the whole company requires dashboards.company.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_share

Path parameters

uuid uuid Required
Dashboard UUID.

Request body

visibility string Required
privatecompany
company_can_edit boolean
Everyone in the company may edit (company dashboards). Default false.
users array<object>
Personal access (replaces the list). Maximum items 500.
user_id integer Required
can_edit boolean

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X PUT 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/share' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "visibility": "private",
    "company_can_edit": false,
    "users": [
        {
            "user_id": 42,
            "can_edit": true
        }
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/share', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "visibility": "private",
      "company_can_edit": false,
      "users": [
          {
              "user_id": 42,
              "can_edit": true
          }
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.put(
    "https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/share",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"visibility":"private","company_can_edit":False,"users":[{"user_id":42,"can_edit":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('PUT', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/share', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'visibility' => 'private',
        'company_can_edit' => false,
        'users' => [
            [
                'user_id' => 42,
                'can_edit' => true,
            ],
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_share",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "visibility": "private",
            "company_can_edit": false,
            "users": [
                {
                    "user_id": 42,
                    "can_edit": true
                }
            ]
        }
    }
}
Response 200
{
  "data": {
    "visibility": "company",
    "company_can_edit": false,
    "owner": {
      "id": 120,
      "uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
      "name": "Daniyar Omarov"
    },
    "shares": [],
    "can_publish_to_company": true
  }
}

PUTChange the card layout

PUT /dashboards/{uuid}/layout

Positions and sizes of cards on the 12-column grid. Requires edit access.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_layout

Path parameters

uuid uuid Required
Dashboard UUID.

Request body

items array<object> Required
Maximum items 60.
uuid uuid Required
Card UUID.
x integer Required
y integer Required
w integer Required
h integer Required

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X PUT 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/layout' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "items": [
        {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "x": 42,
            "y": 42,
            "w": 42,
            "h": 42
        }
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/layout', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "items": [
          {
              "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
              "x": 42,
              "y": 42,
              "w": 42,
              "h": 42
          }
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.put(
    "https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/layout",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"items":[{"uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b","x":42,"y":42,"w":42,"h":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('PUT', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/layout', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'items' => [
            [
                'uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
                'x' => 42,
                'y' => 42,
                'w' => 42,
                'h' => 42,
            ],
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_layout",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "items": [
                {
                    "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
                    "x": 42,
                    "y": 42,
                    "w": 42,
                    "h": 42
                }
            ]
        }
    }
}
Response 200
{
  "data": [
    {
      "uuid": "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
      "spec": {
        "v": 1,
        "type": "kpi",
        "metrics": [
          "audits.avg_result"
        ],
        "compare": true,
        "title": "Average score"
      },
      "x": 0,
      "y": 0,
      "w": 3,
      "h": 2,
      "source": "library",
      "preset": "kpi_avg"
    }
  ]
}

POSTAdd a card

POST /dashboards/{uuid}/widgets

Adds a card (custom spec or a ready-made preset spec from the catalog). Without x/y it goes below the existing cards. Up to 60 cards per dashboard. Requires edit access.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_widgets_create

Path parameters

uuid uuid Required
Dashboard UUID.

Request body

spec object Required
Card definition: {"type", "metrics", "dimension", "dimension2", "list", "period", "title", "target", "compare", "limit", "color", "text"} — see dashboards.catalog for metric, dimension, list and type keys.
x integer
y integer
w integer
Value 1–12.
h integer
Value 1–12.
preset string
Ready-made card key (for reference). Maximum length 60.

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "spec": [],
    "x": 42,
    "y": 42,
    "w": 42
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "spec": [],
      "x": 42,
      "y": 42,
      "w": 42
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"spec":[],"x":42,"y":42,"w":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', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'spec' => [
        ],
        'x' => 42,
        'y' => 42,
        'w' => 42,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_widgets_create",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "spec": [],
            "x": 42,
            "y": 42,
            "w": 42
        }
    }
}
Response 201
{
  "data": {
    "uuid": "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
    "spec": {
      "v": 1,
      "type": "kpi",
      "metrics": [
        "audits.avg_result"
      ],
      "compare": true,
      "title": "Average score"
    },
    "x": 0,
    "y": 0,
    "w": 3,
    "h": 2,
    "source": "library",
    "preset": "kpi_avg"
  }
}

PUTReplace a card definition

PUT /dashboards/{uuid}/widgets/{widget_uuid}
Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_widgets_update

Path parameters

uuid uuid Required
Dashboard UUID.
widget_uuid uuid Required
Card UUID.

Request body

spec object Required
Card definition: {"type", "metrics", "dimension", "dimension2", "list", "period", "title", "target", "compare", "limit", "color", "text"} — see dashboards.catalog for metric, dimension, list and type keys.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X PUT 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "spec": []
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "spec": []
  }),
});
const { data } = await res.json();
import os, requests

res = requests.put(
    "https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"spec":[]},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('PUT', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'spec' => [
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_widgets_update",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "widget_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "spec": []
        }
    }
}
Response 200
{
  "data": {
    "uuid": "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
    "spec": {
      "v": 1,
      "type": "kpi",
      "metrics": [
        "audits.avg_result"
      ],
      "compare": true,
      "title": "Average score"
    },
    "x": 0,
    "y": 0,
    "w": 3,
    "h": 2,
    "source": "library",
    "preset": "kpi_avg"
  }
}

DELDelete a card

DELETE /dashboards/{uuid}/widgets/{widget_uuid}
Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_widgets_delete · destructive

Path parameters

uuid uuid Required
Dashboard UUID.
widget_uuid uuid Required
Card UUID.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X DELETE 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
  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/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/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('DELETE', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/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": "dashboards_widgets_delete",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "widget_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "deleted": true
  }
}

POSTCopy a card to another dashboard

POST /dashboards/{uuid}/widgets/{widget_uuid}/copy
Token scopewrite
Permission Any employee; results follow the token's visibility
MCP tooldashboards_widgets_copy

Path parameters

uuid uuid Required
Dashboard UUID.
widget_uuid uuid Required
Card UUID.

Request body

target_uuid uuid Required
Target dashboard (edit access required).

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/copy' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "target_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/copy', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "target_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/copy",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"target_uuid":"3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/copy', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'target_uuid' => '3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_widgets_copy",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "widget_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "target_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 201
{
  "data": {
    "uuid": "7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
    "spec": {
      "v": 1,
      "type": "kpi",
      "metrics": [
        "audits.avg_result"
      ],
      "compare": true,
      "title": "Average score"
    },
    "x": 0,
    "y": 0,
    "w": 3,
    "h": 2,
    "source": "library",
    "preset": "kpi_avg",
    "dashboard_uuid": "9f8e7d6c-5b4a-4c3d-8e2f-1a0b9c8d7e6f"
  }
}

POSTBuild a card from a description (AI)

POST /dashboards/ai/card

AI turns a request in plain words ("average score by location for 90 days") into a card definition and computes it. Nothing is saved — add it with dashboards.widgets.create. Daily AI limits apply.

Token scopewrite
Permission Any employee; results follow the token's visibility
Plan featureai
MCP tooldashboards_ai_card

Request body

prompt string Required
Maximum length 1000.
spec object
Current card to adjust.
period string
Period; custom needs date_from and date_to.
7d30d90d12mcustom
date_from date
Start of a custom period.
date_to date
End of a custom period.
location_ids array<integer>
Only these locations (within the viewer's permissions).
checklist_ids array<integer>
Only these checklists.
auditor_ids array<integer>
Only these auditors.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/dashboards/ai/card' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "string",
    "spec": [],
    "period": "7d",
    "date_from": "2026-11-01"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/ai/card', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "prompt": "string",
      "spec": [],
      "period": "7d",
      "date_from": "2026-11-01"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/dashboards/ai/card",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"prompt":"string","spec":[],"period":"7d","date_from":"2026-11-01"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'dashboards/ai/card', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'prompt' => 'string',
        'spec' => [
        ],
        'period' => '7d',
        'date_from' => '2026-11-01',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_ai_card",
        "arguments": {
            "prompt": "string",
            "spec": [],
            "period": "7d",
            "date_from": "2026-11-01"
        }
    }
}
Response 200
{
  "data": {
    "spec": {
      "v": 1,
      "type": "kpi",
      "metrics": [
        "audits.avg_result"
      ],
      "compare": true,
      "title": "Average score"
    },
    "explanation": "Average audit score with comparison to the previous period",
    "size": {
      "w": 3,
      "h": 2
    },
    "data": {
      "kind": "value",
      "value": 82.4,
      "previous": 79.1,
      "unit": "%",
      "meta": {
        "from": "2026-10-04",
        "to": "2026-11-03"
      }
    }
  }
}

POSTDraft a dashboard from a description (AI)

POST /dashboards/ai/dashboard

Returns a draft (title, cards and layout) — nothing is saved; pass widgets to dashboards.create.

Token scopewrite
Permission Any employee; results follow the token's visibility
Plan featureai
MCP tooldashboards_ai_dashboard

Request body

prompt string Required
Maximum length 2000.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/dashboards/ai/dashboard' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "string"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/ai/dashboard', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "prompt": "string"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/dashboards/ai/dashboard",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"prompt":"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', 'dashboards/ai/dashboard', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'prompt' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_ai_dashboard",
        "arguments": {
            "prompt": "string"
        }
    }
}
Response 200
{
  "data": {
    "title": "Kitchen quality",
    "icon": "restaurant",
    "explanation": "Scores, failed questions and overdue tasks",
    "widgets": [
      {
        "spec": {
          "v": 1,
          "type": "kpi",
          "metrics": [
            "audits.avg_result"
          ],
          "compare": true,
          "title": "Average score"
        },
        "x": 0,
        "y": 0,
        "w": 3,
        "h": 2,
        "source": "ai"
      }
    ]
  }
}

POSTExplain card data (AI)

POST /dashboards/{uuid}/widgets/{widget_uuid}/explain

A short explanation of what the card shows for the acting user's data and filters.

Token scopewrite
Permission Any employee; results follow the token's visibility
Plan featureai
MCP tooldashboards_ai_explain

Path parameters

uuid uuid Required
Dashboard UUID.
widget_uuid uuid Required
Card UUID.

Request body

period string
Period; custom needs date_from and date_to.
7d30d90d12mcustom
date_from date
Start of a custom period.
date_to date
End of a custom period.
location_ids array<integer>
Only these locations (within the viewer's permissions).
checklist_ids array<integer>
Only these checklists.
auditor_ids array<integer>
Only these auditors.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Read-only token
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/explain' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "period": "7d",
    "date_from": "2026-11-01",
    "date_to": "2026-11-30",
    "location_ids": [
        12,
        14
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/explain', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "period": "7d",
      "date_from": "2026-11-01",
      "date_to": "2026-11-30",
      "location_ids": [
          12,
          14
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/explain",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"period":"7d","date_from":"2026-11-01","date_to":"2026-11-30","location_ids":[12,14]},
)
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', 'dashboards/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/widgets/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/explain', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'period' => '7d',
        'date_from' => '2026-11-01',
        'date_to' => '2026-11-30',
        'location_ids' => [
            12,
            14,
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "dashboards_ai_explain",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "widget_uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "period": "7d",
            "date_from": "2026-11-01",
            "date_to": "2026-11-30",
            "location_ids": [
                12,
                14
            ]
        }
    }
}
Response 200
{
  "data": {
    "text": "The average score grew from 79% to 82%, mostly thanks to Abay Ave."
  }
}