---
title: "Team goals API reference"
canonical_url: https://zentrik.ai/docs/api/team-goals
markdown_url: https://zentrik.ai/docs/api/team-goals.md
last_reviewed: 2026-10-22
---

# Team goals API reference

Manage the team goals shown in Huddles with a workspace API key. These goals are separate from main OKRs; keyResultId optionally connects a goal to an OKR key result. All routes require teamId. Grant team-goals:read, team-goals:write or team-goals:delete for the operations needed. API keys can address teams throughout their workspace. GET responses use Cache-Control: private, no-store.

- Human reference: https://zentrik.ai/docs/api/team-goals
- Complete Markdown index: https://zentrik.ai/docs/api/index.md
- Base URL: `https://zentrik.ai/api`
- Authentication: `Authorization: Bearer YOUR_API_KEY`
- Shared pagination and rate limits: https://zentrik.ai/docs/api/index.md#shared-conventions

## POST /external/v1/team-goals/refresh-huddle — Refresh the Huddle after goal changes

Operation ID: `refresh-team-goal-huddle`

After a batch of goal changes, rebuild the saved Huddle for the chosen team/date so its Goals widget reflects those changes. This is the same full snapshot refresh used by Huddle: it also refreshes customer evidence, delivery, pending actions and comparison data, while preserving conversation sources and reviewed artifacts. An existing daily snapshot is replaced and refreshRevision increases; a missing one is created. Use the current Huddle date unless you intentionally want to rebuild historical state. Only active goals whose dates include this day and which are unassigned to a cycle or belong to the current cycle appear.

Required API key scopes: `team-goals:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `teamId` | UUID | Yes | Explicit team ID in the key’s workspace (query). Find IDs with GET /external/v1/teams and initiatives:read. |
| `date` | string | Yes | Huddle date as YYYY-MM-DD (body). Match the date selected in Huddle. |

### Example request

```curl
curl -X POST 'https://zentrik.ai/api/external/v1/team-goals/refresh-huddle?teamId=33333333-3333-4333-8333-333333333333' -H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' -d '{"date":"2026-10-21"}'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 201 | Refreshed | Reload Huddle to see the saved snapshot, or read GET /external/v1/huddles/:id with huddles:read or initiatives:read. | — |
| 400 | Invalid input | Invalid or unsupported fields, reversed dates, invalid milestone targets, owner outside the team or cycle outside the team. Correct the input before retrying. | — |
| 401 | Unauthorized | Missing, invalid, expired or disabled API key. | — |
| 403 | Missing scope | Grant the scope named for this endpoint. | — |
| 404 | Not found | Team or goal is outside the key workspace/team, or a linked key result is outside the workspace. | — |
| 429 | Rate limited | Wait for Retry-After before retrying. | — |

### Example response

```json
{
  "huddleId": "55555555-5555-4555-8555-555555555555",
  "teamId": "33333333-3333-4333-8333-333333333333",
  "date": "2026-10-21",
  "refreshRevision": 2
}
```

---

## GET /external/v1/team-goals — List team goals

Operation ID: `list-team-goals`

List this team’s goals, newest start date first, then newest creation time. Archived goals are excluded by default. Supplying date selects only active goals whose inclusive date range contains that date, matching Huddle eligibility. Returns an array with X-Total-Count, X-Limit, X-Offset and X-Has-More headers. Concurrent edits can shift offset pages.

Required API key scopes: `team-goals:read`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `teamId` | UUID | Yes | Explicit team ID in the key’s workspace (query). Find IDs with GET /external/v1/teams and initiatives:read. |
| `date` | string | No | YYYY-MM-DD; return only active goals for this date, even if includeArchived=true. |
| `includeArchived` | boolean | No | Literal true or false. Default false. |
| `limit` | integer | No | 1–100; default 20. |
| `offset` | integer | No | 0–1,000,000; default 0. |

### Example request

```curl
curl -X GET 'https://zentrik.ai/api/external/v1/team-goals?teamId=33333333-3333-4333-8333-333333333333' -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | The requested team-goal data or deletion confirmation. | — |
| 400 | Invalid input | Invalid or unsupported fields, reversed dates, invalid milestone targets, owner outside the team or cycle outside the team. Correct the input before retrying. | — |
| 401 | Unauthorized | Missing, invalid, expired or disabled API key. | — |
| 403 | Missing scope | Grant the scope named for this endpoint. | — |
| 404 | Not found | Team or goal is outside the key workspace/team, or a linked key result is outside the workspace. | — |
| 429 | Rate limited | Wait for Retry-After before retrying. | — |

### Example response

```json
[
  {
    "id": "44444444-4444-4444-8444-444444444444",
    "workspaceId": "22222222-2222-4222-8222-222222222222",
    "teamId": "33333333-3333-4333-8333-333333333333",
    "title": "Interview five customers",
    "description": "",
    "startDate": "2026-10-21",
    "endDate": "2026-10-27",
    "progress": 0,
    "status": "active",
    "ownerUserId": null,
    "cycleId": null,
    "keyResultId": null,
    "milestones": [],
    "analysisStatus": "queued",
    "suggestion": null,
    "createdAt": "2026-10-21T09:00:00.000Z",
    "updatedAt": "2026-10-21T09:00:00.000Z"
  }
]
```

---

## GET /external/v1/team-goals/cycles — List goal cycles

Operation ID: `list-team-goal-cycles`

Return the team’s available goal cycles and currentCycleId, or null when there is no current cycle. The cycles array places the current cycle first, followed by the remaining cycles in descending start-date order. Use the returned ID when assigning a goal to a cycle.

Required API key scopes: `team-goals:read`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `teamId` | UUID | Yes | Explicit team ID in the key’s workspace (query). Find IDs with GET /external/v1/teams and initiatives:read. |

### Example request

```curl
curl -X GET 'https://zentrik.ai/api/external/v1/team-goals/cycles?teamId=33333333-3333-4333-8333-333333333333' -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | The requested team-goal data or deletion confirmation. | — |
| 400 | Invalid input | Invalid or unsupported fields, reversed dates, invalid milestone targets, owner outside the team or cycle outside the team. Correct the input before retrying. | — |
| 401 | Unauthorized | Missing, invalid, expired or disabled API key. | — |
| 403 | Missing scope | Grant the scope named for this endpoint. | — |
| 404 | Not found | Team or goal is outside the key workspace/team, or a linked key result is outside the workspace. | — |
| 429 | Rate limited | Wait for Retry-After before retrying. | — |

### Example response

```json
{
  "currentCycleId": null,
  "cycles": []
}
```

---

## GET /external/v1/team-goals/:id — Get a team goal

Operation ID: `get-team-goal`

Read one goal, including its recorded progress, accepted milestones and analysisStatus. suggestion is advisory; creating or editing a goal can queue analysis, but suggestions do not silently replace its wording, milestones or progress. Joined user profiles are not returned.

Required API key scopes: `team-goals:read`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `teamId` | UUID | Yes | Explicit team ID in the key’s workspace (query). Find IDs with GET /external/v1/teams and initiatives:read. |
| `id` | UUID | Yes | Team goal ID (path), not an OKR or key-result ID. |

### Example request

```curl
curl -X GET 'https://zentrik.ai/api/external/v1/team-goals/44444444-4444-4444-8444-444444444444?teamId=33333333-3333-4333-8333-333333333333' -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | The requested team-goal data or deletion confirmation. | — |
| 400 | Invalid input | Invalid or unsupported fields, reversed dates, invalid milestone targets, owner outside the team or cycle outside the team. Correct the input before retrying. | — |
| 401 | Unauthorized | Missing, invalid, expired or disabled API key. | — |
| 403 | Missing scope | Grant the scope named for this endpoint. | — |
| 404 | Not found | Team or goal is outside the key workspace/team, or a linked key result is outside the workspace. | — |
| 429 | Rate limited | Wait for Retry-After before retrying. | — |

### Example response

```json
{
  "id": "44444444-4444-4444-8444-444444444444",
  "workspaceId": "22222222-2222-4222-8222-222222222222",
  "teamId": "33333333-3333-4333-8333-333333333333",
  "title": "Interview five customers",
  "description": "",
  "startDate": "2026-10-21",
  "endDate": "2026-10-27",
  "progress": 0,
  "status": "active",
  "ownerUserId": null,
  "cycleId": null,
  "keyResultId": null,
  "milestones": [],
  "analysisStatus": "queued",
  "suggestion": null,
  "createdAt": "2026-10-21T09:00:00.000Z",
  "updatedAt": "2026-10-21T09:00:00.000Z"
}
```

---

## POST /external/v1/team-goals — Create a team goal

Operation ID: `create-team-goal`

Create a team goal for the Goals widget. The workspace comes from the API key. Each successful POST creates a new goal and queues the existing goal review. Save the returned ID for later PATCH requests. Creation is not idempotent: after an uncertain timeout, list and reconcile goals before retrying. Call POST /external/v1/team-goals/refresh-huddle after the batch to update the widget. Creating the goal alone does not rewrite saved Huddle snapshots.

Required API key scopes: `team-goals:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `teamId` | UUID | Yes | Explicit team ID in the key’s workspace (query). Find IDs with GET /external/v1/teams and initiatives:read. |
| `title` | string | Yes | Nonblank title, at most 200 characters. |
| `startDate` | string | Yes | Inclusive start date, YYYY-MM-DD. |
| `endDate` | string | Yes | Inclusive end date, YYYY-MM-DD, on or after startDate. |
| `description` | string | No | At most 4,000 characters. Empty string clears it. |
| `progress` | integer | No | Recorded progress from 0 to 100. Defaults to 0; milestone completion does not recalculate it. |
| `status` | string | No | planned, active, completed or archived. Defaults to active. |
| `ownerUserId` | UUID or null | No | Use userId from GET /external/v1/teams/:teamId/members (teams:read scope). The owner must still belong to this team when saved. Null clears the owner. |
| `cycleId` | UUID or null | No | Cycle in the same team; find IDs with GET /external/v1/team-goals/cycles. A linked cycle determines the goal dates and overrides supplied dates. Null removes the link. |
| `keyResultId` | UUID or null | No | Optional key result in this workspace. Find IDs through GET /external/v1/okrs with okrs:read. Null removes the link. |
| `milestones` | array | No | Up to 50 entries: title (1–200 characters), targetProgress (integer 1–100), optional description (up to 1,000 characters or null), isCompleted (boolean), and existing id (UUID, for updates). Targets must be unique; supplied order determines display order. |

### Example request

```curl
curl -X POST 'https://zentrik.ai/api/external/v1/team-goals?teamId=33333333-3333-4333-8333-333333333333' -H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' -d '{"title":"Interview five customers","startDate":"2026-10-21","endDate":"2026-10-27"}'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 201 | Created | The saved team goal. Analysis may still be queued; read the goal to check its status. | — |
| 400 | Invalid input | Invalid or unsupported fields, reversed dates, invalid milestone targets, owner outside the team or cycle outside the team. Correct the input before retrying. | — |
| 401 | Unauthorized | Missing, invalid, expired or disabled API key. | — |
| 403 | Missing scope | Grant the scope named for this endpoint. | — |
| 404 | Not found | Team or goal is outside the key workspace/team, or a linked key result is outside the workspace. | — |
| 429 | Rate limited | Wait for Retry-After before retrying. | — |

### Example response

```json
{
  "id": "44444444-4444-4444-8444-444444444444",
  "workspaceId": "22222222-2222-4222-8222-222222222222",
  "teamId": "33333333-3333-4333-8333-333333333333",
  "title": "Interview five customers",
  "description": "",
  "startDate": "2026-10-21",
  "endDate": "2026-10-27",
  "progress": 0,
  "status": "active",
  "ownerUserId": null,
  "cycleId": null,
  "keyResultId": null,
  "milestones": [],
  "analysisStatus": "queued",
  "suggestion": null,
  "createdAt": "2026-10-21T09:00:00.000Z",
  "updatedAt": "2026-10-21T09:00:00.000Z"
}
```

---

## PATCH /external/v1/team-goals/:id — Update a team goal

Operation ID: `update-team-goal`

Update at least one goal field, then call POST /external/v1/team-goals/refresh-huddle to update the widget. Omitted fields remain unchanged. Only ownerUserId, cycleId and keyResultId accept null to clear their links. Supplying milestones replaces the entire list: preserve IDs for milestones you want to keep; [] removes all milestones. Milestone completion does not update recorded progress. Set status to completed or archived to retain the record without showing it as an active Huddle goal.

Required API key scopes: `team-goals:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `teamId` | UUID | Yes | Explicit team ID in the key’s workspace (query). Find IDs with GET /external/v1/teams and initiatives:read. |
| `id` | UUID | Yes | Team goal ID (path), not an OKR or key-result ID. |
| `title` | string | No | Nonblank title, at most 200 characters. |
| `startDate` | string | No | Inclusive start date, YYYY-MM-DD. |
| `endDate` | string | No | Inclusive end date, YYYY-MM-DD, on or after startDate. |
| `description` | string | No | At most 4,000 characters. Empty string clears it. |
| `progress` | integer | No | Recorded progress from 0 to 100. Defaults to 0; milestone completion does not recalculate it. |
| `status` | string | No | planned, active, completed or archived. Defaults to active. |
| `ownerUserId` | UUID or null | No | Use userId from GET /external/v1/teams/:teamId/members (teams:read scope). The owner must still belong to this team when saved. Null clears the owner. |
| `cycleId` | UUID or null | No | Cycle in the same team; find IDs with GET /external/v1/team-goals/cycles. A linked cycle determines the goal dates and overrides supplied dates. Null removes the link. |
| `keyResultId` | UUID or null | No | Optional key result in this workspace. Find IDs through GET /external/v1/okrs with okrs:read. Null removes the link. |
| `milestones` | array | No | Up to 50 entries: title (1–200 characters), targetProgress (integer 1–100), optional description (up to 1,000 characters or null), isCompleted (boolean), and existing id (UUID, for updates). Targets must be unique; supplied order determines display order. |

### Example request

```curl
curl -X PATCH 'https://zentrik.ai/api/external/v1/team-goals/44444444-4444-4444-8444-444444444444?teamId=33333333-3333-4333-8333-333333333333' -H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' -d '{"progress":60}'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | The requested team-goal data or deletion confirmation. | — |
| 400 | Invalid input | Invalid or unsupported fields, reversed dates, invalid milestone targets, owner outside the team or cycle outside the team. Correct the input before retrying. | — |
| 401 | Unauthorized | Missing, invalid, expired or disabled API key. | — |
| 403 | Missing scope | Grant the scope named for this endpoint. | — |
| 404 | Not found | Team or goal is outside the key workspace/team, or a linked key result is outside the workspace. | — |
| 429 | Rate limited | Wait for Retry-After before retrying. | — |

### Example response

```json
{
  "id": "44444444-4444-4444-8444-444444444444",
  "workspaceId": "22222222-2222-4222-8222-222222222222",
  "teamId": "33333333-3333-4333-8333-333333333333",
  "title": "Interview five customers",
  "description": "",
  "startDate": "2026-10-21",
  "endDate": "2026-10-27",
  "progress": 60,
  "status": "active",
  "ownerUserId": null,
  "cycleId": null,
  "keyResultId": null,
  "milestones": [],
  "analysisStatus": "queued",
  "suggestion": null,
  "createdAt": "2026-10-21T09:00:00.000Z",
  "updatedAt": "2026-10-21T09:00:00.000Z"
}
```

---

## DELETE /external/v1/team-goals/:id — Delete a team goal

Operation ID: `delete-team-goal`

Permanently delete a goal and its milestones. Prefer PATCH with status: archived when you need to retain it. A repeated deletion returns 404. Call POST /external/v1/team-goals/refresh-huddle to remove the goal from a saved current snapshot. Other saved snapshots are not rewritten.

Required API key scopes: `team-goals:delete`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `teamId` | UUID | Yes | Explicit team ID in the key’s workspace (query). Find IDs with GET /external/v1/teams and initiatives:read. |
| `id` | UUID | Yes | Team goal ID (path), not an OKR or key-result ID. |

### Example request

```curl
curl -X DELETE 'https://zentrik.ai/api/external/v1/team-goals/44444444-4444-4444-8444-444444444444?teamId=33333333-3333-4333-8333-333333333333' -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | The requested team-goal data or deletion confirmation. | — |
| 400 | Invalid input | Invalid or unsupported fields, reversed dates, invalid milestone targets, owner outside the team or cycle outside the team. Correct the input before retrying. | — |
| 401 | Unauthorized | Missing, invalid, expired or disabled API key. | — |
| 403 | Missing scope | Grant the scope named for this endpoint. | — |
| 404 | Not found | Team or goal is outside the key workspace/team, or a linked key result is outside the workspace. | — |
| 429 | Rate limited | Wait for Retry-After before retrying. | — |

### Example response

```json
{
  "id": "44444444-4444-4444-8444-444444444444"
}
```
