jeCheckDevelopers
Get an API key
Developers/API reference/Roles & permissions

Roles & permissions

The permission catalog, company roles, effective permissions of an employee and the access log. Changes follow the delegation rules of the web app: a non-administrator can grant only permissions they hold themselves, only on their own locations, and cannot change administrators.

GETList the permission catalog

GET /access/permissions

Every permission key with its group, label, description, whether it can be limited to locations (scoped), whether views are logged (sensitive) and the permissions it requires.

Token scoperead
Permission Any employee; results follow the token's visibility
MCP toolaccess_permissions_list · 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/access/permissions' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/permissions', {
  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/access/permissions",
    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', 'access/permissions', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_permissions_list",
        "arguments": {}
    }
}
Response 200
{
  "data": [
    {
      "key": "users.contacts",
      "group": "users",
      "group_label": "Employees",
      "label": "Employee phone and email",
      "hint": "See email and phone in the list and card",
      "description": "Email and phone of employees in the list, card and export.",
      "scoped": true,
      "sensitive": false,
      "requires": []
    }
  ]
}

GETList roles

GET /access/roles

Company roles with their permissions and members. System roles (system_key: employee — everyone, location_manager — managers of locations and regions) are applied automatically and cannot be assigned or deleted.

Token scoperead
Permission access.manage or access.roles or access.log
MCP toolaccess_roles_list · read-only

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.manage / access.roles / access.log
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/access/roles' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/roles', {
  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/access/roles",
    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', 'access/roles', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_roles_list",
        "arguments": {}
    }
}
Response 200
{
  "data": [
    {
      "id": 41,
      "system_key": null,
      "name": "Auditor",
      "description": "Runs audits and reviews results",
      "color": "#5249e6",
      "permissions": [
        {
          "key": "audits.view",
          "scope": "all",
          "location_ids": []
        },
        {
          "key": "tasks.view_all",
          "scope": "list",
          "location_ids": [
            12,
            14
          ]
        }
      ],
      "members": [
        {
          "id": 311,
          "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
          "name": "Aigerim Sadykova"
        }
      ]
    }
  ]
}

GETRetrieve a role

GET /access/roles/{id}
Token scoperead
Permission access.manage or access.roles or access.log
MCP toolaccess_roles_get · read-only

Path parameters

id integer Required
Role id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.manage / access.roles / access.log
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/access/roles/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/roles/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/access/roles/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', 'access/roles/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_roles_get",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "id": 41,
    "system_key": null,
    "name": "Auditor",
    "description": "Runs audits and reviews results",
    "color": "#5249e6",
    "permissions": [
      {
        "key": "audits.view",
        "scope": "all",
        "location_ids": []
      },
      {
        "key": "tasks.view_all",
        "scope": "list",
        "location_ids": [
          12,
          14
        ]
      }
    ],
    "members": [
      {
        "id": 311,
        "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
        "name": "Aigerim Sadykova"
      }
    ]
  }
}

POSTCreate a role

POST /access/roles

A non-administrator can include only permissions they hold, within their own locations. Written to the access log.

Token scopewrite
Permission access.roles
MCP toolaccess_roles_create

Request body

name string Required
Role name (system roles cannot be renamed). Length 1–120.
description string | null
Maximum length 500.
color string | null
Hex color. Maximum length 9.
permissions array<object>
Full list of the role permissions (replaces the current list). Required dependencies are added automatically. scope: all — all locations, own — the member's own locations, managed — locations the member manages, list — location_ids. Maximum items 300.
key string Required
Permission key from GET /access/permissions. Maximum length 64.
scope string
Location scope, default all.
allownmanagedlist
location_ids array<integer>
Locations for scope: list.

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.roles, is read-only
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/access/roles' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Replace the broken fridge seal",
    "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
    "color": "string",
    "permissions": [
        {
            "key": "string",
            "scope": "all",
            "location_ids": [
                12,
                14
            ]
        }
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/roles', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "Replace the broken fridge seal",
      "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
      "color": "string",
      "permissions": [
          {
              "key": "string",
              "scope": "all",
              "location_ids": [
                  12,
                  14
              ]
          }
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/access/roles",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"name":"Replace the broken fridge seal","description":"Seal on fridge #2 is torn, temperature rises to +9 °C.","color":"string","permissions":[{"key":"string","scope":"all","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', 'access/roles', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
        'description' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
        'color' => 'string',
        'permissions' => [
            [
                'key' => 'string',
                'scope' => 'all',
                'location_ids' => [
                    12,
                    14,
                ],
            ],
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_roles_create",
        "arguments": {
            "name": "Replace the broken fridge seal",
            "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
            "color": "string",
            "permissions": [
                {
                    "key": "string",
                    "scope": "all",
                    "location_ids": [
                        12,
                        14
                    ]
                }
            ]
        }
    }
}
Response 201
{
  "data": {
    "id": 41,
    "system_key": null,
    "name": "Auditor",
    "description": "Runs audits and reviews results",
    "color": "#5249e6",
    "permissions": [
      {
        "key": "audits.view",
        "scope": "all",
        "location_ids": []
      },
      {
        "key": "tasks.view_all",
        "scope": "list",
        "location_ids": [
          12,
          14
        ]
      }
    ],
    "members": [
      {
        "id": 311,
        "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
        "name": "Aigerim Sadykova"
      }
    ]
  }
}

PATCHUpdate a role

PATCH /access/roles/{id}

Partial update. permissions, when passed, replaces the whole list. Members get the new permissions immediately.

Token scopewrite
Permission access.roles
MCP toolaccess_roles_update

Path parameters

id integer Required
Role id.

Request body

name string
Role name (system roles cannot be renamed). Length 1–120.
description string | null
Maximum length 500.
color string | null
Hex color. Maximum length 9.
permissions array<object>
Full list of the role permissions (replaces the current list). Required dependencies are added automatically. scope: all — all locations, own — the member's own locations, managed — locations the member manages, list — location_ids. Maximum items 300.
key string Required
Permission key from GET /access/permissions. Maximum length 64.
scope string
Location scope, default all.
allownmanagedlist
location_ids array<integer>
Locations for scope: list.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.roles, is read-only
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/access/roles/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Replace the broken fridge seal",
    "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
    "color": "string",
    "permissions": [
        {
            "key": "string",
            "scope": "all",
            "location_ids": [
                12,
                14
            ]
        }
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/roles/42', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "Replace the broken fridge seal",
      "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
      "color": "string",
      "permissions": [
          {
              "key": "string",
              "scope": "all",
              "location_ids": [
                  12,
                  14
              ]
          }
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.patch(
    "https://app.jecheck.io/api/ext/v1/access/roles/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"name":"Replace the broken fridge seal","description":"Seal on fridge #2 is torn, temperature rises to +9 °C.","color":"string","permissions":[{"key":"string","scope":"all","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('PATCH', 'access/roles/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
        'description' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
        'color' => 'string',
        'permissions' => [
            [
                'key' => 'string',
                'scope' => 'all',
                'location_ids' => [
                    12,
                    14,
                ],
            ],
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_roles_update",
        "arguments": {
            "id": 42,
            "name": "Replace the broken fridge seal",
            "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
            "color": "string",
            "permissions": [
                {
                    "key": "string",
                    "scope": "all",
                    "location_ids": [
                        12,
                        14
                    ]
                }
            ]
        }
    }
}
Response 200
{
  "data": {
    "id": 41,
    "system_key": null,
    "name": "Auditor",
    "description": "Runs audits and reviews results",
    "color": "#5249e6",
    "permissions": [
      {
        "key": "audits.view",
        "scope": "all",
        "location_ids": []
      },
      {
        "key": "tasks.view_all",
        "scope": "list",
        "location_ids": [
          12,
          14
        ]
      }
    ],
    "members": [
      {
        "id": 311,
        "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
        "name": "Aigerim Sadykova"
      }
    ]
  }
}

DELDelete a role

DELETE /access/roles/{id}

Members lose the permissions of the role. System roles cannot be deleted.

Token scopewrite
Permission access.roles
MCP toolaccess_roles_delete · destructive

Path parameters

id integer Required
Role id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.roles, is read-only
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/access/roles/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/roles/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/access/roles/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', 'access/roles/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_roles_delete",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "deleted": true
  }
}

PUTSet role members

PUT /access/roles/{id}/members

Replaces the members of a (non-system) role. Each assignment and removal is checked against the delegation rules and logged.

Token scopewrite
Permission access.manage
MCP toolaccess_roles_members_set

Path parameters

id integer Required
Role id.

Request body

user_ids array<integer> Required
Employee ids. Maximum items 1000.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.manage, is read-only
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/access/roles/42/members' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "user_ids": [
        12,
        14
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/roles/42/members', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "user_ids": [
          12,
          14
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.put(
    "https://app.jecheck.io/api/ext/v1/access/roles/42/members",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"user_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('PUT', 'access/roles/42/members', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'user_ids' => [
            12,
            14,
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_roles_members_set",
        "arguments": {
            "id": 42,
            "user_ids": [
                12,
                14
            ]
        }
    }
}
Response 200
{
  "data": {
    "id": 41,
    "system_key": null,
    "name": "Auditor",
    "description": "Runs audits and reviews results",
    "color": "#5249e6",
    "permissions": [
      {
        "key": "audits.view",
        "scope": "all",
        "location_ids": []
      },
      {
        "key": "tasks.view_all",
        "scope": "list",
        "location_ids": [
          12,
          14
        ]
      }
    ],
    "members": [
      {
        "id": 311,
        "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
        "name": "Aigerim Sadykova"
      }
    ]
  }
}

GETGet permissions of an employee

GET /access/users/{id}

Roles, personal grants and denials, and effective permissions with the locations where each applies (location_ids: null — all locations) and where it comes from. With only users.access the response contains just user and account (administrator and web-cabinet flags).

Token scoperead
Permission access.manage or access.roles or access.log or users.access
MCP toolaccess_users_get · read-only

Path parameters

id integer Required
Employee id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.manage / access.roles / access.log / users.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/access/users/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/users/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/access/users/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', 'access/users/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_users_get",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "user": {
      "id": 311,
      "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
      "name": "Aigerim Sadykova"
    },
    "account": {
      "is_admin": false,
      "is_owner": false,
      "web_access": true,
      "works_anywhere": false,
      "manages_locations": [
        "Abay Ave"
      ],
      "manages_regions": [],
      "can_change_admin": true,
      "can_change_web_access": true
    },
    "role_ids": [
      41
    ],
    "system_role_ids": [
      1,
      2
    ],
    "personal": [
      {
        "key": "users.contacts",
        "effect": "grant",
        "scope": "list",
        "location_ids": [
          12
        ]
      }
    ],
    "effective": [
      {
        "key": "audits.view",
        "location_ids": null,
        "sources": [
          {
            "source": "role",
            "role_id": 41,
            "role": "Auditor",
            "scope": "all",
            "location_ids": []
          }
        ]
      }
    ],
    "can_edit": true
  }
}

PUTSet roles and personal permissions of an employee

PUT /access/users/{id}

Replaces the employee's roles (role_ids) and/or personal permissions (personal); an omitted field keeps its current value. Administrators and yourself cannot be changed. Every change is logged.

Token scopewrite
Permission access.manage
MCP toolaccess_users_update

Path parameters

id integer Required
Employee id.

Request body

role_ids array<integer>
Ids of company (non-system) roles. Maximum items 100.
personal array<object>
Personal grants and denials. Maximum items 300.
key string Required
Permission key from GET /access/permissions. Maximum length 64.
effect string Required
grant or deny (deny overrides roles).
grantdeny
scope string
Location scope, default all.
allownmanagedlist
location_ids array<integer>
Locations for scope: list.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.manage, is read-only
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/access/users/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "role_ids": [
        12,
        14
    ],
    "personal": [
        {
            "key": "string",
            "effect": "grant",
            "scope": "all"
        }
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/users/42', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "role_ids": [
          12,
          14
      ],
      "personal": [
          {
              "key": "string",
              "effect": "grant",
              "scope": "all"
          }
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.put(
    "https://app.jecheck.io/api/ext/v1/access/users/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"role_ids":[12,14],"personal":[{"key":"string","effect":"grant","scope":"all"}]},
)
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', 'access/users/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'role_ids' => [
            12,
            14,
        ],
        'personal' => [
            [
                'key' => 'string',
                'effect' => 'grant',
                'scope' => 'all',
            ],
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_users_update",
        "arguments": {
            "id": 42,
            "role_ids": [
                12,
                14
            ],
            "personal": [
                {
                    "key": "string",
                    "effect": "grant",
                    "scope": "all"
                }
            ]
        }
    }
}
Response 200
{
  "data": {
    "user": {
      "id": 311,
      "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
      "name": "Aigerim Sadykova"
    },
    "account": {
      "is_admin": false,
      "is_owner": false,
      "web_access": true,
      "works_anywhere": false,
      "manages_locations": [
        "Abay Ave"
      ],
      "manages_regions": [],
      "can_change_admin": true,
      "can_change_web_access": true
    },
    "role_ids": [
      41
    ],
    "system_role_ids": [
      1,
      2
    ],
    "personal": [
      {
        "key": "users.contacts",
        "effect": "grant",
        "scope": "list",
        "location_ids": [
          12
        ]
      }
    ],
    "effective": [
      {
        "key": "audits.view",
        "location_ids": null,
        "sources": [
          {
            "source": "role",
            "role_id": 41,
            "role": "Auditor",
            "scope": "all",
            "location_ids": []
          }
        ]
      }
    ],
    "can_edit": true
  }
}

PATCHChange administrator or web-cabinet access

PATCH /access/users/{id}/account

is_admin — only an administrator can appoint or remove administrators (not themselves, not the company owner). web_access: false makes the employee "app only" (requires users.access for the employee). Logged.

Token scopewrite
Permission access.manage or access.roles or access.log or users.access
MCP toolaccess_users_account_update

Path parameters

id integer Required
Employee id.

Request body

is_admin boolean
web_access boolean

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.manage / access.roles / access.log / users.access, is read-only
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/access/users/42/account' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "is_admin": true,
    "web_access": true
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/users/42/account', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "is_admin": true,
      "web_access": true
  }),
});
const { data } = await res.json();
import os, requests

res = requests.patch(
    "https://app.jecheck.io/api/ext/v1/access/users/42/account",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"is_admin":True,"web_access":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('PATCH', 'access/users/42/account', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'is_admin' => true,
        'web_access' => true,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_users_account_update",
        "arguments": {
            "id": 42,
            "is_admin": true,
            "web_access": true
        }
    }
}
Response 200
{
  "data": {
    "user": {
      "id": 311,
      "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
      "name": "Aigerim Sadykova"
    },
    "account": {
      "is_admin": false,
      "is_owner": false,
      "web_access": true,
      "works_anywhere": false,
      "manages_locations": [
        "Abay Ave"
      ],
      "manages_regions": [],
      "can_change_admin": true,
      "can_change_web_access": true
    },
    "role_ids": [
      41
    ],
    "system_role_ids": [
      1,
      2
    ],
    "personal": [
      {
        "key": "users.contacts",
        "effect": "grant",
        "scope": "list",
        "location_ids": [
          12
        ]
      }
    ],
    "effective": [
      {
        "key": "audits.view",
        "location_ids": null,
        "sources": [
          {
            "source": "role",
            "role_id": 41,
            "role": "Auditor",
            "scope": "all",
            "location_ids": []
          }
        ]
      }
    ],
    "can_edit": true
  }
}

GETList access log entries

GET /access/log

Changes of roles and permissions, administrators and managers, views of sensitive data, exports and denied attempts — newest first.

Token scoperead
Permission access.log
MCP toolaccess_log_list · read-only

Query parameters

page integer
Page number, starting from 1. Default 1. Minimum 1.
per_page integer
Items per page (max 100). Default 25. Value 1–100.
user_id integer
Entries where this employee is the actor or the subject.
kind string
Kind of entries.
changesviewexportdenied
permission string
Permission key. Maximum length 64.
date_from date
From date (inclusive).
date_to date
To date (inclusive).

Responses

StatusDescription
200OK — a page of results with meta
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks access.log
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/access/log?user_id=42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/access/log?user_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/access/log",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    params={"user_id":42},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'access/log', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'query' => [
        'user_id' => 42,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "access_log_list",
        "arguments": {
            "user_id": 42
        }
    }
}
Response 200
{
  "data": [
    {
      "id": 90211,
      "event": "role_assign",
      "event_label": "Role assigned",
      "actor": {
        "id": 120,
        "uuid": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
        "name": "Daniyar Omarov"
      },
      "subject": {
        "id": 311,
        "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
        "name": "Aigerim Sadykova"
      },
      "role": {
        "id": 41,
        "name": "Auditor"
      },
      "permission": null,
      "location": null,
      "data": {
        "name": "Auditor"
      },
      "ip": "10.0.0.4",
      "created_at": "2026-11-03T10:15:00+05:00"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total": 1,
    "has_more": false
  }
}