---
title: Ideas
description: List, read, create, update and delete Ideas on your organization's improvement board.
product: gps
section: api
order: 16
---

# Ideas

Ideas are the cards on your organization's improvement board in GPS. Each Idea sits in a stage (`ideas`, `review`, `in_progress` or `implemented`) and can link to a Goal, an Objective or an Action.

| Method | Path | Scope | Webhook |
|--------|------|-------|---------|
| `GET` | [`/ideas`](#list-ideas) | `ideas:read` | |
| `GET` | [`/ideas/{id}`](#get-an-idea) | `ideas:read` | |
| `POST` | [`/ideas`](#create-an-idea) | `ideas:write` | None |
| `PATCH` | [`/ideas/{id}`](#update-an-idea) | `ideas:write` | None |
| `DELETE` | [`/ideas/{id}`](#delete-an-idea) | `ideas:write` | None |

Idea changes don't fire webhooks. Hidden Ideas aren't returned: lists leave them out and the other endpoints return `404`. Related: [Idea 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 Idea object

| Field | Type | Notes |
|-------|------|-------|
| `id` | string | Idea ID. |
| `title` | string | |
| `description` | string or null | |
| `stage` | string | `ideas`, `review`, `in_progress` or `implemented`. Set it with `columnKey` in request bodies. |
| `tag` | string or null | |
| `dueDate` | string or null | ISO 8601 timestamp. |
| `boardId` | string or null | The board the Idea is on. |
| `linkedGoalId` | string or null | |
| `linkedObjectiveId` | string or null | |
| `linkedActionId` | string or null | |
| `assignees` | string[] | Member IDs. Read-only through the API; assign people in GPS. |
| `createdBy` | string or null | Member ID of the creator. |
| `organizationId` | string | |
| `createdAt` | string | ISO 8601 timestamp. |
| `updatedAt` | string | ISO 8601 timestamp. |

## List Ideas

`GET /ideas`

Returns visible Ideas, grouped by stage and in their board order within each stage.

**Scope:** `ideas:read`

**Query parameters**

| Parameter | Type | Notes |
|-----------|------|-------|
| `stage` | string | One of `ideas`, `review`, `in_progress`, `implemented`. |
| `limit` | integer | 1–200, default 50. |
| `offset` | integer | Default 0. |

```bash
curl "https://plan.empiraa.app/api/v1/ideas?stage=review" \
  -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/ideas?stage=review', { headers });
const { data: ideas, meta } = await res.json();
```

**Response `200`**

```json
{
  "data": [
    {
      "id": "2c4e6a8c-0d1f-4a3b-9c5d-6e7f8a9b0c1d",
      "title": "Automate the monthly client report",
      "description": "Pull figures straight from the accounting system.",
      "stage": "review",
      "tag": "Efficiency",
      "dueDate": "2027-01-31T00:00:00.000Z",
      "boardId": null,
      "linkedGoalId": null,
      "linkedObjectiveId": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
      "linkedActionId": null,
      "assignees": ["6e8a0c2e-4f6b-4d8f-a0b1-3c4d5e6f7a81"],
      "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
      "organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
      "createdAt": "2026-10-14T04:30:00.000Z",
      "updatedAt": "2026-11-02T01:12:45.000Z"
    }
  ],
  "meta": { "total": 1, "limit": 50, "offset": 0 }
}
```

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

## Get an Idea

`GET /ideas/{id}`

**Scope:** `ideas:read`

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

```bash
curl https://plan.empiraa.app/api/v1/ideas/2c4e6a8c-0d1f-4a3b-9c5d-6e7f8a9b0c1d \
  -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/ideas/2c4e6a8c-0d1f-4a3b-9c5d-6e7f8a9b0c1d', { headers });
const { data: idea } = await res.json();
```

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

**Errors:** `404 NOT_FOUND` if the Idea doesn't exist, is in another organization, or is hidden.

## Create an Idea

`POST /ideas`

**Scope:** `ideas:write` · **Webhook:** none.

**Request body**

| Field | Type | Required | Allowed values | Notes |
|-------|------|----------|----------------|-------|
| `title` | string | Yes | At least 1 character after trimming | |
| `description` | string or null | No | Up to 5,000 characters | |
| `columnKey` | string | No | `ideas`, `review`, `in_progress`, `implemented` | The stage. Default `ideas`. Returned as `stage`. |
| `tag` | string or null | No | Up to 40 characters | |
| `dueDate` | string or null | No | ISO 8601 | |
| `boardId` | string or null | No | A board ID | |
| `linkedGoalId` | string or null | No | A Goal in your organization | |
| `linkedObjectiveId` | string or null | No | An Objective in your organization | |
| `linkedActionId` | string or null | No | An Action in your organization | |
| `onBehalfOf` | string | No | Active member ID | Who the Idea is created by. See [Attribution](../conventions.md#attribution-with-onbehalfof). |

```bash
curl -X POST https://plan.empiraa.app/api/v1/ideas \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Automate the monthly client report",
    "description": "Pull figures straight from the accounting system.",
    "tag": "Efficiency",
    "linkedObjectiveId": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d"
  }'
```

```js
const res = await fetch('https://plan.empiraa.app/api/v1/ideas', {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    title: 'Automate the monthly client report',
    description: 'Pull figures straight from the accounting system.',
    tag: 'Efficiency',
    linkedObjectiveId: 'c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d',
  }),
});
const { data: idea } = await res.json();
```

**Response `201`**

```json
{
  "data": {
    "id": "2c4e6a8c-0d1f-4a3b-9c5d-6e7f8a9b0c1d",
    "title": "Automate the monthly client report",
    "description": "Pull figures straight from the accounting system.",
    "stage": "ideas",
    "tag": "Efficiency",
    "dueDate": null,
    "boardId": null,
    "linkedGoalId": null,
    "linkedObjectiveId": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
    "linkedActionId": null,
    "assignees": [],
    "createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
    "organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
    "createdAt": "2026-11-12T03:19:21.870Z",
    "updatedAt": "2026-11-12T03:19:21.870Z"
  }
}
```

**Errors**

| Status | `code` | When |
|--------|--------|------|
| 422 | `VALIDATION_ERROR` | Missing `title`, a value too long, or an unknown `columnKey`. |
| 422 | `INVALID_REFERENCE` | A linked Goal, Objective or Action isn't in your organization. |
| 422 | `INVALID_USERS` | `onBehalfOf` isn't an active member. |
| 422 | `NO_KEY_OWNER` | No `onBehalfOf`, and the key's creator is no longer a member. |

## Update an Idea

`PATCH /ideas/{id}`

Changes only the fields you send. To move an Idea to another stage, send `columnKey`.

**Scope:** `ideas:write` · **Webhook:** none.

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

**Request body:** any field from [Create an Idea](#create-an-idea) except `onBehalfOf`, with the same rules.

```bash
curl -X PATCH https://plan.empiraa.app/api/v1/ideas/2c4e6a8c-0d1f-4a3b-9c5d-6e7f8a9b0c1d \
  -H "X-API-Key: $GPS_API_KEY_ID" \
  -H "X-API-Secret: $GPS_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "columnKey": "in_progress" }'
```

```js
const res = await fetch('https://plan.empiraa.app/api/v1/ideas/2c4e6a8c-0d1f-4a3b-9c5d-6e7f8a9b0c1d', {
  method: 'PATCH',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({ columnKey: 'in_progress' }),
});
const { data: idea } = await res.json();
```

**Response `200`**: `{ "data": <Idea> }` with the changes applied (here `"stage": "in_progress"`) and a new `updatedAt`. Every other field is as in [Create an Idea](#create-an-idea).

**Errors:** `404 NOT_FOUND` if the Idea doesn't exist, is in another organization or is hidden; `422 VALIDATION_ERROR` or `422 INVALID_REFERENCE` as for [Create an Idea](#create-an-idea).

## Delete an Idea

`DELETE /ideas/{id}`

Permanently deletes the Idea.

**Scope:** `ideas:write` · **Webhook:** none.

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

```bash
curl -X DELETE https://plan.empiraa.app/api/v1/ideas/2c4e6a8c-0d1f-4a3b-9c5d-6e7f8a9b0c1d \
  -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/ideas/2c4e6a8c-0d1f-4a3b-9c5d-6e7f8a9b0c1d', {
  method: 'DELETE',
  headers,
});
console.log(res.status); // 204
```

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

**Errors:** `404 NOT_FOUND` if the Idea doesn't exist, is in another organization, or is hidden.
