---
title: Actions
description: List, read, create, update and delete the Actions that move Goals and Objectives forward.
product: gps
section: api
order: 14
---

# Actions

An Action is a piece of work that moves an [Objective](objectives.md) or a [Goal](goals.md) forward. Every Action created through the API must link to an Objective (`objectiveId`), a Goal (`goalId`), or both.

| Method | Path | Scope | Webhook |
|--------|------|-------|---------|
| `GET` | [`/actions`](#list-actions) | `actions:read` | |
| `GET` | [`/objectives/{objectiveId}/actions`](#list-an-objectives-actions) | `actions:read` | |
| `GET` | [`/goals/{goalId}/actions`](#list-a-goals-actions) | `actions:read` | |
| `GET` | [`/actions/{id}`](#get-an-action) | `actions:read` | |
| `POST` | [`/actions`](#create-an-action) | `actions:write` | `action.created` |
| `PATCH` | [`/actions/{id}`](#update-an-action) | `actions:write` | `action.updated` |
| `DELETE` | [`/actions/{id}`](#delete-an-action) | `actions:write` | `action.deleted` |

Related: [Action comments](comments.md).

The JavaScript examples assume a `headers` object holding your key:

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

## The Action object

| Field | Type | Notes |
|-------|------|-------|
| `id` | string | Action ID. |
| `title` | string | |
| `description` | string or null | |
| `notes` | string or null | |
| `objectiveId` | string or null | The Objective it belongs to. |
| `goalId` | string or null | The Goal it belongs to. |
| `assignees` | string[] | Member IDs of the people doing it. |
| `priority` | string or null | `High`, `Medium` or `Low`. |
| `status` | string | `to_do`, `started`, `on_track`, `off_track`, `completed`, `cancelled` or `archived`. Some older Actions store `To Do`, which means `to_do`. |
| `startDate` | string or null | ISO 8601 timestamp. |
| `dueDate` | string or null | ISO 8601 timestamp. |
| `completedDate` | string or null | ISO 8601 timestamp. |
| `reminderDate` | string or null | When GPS reminds the assignees. Set in GPS. |
| `reminderFrequency` | string or null | `daily`, `weekly` or `monthly`. Set in GPS. |
| `reminderSent` | boolean | Whether the reminder has gone out. |
| `emailReminder` | boolean | Whether the reminder is emailed. |
| `appReminder` | boolean | Whether the reminder shows in GPS. |
| `isRepeatable` | boolean | Whether GPS offers to repeat the Action once it's done. Always `false` for Actions created through the API. |
| `repeatFrequency` | string or null | `weekly` or `monthly`. |
| `repeatDay` | integer or null | Day of the week (1 = Monday, 7 = Sunday) or day of the month (1–31) to repeat on. |
| `isPrivate` | boolean | Private in GPS. API keys still see private Actions. |
| `createdBy` | string or null | Member ID of the creator. |
| `sourceMeetingId` | string or null | The Meeting it was captured in, if any. |
| `portalOriginMasterUserId` | string or null | Set when a consultant raised the Action from myPortal. Read-only. |
| `positionInSet` | integer or null | Its place in a set of Actions sent together from myPortal. |
| `createdAt` | string | ISO 8601 timestamp. |
| `updatedAt` | string | ISO 8601 timestamp. |

## List Actions

`GET /actions`

Returns every Action linked to one of the organization's Goals or Objectives that isn't archived. Order isn't guaranteed.

**Scope:** `actions:read`

**Query parameters**

| Parameter | Type | Notes |
|-----------|------|-------|
| `status` | string | One of `to_do`, `started`, `on_track`, `off_track`, `completed`, `cancelled`, `archived`. `To Do` is accepted for `to_do`. |
| `objectiveId` | string | Only Actions with this `objectiveId`. |
| `goalId` | string | Only Actions with this `goalId`. |
| `limit` | integer | 1–200, default 50. |
| `offset` | integer | Default 0. |

```bash
curl "https://plan.empiraa.app/api/v1/actions?status=to_do&objectiveId=c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d" \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"
```

```js
const params = new URLSearchParams({ status: 'to_do', objectiveId: 'c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d' });
const res = await fetch(`https://plan.empiraa.app/api/v1/actions?${params}`, { headers });
const { data: actions, meta } = await res.json();
```

**Response `200`**

```json
{
  "data": [
    {
      "id": "e5a7c9e1-6f8b-4c2d-8e9f-3a4b5c6d7e8f",
      "title": "Send renewal offers to the top 20 clients",
      "description": null,
      "notes": "Use the annual pricing sheet.",
      "objectiveId": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
      "goalId": null,
      "assignees": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
      "priority": "High",
      "status": "to_do",
      "startDate": null,
      "dueDate": "2026-11-28T00:00:00.000Z",
      "completedDate": null,
      "reminderDate": null,
      "reminderFrequency": null,
      "reminderSent": false,
      "emailReminder": false,
      "appReminder": true,
      "isRepeatable": false,
      "repeatFrequency": null,
      "repeatDay": null,
      "isPrivate": false,
      "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
      "sourceMeetingId": null,
      "portalOriginMasterUserId": null,
      "positionInSet": null,
      "createdAt": "2026-11-10T23:00:00.000Z",
      "updatedAt": "2026-11-10T23: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 an Objective's Actions

`GET /objectives/{objectiveId}/actions`

Returns the Actions whose `objectiveId` is this Objective. Order isn't guaranteed.

**Scope:** `actions:read`

| Parameter | In | Notes |
|-----------|----|-------|
| `objectiveId` | path | Objective ID. |
| `status` | query | Same values as [List Actions](#list-actions). |
| `limit`, `offset` | query | See [Pagination](../conventions.md#pagination). |

```bash
curl https://plan.empiraa.app/api/v1/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d/actions \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"
```

```js
const res = await fetch(
  'https://plan.empiraa.app/api/v1/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d/actions',
  { headers },
);
const { data: actions, meta } = await res.json();
```

**Response `200`**: a list of [Action objects](#the-action-object) with `meta`, as in [List Actions](#list-actions).

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

## List a Goal's Actions

`GET /goals/{goalId}/actions`

Returns the Actions linked **directly** to the Goal through `goalId`. Actions that link only to one of the Goal's Objectives aren't included; use [List an Objective's Actions](#list-an-objectives-actions) for those. Order isn't guaranteed.

**Scope:** `actions:read`

| Parameter | In | Notes |
|-----------|----|-------|
| `goalId` | path | Goal ID. |
| `status` | query | Same values as [List Actions](#list-actions). |
| `limit`, `offset` | query | See [Pagination](../conventions.md#pagination). |

```bash
curl https://plan.empiraa.app/api/v1/goals/a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e/actions \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"
```

```js
const res = await fetch(
  'https://plan.empiraa.app/api/v1/goals/a1f3c5e7-2b4d-4f6a-8c9e-0b1d3f5a7c9e/actions',
  { headers },
);
const { data: actions, meta } = await res.json();
```

**Response `200`**: a list of [Action objects](#the-action-object) with `meta`, as in [List Actions](#list-actions).

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

## Get an Action

`GET /actions/{id}`

**Scope:** `actions:read`

| Path parameter | Notes |
|----------------|-------|
| `id` | Action ID. |

```bash
curl https://plan.empiraa.app/api/v1/actions/e5a7c9e1-6f8b-4c2d-8e9f-3a4b5c6d7e8f \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"
```

```js
const res = await fetch('https://plan.empiraa.app/api/v1/actions/e5a7c9e1-6f8b-4c2d-8e9f-3a4b5c6d7e8f', { headers });
const { data: action } = await res.json();
```

**Response `200`**: `{ "data": <Action> }`, with the same fields as the item in [List Actions](#list-actions).

**Errors:** `404 NOT_FOUND` if the Action doesn't exist, is in another organization, or its Goal or Objective is archived.

## Create an Action

`POST /actions`

**Scope:** `actions:write` · **Webhook:** `action.created`, with the new Action as `data`.

**Request body**

| Field | Type | Required | Allowed values | Notes |
|-------|------|----------|----------------|-------|
| `title` | string | Yes | At least 1 character after trimming | |
| `objectiveId` | string or null | One of `objectiveId` or `goalId` | An Objective in your organization | |
| `goalId` | string or null | One of `objectiveId` or `goalId` | A Goal in your organization | |
| `description` | string or null | No | | |
| `notes` | string or null | No | | |
| `status` | string | No | `to_do`, `started`, `on_track`, `off_track`, `completed`, `cancelled`, `archived` (or `To Do`) | Default `started`. |
| `priority` | string or null | No | `High`, `Medium`, `Low` | Default `Medium`. |
| `assignees` | string[] | No | Active member IDs | Default `[]`. |
| `startDate` | string or null | No | ISO 8601 | |
| `dueDate` | string or null | No | ISO 8601 | |
| `completedDate` | string or null | No | ISO 8601 | |
| `isPrivate` | boolean | No | | Default `false`. |
| `onBehalfOf` | string | No | Active member ID | Who the Action is created by. See [Attribution](../conventions.md#attribution-with-onbehalfof). |

Reminders and repeat settings can't be set through the API.

```bash
curl -X POST https://plan.empiraa.app/api/v1/actions \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Send renewal offers to the top 20 clients",
    "objectiveId": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
    "status": "to_do",
    "priority": "High",
    "dueDate": "2026-11-28",
    "assignees": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"]
  }'
```

```js
const res = await fetch('https://plan.empiraa.app/api/v1/actions', {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    title: 'Send renewal offers to the top 20 clients',
    objectiveId: 'c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d',
    status: 'to_do',
    priority: 'High',
    dueDate: '2026-11-28',
    assignees: ['5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70'],
  }),
});
const { data: action } = await res.json();
```

**Response `201`**

```json
{
  "data": {
    "id": "e5a7c9e1-6f8b-4c2d-8e9f-3a4b5c6d7e8f",
    "title": "Send renewal offers to the top 20 clients",
    "description": null,
    "notes": null,
    "objectiveId": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
    "goalId": null,
    "assignees": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
    "priority": "High",
    "status": "to_do",
    "startDate": null,
    "dueDate": "2026-11-28T00:00:00.000Z",
    "completedDate": null,
    "reminderDate": null,
    "reminderFrequency": null,
    "reminderSent": false,
    "emailReminder": false,
    "appReminder": true,
    "isRepeatable": false,
    "repeatFrequency": null,
    "repeatDay": null,
    "isPrivate": false,
    "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
    "sourceMeetingId": null,
    "portalOriginMasterUserId": null,
    "positionInSet": null,
    "createdAt": "2026-11-12T03:17:30.004Z",
    "updatedAt": "2026-11-12T03:17:30.004Z"
  }
}
```

**Errors**

| Status | `code` | When |
|--------|--------|------|
| 422 | `VALIDATION_ERROR` | Missing `title`; neither `objectiveId` nor `goalId`; a value outside the allowed list. |
| 422 | `INVALID_REFERENCE` | `objectiveId` or `goalId` isn't in your organization (or is archived). |
| 422 | `INVALID_USERS` | An `assignees` 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 Action

`PATCH /actions/{id}`

Changes only the fields you send. After the change the Action must still link to an Objective or a Goal, so you can clear one of `objectiveId` and `goalId` (set it to `null`) but not both.

**Scope:** `actions:write` · **Webhook:** `action.updated`, with the updated Action as `data`.

| Path parameter | Notes |
|----------------|-------|
| `id` | Action ID. |

**Request body:** any field from [Create an Action](#create-an-action) except `onBehalfOf`, with the same rules. `assignees` replaces the whole list.

Send at least one field.

```bash
curl -X PATCH https://plan.empiraa.app/api/v1/actions/e5a7c9e1-6f8b-4c2d-8e9f-3a4b5c6d7e8f \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "status": "completed", "completedDate": "2026-11-12" }'
```

```js
const res = await fetch('https://plan.empiraa.app/api/v1/actions/e5a7c9e1-6f8b-4c2d-8e9f-3a4b5c6d7e8f', {
  method: 'PATCH',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({ status: 'completed', completedDate: '2026-11-12' }),
});
const { data: action } = await res.json();
```

**Response `200`**: `{ "data": <Action> }` with the changes applied, for example `"status": "completed"` and `"completedDate": "2026-11-12T00:00:00.000Z"`. Every other field is as in [List Actions](#list-actions).

**Errors**

| Status | `code` | When |
|--------|--------|------|
| 404 | `NOT_FOUND` | The Action doesn't exist, is in another organization, or its Goal or Objective is archived. |
| 422 | `VALIDATION_ERROR` | A value outside the allowed list, or the change would leave neither `objectiveId` nor `goalId`. |
| 422 | `INVALID_REFERENCE` | A new `objectiveId` or `goalId` isn't in your organization. |
| 422 | `INVALID_USERS` | An `assignees` ID isn't an active member. |

## Delete an Action

`DELETE /actions/{id}`

Permanently deletes the Action, its comments and its reminders.

**Scope:** `actions:write` · **Webhook:** `action.deleted`, with `data` set to `{ "id": "<action id>" }`.

| Path parameter | Notes |
|----------------|-------|
| `id` | Action ID. |

```bash
curl -X DELETE https://plan.empiraa.app/api/v1/actions/e5a7c9e1-6f8b-4c2d-8e9f-3a4b5c6d7e8f \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET"
```

```js
const res = await fetch('https://plan.empiraa.app/api/v1/actions/e5a7c9e1-6f8b-4c2d-8e9f-3a4b5c6d7e8f', {
  method: 'DELETE',
  headers,
});
console.log(res.status); // 204
```

**Response `204`**: no body.

**Errors:** `404 NOT_FOUND` if the Action doesn't exist, is in another organization, or its Goal or Objective is archived.
