Goals

List, read, create, update and delete the Goals at the top of your organization's strategy.

A Goal is a top-level outcome in your organization's strategy. Objectives measure it and Actions move it forward.

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

Related: GET /goals/{goalId}/objectives, GET /goals/{goalId}/actions and Goal 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 Goal object#

Field Type Notes
id string Goal ID.
title string
description string or null
type string KPI, OKR or ROCK.
progress string Progress percentage as a decimal string, for example "45". Can exceed 100.
status string or null not_started, started, on_track, off_track or completed.
startDate string or null ISO 8601 timestamp.
dueDate string or null ISO 8601 timestamp.
completedDate string or null ISO 8601 timestamp.
assignedTo string[] Member IDs of the people responsible.
isPrivate boolean Private in GPS. API keys still see private Goals.
invitedUsers string[] Member IDs who can see the Goal when it's private.
archived boolean Archived Goals aren't returned by this API, so this is false in responses.
priority string or null High, Medium or Low.
color string or null Accent color as a hex value, for example "#3b82f6".
sortOrder integer Position in the organization's Goal list.
createdBy string or null Member ID of the creator.
organizationId string
sourceMeetingId string or null The Meeting the Goal was created in, if any.
createdAt string ISO 8601 timestamp.
updatedAt string ISO 8601 timestamp.

List Goals#

GET /goals

Returns the organization's Goals that aren't archived, completed ones included, in the order they appear in GPS (by sortOrder, then oldest first).

Scope: goals:read

Query parameters

Parameter Type Notes
status string One of not_started, started, on_track, off_track, completed. Older values are still accepted: at_risk and missed mean off_track, and active applies no filter. archived returns 400.
limit integer 1–200, default 50. See Pagination.
offset integer Default 0.
curl "https://plan.empiraa.app/api/v1/goals?status=on_track" \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"

Response 200

JSON
{
  "data": [
    {
      "id": "a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e",
      "title": "Grow recurring revenue",
      "description": "Lift monthly recurring revenue across every service line.",
      "type": "OKR",
      "progress": "45",
      "status": "on_track",
      "startDate": "2026-07-01T00:00:00.000Z",
      "dueDate": "2027-06-30T00:00:00.000Z",
      "completedDate": null,
      "assignedTo": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
      "isPrivate": false,
      "invitedUsers": [],
      "archived": false,
      "priority": "High",
      "color": "#3b82f6",
      "sortOrder": 0,
      "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
      "organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
      "sourceMeetingId": null,
      "createdAt": "2026-06-24T01:02:03.456Z",
      "updatedAt": "2026-10-30T22:45:10.120Z"
    }
  ],
  "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.

Get a Goal#

GET /goals/{id}

Scope: goals:read

Path parameter Notes
id Goal ID.
curl https://plan.empiraa.app/api/v1/goals/a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"

Response 200

JSON
{
  "data": {
    "id": "a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e",
    "title": "Grow recurring revenue",
    "description": "Lift monthly recurring revenue across every service line.",
    "type": "OKR",
    "progress": "45",
    "status": "on_track",
    "startDate": "2026-07-01T00:00:00.000Z",
    "dueDate": "2027-06-30T00:00:00.000Z",
    "completedDate": null,
    "assignedTo": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
    "isPrivate": false,
    "invitedUsers": [],
    "archived": false,
    "priority": "High",
    "color": "#3b82f6",
    "sortOrder": 0,
    "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
    "organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
    "sourceMeetingId": null,
    "createdAt": "2026-06-24T01:02:03.456Z",
    "updatedAt": "2026-10-30T22:45:10.120Z"
  }
}

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

Create a Goal#

POST /goals

Scope: goals:write · Webhook: goal.created, with the new Goal as data.

Request body

Field Type Required Allowed values Notes
title string Yes At least 1 character after trimming
description string or null No
type string No KPI, OKR, ROCK Default OKR. Can't be changed later through the API.
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
startDate string or null No ISO 8601 date or date-time
dueDate string or null No ISO 8601 date or date-time On or after startDate.
completedDate string or null No ISO 8601 date or date-time
assignedTo string[] No Active member IDs Default [].
isPrivate boolean No Default false.
invitedUsers string[] No Member IDs Default [].
onBehalfOf string No Active member ID Who the Goal is created by. Default: the key's creator. See Attribution.
curl -X POST https://plan.empiraa.app/api/v1/goals \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Grow recurring revenue",
    "type": "OKR",
    "priority": "High",
    "startDate": "2026-07-01",
    "dueDate": "2027-06-30",
    "assignedTo": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"]
  }'

Response 201

JSON
{
  "data": {
    "id": "a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e",
    "title": "Grow recurring revenue",
    "description": null,
    "type": "OKR",
    "progress": "0",
    "status": "not_started",
    "startDate": "2026-07-01T00:00:00.000Z",
    "dueDate": "2027-06-30T00:00:00.000Z",
    "completedDate": null,
    "assignedTo": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
    "isPrivate": false,
    "invitedUsers": [],
    "archived": false,
    "priority": "High",
    "color": null,
    "sortOrder": 0,
    "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
    "organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
    "sourceMeetingId": null,
    "createdAt": "2026-11-12T03:15:42.118Z",
    "updatedAt": "2026-11-12T03:15:42.118Z"
  }
}

Errors

Status code When
422 VALIDATION_ERROR Missing title, a value outside the allowed list, progress outside 0–1000, or dueDate before startDate.
422 INVALID_USERS An assignedTo or onBehalfOf ID isn't an active member. The body lists invalidIds.
422 NO_KEY_OWNER No onBehalfOf, and the key's creator is no longer a member.

Update a Goal#

PATCH /goals/{id}

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

Scope: goals:write · Webhook: goal.updated, with the updated Goal as data.

Path parameter Notes
id Goal ID.

Request body: any of these fields.

Field Type Allowed values Notes
title string At least 1 character after trimming
description string or null
status string not_started, started, on_track, off_track, completed
progress number or string 0–1000 Percentage.
priority string or null High, Medium, Low
startDate string or null ISO 8601
dueDate string or null ISO 8601 If you send both dates, dueDate must be on or after startDate.
completedDate string or null ISO 8601
assignedTo string[] Active member IDs Replaces the whole list.
isPrivate boolean
invitedUsers string[] Member IDs Replaces the whole list.

type and onBehalfOf are ignored on update.

curl -X PATCH https://plan.empiraa.app/api/v1/goals/a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "status": "off_track", "progress": 38 }'

Response 200

JSON
{
  "data": {
    "id": "a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e",
    "title": "Grow recurring revenue",
    "description": "Lift monthly recurring revenue across every service line.",
    "type": "OKR",
    "progress": "38",
    "status": "off_track",
    "startDate": "2026-07-01T00:00:00.000Z",
    "dueDate": "2027-06-30T00:00:00.000Z",
    "completedDate": null,
    "assignedTo": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
    "isPrivate": false,
    "invitedUsers": [],
    "archived": false,
    "priority": "High",
    "color": "#3b82f6",
    "sortOrder": 0,
    "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
    "organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
    "sourceMeetingId": null,
    "createdAt": "2026-06-24T01:02:03.456Z",
    "updatedAt": "2026-10-30T22:45:10.120Z"
  }
}

Errors

Status code When
404 NOT_FOUND The Goal doesn't exist, is in another organization, or is archived.
422 VALIDATION_ERROR A value outside the allowed list or range, or dueDate before startDate.
422 INVALID_USERS An assignedTo ID isn't an active member.

Delete a Goal#

DELETE /goals/{id}

Permanently deletes the Goal and everything under it: its Objectives and their sub-Objectives, the Actions linked to the Goal or those Objectives, their check-in history and their comments.

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

Path parameter Notes
id Goal ID.
curl -X DELETE https://plan.empiraa.app/api/v1/goals/a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e \
  -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 Goal doesn't exist, is in another organization, or is archived.