jeCheckDevelopers
Get an API key
Developers/API reference/Recurring projects

Recurring projects

Recurring projects such as month-end closing: a template of items with due dates in working days relative to the period anchor date (e.g. the last day of the month), owners (a person, a team, a position at each location or the location manager), items repeated for every location, and dependencies. On schedule a regular project is created for each period (see the Projects and Tasks sections). The period summary is a matrix of items × locations with readiness, delays and the chains they hold. Configuring requires projects.cycles or the admin role in the recurring project; available on plans with recurring projects.

GETList recurring projects

GET /project-cycles

Recurring projects the token can see: all with projects.cycles, otherwise those it owns or is a member of, and those open to the company. Each has the latest period and its progress.

Token scoperead
Permission Any employee; results follow the token's visibility
Plan featureproject_cycles
MCP toolcycles_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.

Responses

StatusDescription
200OK — a page of results with meta
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/project-cycles' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles', {
  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/project-cycles",
    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', 'project-cycles', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_list",
        "arguments": {}
    }
}
Response 200
{
  "data": [
    {
      "id": 7,
      "uuid": "4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
      "name": "Month-end closing",
      "description": "Documents, stocktaking, reconciliations and the owner report",
      "icon": "event_available",
      "color": "#0e9aa7",
      "key": "CLS",
      "active": true,
      "owner": {
        "id": 311,
        "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
        "name": "Aigerim Sadykova"
      },
      "schedule": {
        "frequency": "monthly",
        "anchor": "end",
        "day": 25,
        "weekday": 5,
        "create_offset": -5,
        "hour": 9,
        "holidays": [
          "2026-12-16"
        ]
      },
      "schedule_text": "every month, by the last day; the project is created 5 working days before",
      "next_run_at": "2026-12-24T04:00:00+00:00",
      "last_period": "2026-11",
      "items_count": 13,
      "per_location_items": 6,
      "location_ids": null,
      "members": [
        {
          "user": {
            "id": 412,
            "uuid": "2f3e4d5c-6b7a-4899-8a0b-2c3d4e5f6a7b",
            "name": "Daniyar Ospanov"
          },
          "role": "viewer"
        }
      ],
      "settings": {
        "visibility": "members",
        "notify_start": true,
        "notify_unblocked": true
      },
      "role": "admin",
      "created_at": "2026-11-06T10:00:00+05:00",
      "updated_at": "2026-11-06T10:05:00+05:00",
      "url": "https://app.jecheck.io/ui/projects/cycles/4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
      "template": {
        "sections": [
          "Location documents",
          "Stocktaking"
        ],
        "items": [
          {
            "id": "i_k2m9qa",
            "title": "All supplier invoices entered",
            "description": "",
            "section": 0,
            "per_location": true,
            "assign": {
              "type": "manager",
              "ids": []
            },
            "start_offset": null,
            "due_offset": -1,
            "priority": "medium",
            "requires_photo": false,
            "requires_review": false,
            "subtasks": [],
            "tags": [],
            "after": []
          }
        ]
      }
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total": 1,
    "has_more": false
  }
}

GETList ready-made templates

GET /project-cycles/templates

Built-in recurring project templates (month-end closing, stocktaking, quarter closing, blank). Pass the id as template when creating.

Token scoperead
Permission Any employee; results follow the token's visibility
Plan featureproject_cycles
MCP toolcycles_templates · 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/project-cycles/templates' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/templates', {
  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/project-cycles/templates",
    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', 'project-cycles/templates', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_templates",
        "arguments": {}
    }
}
Response 200
{
  "data": [
    {
      "id": "month_close",
      "name": "Month-end closing",
      "description": "Documents, stocktaking, reconciliations…",
      "icon": "event_available",
      "items": 13,
      "frequency": "monthly",
      "per_location_items": 6
    }
  ]
}

GETRetrieve a recurring project

GET /project-cycles/{uuid}

Card, schedule and the full template (sections and items).

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

Path parameters

uuid uuid Required
Recurring project 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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/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/project-cycles/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', 'project-cycles/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": "cycles_get",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "id": 7,
    "uuid": "4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
    "name": "Month-end closing",
    "description": "Documents, stocktaking, reconciliations and the owner report",
    "icon": "event_available",
    "color": "#0e9aa7",
    "key": "CLS",
    "active": true,
    "owner": {
      "id": 311,
      "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
      "name": "Aigerim Sadykova"
    },
    "schedule": {
      "frequency": "monthly",
      "anchor": "end",
      "day": 25,
      "weekday": 5,
      "create_offset": -5,
      "hour": 9,
      "holidays": [
        "2026-12-16"
      ]
    },
    "schedule_text": "every month, by the last day; the project is created 5 working days before",
    "next_run_at": "2026-12-24T04:00:00+00:00",
    "last_period": "2026-11",
    "items_count": 13,
    "per_location_items": 6,
    "location_ids": null,
    "members": [
      {
        "user": {
          "id": 412,
          "uuid": "2f3e4d5c-6b7a-4899-8a0b-2c3d4e5f6a7b",
          "name": "Daniyar Ospanov"
        },
        "role": "viewer"
      }
    ],
    "settings": {
      "visibility": "members",
      "notify_start": true,
      "notify_unblocked": true
    },
    "role": "admin",
    "created_at": "2026-11-06T10:00:00+05:00",
    "updated_at": "2026-11-06T10:05:00+05:00",
    "url": "https://app.jecheck.io/ui/projects/cycles/4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
    "template": {
      "sections": [
        "Location documents",
        "Stocktaking"
      ],
      "items": [
        {
          "id": "i_k2m9qa",
          "title": "All supplier invoices entered",
          "description": "",
          "section": 0,
          "per_location": true,
          "assign": {
            "type": "manager",
            "ids": []
          },
          "start_offset": null,
          "due_offset": -1,
          "priority": "medium",
          "requires_photo": false,
          "requires_review": false,
          "subtasks": [],
          "tags": [],
          "after": []
        }
      ]
    }
  }
}

POSTCreate a recurring project

POST /project-cycles

From a ready-made template, from the tasks of an existing project (from_project_uuid: sections, owners, dependencies, due dates as working days from the project due date) or blank. The first period is planned right away. Requires projects.cycles.

Token scopewrite
Permission projects.cycles
Plan featureproject_cycles
MCP toolcycles_create

Request body

name string
Name; the period project is called «name · period». Length 2–200.
key string
Key prefix of period projects (2–5 Latin letters/digits): CLS → CLS2611. Maximum length 5.
description string | null
Description. Maximum length 20000.
icon string
Material Symbols icon name. Maximum length 40.
color string
Hex color. Maximum length 9.
schedule object
Schedule.
frequency string
How often.
monthlyquarterlyweekly
anchor string
Anchor date of a month or quarter: last day, first day, or day of the month.
endstartday
day integer
Day of the month for anchor: day. Value 1–31.
weekday integer
Weekly: anchor weekday, 1 = Monday. Value 1–7.
create_offset integer
When to create the period project: working days from the anchor date. Value -60–20.
hour integer
Hour of creation, company local time. Value 0–23.
holidays array<string>
Company days off (YYYY-MM-DD) skipped in working-day offsets in addition to weekends.
template_sections array<string>
Sections of the template.
template_items array<object>
Template items (replaces the whole list). Maximum items 200.
id string
Stable item id (i_ + letters/digits); generated if omitted. Used by after and as the summary row.
title string Required
Item title — the task title. Maximum length 512.
description string
Task description. Maximum length 20000.
section integer | null
Section index in template_sections.
per_location boolean
Create a separate task for every location of the recurring project.
assign object
Who gets the task.
type string
users — the listed people; team — a team queue; position — employees with the position at the task location; manager — the location manager; owner — the recurring project owner.
noneusersteampositionmanagerowner
ids array<integer>
User, team or position ids.
start_offset integer | null
Start: working days from the anchor date (negative — before).
due_offset integer | null
Due date: working days from the anchor date (−2 — two working days before; 3 — three after).
priority string
Task priority.
urgenthighmediumlow
requires_photo boolean
A photo is required to complete the task.
requires_review boolean
The task goes to review before it is done.
subtasks array<string>
Checklist inside the task.
tags array<string>
Tags.
after array<string>
Ids of items that must be done first; for items per location the dependency links tasks of the same location.
location_ids array<integer> | null
Locations for items per location; null — all locations of the company.
owner_id integer
Owner: admin of period projects, author of tasks.
members array<object>
Members (replaces the list). Maximum items 200.
user_id integer Required
User id.
role string
Role in period projects; admin also configures the recurring project.
admineditorcommenterviewer
settings object
Settings.
visibility string
Visibility of period projects.
memberscompany
notify_start boolean
Send each owner one message when a period starts.
notify_unblocked boolean
Notify assignees when the previous step is done.
active boolean
Create periods on schedule.
template string
Ready-made template id (see cycles.templates). Maximum length 40.
from_project_uuid uuid
Build the template from this project's tasks.

Responses

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

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/project-cycles",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"name":"Replace the broken fridge seal","key":"string","description":"Seal on fridge #2 is torn, temperature rises to +9 °C.","icon":"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', 'project-cycles', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
        'key' => 'string',
        'description' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
        'icon' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_create",
        "arguments": {
            "name": "Replace the broken fridge seal",
            "key": "string",
            "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
            "icon": "string"
        }
    }
}
Response 201
{
  "data": {
    "id": 7,
    "uuid": "4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
    "name": "Month-end closing",
    "description": "Documents, stocktaking, reconciliations and the owner report",
    "icon": "event_available",
    "color": "#0e9aa7",
    "key": "CLS",
    "active": true,
    "owner": {
      "id": 311,
      "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
      "name": "Aigerim Sadykova"
    },
    "schedule": {
      "frequency": "monthly",
      "anchor": "end",
      "day": 25,
      "weekday": 5,
      "create_offset": -5,
      "hour": 9,
      "holidays": [
        "2026-12-16"
      ]
    },
    "schedule_text": "every month, by the last day; the project is created 5 working days before",
    "next_run_at": "2026-12-24T04:00:00+00:00",
    "last_period": "2026-11",
    "items_count": 13,
    "per_location_items": 6,
    "location_ids": null,
    "members": [
      {
        "user": {
          "id": 412,
          "uuid": "2f3e4d5c-6b7a-4899-8a0b-2c3d4e5f6a7b",
          "name": "Daniyar Ospanov"
        },
        "role": "viewer"
      }
    ],
    "settings": {
      "visibility": "members",
      "notify_start": true,
      "notify_unblocked": true
    },
    "role": "admin",
    "created_at": "2026-11-06T10:00:00+05:00",
    "updated_at": "2026-11-06T10:05:00+05:00",
    "url": "https://app.jecheck.io/ui/projects/cycles/4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
    "template": {
      "sections": [
        "Location documents",
        "Stocktaking"
      ],
      "items": [
        {
          "id": "i_k2m9qa",
          "title": "All supplier invoices entered",
          "description": "",
          "section": 0,
          "per_location": true,
          "assign": {
            "type": "manager",
            "ids": []
          },
          "start_offset": null,
          "due_offset": -1,
          "priority": "medium",
          "requires_photo": false,
          "requires_review": false,
          "subtasks": [],
          "tags": [],
          "after": []
        }
      ]
    }
  }
}

PATCHUpdate a recurring project

PATCH /project-cycles/{uuid}

Partial update. Template changes apply to the next periods; existing period projects are not changed. Requires the admin role in the recurring project.

Token scopewrite
Permission Any employee; results follow the token's visibility
Plan featureproject_cycles
MCP toolcycles_update

Path parameters

uuid uuid Required
Recurring project UUID.

Request body

name string
Name; the period project is called «name · period». Length 2–200.
key string
Key prefix of period projects (2–5 Latin letters/digits): CLS → CLS2611. Maximum length 5.
description string | null
Description. Maximum length 20000.
icon string
Material Symbols icon name. Maximum length 40.
color string
Hex color. Maximum length 9.
schedule object
Schedule.
frequency string
How often.
monthlyquarterlyweekly
anchor string
Anchor date of a month or quarter: last day, first day, or day of the month.
endstartday
day integer
Day of the month for anchor: day. Value 1–31.
weekday integer
Weekly: anchor weekday, 1 = Monday. Value 1–7.
create_offset integer
When to create the period project: working days from the anchor date. Value -60–20.
hour integer
Hour of creation, company local time. Value 0–23.
holidays array<string>
Company days off (YYYY-MM-DD) skipped in working-day offsets in addition to weekends.
template_sections array<string>
Sections of the template.
template_items array<object>
Template items (replaces the whole list). Maximum items 200.
id string
Stable item id (i_ + letters/digits); generated if omitted. Used by after and as the summary row.
title string Required
Item title — the task title. Maximum length 512.
description string
Task description. Maximum length 20000.
section integer | null
Section index in template_sections.
per_location boolean
Create a separate task for every location of the recurring project.
assign object
Who gets the task.
type string
users — the listed people; team — a team queue; position — employees with the position at the task location; manager — the location manager; owner — the recurring project owner.
noneusersteampositionmanagerowner
ids array<integer>
User, team or position ids.
start_offset integer | null
Start: working days from the anchor date (negative — before).
due_offset integer | null
Due date: working days from the anchor date (−2 — two working days before; 3 — three after).
priority string
Task priority.
urgenthighmediumlow
requires_photo boolean
A photo is required to complete the task.
requires_review boolean
The task goes to review before it is done.
subtasks array<string>
Checklist inside the task.
tags array<string>
Tags.
after array<string>
Ids of items that must be done first; for items per location the dependency links tasks of the same location.
location_ids array<integer> | null
Locations for items per location; null — all locations of the company.
owner_id integer
Owner: admin of period projects, author of tasks.
members array<object>
Members (replaces the list). Maximum items 200.
user_id integer Required
User id.
role string
Role in period projects; admin also configures the recurring project.
admineditorcommenterviewer
settings object
Settings.
visibility string
Visibility of period projects.
memberscompany
notify_start boolean
Send each owner one message when a period starts.
notify_unblocked boolean
Notify assignees when the previous step is done.
active boolean
Create periods on schedule.

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

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

$res = $client->request('PATCH', 'project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
        'key' => 'string',
        'description' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
        'icon' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_update",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "name": "Replace the broken fridge seal",
            "key": "string",
            "description": "Seal on fridge #2 is torn, temperature rises to +9 °C.",
            "icon": "string"
        }
    }
}
Response 200
{
  "data": {
    "id": 7,
    "uuid": "4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
    "name": "Month-end closing",
    "description": "Documents, stocktaking, reconciliations and the owner report",
    "icon": "event_available",
    "color": "#0e9aa7",
    "key": "CLS",
    "active": true,
    "owner": {
      "id": 311,
      "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
      "name": "Aigerim Sadykova"
    },
    "schedule": {
      "frequency": "monthly",
      "anchor": "end",
      "day": 25,
      "weekday": 5,
      "create_offset": -5,
      "hour": 9,
      "holidays": [
        "2026-12-16"
      ]
    },
    "schedule_text": "every month, by the last day; the project is created 5 working days before",
    "next_run_at": "2026-12-24T04:00:00+00:00",
    "last_period": "2026-11",
    "items_count": 13,
    "per_location_items": 6,
    "location_ids": null,
    "members": [
      {
        "user": {
          "id": 412,
          "uuid": "2f3e4d5c-6b7a-4899-8a0b-2c3d4e5f6a7b",
          "name": "Daniyar Ospanov"
        },
        "role": "viewer"
      }
    ],
    "settings": {
      "visibility": "members",
      "notify_start": true,
      "notify_unblocked": true
    },
    "role": "admin",
    "created_at": "2026-11-06T10:00:00+05:00",
    "updated_at": "2026-11-06T10:05:00+05:00",
    "url": "https://app.jecheck.io/ui/projects/cycles/4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
    "template": {
      "sections": [
        "Location documents",
        "Stocktaking"
      ],
      "items": [
        {
          "id": "i_k2m9qa",
          "title": "All supplier invoices entered",
          "description": "",
          "section": 0,
          "per_location": true,
          "assign": {
            "type": "manager",
            "ids": []
          },
          "start_offset": null,
          "due_offset": -1,
          "priority": "medium",
          "requires_photo": false,
          "requires_review": false,
          "subtasks": [],
          "tags": [],
          "after": []
        }
      ]
    }
  }
}

DELDelete a recurring project

DELETE /project-cycles/{uuid}

Stops creating periods and removes the recurring project. Period projects already created stay as regular projects.

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

Path parameters

uuid uuid Required
Recurring project 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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/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/project-cycles/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', 'project-cycles/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": "cycles_delete",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "deleted": true
  }
}

POSTDuplicate a recurring project

POST /project-cycles/{uuid}/duplicate

Copy of the template and schedule, paused, without periods. Requires projects.cycles.

Token scopewrite
Permission Any employee; results follow the token's visibility
Plan featureproject_cycles
MCP toolcycles_duplicate

Path parameters

uuid uuid Required
Recurring project UUID.

Request body

name string
Name of the copy. Length 2–200.

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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/duplicate' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Replace the broken fridge seal"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/duplicate', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "Replace the broken fridge seal"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/duplicate",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"name":"Replace the broken fridge seal"},
)
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', 'project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/duplicate', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_duplicate",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "name": "Replace the broken fridge seal"
        }
    }
}
Response 201
{
  "data": {
    "id": 7,
    "uuid": "4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
    "name": "Month-end closing",
    "description": "Documents, stocktaking, reconciliations and the owner report",
    "icon": "event_available",
    "color": "#0e9aa7",
    "key": "CLS",
    "active": true,
    "owner": {
      "id": 311,
      "uuid": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
      "name": "Aigerim Sadykova"
    },
    "schedule": {
      "frequency": "monthly",
      "anchor": "end",
      "day": 25,
      "weekday": 5,
      "create_offset": -5,
      "hour": 9,
      "holidays": [
        "2026-12-16"
      ]
    },
    "schedule_text": "every month, by the last day; the project is created 5 working days before",
    "next_run_at": "2026-12-24T04:00:00+00:00",
    "last_period": "2026-11",
    "items_count": 13,
    "per_location_items": 6,
    "location_ids": null,
    "members": [
      {
        "user": {
          "id": 412,
          "uuid": "2f3e4d5c-6b7a-4899-8a0b-2c3d4e5f6a7b",
          "name": "Daniyar Ospanov"
        },
        "role": "viewer"
      }
    ],
    "settings": {
      "visibility": "members",
      "notify_start": true,
      "notify_unblocked": true
    },
    "role": "admin",
    "created_at": "2026-11-06T10:00:00+05:00",
    "updated_at": "2026-11-06T10:05:00+05:00",
    "url": "https://app.jecheck.io/ui/projects/cycles/4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
    "template": {
      "sections": [
        "Location documents",
        "Stocktaking"
      ],
      "items": [
        {
          "id": "i_k2m9qa",
          "title": "All supplier invoices entered",
          "description": "",
          "section": 0,
          "per_location": true,
          "assign": {
            "type": "manager",
            "ids": []
          },
          "start_offset": null,
          "due_offset": -1,
          "priority": "medium",
          "requires_photo": false,
          "requires_review": false,
          "subtasks": [],
          "tags": [],
          "after": []
        }
      ]
    }
  }
}

POSTPreview a period

POST /project-cycles/{uuid}/preview

Dates of every item for the next period (or period), when the project is created, how many tasks it will have, and hints about the template (no due date, no owner, due before a dependency).

Token scopewrite
Permission Any employee; results follow the token's visibility
Plan featureproject_cycles
MCP toolcycles_preview

Path parameters

uuid uuid Required
Recurring project UUID.

Request body

period string
Period code: 2026-11, 2026-Q4 or 2026-W45. Maximum length 10.

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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/preview' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "period": "string"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/preview', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "period": "string"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/preview",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"period":"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', 'project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/preview', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'period' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_preview",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "period": "string"
        }
    }
}
Response 200
{
  "data": {
    "period": "2026-12",
    "label": "December 2026",
    "anchor": "2026-12-31",
    "create_at": "2026-12-24T04:00:00+00:00",
    "exists": false,
    "items": [
      {
        "id": "i_k2m9qa",
        "start": null,
        "due": "2026-12-30",
        "tasks": 4
      }
    ],
    "tasks": 31,
    "warnings": [
      {
        "item": "i_p8x1za",
        "kind": "no_assignee",
        "text": "No owner"
      }
    ],
    "schedule_text": "every month, by the last day"
  }
}

POSTStart a period now

POST /project-cycles/{uuid}/run

Creates the project of the next period (or of period) right away instead of waiting for the schedule. Returns the period project. Requires the admin role in the recurring project.

Token scopewrite
Permission Any employee; results follow the token's visibility
Plan featureproject_cycles
MCP toolcycles_run

Path parameters

uuid uuid Required
Recurring project UUID.

Request body

period string
Period code; default — the next period not created yet. Maximum length 10.

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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/run' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "period": "string"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/run', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "period": "string"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/run",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"period":"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', 'project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/run', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'period' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_run",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "period": "string"
        }
    }
}
Response 201
{
  "data": {
    "project_uuid": "8c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
    "key": "CLS2612",
    "name": "Month-end closing · December 2026",
    "period": "2026-12",
    "url": "https://app.jecheck.io/ui/projects/8c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f"
  }
}

GETList periods

GET /project-cycles/{uuid}/periods

Period projects, newest first: progress, tasks done late, overdue now, when the period was closed and how many working days after (+) or before (−) the planned finish (the latest due date of its tasks).

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

Path parameters

uuid uuid Required
Recurring project 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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/periods' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/periods', {
  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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/periods",
    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', 'project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/periods', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_periods",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": [
    {
      "project_uuid": "8c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
      "key": "CLS2611",
      "period": "2026-11",
      "label": "November 2026",
      "anchor": "2026-11-30",
      "status": "active",
      "total": 19,
      "done": 19,
      "progress": 100,
      "done_late": 3,
      "overdue": 0,
      "on_time": 84,
      "planned": "2026-12-10",
      "finished": "2026-12-08",
      "finish_delta": -2
    }
  ]
}

GETDelay rankings

GET /project-cycles/{uuid}/rankings

Who is late most often over the last periods periods: locations, teams, people and items, with the share of late tasks and the average delay in working days.

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

Path parameters

uuid uuid Required
Recurring project UUID.

Query parameters

periods integer
How many recent periods to count. Default 6. Value 1–24.

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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/rankings?periods=6' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/rankings?periods=6', {
  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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/rankings",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    params={"periods":6},
)
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', 'project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/rankings', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'query' => [
        'periods' => 6,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_rankings",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "periods": 6
        }
    }
}
Response 200
{
  "data": {
    "periods": 6,
    "locations": [
      {
        "id": "12",
        "label": "Abay 10",
        "total": 54,
        "late": 9,
        "late_days": 21,
        "rate": 17,
        "avg_days": 2.3
      }
    ],
    "teams": [],
    "people": [],
    "items": []
  }
}

GETChange log

GET /project-cycles/{uuid}/events

History of the recurring project: created, template and schedule changes, pause, periods started, due dates shifted.

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

Path parameters

uuid uuid Required
Recurring project 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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/events' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/events', {
  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/project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/events",
    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', 'project-cycles/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/events', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_events",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": [
    {
      "id": 41,
      "type": "shifted",
      "payload": {
        "period": "2026-11",
        "days": 2,
        "tasks": 6,
        "cascade": true
      },
      "user": {
        "id": 311,
        "name": "Aigerim Sadykova"
      },
      "at": "2026-11-30T12:00:00+05:00"
    }
  ]
}

GETPeriod summary

GET /project-cycles/periods/{uuid}/summary

The summary matrix of a period project: rows — template items, columns — locations (cells by location id; items not per location are in general). Cell state: done, done_late, overdue, blocked (waits for another task), review, in_progress, todo, planned (starts later). Also readiness by location, team, person and section, delays (overdue tasks and the number of open tasks waiting for them down the chain), plan/fact chart by day and a finish forecast. Requires access to the period project.

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

Path parameters

uuid uuid Required
UUID of a period project (a project created by a recurring project).

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/project-cycles/periods/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/summary' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/periods/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/summary', {
  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/project-cycles/periods/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/summary",
    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', 'project-cycles/periods/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/summary', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "cycles_summary",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "project": {
      "uuid": "8c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
      "key": "CLS2611",
      "name": "Month-end closing · November 2026",
      "period": "2026-11",
      "anchor": "2026-11-30",
      "start": "2026-11-23",
      "status": "active"
    },
    "cycle": {
      "uuid": "4b0e6c2a-1d3f-4a5b-9c8d-7e6f5a4b3c2d",
      "name": "Month-end closing"
    },
    "today": "2026-11-30",
    "sections": [
      "Location documents"
    ],
    "locations": [
      {
        "id": "12",
        "name": "Abay 10"
      }
    ],
    "rows": [
      {
        "id": "i_k2m9qa",
        "title": "All supplier invoices entered",
        "section": 0,
        "per_location": true,
        "extra": false,
        "cells": {
          "12": [
            {
              "key": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
              "seq": 1,
              "title": "All supplier invoices entered",
              "state": "overdue",
              "open": true,
              "status": 0,
              "due": "2026-11-27",
              "start": null,
              "done_at": null,
              "late": 1,
              "blockers": 0,
              "assignees": [
                "311"
              ],
              "team": null,
              "location": "12",
              "section": 201,
              "item": "i_k2m9qa"
            }
          ]
        },
        "general": [],
        "total": 2,
        "done": 1,
        "progress": 50,
        "overdue": 1,
        "due": "2026-11-27"
      }
    ],
    "totals": {
      "total": 19,
      "done": 4,
      "progress": 21,
      "plan": 37,
      "overdue": 3,
      "blocked": 6,
      "done_late": 1,
      "unassigned": 2,
      "states": {
        "overdue": 3,
        "done": 3
      }
    },
    "by_location": [
      {
        "id": "12",
        "total": 9,
        "done": 2,
        "overdue": 2,
        "done_late": 0,
        "blocked": 4,
        "late_days": 3,
        "progress": 22
      }
    ],
    "by_team": [],
    "by_person": [],
    "by_section": [],
    "delays": [
      {
        "key": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
        "seq": 1,
        "title": "All supplier invoices entered",
        "state": "overdue",
        "open": true,
        "status": 0,
        "due": "2026-11-27",
        "start": null,
        "done_at": null,
        "late": 1,
        "blockers": 0,
        "assignees": [
          "311"
        ],
        "team": null,
        "location": "12",
        "section": 201,
        "item": "i_k2m9qa",
        "downstream": 7,
        "waiting": 2
      }
    ],
    "chart": [
      {
        "date": "2026-11-23",
        "plan": 0,
        "fact": 0
      }
    ],
    "forecast": {
      "done": false,
      "date": "2026-12-08",
      "late": 2,
      "left": 15,
      "pace": 1.6
    }
  }
}

POSTShift due dates

POST /project-cycles/periods/{uuid}/shift

Moves due dates and start dates of open tasks by days working days (weekends and company days off are skipped). task_uuids — which tasks (empty — all open); cascade — together with the tasks that depend on them down the chain. Each change is recorded in the task history; assignees get one notification. Requires the editor role in the period project.

Token scopewrite
Permission Any employee; results follow the token's visibility
Plan featureproject_cycles
MCP toolcycles_shift

Path parameters

uuid uuid Required
UUID of a period project (a project created by a recurring project).

Request body

days integer Required
Working days; negative — earlier. Value -60–60.
task_uuids array<string>
Tasks to shift; empty — all open tasks of the period. Maximum items 500.
cascade boolean
Also shift dependent tasks. Default true.
reason string
Reason, saved in the task history. 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 — 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/project-cycles/periods/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/shift' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "days": 42,
    "task_uuids": [
        "example"
    ],
    "cascade": true,
    "reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/project-cycles/periods/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/shift', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "days": 42,
      "task_uuids": [
          "example"
      ],
      "cascade": true,
      "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/project-cycles/periods/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/shift",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"days":42,"task_uuids":["example"],"cascade":True,"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', 'project-cycles/periods/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/shift', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'days' => 42,
        'task_uuids' => [
            'example',
        ],
        'cascade' => true,
        '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": "cycles_shift",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "days": 42,
            "task_uuids": [
                "example"
            ],
            "cascade": true,
            "reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
        }
    }
}
Response 200
{
  "data": {
    "moved": 6,
    "tasks": [
      "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
    ]
  }
}