> For the complete documentation index, see [llms.txt](https://help.rhythms.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.rhythms.ai/connectivity/api-reference/rhythms-rest-api-v2.md).

# Rhythms REST API v2

REST API v2 is the Rhythms API for third-party integrations, reporting, automation and bulk changes. It offers predictable JSON endpoints under `https://api.rhythms.ai/rest/v2/` for Objectives, Key Results, Initiatives, Check-ins, Labels, Teams and Users. This article mirrors the published reference at [apidocs.rhythms.ai/rest/v2](https://apidocs.rhythms.ai/rest/v2), where you can also send test requests. To get a token, see [Get access to the Rhythms REST API](/connectivity/api-reference/get-access-to-the-rhythms-rest-api.md).

## Basics

**Base URL:** `https://api.rhythms.ai`. All REST API v2 endpoints are prefixed with `/rest/v2/`.

**Response format:** JSON. Successful responses wrap the payload in a `data` object. Error responses use standard HTTP status codes with a short message.

**Authentication:** every request needs a Bearer token in the `Authorization` header. The API runs each request with the permissions of the user the token belongs to.

{% code collapsedlinecount="10" %}

```
Authorization: Bearer ‹token›
```

{% endcode %}

## Rate limiting

Rhythms does not publish fixed per-token rate limits today. Keep request volume reasonable — page with `limit=100`, use `count=true` instead of walking every page to get a total, and cache what you can. The API reserves `429 Too Many Requests`; handle it with a back-off and retry. If you expect sustained high volume, tell `support@rhythms.ai` about the integration.

## Pagination and counts

List endpoints support pagination through query parameters.

| Parameter   | Type   | Default | Description                                                                                                            |
| ----------- | ------ | ------- | ---------------------------------------------------------------------------------------------------------------------- |
| `page`      | number | `1`     | Page number.                                                                                                           |
| `limit`     | number | `20`    | Records per page, maximum `100`.                                                                                       |
| `per_page`  | number | `20`    | Alias for `limit`.                                                                                                     |
| `limit_max` | number | —       | Maximum records per page; used as the page size when `limit` is not set.                                               |
| `count`     | string | —       | Set to `true` to include the total in `data.pagy.count`. Use this for totals instead of paginating through every page. |

Paginated responses include metadata in `data.pagy`; `next` is the next page number, or `null` when there are no more pages.

## Making requests

### A first request

{% code collapsedlinecount="10" %}

```
curl -H "Authorization: Bearer tokp_1abc23c45d..." \
     -H "Accept: application/json" \
     "https://api.rhythms.ai/rest/v2/objectives?limit=1"
```

{% endcode %}

List endpoints return `data.records` (an array) plus `data.pagy`:

{% code collapsedlinecount="10" %}

```
{
  "data": {
    "type": "Okrs::Objective",
    "records": [
      {
        "uuid": "...",
        "type": "Okrs::Objective",
        "short_id": "OBJ-001",
        "title": "Grow enterprise revenue",
        "current_status": "on_track",
        "start_date": "2026-07-01",
        "end_date": "2026-09-30",
        "owner_uuids": ["..."]
      }
    ],
    "pagy": { "next": 2 }
  }
}
```

{% endcode %}

Single-record endpoints return `data.record` instead of `data.records`. Record fields are abbreviated above; the live docs carry the full schema.

### Filtering

List endpoints accept a `q` parameter using bracket notation: the attribute name followed by a predicate.

| Predicate            | Meaning                                                                                 |
| -------------------- | --------------------------------------------------------------------------------------- |
| `_eq`                | exact match                                                                             |
| `_cont`              | contains (substring)                                                                    |
| `_in`                | value is in a list, e.g. `q[current_status_in][]=behind&q[current_status_in][]=at_risk` |
| `_gteq` / `_lteq`    | greater than or equal / less than or equal — useful on dates                            |
| `_present` / `_null` | attribute is set / attribute is null                                                    |

{% code collapsedlinecount="10" %}

```
# objectives that have not started
GET /rest/v2/objectives?q[current_status_eq]=not_started

# check-ins since 1 July 2026
GET /rest/v2/checkins?q[checkin_date_gteq]=2026-07-01

# users whose display name contains "Alice"
GET /rest/v2/users?q[display_name_cont]=Alice

# labels that are groups, sorted by name
GET /rest/v2/labels?q[is_group_eq]=true&q[s]=name+asc
```

{% endcode %}

Add `q[s]=‹attribute›+asc|desc` to sort. The attribute before the predicate must be filterable for that endpoint; each section below lists the filterable attributes.

### Paging

{% code collapsedlinecount="10" %}

```
GET /rest/v2/objectives?page=1&limit=1
```

{% endcode %}

`data.pagy.next` holds the next page number, or `null` on the last page. Add `count=true` to include a total in `data.pagy.count`.

### When authentication fails

A missing or invalid token returns `401`:

{% code collapsedlinecount="10" %}

```
{ "error": "Invalid Authorization header" }
```

{% endcode %}

A valid token whose user is not allowed to perform the action returns `403`.

### Going further

The live docs at [apidocs.rhythms.ai/rest/v2](https://apidocs.rhythms.ai/rest/v2) carry the full schema for every endpoint, let you send a real authenticated request from the browser, and let you load the API definition into an AI coding assistant such as Cursor.

## API sections

REST API v2 has 7 sections and 34 operations.

| Section     | Operations |
| ----------- | ---------- |
| Checkins    | 5          |
| Initiatives | 5          |
| Key Results | 5          |
| Labels      | 5          |
| Objectives  | 5          |
| Teams       | 5          |
| Users       | 4          |

## Checkins

Check-ins record progress updates, status, notes, values and labels for a goal.

| Method   | Path                     | Summary            |
| -------- | ------------------------ | ------------------ |
| `GET`    | `/rest/v2/checkins`      | List check-ins.    |
| `POST`   | `/rest/v2/checkins`      | Create a check-in. |
| `GET`    | `/rest/v2/checkins/{id}` | Get a check-in.    |
| `PUT`    | `/rest/v2/checkins/{id}` | Update a check-in. |
| `DELETE` | `/rest/v2/checkins/{id}` | Delete a check-in. |

### List check-ins

`GET /rest/v2/checkins` returns check-ins newest first and supports filtering and pagination.

**Filterable attributes:** `uuid`, `goal_uuid`, `creator_uuid`, `checkin_date`, `created_at`, `status`, `value`, `source_type`.

### Create a check-in

`POST /rest/v2/checkins` creates a check-in for a goal with an optional value, status and note. Fields are sent at the top level of the JSON body (no wrapper object).

| Field             | Type           | Required | Notes                                                                                                                                       |
| ----------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `goal_uuid`       | string         | yes      | UUID of the goal being checked in. (`checkinable_uuid` is accepted as a deprecated alias.)                                                  |
| `timezone_offset` | number         | yes      | Your offset from UTC in minutes, as JavaScript's `getTimezoneOffset()` reports it (negative east of UTC). Must be between `-840` and `720`. |
| `value`           | number         | no       | Check-in value. The goal must have a metric.                                                                                                |
| `status`          | string         | no       | Defaults to `not_started`; one of `not_started`, `in_progress`, `on_track`, `behind`, `at_risk`, `closed`, `postponed`.                     |
| `note`            | string         | no       | Check-in note.                                                                                                                              |
| `checkin_date`    | string         | no       | `YYYY-MM-DD`. Defaults to today in your timezone; cannot be in the future.                                                                  |
| `score`           | number         | no       | Score; only stored when `status` is `closed`.                                                                                               |
| `progress_mode`   | string         | no       | Sets the goal's progress mode: `manual`, `rollup` or `integration`.                                                                         |
| `labels`          | array\[string] | no       | Label names to apply to the check-in.                                                                                                       |

Checking in on a draft goal publishes it, provided the token's user is allowed to publish it.

### Get, update and delete a check-in

* `GET /rest/v2/checkins/{id}` retrieves a check-in by UUID.
* `PUT /rest/v2/checkins/{id}` updates `value`, `status`, `note`, `score` and `labels`. At least one must be provided. A score is kept only while the status is `closed`.
* `DELETE /rest/v2/checkins/{id}` deletes a check-in. If it was the goal's latest check-in, the goal's cached status and progress are recalculated.

## Goals: Objectives, Key Results and Initiatives

Objectives, Key Results and Initiatives share the same core goal fields and CRUD pattern. Each type has its own endpoint namespace.

### Endpoint namespaces

| Goal type   | List/create path       | Specific record path        |
| ----------- | ---------------------- | --------------------------- |
| Objectives  | `/rest/v2/objectives`  | `/rest/v2/objectives/{id}`  |
| Key Results | `/rest/v2/key_results` | `/rest/v2/key_results/{id}` |
| Initiatives | `/rest/v2/initiatives` | `/rest/v2/initiatives/{id}` |

`{id}` accepts either the goal's UUID or its short ID such as `OBJ-001` or `KR-014`. List endpoints return published goals only, ordered by title.

### Shared create fields

Used by `POST /rest/v2/objectives`, `POST /rest/v2/key_results` and `POST /rest/v2/initiatives`. Fields are sent at the top level of the JSON body.

| Field                 | Type           | Required | Notes                                                                                                                                                                                                                                    |
| --------------------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title`               | string         | yes      | Goal title.                                                                                                                                                                                                                              |
| `description`         | string         | no       | Goal description.                                                                                                                                                                                                                        |
| `time_period_uuid`    | string         | yes      | Time period UUID. Its dates define the allowed goal date range.                                                                                                                                                                          |
| `start_date`          | string         | no       | `YYYY-MM-DD`; must fall within the time period and be on or before `end_date`. Defaults to the period start.                                                                                                                             |
| `end_date`            | string         | no       | `YYYY-MM-DD`; must fall within the time period and be on or after `start_date`. Defaults to the period end.                                                                                                                              |
| `goal_type`           | string         | no       | Defaults to `aspirational`; one of `aspirational`, `committed`.                                                                                                                                                                          |
| `visibility`          | string         | no       | Defaults to `public`; one of `public`, `limited`.                                                                                                                                                                                        |
| `team_uuids`          | array\[string] | no       | Team UUIDs; default `[]`.                                                                                                                                                                                                                |
| `owner_uuids`         | array\[string] | no       | Owner UUIDs; default `[]`.                                                                                                                                                                                                               |
| `labels`              | array\[string] | no       | Label names; default `[]`. Unknown names are created when the token's user may create labels.                                                                                                                                            |
| `delegate_uuids`      | array\[string] | no       | Delegate UUIDs; default `[]`.                                                                                                                                                                                                            |
| `metric`              | object         | no       | Metric configuration.                                                                                                                                                                                                                    |
| `metric.name`         | string         | no       | Defaults to `Progress`.                                                                                                                                                                                                                  |
| `metric.start_value`  | number         | no       | Defaults to `0`.                                                                                                                                                                                                                         |
| `metric.target_value` | number         | no       | Defaults to `100`.                                                                                                                                                                                                                       |
| `metric.unit`         | string         | no       | One of `percentage`, `number`, `dollar`, `euro`, `pound`, `swiss_franc`. Defaults to `number` for Key Results and to `percentage` for Objectives and Initiatives — set it explicitly if you need a particular unit.                      |
| `metric.metric_type`  | string         | no       | Defaults to `reach`; one of `reach`, `stay_above`, `stay_below`, `stay_between`.                                                                                                                                                         |
| `parents`             | array\[object] | no       | Parent goal **objects**, not strings: `{"uuid": "‹parent UUID›", "type": "‹parent type›", "is_contributing": true}`. `is_contributing` is optional — omit it to use your workspace's OKR model default for this goal type. Default `[]`. |
| `visible_entities`    | array\[object] | no       | Who can see the goal when `visibility` is `limited`. Array of **objects**: `{"entity_type": "User" \| "Team", "entity_uuid": "<UUID>", "mode": "<visibility mode>"}`. Default `[]`.                                                      |

### Shared update fields

Used by `PUT /rest/v2/objectives/{id}`, `PUT /rest/v2/key_results/{id}` and `PUT /rest/v2/initiatives/{id}`. Update fields include `title`, `description`, `goal_type`, `visibility`, `current_status`, `start_date`, `end_date`, `time_period_uuid`, `team_uuids`, `owner_uuids`, `labels`, `delegate_uuids`, `progress_mode`, `metric`, `parents` and `visible_entities`. Only the fields you send are changed. Changing `time_period_uuid` resets omitted start and end dates to the new period's boundaries. When you resend `parents`, include every parent that should remain; a parent left out is unlinked, and a parent resent without `is_contributing` keeps its current contribution.

### Shared delete options

Used by `DELETE /rest/v2/objectives/{id}`, `DELETE /rest/v2/key_results/{id}` and `DELETE /rest/v2/initiatives/{id}`. Deleting through the API removes the goal permanently — unlike deleting in the Rhythms app, it does not go to Trash and cannot be restored. Check the goal's children and links first.

| Field                             | Type    | Default | Notes                                         |
| --------------------------------- | ------- | ------- | --------------------------------------------- |
| `including_immediate_objectives`  | boolean | `false` | Also delete immediate child objectives.       |
| `including_immediate_initiatives` | boolean | `false` | Also delete immediate child initiatives.      |
| `including_immediate_key_results` | boolean | `false` | Also delete immediate child key results.      |
| `delete_hierarchy`                | boolean | `false` | Delete the entire hierarchy beneath the goal. |

### Goal list filters

The Objective, Key Result and Initiative list endpoints support filtering on `uuid`, `type`, `current_status`, `last_checkin_date`, `time_period_uuid`, `creator_uuid` and `discarded_at`, plus associated filters for time periods, owners, labels, ancestors, teams, child links and delegates.

## Labels

Labels can be regular labels or label groups, and can be attached to OKRs and check-ins.

| Method   | Path                   | Summary                       |
| -------- | ---------------------- | ----------------------------- |
| `GET`    | `/rest/v2/labels`      | List labels and label groups. |
| `POST`   | `/rest/v2/labels`      | Create a label.               |
| `GET`    | `/rest/v2/labels/{id}` | Get a label.                  |
| `PUT`    | `/rest/v2/labels/{id}` | Update a label.               |
| `DELETE` | `/rest/v2/labels/{id}` | Delete a label.               |

`GET /rest/v2/labels` supports filtering on `uuid`, `name`, `parent_uuid` and `is_group`.

To create or update a label, send a `label` wrapper object. Create accepts `name` (required, unique in the workspace), `description`, `parent_uuid` (the parent must be a group), `is_group` and `color` (a hex code such as `#eb5757`). Update accepts `name`, `description`, `parent_uuid` and `color`; send a blank `parent_uuid` to move a label back to the root, and a blank `color` to clear it. Whether a label is a group cannot be changed after creation. Creating labels requires a Rhythms Admin token.

Deleting a label also deletes its child labels.

## Teams

Teams can have a parent team, owners, members, and an active or archived status.

| Method   | Path                  | Summary        |
| -------- | --------------------- | -------------- |
| `GET`    | `/rest/v2/teams`      | List teams.    |
| `POST`   | `/rest/v2/teams`      | Create a team. |
| `GET`    | `/rest/v2/teams/{id}` | Get a team.    |
| `PUT`    | `/rest/v2/teams/{id}` | Update a team. |
| `DELETE` | `/rest/v2/teams/{id}` | Delete a team. |

`GET /rest/v2/teams` supports filtering on `uuid`, `display_name`, `parent_uuid`, `status`, `is_org` and `depth`, plus associated team membership filters.

To create or update a team, send a `team` wrapper object.

* **Create** accepts `display_name` (required), `description`, `parent_uuid`, `owner_uuids` and `member_uuids`. A user listed in both arrays becomes a Team Owner.
* **Update** accepts the same fields plus `status`, one of `active` or `archived`. Status can only be set on update, not on create. Sending `owner_uuids` replaces the full set of Team Owners; sending `member_uuids` adds the listed members.

A team can be deleted only if it has no active sub-teams and no OKR relationships.

## Users

Users can be listed, created, retrieved and updated. REST API v2 has no user delete operation; deactivate a user by setting `status` to `closed`.

| Method | Path                  | Summary        |
| ------ | --------------------- | -------------- |
| `GET`  | `/rest/v2/users`      | List users.    |
| `POST` | `/rest/v2/users`      | Create a user. |
| `GET`  | `/rest/v2/users/{id}` | Get a user.    |
| `PUT`  | `/rest/v2/users/{id}` | Update a user. |

`GET /rest/v2/users` supports filtering on `uuid`, `display_name`, `status`, `manager_uuid` and `license_type`, plus associated filters for direct teams, team memberships, profile fields and manager fields.

To create or update a user, send a `user` wrapper object.

* **Create** requires `email` (unique in the workspace) and `display_name`. Optional: `license_type` (`standard`, `hris_only` or `guest`; default `standard`), `status` (`active` or `closed`; default `active`), `manager_uuid`, and `profile_attributes` with `first_name`, `last_name`, `job_title`, `timezone`, `locale` and `about`.
* **Update** accepts `display_name`, `avatar_url` (applied only when the user has no avatar yet), `status`, `manager_uuid`, `license_type` and `profile_attributes`.

A `manager_uuid` must refer to an existing user, otherwise the request returns `422`.

## Common response codes

| Code  | Meaning                                                                           |
| ----- | --------------------------------------------------------------------------------- |
| `200` | Success.                                                                          |
| `201` | Created.                                                                          |
| `400` | Bad Request — invalid parameters or malformed request.                            |
| `401` | Unauthorized — invalid or missing token, or the token's user is no longer active. |
| `403` | Forbidden — the token's user is not allowed to perform this action in Rhythms.    |
| `404` | Not Found — resource does not exist.                                              |
| `422` | Unprocessable Entity — validation error; the body explains which rule failed.     |
| `429` | Too Many Requests — back off and retry.                                           |
| `500` | Internal Server Error — unexpected server error.                                  |

## Schemas

REST API v2 references 10 response schemas: `label`, `okrs_checkin`, `okrs_initiative`, `okrs_key_result`, `okrs_objective`, `rest_v2_goal_collection_response`, `rest_v2_goal_response`, `rest_v2_user_collection_response`, `rest_v2_user_response` and `team`.

Goal schemas for objectives, key results and initiatives share a common shape with fields for UUID, type, short ID, title, description, dates, goal type, time period, current metric (target, unit and metric type), owners, visible parent links with contribution percentages, creator, status, progress, visibility, data sources and discarded metadata.

Collection response schemas wrap records in `data.records` and can include `data.pagy`, `data.checks` and `data.filters`. Single-record response schemas wrap the record in `data.record` and can include `data.checks` and `data.access_source`.

## Related articles

* [Get access to the Rhythms REST API](/connectivity/api-reference/get-access-to-the-rhythms-rest-api.md)
* [Use Rhythms from Claude, ChatGPT or Cursor](/connectivity/use-rhythms-from-claude-chatgpt-or-cursor.md)
* [Rhythms OKR Roles and Permissions](/workspace-settings-and-administration/team-users-and-access/okr-roles-and-permissions.md)
* [How to Set Up Auto-Updates](/connectivity/connectors/set-up-automatic-updates-from-a-connector.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.rhythms.ai/connectivity/api-reference/rhythms-rest-api-v2.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
