Rhythms REST API v2
Last updated: September 4, 2026
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, where you can also send test requests. To get a token, see Get access to the Rhythms REST API.
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.
Authorization: Bearer <token>
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
curl -H "Authorization: Bearer tokp_1abc23c45d..." \
-H "Accept: application/json" \
"https://api.rhythms.ai/rest/v2/objectives?limit=1"
List endpoints return data.records (an array) plus data.pagy:
{
"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 }
}
}
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 |
# 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
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
GET /rest/v2/objectives?page=1&limit=1
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:
{ "error": "Invalid Authorization header" }
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 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}updatesvalue,status,note,scoreandlabels. At least one must be provided. A score is kept only while the status isclosed.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_uuidsandmember_uuids. A user listed in both arrays becomes a Team Owner. - Update accepts the same fields plus
status, one ofactiveorarchived. Status can only be set on update, not on create. Sendingowner_uuidsreplaces the full set of Team Owners; sendingmember_uuidsadds 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) anddisplay_name. Optional:license_type(standard,hris_onlyorguest; defaultstandard),status(activeorclosed; defaultactive),manager_uuid, andprofile_attributeswithfirst_name,last_name,job_title,timezone,localeandabout. - Update accepts
display_name,avatar_url(applied only when the user has no avatar yet),status,manager_uuid,license_typeandprofile_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.