Recurring Tasks API
All endpoints require a Bearer JWT token. Recurring tasks are scoped to a workspace.
Base URL: https://api.hule-do.com/api
POST /workspaces/:workspaceId/recurring-tasks
Create a new recurring task series.
Request:
{
"title": "Weekly standup notes",
"description": "What I worked on, what's blocking me, next steps",
"listId": "667f...",
"spaceId": "667f...",
"interval": {
"frequency": "week",
"every": 1,
"daysOfWeek": ["mon", "wed", "fri"],
"timeOfDay": "09:00",
"timezone": "Europe/Moscow"
},
"endCondition": {
"type": "after_n",
"count": 50
},
"spawnStatusId": "667f...",
"priority": "medium",
"tagIds": ["667f..."]
}interval fields:
| Field | Type | Required | Notes |
|---|---|---|---|
frequency | string | yes | day, week, month, year |
every | number | yes | Step: every N days/weeks/months/years |
daysOfWeek | string[] | for week | mon, tue, wed, thu, fri, sat, sun |
monthDay | number | for month | Day of month (1–31) |
monthOfYear | number | for year | Month (1–12) |
timeOfDay | string | yes | HH:MM in 24h format |
timezone | string | yes | IANA timezone, e.g. America/New_York |
endCondition fields:
| Type | Additional fields |
|---|---|
never | — |
on_date | date (ISO 8601) |
after_n | count (number) |
Response: 201
{
"id": "667f...",
"title": "Weekly standup notes",
"interval": { /* ... */ },
"nextRunAt": "2026-06-02T06:00:00.000Z",
"spawnCount": 0,
"active": true,
"createdAt": "2026-05-28T10:00:00.000Z"
}GET /workspaces/:workspaceId/recurring-tasks
List all recurring task series in the workspace.
Response: 200 — array of recurring job objects.
GET /workspaces/:workspaceId/recurring-tasks/:id
Get a single recurring job.
Response: 200 — recurring job object.
PATCH /workspaces/:workspaceId/recurring-tasks/:id
Update a recurring job. Omitted fields are not changed.
Request:
{
"active": false,
"title": "Updated meeting title"
}Response: 200 — updated recurring job.
DELETE /workspaces/:workspaceId/recurring-tasks/:id
Delete the recurring series. Existing spawned task instances are preserved.
Response: 204 No Content
POST /workspaces/:workspaceId/recurring-tasks/:id/skip
Skip the next occurrence. The job's nextRunAt is advanced past the current candidate by adding it to skipDates[].
Request: (no body)
Response: 204 No Content
POST /workspaces/:workspaceId/recurring-tasks/:id/skip-until
Skip all occurrences until a specific date. Pass null to clear.
Request:
{
"skipUntil": "2026-06-15T00:00:00.000Z"
}Response: 204 No Content
POST /workspaces/:workspaceId/recurring-tasks/:jobId/instances/:taskId/edit-mode
Edit a spawned task instance with control over how the change propagates.
Request:
{
"mode": "this",
"updates": {
"title": "Updated standup topic",
"priority": "high"
}
}mode values:
| Mode | Behaviour |
|---|---|
this | Update only this instance. The occurrence date is added to the series' skip list. |
this_and_following | Split the series. End the current series before this date and create a new one with the changes. |
all | Update this instance + the template + all past instances (title, description, priority, tags). |
Response: 200 — updated TaskDto.
GET /workspaces/:workspaceId/recurring-tasks/:id/instances
List all task instances spawned from this recurring job.
Query parameters: page, limit.
Response: 200
{
"items": [ /* TaskDto[] */ ],
"total": 12,
"page": 1,
"limit": 20
}Errors
| Code | Meaning |
|---|---|
400 | Invalid interval or end condition |
404 | Recurring job not found |
Related
- API Examples — full walkthroughs.
- Recurring tasks (guide) — concepts and usage.
- Recurring jobs (concept) — how the scheduler works.