# Series API – Tiempo

> Recurring bookings from an RRULE, created as real bookings over a rolling horizon. A date that doesn’t fit is skipped and reported, not failed.

Source: https://tiempo.one/docs/series

Recurring bookings from an RRULE, created as real bookings over a rolling horizon. A date that doesn’t fit is skipped and reported, not failed.

## Create a series

`POST /v1/series`

Recurring bookings from an RRULE at a local start time. Occurrences are created as confirmed bookings up to horizon_days ahead and kept topped up. A date that doesn’t fit is skipped with series.occurrence_skipped.

Send the business’s id in `Tiempo-Account`.

### Body parameters

-   `rrule` (string, required)
    
    An RFC 5545 rule, for example FREQ=WEEKLY;INTERVAL=2;BYDAY=TU.
    
-   `start_date` (date, required)
    
    First local date, inclusive.
    
-   `start_time` (string, required)
    
    Local start time, HH:mm.
    
-   `timezone` (string)
    
    IANA name, for example Europe/London.
    
-   `template` (object, required)
    
    What each occurrence books: segments with offsets and durations, seats and metadata.
    
    -   `segments` (array of objects, required)
        
        The time to hold: one entry per resource and period. Segments can be on different resources and needn’t touch.
        
        -   `resource` (string, required)
            
            A resource id.
            
        -   `offset_minutes` (integer)
            
            Minutes after the start time the segment begins. Defaults to 0.
            
        -   `duration_minutes` (integer, required)
            
            Length of the segment; or, on a search, one length for every segment when they all run together (then segments give no length or offset).
            
        -   `quantity` (integer)
            
            Units of capacity to take. Defaults to 1 (for blocks, the whole capacity).
            
        -   `location` (string)
            
            A location id.
            
        -   `buffer_before_minutes` (integer)
            
            Minutes the resource is also held before the time (travel, setup), 0–1440. On a booking segment it overrides the resource’s default; the appointment’s start and end don’t change.
            
        -   `buffer_after_minutes` (integer)
            
            Minutes the resource is also held after the time (cleanup, turnaround), 0–1440. On a booking segment it overrides the resource’s default. Buffers must fit in working hours and can’t overlap blocks or other bookings.
            
    -   `seats` (integer)
        
        Makes the booking a session that attendees take seats in.
        
    -   `metadata` (map)
        
        Up to 50 keys of your own (strings, keys up to 40 characters, values up to 500). Returned on every read and webhook. On update, an empty string removes a key.
        
-   `horizon_days` (integer)
    
    How far ahead occurrences are created. Defaults to 90, at most 730.
    
-   `conflict_policy` (string)
    
    reject (default) refuses time that isn’t free or isn’t working hours. capacity_only ignores working hours, overrides and blocks (staff booking a client in after hours) but still never overlaps another booking. allow places it anyway (a deliberate overbook) and flags it.
    
-   `metadata` (map)
    
    Up to 50 keys of your own (strings, keys up to 40 characters, values up to 500). Returned on every read and webhook. On update, an empty string removes a key.
    

POST /v1/series

```bash
curl -X POST "https://api.tiempo.one/v1/series" \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…" \
  -H "Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw" \
  -H "Content-Type: application/json" \
  -d '{
    "rrule": "FREQ=WEEKLY;INTERVAL=2;BYDAY=TU",
    "start_date": "2026-11-17",
    "start_time": "10:00",
    "timezone": "Europe/London",
    "template": {
      "segments": [
        {
          "resource": "res_ann",
          "duration_minutes": 45
        }
      ]
    }
  }'
```

200Response

```json
{
  "id": "ser_01m4k2p9z7x3c5v1b8n4m6q2w0",
  "object": "series",
  "livemode": false,
  "status": "active",
  "rrule": "FREQ=WEEKLY;INTERVAL=2;BYDAY=TU",
  "start_date": "2026-11-17",
  "start_time": "10:00",
  "timezone": "Europe/London",
  "template": {
    "segments": [
      {
        "resource": "res_01m42ytc9b53589z4rp11zedjy",
        "offset_minutes": 0,
        "duration_minutes": 45,
        "quantity": 1,
        "location": null,
        "buffer_before_minutes": null,
        "buffer_after_minutes": null
      }
    ],
    "seats": null,
    "metadata": {}
  },
  "horizon_days": 90,
  "conflict_policy": "reject",
  "generated_until": "2027-01-02T08:10:00Z",
  "ends_before": null,
  "split_from": null,
  "split_into": null,
  "metadata": {
    "your_plan_id": "plan_7"
  },
  "created_at": "2026-10-04T08:10:00Z",
  "updated_at": "2026-10-04T08:10:00Z"
}
```

## List series

`GET /v1/series`

Send the business’s id in `Tiempo-Account`.

### Query parameters

-   `status` (string)
    
    Only objects with this status.
    
-   `limit` (integer)
    
    1 to 100. Defaults to 20.
    
-   `starting_after` (string)
    
    An object id from the previous page; returns the page after it.
    
-   `ending_before` (string)
    
    An object id; returns the page before it.
    
-   `metadata[key]` (string)
    
    Only objects whose metadata has this key and value. Repeat for several.
    

GET /v1/series

```bash
curl "https://api.tiempo.one/v1/series?status=active" \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…" \
  -H "Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw"
```

200Response

```json
{
  "object": "list",
  "data": [
    {
      "id": "ser_01m4k2p9z7x3c5v1b8n4m6q2w0",
      "object": "series",
      "livemode": false,
      "status": "active",
      "rrule": "FREQ=WEEKLY;INTERVAL=2;BYDAY=TU",
      "start_date": "2026-11-17",
      "start_time": "10:00",
      "timezone": "Europe/London",
      "template": {
        "segments": [
          {
            "resource": "res_01m42ytc9b53589z4rp11zedjy",
            "offset_minutes": 0,
            "duration_minutes": 45,
            "quantity": 1,
            "location": null,
            "buffer_before_minutes": null,
            "buffer_after_minutes": null
          }
        ],
        "seats": null,
        "metadata": {}
      },
      "horizon_days": 90,
      "conflict_policy": "reject",
      "generated_until": "2027-01-02T08:10:00Z",
      "ends_before": null,
      "split_from": null,
      "split_into": null,
      "metadata": {
        "your_plan_id": "plan_7"
      },
      "created_at": "2026-10-04T08:10:00Z",
      "updated_at": "2026-10-04T08:10:00Z"
    }
  ],
  "has_more": false
}
```

## Retrieve a series

`GET /v1/series/{id}`

Send the business’s id in `Tiempo-Account`.

### Path parameters

-   `id` (string, required)
    
    The object’s id.
    

GET /v1/series/{id}

```bash
curl "https://api.tiempo.one/v1/series/ser_01m4k2p9z7x3c5v1b8n4m6q2w0" \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…" \
  -H "Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw"
```

200Response

```json
{
  "id": "ser_01m4k2p9z7x3c5v1b8n4m6q2w0",
  "object": "series",
  "livemode": false,
  "status": "active",
  "rrule": "FREQ=WEEKLY;INTERVAL=2;BYDAY=TU",
  "start_date": "2026-11-17",
  "start_time": "10:00",
  "timezone": "Europe/London",
  "template": {
    "segments": [
      {
        "resource": "res_01m42ytc9b53589z4rp11zedjy",
        "offset_minutes": 0,
        "duration_minutes": 45,
        "quantity": 1,
        "location": null,
        "buffer_before_minutes": null,
        "buffer_after_minutes": null
      }
    ],
    "seats": null,
    "metadata": {}
  },
  "horizon_days": 90,
  "conflict_policy": "reject",
  "generated_until": "2027-01-02T08:10:00Z",
  "ends_before": null,
  "split_from": null,
  "split_into": null,
  "metadata": {
    "your_plan_id": "plan_7"
  },
  "created_at": "2026-10-04T08:10:00Z",
  "updated_at": "2026-10-04T08:10:00Z"
}
```

## Update a series

`POST /v1/series/{id}`

Edits a series in place from a date (default now): a new instructor, time, rule or template. Occurrences keep their booking ids and attendees where their date is kept; dates the new rule drops are cancelled. If any occurrence wouldn’t fit, nothing changes (409 series_update_conflict) unless on_conflict is "flag".

Send the business’s id in `Tiempo-Account`.

### Path parameters

-   `id` (string, required)
    
    The object’s id.
    

### Body parameters

-   `horizon_days` (integer)
    
    How far ahead occurrences are created. Defaults to 90, at most 730.
    
-   `metadata` (map)
    
    Up to 50 keys of your own (strings, keys up to 40 characters, values up to 500). Returned on every read and webhook. On update, an empty string removes a key.
    
-   `rrule` (string)
    
    An RFC 5545 rule, for example FREQ=WEEKLY;INTERVAL=2;BYDAY=TU.
    
-   `start_time` (string)
    
    Local start time, HH:mm.
    
-   `template` (object)
    
    What each occurrence books: segments with offsets and durations, seats and metadata.
    
    -   `segments` (array of objects, required)
        
        The time to hold: one entry per resource and period. Segments can be on different resources and needn’t touch.
        
        -   `resource` (string, required)
            
            A resource id.
            
        -   `offset_minutes` (integer)
            
            Minutes after the start time the segment begins. Defaults to 0.
            
        -   `duration_minutes` (integer, required)
            
            Length of the segment; or, on a search, one length for every segment when they all run together (then segments give no length or offset).
            
        -   `quantity` (integer)
            
            Units of capacity to take. Defaults to 1 (for blocks, the whole capacity).
            
        -   `location` (string)
            
            A location id.
            
        -   `buffer_before_minutes` (integer)
            
            Minutes the resource is also held before the time (travel, setup), 0–1440. On a booking segment it overrides the resource’s default; the appointment’s start and end don’t change.
            
        -   `buffer_after_minutes` (integer)
            
            Minutes the resource is also held after the time (cleanup, turnaround), 0–1440. On a booking segment it overrides the resource’s default. Buffers must fit in working hours and can’t overlap blocks or other bookings.
            
    -   `seats` (integer)
        
        Makes the booking a session that attendees take seats in.
        
    -   `metadata` (map)
        
        Up to 50 keys of your own (strings, keys up to 40 characters, values up to 500). Returned on every read and webhook. On update, an empty string removes a key.
        
-   `conflict_policy` (string)
    
    reject (default) refuses time that isn’t free or isn’t working hours. capacity_only ignores working hours, overrides and blocks (staff booking a client in after hours) but still never overlaps another booking. allow places it anyway (a deliberate overbook) and flags it.
    
-   `from` (timestamp)
    
    Start of the window.
    
-   `on_conflict` (string)
    
    reject (default) changes nothing if an occurrence wouldn’t fit; flag applies the edit and flags those occurrences.
    

POST /v1/series/{id}

```bash
curl -X POST "https://api.tiempo.one/v1/series/ser_01m4k2p9z7x3c5v1b8n4m6q2w0" \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…" \
  -H "Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw" \
  -H "Content-Type: application/json" \
  -d '{
    "template": {
      "segments": [
        {
          "resource": "res_bob",
          "duration_minutes": 45
        }
      ]
    },
    "from": "2026-12-01"
  }'
```

200Response

```json
{
  "id": "ser_01m4k2p9z7x3c5v1b8n4m6q2w0",
  "object": "series",
  "livemode": false,
  "status": "active",
  "rrule": "FREQ=WEEKLY;INTERVAL=2;BYDAY=TU",
  "start_date": "2026-11-17",
  "start_time": "10:00",
  "timezone": "Europe/London",
  "template": {
    "segments": [
      {
        "resource": "res_01m42ytc9b53589z4rp11zedjy",
        "offset_minutes": 0,
        "duration_minutes": 45,
        "quantity": 1,
        "location": null,
        "buffer_before_minutes": null,
        "buffer_after_minutes": null
      }
    ],
    "seats": null,
    "metadata": {}
  },
  "horizon_days": 90,
  "conflict_policy": "reject",
  "generated_until": "2027-01-02T08:10:00Z",
  "ends_before": null,
  "split_from": null,
  "split_into": null,
  "metadata": {
    "your_plan_id": "plan_7"
  },
  "created_at": "2026-10-04T08:10:00Z",
  "updated_at": "2026-10-04T08:10:00Z"
}
```

## Split a series

`POST /v1/series/{id}/split`

Ends the series before an occurrence and starts a new one from it with any changes, for a different timezone or other changes an in-place edit can’t make.

Send the business’s id in `Tiempo-Account`.

### Path parameters

-   `id` (string, required)
    
    The object’s id.
    

### Body parameters

-   `at` (timestamp, required)
    
    The start of the occurrence to split at.
    
-   `rrule` (string)
    
    An RFC 5545 rule, for example FREQ=WEEKLY;INTERVAL=2;BYDAY=TU.
    
-   `start_time` (string)
    
    Local start time, HH:mm.
    
-   `timezone` (string)
    
    IANA name, for example Europe/London.
    
-   `template` (object)
    
    What each occurrence books: segments with offsets and durations, seats and metadata.
    
    -   `segments` (array of objects, required)
        
        The time to hold: one entry per resource and period. Segments can be on different resources and needn’t touch.
        
        -   `resource` (string, required)
            
            A resource id.
            
        -   `offset_minutes` (integer)
            
            Minutes after the start time the segment begins. Defaults to 0.
            
        -   `duration_minutes` (integer, required)
            
            Length of the segment; or, on a search, one length for every segment when they all run together (then segments give no length or offset).
            
        -   `quantity` (integer)
            
            Units of capacity to take. Defaults to 1 (for blocks, the whole capacity).
            
        -   `location` (string)
            
            A location id.
            
        -   `buffer_before_minutes` (integer)
            
            Minutes the resource is also held before the time (travel, setup), 0–1440. On a booking segment it overrides the resource’s default; the appointment’s start and end don’t change.
            
        -   `buffer_after_minutes` (integer)
            
            Minutes the resource is also held after the time (cleanup, turnaround), 0–1440. On a booking segment it overrides the resource’s default. Buffers must fit in working hours and can’t overlap blocks or other bookings.
            
    -   `seats` (integer)
        
        Makes the booking a session that attendees take seats in.
        
    -   `metadata` (map)
        
        Up to 50 keys of your own (strings, keys up to 40 characters, values up to 500). Returned on every read and webhook. On update, an empty string removes a key.
        
-   `conflict_policy` (string)
    
    reject (default) refuses time that isn’t free or isn’t working hours. capacity_only ignores working hours, overrides and blocks (staff booking a client in after hours) but still never overlaps another booking. allow places it anyway (a deliberate overbook) and flags it.
    
-   `metadata` (map)
    
    Up to 50 keys of your own (strings, keys up to 40 characters, values up to 500). Returned on every read and webhook. On update, an empty string removes a key.
    

POST /v1/series/{id}/split

```bash
curl -X POST "https://api.tiempo.one/v1/series/ser_01m4k2p9z7x3c5v1b8n4m6q2w0/split" \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…" \
  -H "Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw" \
  -H "Content-Type: application/json" \
  -d '{
    "at": "2026-12-15T10:00:00Z",
    "start_time": "11:00"
  }'
```

200Response

```json
{
  "id": "ser_01m4k2p9z7x3c5v1b8n4m6q2w0",
  "object": "series",
  "livemode": false,
  "status": "active",
  "rrule": "FREQ=WEEKLY;INTERVAL=2;BYDAY=TU",
  "start_date": "2026-11-17",
  "start_time": "10:00",
  "timezone": "Europe/London",
  "template": {
    "segments": [
      {
        "resource": "res_01m42ytc9b53589z4rp11zedjy",
        "offset_minutes": 0,
        "duration_minutes": 45,
        "quantity": 1,
        "location": null,
        "buffer_before_minutes": null,
        "buffer_after_minutes": null
      }
    ],
    "seats": null,
    "metadata": {}
  },
  "horizon_days": 90,
  "conflict_policy": "reject",
  "generated_until": "2027-01-02T08:10:00Z",
  "ends_before": null,
  "split_from": null,
  "split_into": null,
  "metadata": {
    "your_plan_id": "plan_7"
  },
  "created_at": "2026-10-04T08:10:00Z",
  "updated_at": "2026-10-04T08:10:00Z"
}
```

## Cancel a series

`POST /v1/series/{id}/cancel`

Cancels occurrences from a date (default now) and ends the series there.

Send the business’s id in `Tiempo-Account`.

### Path parameters

-   `id` (string, required)
    
    The object’s id.
    

### Body parameters

-   `from` (timestamp)
    
    Start of the window.
    

POST /v1/series/{id}/cancel

```bash
curl -X POST "https://api.tiempo.one/v1/series/ser_01m4k2p9z7x3c5v1b8n4m6q2w0/cancel" \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…" \
  -H "Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-12-01"
  }'
```

200Response

```json
{
  "id": "ser_01m4k2p9z7x3c5v1b8n4m6q2w0",
  "object": "series",
  "livemode": false,
  "status": "active",
  "rrule": "FREQ=WEEKLY;INTERVAL=2;BYDAY=TU",
  "start_date": "2026-11-17",
  "start_time": "10:00",
  "timezone": "Europe/London",
  "template": {
    "segments": [
      {
        "resource": "res_01m42ytc9b53589z4rp11zedjy",
        "offset_minutes": 0,
        "duration_minutes": 45,
        "quantity": 1,
        "location": null,
        "buffer_before_minutes": null,
        "buffer_after_minutes": null
      }
    ],
    "seats": null,
    "metadata": {}
  },
  "horizon_days": 90,
  "conflict_policy": "reject",
  "generated_until": "2027-01-02T08:10:00Z",
  "ends_before": null,
  "split_from": null,
  "split_into": null,
  "metadata": {
    "your_plan_id": "plan_7"
  },
  "created_at": "2026-10-04T08:10:00Z",
  "updated_at": "2026-10-04T08:10:00Z"
}
```

## List skipped occurrences

`GET /v1/series/{id}/skipped_occurrences`

Dates the series couldn’t book, with the reasons.

Send the business’s id in `Tiempo-Account`.

### Path parameters

-   `id` (string, required)
    
    The object’s id.
    

GET /v1/series/{id}/skipped_occurrences

```bash
curl "https://api.tiempo.one/v1/series/ser_01m4k2p9z7x3c5v1b8n4m6q2w0/skipped_occurrences" \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…" \
  -H "Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw"
```

200Response

```json
{
  "object": "list",
  "data": [],
  "has_more": false
}
```

The machine-readable description is at [/openapi.json](https://tiempo.one/openapi.json). Try calls in [Swagger UI](https://tiempo.one/swagger-ui.html).[Tiempo home](https://tiempo.one/)[Dashboard](https://tiempo.one/app)
