Ideas
List, read, create, update and delete Ideas on your organization's improvement board.
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 |
ideas:read |
|
GET |
/ideas/{id} |
ideas:read |
|
POST |
/ideas |
ideas:write |
None |
PATCH |
/ideas/{id} |
ideas:write |
None |
DELETE |
/ideas/{id} |
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.
The JavaScript examples assume a headers object holding your key:
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. |
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"
const res = await fetch('https://plan.empiraa.app/api/v1/ideas?stage=review', { headers });
const { data: ideas, meta } = await res.json();
Response 200
{
"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. |
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"
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.
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. |
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"
}'
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
{
"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 except onBehalfOf, with the same rules.
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" }'
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.
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.
Delete an Idea#
DELETE /ideas/{id}
Permanently deletes the Idea.
Scope: ideas:write · Webhook: none.
| Path parameter | Notes |
|---|---|
id |
Idea ID. |
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"
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.

