jeCheckDevelopers
Get an API key

GETList automation scenarios

GET /automation/scenarios
Token scoperead
Permission automation.manage
MCP toolautomation_scenarios_list · read-only

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks automation.manage
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/automation/scenarios' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/scenarios', {
  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/automation/scenarios",
    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', 'automation/scenarios', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_scenarios_list",
        "arguments": {}
    }
}
Response 200
{
  "data": [
    {
      "id": 5,
      "name": "Standard",
      "is_default": true,
      "definition": {
        "v": 1,
        "steps": [
          {
            "type": "assign",
            "to": "responsible_role"
          },
          {
            "type": "condition",
            "field": "weight",
            "op": "gte",
            "value": 3,
            "then": [
              {
                "type": "deadline",
                "hours": 4
              },
              {
                "type": "require_photo"
              },
              {
                "type": "require_confirmation",
                "by": "managers"
              }
            ],
            "else": [
              {
                "type": "deadline",
                "hours": 24
              },
              {
                "type": "require_photo"
              }
            ]
          }
        ]
      },
      "checklists": [
        {
          "id": 101,
          "name": "Kitchen daily check"
        }
      ],
      "created_at": "2026-09-01T10:00:00+05:00",
      "updated_at": "2026-10-20T09:00:00+05:00"
    }
  ]
}

GETRetrieve a scenario

GET /automation/scenarios/{id}
Token scoperead
Permission automation.manage
MCP toolautomation_scenarios_get · read-only

Path parameters

id integer Required
Scenario id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks automation.manage
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/automation/scenarios/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/scenarios/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/automation/scenarios/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', 'automation/scenarios/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_scenarios_get",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "id": 5,
    "name": "Standard",
    "is_default": true,
    "definition": {
      "v": 1,
      "steps": [
        {
          "type": "assign",
          "to": "responsible_role"
        },
        {
          "type": "condition",
          "field": "weight",
          "op": "gte",
          "value": 3,
          "then": [
            {
              "type": "deadline",
              "hours": 4
            },
            {
              "type": "require_photo"
            },
            {
              "type": "require_confirmation",
              "by": "managers"
            }
          ],
          "else": [
            {
              "type": "deadline",
              "hours": 24
            },
            {
              "type": "require_photo"
            }
          ]
        }
      ]
    },
    "checklists": [
      {
        "id": 101,
        "name": "Kitchen daily check"
      }
    ],
    "created_at": "2026-09-01T10:00:00+05:00",
    "updated_at": "2026-10-20T09:00:00+05:00"
  }
}

POSTCreate a scenario

POST /automation/scenarios

The definition is validated by the same engine as in the web editor; errors come back as validation_failed on definition.

Token scopewrite
Permission automation.manage
Plan featureautomation
MCP toolautomation_scenarios_create

Request body

name string Required
Scenario name. Maximum length 255.
definition object Required
Block tree {"steps": [...]}. Action blocks: assign, deadline, require_photo, require_confirmation, remind, escalate, group, ai_text, ai_verify, digest, skip; condition blocks on weight, section, checklist_question, department, is_repeat, audit_score with then / else branches; assign targets: responsible_role, role, managers, auditor, user, team. See automation.presets for complete examples.
is_default boolean
Use for checklists without their own scenario.
checklist_ids array<integer>
Checklists that use this scenario (replaces the list).

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks automation.manage, is read-only, or the plan has no automation
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/automation/scenarios' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Replace the broken fridge seal",
    "definition": [],
    "is_default": true,
    "checklist_ids": [
        12,
        14
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/scenarios', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "Replace the broken fridge seal",
      "definition": [],
      "is_default": true,
      "checklist_ids": [
          12,
          14
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/automation/scenarios",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"name":"Replace the broken fridge seal","definition":[],"is_default":True,"checklist_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', 'automation/scenarios', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
        'definition' => [
        ],
        'is_default' => true,
        'checklist_ids' => [
            12,
            14,
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_scenarios_create",
        "arguments": {
            "name": "Replace the broken fridge seal",
            "definition": [],
            "is_default": true,
            "checklist_ids": [
                12,
                14
            ]
        }
    }
}
Response 201
{
  "data": {
    "id": 5,
    "name": "Standard",
    "is_default": true,
    "definition": {
      "v": 1,
      "steps": [
        {
          "type": "assign",
          "to": "responsible_role"
        },
        {
          "type": "condition",
          "field": "weight",
          "op": "gte",
          "value": 3,
          "then": [
            {
              "type": "deadline",
              "hours": 4
            },
            {
              "type": "require_photo"
            },
            {
              "type": "require_confirmation",
              "by": "managers"
            }
          ],
          "else": [
            {
              "type": "deadline",
              "hours": 24
            },
            {
              "type": "require_photo"
            }
          ]
        }
      ]
    },
    "checklists": [
      {
        "id": 101,
        "name": "Kitchen daily check"
      }
    ],
    "created_at": "2026-09-01T10:00:00+05:00",
    "updated_at": "2026-10-20T09:00:00+05:00"
  }
}

PATCHUpdate a scenario

PATCH /automation/scenarios/{id}

Partial update; definition replaces the whole block tree.

Token scopewrite
Permission automation.manage
Plan featureautomation
MCP toolautomation_scenarios_update

Path parameters

id integer Required
Scenario id.

Request body

name string
Scenario name. Maximum length 255.
definition object
Block tree {"steps": [...]}. Action blocks: assign, deadline, require_photo, require_confirmation, remind, escalate, group, ai_text, ai_verify, digest, skip; condition blocks on weight, section, checklist_question, department, is_repeat, audit_score with then / else branches; assign targets: responsible_role, role, managers, auditor, user, team. See automation.presets for complete examples.
is_default boolean
Use for checklists without their own scenario.
checklist_ids array<integer>
Checklists that use this scenario (replaces the list).

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks automation.manage, is read-only, or the plan has no automation
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/automation/scenarios/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Replace the broken fridge seal",
    "definition": [],
    "is_default": true,
    "checklist_ids": [
        12,
        14
    ]
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/scenarios/42', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "Replace the broken fridge seal",
      "definition": [],
      "is_default": true,
      "checklist_ids": [
          12,
          14
      ]
  }),
});
const { data } = await res.json();
import os, requests

res = requests.patch(
    "https://app.jecheck.io/api/ext/v1/automation/scenarios/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"name":"Replace the broken fridge seal","definition":[],"is_default":True,"checklist_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', 'automation/scenarios/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
        'definition' => [
        ],
        'is_default' => true,
        'checklist_ids' => [
            12,
            14,
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_scenarios_update",
        "arguments": {
            "id": 42,
            "name": "Replace the broken fridge seal",
            "definition": [],
            "is_default": true,
            "checklist_ids": [
                12,
                14
            ]
        }
    }
}
Response 200
{
  "data": {
    "id": 5,
    "name": "Standard",
    "is_default": true,
    "definition": {
      "v": 1,
      "steps": [
        {
          "type": "assign",
          "to": "responsible_role"
        },
        {
          "type": "condition",
          "field": "weight",
          "op": "gte",
          "value": 3,
          "then": [
            {
              "type": "deadline",
              "hours": 4
            },
            {
              "type": "require_photo"
            },
            {
              "type": "require_confirmation",
              "by": "managers"
            }
          ],
          "else": [
            {
              "type": "deadline",
              "hours": 24
            },
            {
              "type": "require_photo"
            }
          ]
        }
      ]
    },
    "checklists": [
      {
        "id": 101,
        "name": "Kitchen daily check"
      }
    ],
    "created_at": "2026-09-01T10:00:00+05:00",
    "updated_at": "2026-10-20T09:00:00+05:00"
  }
}

DELDelete a scenario

DELETE /automation/scenarios/{id}

Checklists that used it fall back to the default scenario.

Token scopewrite
Permission automation.manage
Plan featureautomation
MCP toolautomation_scenarios_delete · destructive

Path parameters

id integer Required
Scenario id.

Responses

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

GETList scenario templates

GET /automation/presets

Ready-made scenarios with complete definitions, plus the catalog of block types the editor supports.

Token scoperead
Permission automation.manage
MCP toolautomation_presets · read-only

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks automation.manage
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/automation/presets' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/presets', {
  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/automation/presets",
    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', 'automation/presets', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_presets",
        "arguments": {}
    }
}
Response 200
{
  "data": {
    "presets": [
      {
        "key": "standard",
        "name": "Standard",
        "description": "Critical — 4 hours, photo and confirmation; others — 24 hours and photo.",
        "definition": {
          "v": 1,
          "steps": [
            {
              "type": "assign",
              "to": "responsible_role"
            },
            {
              "type": "condition",
              "field": "weight",
              "op": "gte",
              "value": 3,
              "then": [
                {
                  "type": "deadline",
                  "hours": 4
                },
                {
                  "type": "require_photo"
                },
                {
                  "type": "require_confirmation",
                  "by": "managers"
                }
              ],
              "else": [
                {
                  "type": "deadline",
                  "hours": 24
                },
                {
                  "type": "require_photo"
                }
              ]
            }
          ]
        }
      }
    ],
    "actions": [
      "assign",
      "deadline",
      "require_photo",
      "require_confirmation",
      "remind",
      "escalate",
      "group",
      "ai_text",
      "ai_verify",
      "digest",
      "skip"
    ],
    "condition_fields": [
      "weight",
      "section",
      "checklist_question",
      "department",
      "is_repeat",
      "audit_score"
    ],
    "assign_to": [
      "responsible_role",
      "role",
      "managers",
      "auditor",
      "user",
      "team"
    ],
    "escalate_to": [
      "managers",
      "role",
      "user",
      "admins"
    ]
  }
}

POSTPreview a scenario on an audit

POST /automation/preview

Dry run: what the (possibly unsaved) definition would do with each violation of a completed audit — no tasks are created.

Token scopewrite
Permission automation.manage
Plan featureautomation
MCP toolautomation_preview

Request body

audit_id integer Required
Completed audit id.
definition object Required

Responses

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

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/automation/preview",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"audit_id":42,"definition":[]},
)
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', 'automation/preview', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'audit_id' => 42,
        'definition' => [
        ],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_preview",
        "arguments": {
            "audit_id": 42,
            "definition": []
        }
    }
}
Response 200
{
  "data": {
    "errors": [],
    "items": [
      {
        "action": "task",
        "assign": {
          "to": "responsible_role"
        },
        "deadline_hours": 4,
        "require_photo": true,
        "require_confirmation": "managers",
        "question": "Fridge temperature is within norm",
        "section": "Kitchen",
        "weight": 3,
        "assignees": [
          "Aigerim"
        ],
        "fallback": false,
        "is_repeat": false,
        "note": null
      }
    ]
  }
}

GETGet the corrective-action loop of a task

GET /automation/loop/{uuid}

For a task created from an audit violation: the audit, deadline, whether a photo "after" and confirmation are required, photos before/after, whether the acting user may accept it, AI verdict and the loop history.

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

Path parameters

uuid uuid Required
Task 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/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/loop/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/automation/loop/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', 'automation/loop/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": "automation_loop_get",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    "status": "review",
    "source": "auto",
    "audit": {
      "id": 99812,
      "uuid": "0a9b8c7d-6e5f-4a3b-8c1d-2e3f4a5b6c7d",
      "checklist": "Kitchen daily check",
      "score": 82,
      "confirmed_at": "2026-11-03T09:40:00+05:00"
    },
    "deadline_at": "2026-11-03T13:40:00+05:00",
    "requires_photo": true,
    "requires_confirmation": true,
    "has_after_photo": true,
    "can_confirm": true,
    "is_repeat": false,
    "reopen_count": 0,
    "unassigned_role": false,
    "seen_again": 0,
    "before_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/audits/before.jpg"
    ],
    "after_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/tasks/images/0001/after.jpg"
    ],
    "ai_verdict": null,
    "events": [
      {
        "type": "submitted",
        "label": "Submitted for review",
        "actor": "user",
        "user": "Aigerim",
        "payload": {
          "comment": "Seal replaced"
        },
        "at": "2026-11-03T12:30:00+05:00"
      }
    ]
  }
}

POSTUpload a photo "after"

POST /automation/loop/{uuid}/photo

Attaches a photo of the fixed violation (JPEG/PNG/HEIC…, up to 15 MB; resized to 1920 px). Required before submit when requires_photo is true.

Token scopewrite
Permission Any employee; results follow the token's visibility
MCP toolautomation_loop_photo
Content typemultipart/form-data application/json

Path parameters

uuid uuid Required
Task UUID.

Request body

photo file Required
Image file (multipart; in MCP — object with name and content_base64).

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/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/photo' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -F '[email protected]'
const form = new FormData();
form.append('photo', fileInput.files[0]);

const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/photo', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.JECHECK_TOKEN}` },
  body: form,
});
const { data } = await res.json();
import os, requests

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

$res = $client->request('POST', 'automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/photo', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'multipart' => [
        ['name' => 'photo', 'contents' => fopen('photo.jpg', 'r')],
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_loop_photo",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 201
{
  "data": {
    "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    "status": "review",
    "source": "auto",
    "audit": {
      "id": 99812,
      "uuid": "0a9b8c7d-6e5f-4a3b-8c1d-2e3f4a5b6c7d",
      "checklist": "Kitchen daily check",
      "score": 82,
      "confirmed_at": "2026-11-03T09:40:00+05:00"
    },
    "deadline_at": "2026-11-03T13:40:00+05:00",
    "requires_photo": true,
    "requires_confirmation": true,
    "has_after_photo": true,
    "can_confirm": true,
    "is_repeat": false,
    "reopen_count": 0,
    "unassigned_role": false,
    "seen_again": 0,
    "before_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/audits/before.jpg"
    ],
    "after_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/tasks/images/0001/after.jpg"
    ],
    "ai_verdict": null,
    "events": [
      {
        "type": "submitted",
        "label": "Submitted for review",
        "actor": "user",
        "user": "Aigerim",
        "payload": {
          "comment": "Seal replaced"
        },
        "at": "2026-11-03T12:30:00+05:00"
      }
    ]
  }
}

POSTMark the violation as fixed

POST /automation/loop/{uuid}/submit

The executor reports the fix: the task goes to review (if confirmation is required) or is completed. A queued team task becomes the acting user's.

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

Path parameters

uuid uuid Required
Task UUID.

Request body

comment string
Maximum length 500.

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 — A photo "after" is required first
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "comment": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "comment": "Seal on fridge #2 is torn, temperature rises to +9 °C."
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"comment":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'comment' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_loop_submit",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "comment": "Seal on fridge #2 is torn, temperature rises to +9 °C."
        }
    }
}
Response 200
{
  "data": {
    "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    "status": "review",
    "source": "auto",
    "audit": {
      "id": 99812,
      "uuid": "0a9b8c7d-6e5f-4a3b-8c1d-2e3f4a5b6c7d",
      "checklist": "Kitchen daily check",
      "score": 82,
      "confirmed_at": "2026-11-03T09:40:00+05:00"
    },
    "deadline_at": "2026-11-03T13:40:00+05:00",
    "requires_photo": true,
    "requires_confirmation": true,
    "has_after_photo": true,
    "can_confirm": true,
    "is_repeat": false,
    "reopen_count": 0,
    "unassigned_role": false,
    "seen_again": 0,
    "before_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/audits/before.jpg"
    ],
    "after_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/tasks/images/0001/after.jpg"
    ],
    "ai_verdict": null,
    "events": [
      {
        "type": "submitted",
        "label": "Submitted for review",
        "actor": "user",
        "user": "Aigerim",
        "payload": {
          "comment": "Seal replaced"
        },
        "at": "2026-11-03T12:30:00+05:00"
      }
    ]
  }
}

POSTAccept the fix

POST /automation/loop/{uuid}/confirm

A confirmer (location manager, auditor or the one set by the scenario — see can_confirm) accepts the fix; the task is completed.

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

Path parameters

uuid uuid Required
Task 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 — The acting user is not a confirmer of this task or it is not on review
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/confirm' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/confirm', {
  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/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/confirm",
    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', 'automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/confirm', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_loop_confirm",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    "status": "review",
    "source": "auto",
    "audit": {
      "id": 99812,
      "uuid": "0a9b8c7d-6e5f-4a3b-8c1d-2e3f4a5b6c7d",
      "checklist": "Kitchen daily check",
      "score": 82,
      "confirmed_at": "2026-11-03T09:40:00+05:00"
    },
    "deadline_at": "2026-11-03T13:40:00+05:00",
    "requires_photo": true,
    "requires_confirmation": true,
    "has_after_photo": true,
    "can_confirm": true,
    "is_repeat": false,
    "reopen_count": 0,
    "unassigned_role": false,
    "seen_again": 0,
    "before_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/audits/before.jpg"
    ],
    "after_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/tasks/images/0001/after.jpg"
    ],
    "ai_verdict": null,
    "events": [
      {
        "type": "submitted",
        "label": "Submitted for review",
        "actor": "user",
        "user": "Aigerim",
        "payload": {
          "comment": "Seal replaced"
        },
        "at": "2026-11-03T12:30:00+05:00"
      }
    ]
  }
}

POSTReturn the fix for rework

POST /automation/loop/{uuid}/reject

A confirmer returns the task to the executor with a reason; reminders restart.

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

Path parameters

uuid uuid Required
Task UUID.

Request body

reason string Required
Maximum length 1000.

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/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/reject' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/reject', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/reject",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"reason":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'automation/loop/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/reject', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'reason' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "automation_loop_reject",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
        }
    }
}
Response 200
{
  "data": {
    "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    "status": "review",
    "source": "auto",
    "audit": {
      "id": 99812,
      "uuid": "0a9b8c7d-6e5f-4a3b-8c1d-2e3f4a5b6c7d",
      "checklist": "Kitchen daily check",
      "score": 82,
      "confirmed_at": "2026-11-03T09:40:00+05:00"
    },
    "deadline_at": "2026-11-03T13:40:00+05:00",
    "requires_photo": true,
    "requires_confirmation": true,
    "has_after_photo": true,
    "can_confirm": true,
    "is_repeat": false,
    "reopen_count": 0,
    "unassigned_role": false,
    "seen_again": 0,
    "before_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/audits/before.jpg"
    ],
    "after_photos": [
      "https://bucket.s3.eu-central-1.amazonaws.com/tasks/images/0001/after.jpg"
    ],
    "ai_verdict": null,
    "events": [
      {
        "type": "submitted",
        "label": "Submitted for review",
        "actor": "user",
        "user": "Aigerim",
        "payload": {
          "comment": "Seal replaced"
        },
        "at": "2026-11-03T12:30:00+05:00"
      }
    ]
  }
}