Goals
A goal is a target for one metric over a period, such as "12 weeks with a clean pipeline by 31 December", owned by a person and usually a team. Harmony reads the metric from Harmony itself, from a Loop that reports it, or from people's check-ins, and works out how far along the goal is and whether it is keeping pace. Work counts toward a goal when it is linked to it.
Anyone's key can read active goals. Creating, editing, checking in, linking and archiving need an admin's key: one created by an organization owner or admin.
The Goal object
{
"id": "goal_pipeline",
"object": "goal",
"title": "A clean pipeline every Monday",
"description": "Every open opportunity has an owner, a next step and a close date by Monday morning.",
"owner": "usr_sam",
"team": "team_gtm",
"metric": { "label": "Weeks with a clean pipeline", "unit": null, "direction": "increase" },
"target": 12,
"baseline": 0,
"period_start": 1759276800,
"period_end": 1767139200,
"source": { "kind": "connector", "metric": null, "loop": "loop_pipeline_hygiene", "outcome_label": "Clean pipeline" },
"status": "active",
"progress": 0.17,
"on_track": false,
"latest": { "day": "2026-10-26", "value": 2, "source": "connector" },
"links": [
{ "kind": "loop", "id": "loop_pipeline_hygiene", "name": "Pipeline hygiene", "linked": 1760918400 }
],
"blocked_by": [],
"suggestion": null,
"confirmed": 1759320000
}
| Field | Type | Description |
|---|---|---|
id |
string | The goal's ID. |
object |
string | Always goal. |
title, description |
string | What the goal is, in its owner's words. description may be null. |
owner |
string or null | The user ID of the person who owns it. |
team |
string or null | The team it belongs to. |
metric |
object | label, unit (or null) and direction: increase when the goal is met by the metric rising, decrease when by it falling. |
target, baseline |
number | Where the metric should get to, and where it started (baseline may be null). |
period_start, period_end |
integer | The goal's period, in Unix seconds. |
source |
object | Where readings come from. kind is harmony (read daily from Harmony, metric names which), connector (reported by a Loop's runs: loop and outcome_label) or manual (check-ins only). |
status |
string | suggested, active or archived. |
progress |
number or null | How far from baseline to target, 0–1. null until there is a reading. |
on_track |
boolean or null | Whether progress keeps pace with the time gone in the period. null until there is a reading. |
latest |
object or null | The most recent reading: day, value and source. |
links |
array | The work linked to it: kind (loop, workstream, install or goal), id, name and when it was linked. A goal link means this goal is blocked by that one. |
blocked_by |
string[] | IDs of the goals this one waits for. |
suggestion |
object or null | While it is a suggestion: why Harmony suggested it and what it read (from). |
confirmed |
integer or null | When an admin confirmed it, in Unix seconds. |
The Harmony metrics a goal can read are tasks_closed, loop_runs_completed, accepted_outcomes, reports_delivered and hours_returned.
List goals
Returns active goals, newest first. An admin's key can pass status=all to include archived ones.
GET /v1/goals
Scope: goals:read
| Parameter | In | Type | Description |
|---|---|---|---|
limit |
query | integer, optional | 1–100. Default 20. |
cursor |
query | string, optional | The next_cursor from the previous page. See Pagination. |
status |
query | string, optional | active (default) or all (admins' keys). |
curl https://api.gonimbus.ai/v1/goals \
-H "Authorization: Bearer $HARMONY_API_KEY"
Retrieve a goal
GET /v1/goals/{goal}
Scope: goals:read. Returns a Goal.
Create a goal
Creates a goal that is active at once. Needs an admin's key. Send an Idempotency-Key header to retry safely; see Idempotent requests.
POST /v1/goals
Scope: goals:write
| Parameter | Type | Description |
|---|---|---|
title |
string | Up to 140 characters. |
metric |
object | label (required), unit (optional, up to 20 characters), direction (increase, the default, or decrease). |
target |
number | Where the metric should get to. |
period_start, period_end |
string | YYYY-MM-DD or an ISO 8601 timestamp. |
description |
string, optional | Up to 1,000 characters. |
owner |
string, optional | The owner's user ID. |
team |
string, optional | A team ID. |
baseline |
number, optional | Where the metric starts. |
source |
object, optional | { "kind": "manual" } (default), { "kind": "harmony", "metric": "tasks_closed" } or { "kind": "connector", "loop": "<loop id>", "outcome_label": "<label>" }. |
::: code-group
curl https://api.gonimbus.ai/v1/goals \
-H "Authorization: Bearer $HARMONY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "Close 40 support tasks a week",
"team": "team_support",
"metric": { "label": "Tasks closed", "direction": "increase" },
"target": 40,
"period_start": "2026-11-01",
"period_end": "2026-11-30",
"source": { "kind": "harmony", "metric": "tasks_closed" }
}'
const goal = await fetch('https://api.gonimbus.ai/v1/goals', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HARMONY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
title: 'Close 40 support tasks a week',
team: 'team_support',
metric: { label: 'Tasks closed', direction: 'increase' },
target: 40,
period_start: '2026-11-01',
period_end: '2026-11-30',
source: { kind: 'harmony', metric: 'tasks_closed' },
}),
}).then((r) => r.json())
:::
Returns the new Goal with status 201.
Update a goal
Changes only the fields you send. Takes the same fields as creating one. Needs an admin's key.
PATCH /v1/goals/{goal}
Scope: goals:write
Check in on a goal
Records a reading of the goal's metric for a day: today in UTC when day is left out. A second check-in for the same day replaces the first. Needs an admin's key.
POST /v1/goals/{goal}/check_ins
Scope: goals:write
| Parameter | Type | Description |
|---|---|---|
value |
number | The reading. |
day |
string, optional | YYYY-MM-DD. |
note |
string, optional | Context people see beside the reading. |
curl https://api.gonimbus.ai/v1/goals/goal_pipeline/check_ins \
-H "Authorization: Bearer $HARMONY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "value": 3, "note": "Two stale deals closed out on Friday." }'
Returns the updated Goal with status 201.
List a goal's check-ins
Returns the goal's readings, newest first: those Harmony took and those people checked in. Each has goal, day, value, source (harmony, connector or manual), note and recorded_by.
GET /v1/goals/{goal}/check_ins
Scope: goals:read. Takes limit and cursor.
Link work to a goal
Links a Loop, a workstream, a Marketplace install or another goal. Linking another goal (kind: "goal") means this goal is blocked by that one; two goals can't block each other. Needs an admin's key.
POST /v1/goals/{goal}/links
Scope: goals:write
| Parameter | Type | Description |
|---|---|---|
kind |
string | loop, workstream, install or goal. |
id |
string | The ID of what you're linking. |
To remove a link:
DELETE /v1/goals/{goal}/links/{kind}/{id}
Both return the updated Goal.
See what blocks a goal
Returns the goals this one waits for (blocked_by) and the active goals that wait for it (blocking), as full Goal objects.
GET /v1/goals/{goal}/dependencies
Scope: goals:read
Archive a goal
Archives the goal. Needs an admin's key.
POST /v1/goals/{goal}/archive
Scope: goals:write
Suggested goals
Harmony suggests goals from what it knows about your teams' work. A suggestion has status: "suggested" and a suggestion explaining why, and waits for an admin.
| Endpoint | Scope | What it does |
|---|---|---|
GET /v1/goal_suggestions |
goals:read |
Lists suggestions. Empty for a key that isn't an admin's. Takes limit and cursor. |
POST /v1/goal_suggestions/{goal}/confirm |
goals:write |
Makes the suggestion a live goal. Send any of the create fields to change it as you confirm. |
POST /v1/goal_suggestions/{goal}/dismiss |
goals:write |
Dismisses it. |
Errors
| Status | Code | When |
|---|---|---|
400 |
parameter_invalid |
A field is missing or invalid, such as an unknown Harmony metric, or a link that would make two goals block each other. |
403 |
insufficient_scope |
The key lacks the scope, or a write was made with a key that isn't an admin's. |
404 |
resource_missing |
No goal, Loop, workstream or install with that ID. |
FAQs
Who can see a goal? Every key in the organization can read active goals. Archived goals and suggestions are for admins' keys.
How often does a Harmony-sourced goal update? Harmony takes a reading once a day. Connector goals update when their Loop runs; manual goals when someone checks in.
Where do people see goals in the app? On the Outcomes page. See Outcomes and goals.