Objectives

List, read, create, update and delete the Objectives that measure each Goal.

An Objective measures progress toward a Goal. It usually sits directly under a Goal (parentGoalId) or under another Objective as a sub-Objective (parentObjectiveId). To record a new value, use a check-in.

Method Path Scope Webhook
GET /objectives objectives:read
GET /goals/{goalId}/objectives objectives:read
GET /objectives/{id} objectives:read
POST /objectives objectives:write objective.created
PATCH /objectives/{id} objectives:write objective.updated
DELETE /objectives/{id} objectives:write objective.deleted

Related: GET /objectives/{objectiveId}/actions, check-ins and Objective comments.

The JavaScript examples assume a headers object holding your key:

JavaScript
const headers = {
  'X-API-Key': process.env.GPS_API_KEY_ID,
  'X-API-Secret': process.env.GPS_API_SECRET,
};

The Objective object#

Field Type Notes
id string Objective ID.
title string
description string or null
type string KPI, OKR or ROCK.
measurementType string How the value is measured: $, %, Number, Status (On Track / Off Track / Complete), or the name of a measurement type your organization defined.
aggregationType string or null How child values combine (cumulative or average), when set.
progress string Progress percentage as a decimal string. Can exceed 100 when a target is beaten.
status string or null not_started, started, on_track, off_track or completed. null means GPS works the status out from progress and dates.
startDate string or null ISO 8601 timestamp.
dueDate string or null ISO 8601 timestamp.
completedDate string or null ISO 8601 timestamp.
targetValue number or null The value that means 100%.
currentValue number or null The latest recorded value.
unit string or null Unit label, for example customers.
assignedTo string[] Member IDs of the people responsible.
isPrivate boolean Private in GPS. API keys still see private Objectives.
invitedUsers string[] Member IDs who can see the Objective when it's private.
parentGoalId string or null The Goal it sits under.
parentObjectiveId string or null The Objective it sits under, for a sub-Objective.
checkinFrequency string or null weekly, monthly or quarterly.
checkinDay integer or null Day of the week (1–7) for weekly check-ins, or day of the month (1–31) for monthly and quarterly ones.
trackingPeriod string total, weekly, monthly or yearly: whether progress is measured over the whole Objective or resets each period.
lastResetDate string or null When progress last reset for a periodic trackingPeriod.
lastPeriodProgress string or null The progress the last finished period ended on.
archived boolean Archived Objectives are hidden from most endpoints, so this is usually false.
priority string or null High, Medium or Low.
headerImageUrl string or null Display setting in GPS.
headerColor string or null Display setting in GPS.
calculationMode string manual (progress is recorded) or auto (progress is calculated from sub-Objectives).
isRollupLocked boolean true when progress is read-only because it's calculated from sub-Objectives.
aggregatedProgress string or null Progress rolled up from sub-Objectives.
integrationFormula object or null Settings for updating this Objective from a connected integration. Read-only through the API.
lastIntegrationSync string or null When a connected integration last updated it.
integrationSyncStatus string or null active, paused or error.
autoSyncEnabled boolean Whether a connected integration updates it automatically.
sortOrder integer Position within its Goal.
weight string or null Share of the Goal's progress (0–100). null means an equal share.
createdBy string or null Member ID of the creator.
organizationId string
sourceMeetingId string or null The Meeting it was created in, if any.
createdAt string ISO 8601 timestamp.
updatedAt string ISO 8601 timestamp.

List Objectives#

GET /objectives

Returns the organization's Objectives that aren't archived, in GPS order (by sortOrder, then oldest first).

Scope: objectives:read

Query parameters

Parameter Type Notes
status string One of not_started, started, on_track, off_track, completed. at_risk and missed mean off_track; active applies no filter; archived returns 400.
goalId string Only Objectives directly under this Goal.
limit integer 1–200, default 50.
offset integer Default 0.
curl "https://plan.empiraa.app/api/v1/objectives?goalId=a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e&status=off_track" \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"

Response 200

JSON
{
  "data": [
    {
      "id": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
      "title": "Reach $120k monthly recurring revenue",
      "description": null,
      "type": "KPI",
      "measurementType": "$",
      "aggregationType": null,
      "progress": "45",
      "status": "off_track",
      "startDate": "2026-07-01T00:00:00.000Z",
      "dueDate": "2027-06-30T00:00:00.000Z",
      "completedDate": null,
      "targetValue": 120000,
      "currentValue": 54000,
      "unit": null,
      "assignedTo": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
      "isPrivate": false,
      "invitedUsers": [],
      "parentGoalId": "a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e",
      "parentObjectiveId": null,
      "checkinFrequency": "monthly",
      "checkinDay": 1,
      "trackingPeriod": "total",
      "lastResetDate": null,
      "lastPeriodProgress": null,
      "archived": false,
      "priority": "High",
      "headerImageUrl": null,
      "headerColor": "#1e293b",
      "calculationMode": "manual",
      "isRollupLocked": false,
      "aggregatedProgress": null,
      "integrationFormula": null,
      "lastIntegrationSync": null,
      "integrationSyncStatus": null,
      "autoSyncEnabled": false,
      "sortOrder": 0,
      "weight": null,
      "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
      "organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
      "sourceMeetingId": null,
      "createdAt": "2026-06-24T01:10:00.000Z",
      "updatedAt": "2026-11-01T00:00:00.000Z"
    }
  ],
  "meta": { "total": 1, "limit": 50, "offset": 0 }
}

Errors: 400 INVALID_STATUS_QUERY for an unknown status; 422 VALIDATION_ERROR for a bad limit or offset.

List a Goal's Objectives#

GET /goals/{goalId}/objectives

Returns the Objectives directly under a Goal (not their sub-Objectives). Unlike GET /objectives, this list can include archived Objectives, so check archived. Order isn't guaranteed.

Scope: objectives:read

Parameter In Notes
goalId path Goal ID.
status query Same values as List Objectives.
limit, offset query See Pagination.
curl https://plan.empiraa.app/api/v1/goals/a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e/objectives \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"

Response 200: a list of Objective objects with meta, as in List Objectives.

Errors: 404 NOT_FOUND if the Goal doesn't exist, is in another organization or is archived; 400 INVALID_STATUS_QUERY; 422 VALIDATION_ERROR for a bad limit or offset.

Get an Objective#

GET /objectives/{id}

Scope: objectives:read

Path parameter Notes
id Objective ID.
curl https://plan.empiraa.app/api/v1/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"

Response 200: { "data": <Objective> }, with the same fields as the item in List Objectives.

Errors: 404 NOT_FOUND if the Objective doesn't exist, is in another organization, or is archived.

Create an Objective#

POST /objectives

Neither parent is required, but an Objective can't have both a parentGoalId and a parentObjectiveId.

progress isn't calculated from currentValue when you create an Objective. To set a value and have GPS work out progress from the target, create the Objective, then record a check-in.

Scope: objectives:write · Webhook: objective.created, with the new Objective as data.

Request body

Field Type Required Allowed values Notes
title string Yes At least 1 character after trimming
description string or null No
parentGoalId string or null No A Goal in your organization Not with parentObjectiveId.
parentObjectiveId string or null No An Objective in your organization Not with parentGoalId.
type string No KPI, OKR, ROCK Default OKR.
measurementType string No $, %, Number, Status (On Track / Off Track / Complete), or one of your organization's measurement types Default Number. Common words are recognized: currency, usd, money become $; percent, percentage become %; count becomes Number; status, binary, boolean become the status type.
status string No not_started, started, on_track, off_track, completed Default not_started.
progress number or string No 0–1000 Percentage. Default 0.
priority string or null No High, Medium, Low
targetValue number or null No Greater than 0
currentValue number, string or null No A string has $, ,, % and spaces removed before it's read as a number.
unit string or null No For example customers.
startDate string or null No ISO 8601
dueDate string or null No ISO 8601 On or after startDate.
completedDate string or null No ISO 8601
assignedTo string[] No Active member IDs Default [].
isPrivate boolean No Default false.
invitedUsers string[] No Member IDs Default [].
checkinFrequency string or null No weekly, monthly, quarterly
checkinDay integer or null No 1–31 See checkinDay in the Objective object.
trackingPeriod string No total, weekly, monthly, yearly Default total.
onBehalfOf string No Active member ID Who the Objective is created by. See Attribution.
curl -X POST https://plan.empiraa.app/api/v1/objectives \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Reach $120k monthly recurring revenue",
    "parentGoalId": "a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e",
    "type": "KPI",
    "measurementType": "currency",
    "targetValue": 120000,
    "checkinFrequency": "monthly",
    "checkinDay": 1,
    "assignedTo": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"]
  }'

Response 201

JSON
{
  "data": {
    "id": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
    "title": "Reach $120k monthly recurring revenue",
    "description": null,
    "type": "KPI",
    "measurementType": "$",
    "aggregationType": null,
    "progress": "0",
    "status": "not_started",
    "startDate": null,
    "dueDate": null,
    "completedDate": null,
    "targetValue": 120000,
    "currentValue": null,
    "unit": null,
    "assignedTo": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
    "isPrivate": false,
    "invitedUsers": [],
    "parentGoalId": "a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e",
    "parentObjectiveId": null,
    "checkinFrequency": "monthly",
    "checkinDay": 1,
    "trackingPeriod": "total",
    "lastResetDate": null,
    "lastPeriodProgress": null,
    "archived": false,
    "priority": null,
    "headerImageUrl": null,
    "headerColor": "#1e293b",
    "calculationMode": "manual",
    "isRollupLocked": false,
    "aggregatedProgress": null,
    "integrationFormula": null,
    "lastIntegrationSync": null,
    "integrationSyncStatus": null,
    "autoSyncEnabled": false,
    "sortOrder": 0,
    "weight": null,
    "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
    "organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
    "sourceMeetingId": null,
    "createdAt": "2026-11-12T03:16:05.300Z",
    "updatedAt": "2026-11-12T03:16:05.300Z"
  }
}

Errors

Status code When
422 VALIDATION_ERROR Missing title; both parents sent; targetValue not above 0; checkinDay outside 1–31; a value outside the allowed list; dueDate before startDate.
422 INVALID_REFERENCE parentGoalId or parentObjectiveId isn't in your organization (or is archived).
422 INVALID_USERS An assignedTo or onBehalfOf ID isn't an active member.
422 NO_KEY_OWNER No onBehalfOf, and the key's creator is no longer a member.

Update an Objective#

PATCH /objectives/{id}

Changes only the fields you send. A body with no recognized fields changes nothing, returns the Objective as it is, and fires no webhook.

Changing currentValue here doesn't recalculate progress. To record a new value with its progress, use a check-in.

Scope: objectives:write · Webhook: objective.updated, with the updated Objective as data.

Path parameter Notes
id Objective ID.

Request body: any field from Create an Objective except onBehalfOf, with the same rules. Also:

  • Set parentGoalId or parentObjectiveId to null to detach the Objective from that parent.
  • assignedTo and invitedUsers replace the whole list.
  • measurementType is stored exactly as you send it on update; the common words listed under Create aren't converted.
curl -X PATCH https://plan.empiraa.app/api/v1/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "status": "on_track", "dueDate": "2027-03-31" }'

Response 200: { "data": <Objective> } with the changes applied, for example "status": "on_track" and "dueDate": "2027-03-31T00:00:00.000Z". Every other field is as in List Objectives.

Errors: 404 NOT_FOUND if the Objective doesn't exist, is in another organization or is archived; otherwise the same 422 errors as Create an Objective.

Delete an Objective#

DELETE /objectives/{id}

Permanently deletes the Objective and everything under it: its sub-Objectives, its Actions, its check-in history and its comments.

Scope: objectives:write · Webhook: objective.deleted, with data set to { "id": "<objective id>" }. Sub-Objectives and Actions removed with it don't fire their own events.

Path parameter Notes
id Objective ID.
curl -X DELETE https://plan.empiraa.app/api/v1/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"

Response 204: no body.

Errors: 404 NOT_FOUND if the Objective doesn't exist, is in another organization, or is archived.