Skip to content

Recurring & repeating tasks

Bordio has two different ways a task can repeat. They are mutually exclusive — pick one:

  • On a schedule (recurrence) — the task/event repeats on fixed dates, e.g. every Monday. Uses an iCalendar RRULE. Works for tasks and events. Covered first, below.
  • After completion (repeat_on_completion) — a fresh copy is created only when you close the task, with dates shifted forward. Good for “do this again N days after I finish it”. Tasks only. See Repeat after completion.

A task or event can repeat on a schedule — a daily task, a weekly standup, a monthly review. You create a repeating series by adding a recurrence object to the normal create request. The result is a series (one template you address by its id), not a pile of copies.

Recurrence uses the iCalendar RRULE format (RFC 5545) — the same rule syntax as Google Calendar and CalDAV. If you have ever built a recurrence with a calendar library, you already know it.

Add recurrence to POST /scheduled-events. The series is anchored at start_at.

Terminal window
curl -X POST "https://api.bordio.com/public/v1/scheduled-events" \
-H "Authorization: Bearer brd_sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Team standup",
"start_at": "2026-07-06T09:00:00Z",
"end_at": "2026-07-06T09:15:00Z",
"recurrence": {
"rule": "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR",
"until": "2026-12-31"
}
}'

This creates a standup every Monday, Wednesday and Friday from 06 Jul 2026 until the end of the year.

Add recurrence to POST /tasks. The series is anchored by the task’s date (the day the task is scheduled) — you must include date, otherwise the request is rejected with 422.

Terminal window
curl -X POST "https://api.bordio.com/public/v1/tasks" \
-H "Authorization: Bearer brd_sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Water the plants",
"project_id": "proj_6594a1b2c3d4e5f6a7b8c9d0",
"date": "2026-07-06",
"recurrence": {
"rule": "RRULE:FREQ=DAILY",
"until": "2026-08-31"
}
}'
Field Required Description
rule yes A bare iCalendar RRULE string, e.g. RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR.
until no ISO-8601 date/datetime — the inclusive end of the series.

The rule is a standard RRULE line. The most common parts:

  • FREQ= — how often: DAILY, WEEKLY, MONTHLY or YEARLY.
  • INTERVAL= — every N periods (e.g. INTERVAL=2 with FREQ=WEEKLY = every two weeks). Defaults to 1.
  • BYDAY= — days of the week: MO,TU,WE,TH,FR,SA,SU (use with FREQ=WEEKLY).
  • BYMONTHDAY= — day of the month, e.g. BYMONTHDAY=1 (use with FREQ=MONTHLY).

Examples:

Goal rule
Every day RRULE:FREQ=DAILY
Every weekday RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
Every other Monday RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO
First of every month RRULE:FREQ=MONTHLY;BYMONTHDAY=1
Every year RRULE:FREQ=YEARLY

To keep series valid and safe, the API is strict about the rule:

  • FREQ is required and must be one of DAILY, WEEKLY, MONTHLY, YEARLY. Sub-daily frequencies (HOURLY, MINUTELY, SECONDLY) are rejected.
  • Do not include DTSTART. The start is derived from the resource (start_at for events, date for tasks). A DTSTART in the rule is rejected.
  • Do not use COUNT. Bound the series with the until field instead.
  • INTERVAL, if present, must be a positive integer (≤ 1000).
  • The rule must be a single RRULE: line (no RDATE/EXDATE, no line breaks).

Anything else returns 422 validation_error with code invalid_recurrence_rule.

Use the until field for the end date. If you omit until, the series is open-ended and repeats indefinitely.

Recurring resources include a recurrence object in responses; single (non-recurring) ones return recurrence: null.

{
"data": {
"id": "event_6594a1b2c3d4e5f6a7b8c9d0",
"title": "Team standup",
"start_at": "2026-07-06T09:00:00.000Z",
"recurrence": {
"rule": "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR",
"until": "2026-12-31T00:00:00.000Z"
}
}
}

The returned rule is normalized to the RRULE: line — the internal DTSTART is not exposed.

Instead of a fixed schedule, a task can spawn a new copy of itself when you complete it (move it to a closed status). This is a different mechanism from recurrence above — there is no RRULE, and nothing is created until the task is actually closed. Add repeat_on_completion to POST /tasks:

Terminal window
curl -X POST "https://api.bordio.com/public/v1/tasks" \
-H "Authorization: Bearer brd_sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Change the water filter",
"project_id": "proj_6594a1b2c3d4e5f6a7b8c9d0",
"estimated_time_seconds": 1800,
"repeat_on_completion": {
"start_date_shift_days": 30,
"due_date_shift_days": 2,
"until": "2027-12-31"
}
}'

When this task is closed, Bordio creates a fresh copy: same title, project, assignees, priority and subtasks, with its start date set 30 days after the completion date and its due date 2 days after that. The copy repeats the same way, until until passes.

Field Required Description
start_date_shift_days no Days after completion the next copy starts. 0 = same day. Omit or pass null to leave the copy unscheduled — it lands in the waiting list.
due_date_shift_days no Days after the copy’s start that its due date falls. Omit for no due date. Ignored when the copy has no start date.
until no Stop repeating after this date (ISO-8601). Omit to repeat indefinitely.
initial_task_status_id no Status each new copy starts in (task_status_...). Defaults to the task’s status — set it so the copy begins as e.g. “To do” instead of the closed status that triggered it.

Notes:

  • Only when closed. The copy is created when the task moves to a status whose state is closed — not on every edit.
  • Fields are inherited. Title, description, project, assignees, reporters, priority, subtasks and the time estimate carry over to each copy automatically; you only specify the date shifts.
  • Waiting-list repeats. Without start_date_shift_days the copy has no start date, so it waits in the waiting list until someone schedules it. Because the due date is computed from the start date, due_date_shift_days has no effect in this mode.
  • Mutually exclusive with recurrence. Sending both on the same task returns 422.

Responses include a repeat_on_completion object (or null):

{
"data": {
"id": "task_6594a1b2c3d4e5f6a7b8c9d0",
"repeat_on_completion": {
"start_date_shift_days": 30,
"due_date_shift_days": 2,
"until": "2027-12-31T00:00:00.000Z"
}
}
}
  • The series (the template) has a plain id — task_<id> / event_<id> — the id you get back from create. Reading, updating or deleting it applies to the whole series.
  • An individual occurrence has its own id that carries the occurrence date, e.g. task_<id>_20260113T090000Z (see Identifiers). Read it back from the API and use it verbatim to address that single occurrence — never build it yourself.
  • Create a recurring task or event.
  • Read, update and delete both a series and an individual occurrence by its id.

Not yet available:

  • Scoped edits — “this and following” / “all” semantics. An update currently affects the single id you address (the series, or one occurrence), not a chosen span of the series.

This page will be updated as scoped edits land.