Check-ins
Read an Objective's check-in history and record a new value and its progress.
A check-in records an Objective's latest value. GPS stores the value, works out the new progress, and keeps the change in the Objective's history. The history also holds check-ins made in GPS and updates from connected integrations.
| Method | Path | Scope | Webhook |
|---|---|---|---|
GET |
/objectives/{objectiveId}/progress |
progress:read |
|
POST |
/objectives/{objectiveId}/progress |
progress:write |
None (see below) |
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 check-in object#
| Field | Type | Notes |
|---|---|---|
id |
string | Check-in ID. |
objectiveId |
string | |
notes |
string or null | |
updatedBy |
string or null | Member ID of who recorded it. null for some integration updates. |
value |
number or null | The value sent with the check-in. |
previousProgress |
number or null | Progress percentage before. |
newProgress |
number or null | Progress percentage after. |
progressChange |
number or null | newProgress minus previousProgress. |
previousValue |
number or null | The Objective's currentValue before. |
newValue |
number or null | The Objective's currentValue after. |
createdAt |
string | ISO 8601 timestamp. |
List check-ins#
GET /objectives/{objectiveId}/progress
Returns the Objective's check-in history, newest first.
Scope: progress:read
| Parameter | In | Notes |
|---|---|---|
objectiveId |
path | Objective ID. |
limit, offset |
query | See Pagination. |
curl https://plan.empiraa.app/api/v1/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d/progress \
-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/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d/progress',
{ headers },
);
const { data: checkIns, meta } = await res.json();
Response 200
{
"data": [
{
"id": "4b6d8f0b-2c3e-4f5a-b6c7-8d9e0f1a2b3c",
"objectiveId": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
"notes": "Two new annual contracts signed",
"updatedBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
"value": 60000,
"previousProgress": 45,
"newProgress": 50,
"progressChange": 5,
"previousValue": 54000,
"newValue": 60000,
"createdAt": "2026-11-12T03:15:42.118Z"
}
],
"meta": { "total": 1, "limit": 50, "offset": 0 }
}
Errors: 404 NOT_FOUND if the Objective doesn't exist or is in another organization; 422 VALIDATION_ERROR for a bad limit or offset.
Record a check-in#
POST /objectives/{objectiveId}/progress
Sets the Objective's currentValue to value, recalculates its progress, and adds an entry to its history.
How progress is worked out:
- The Objective has a
targetValue: progress isvalue ÷ targetValue × 100. With a target of 120,000, a value of 60,000 gives 50%. - No
targetValue:valueis taken as the progress percentage itself. - Status Objectives (measurement type
Status (On Track / Off Track / Complete)) are done or not done: avalueof 1 or more records done, anything less records not done.
Progress can go past 100%, since beating a target is a real result, but it must stay between 0% and 1000% (0% and 100% for status Objectives). A check-in that would land outside that range is refused.
A check-in doesn't change the Objective's status.
Scope: progress:write
Webhook: none. A check-in through the API doesn't fire objective.updated, even though the Objective's progress changes. If you rely on webhooks, re-read the Objective after the check-in.
| Path parameter | Notes |
|---|---|
objectiveId |
Objective ID. |
Request body
| Field | Type | Required | Allowed values | Notes |
|---|---|---|---|---|
value |
number | Yes | The Objective's new value. | |
notes |
string or null | No | Up to 2,000 characters | |
onBehalfOf |
string | No | Active member ID | Who recorded it (updatedBy). Default: the key's creator. See Attribution. |
curl -X POST https://plan.empiraa.app/api/v1/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d/progress \
-H "X-API-Key: $GPS_API_KEY_ID" \
-H "X-API-Secret: $GPS_API_SECRET" \
-H "Content-Type: application/json" \
-d '{ "value": 60000, "notes": "Two new annual contracts signed" }'
const res = await fetch(
'https://plan.empiraa.app/api/v1/objectives/c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d/progress',
{
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ value: 60000, notes: 'Two new annual contracts signed' }),
},
);
const { data: checkIn } = await res.json();
Response 201
{
"data": {
"id": "4b6d8f0b-2c3e-4f5a-b6c7-8d9e0f1a2b3c",
"objectiveId": "c7e9a1b3-4d5f-4a6b-9c8d-1e2f3a4b5c6d",
"notes": "Two new annual contracts signed",
"updatedBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
"value": 60000,
"previousProgress": 45,
"newProgress": 50,
"progressChange": 5,
"previousValue": 54000,
"newValue": 60000,
"createdAt": "2026-11-12T03:15:42.118Z"
}
}
Errors
| Status | code |
When |
|---|---|---|
| 404 | NOT_FOUND |
The Objective doesn't exist or is in another organization. |
| 422 | VALIDATION_ERROR |
value is missing or not a number, notes is too long, or the resulting progress is out of range. The error text explains the range for that Objective. |
| 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. |
Example of an out-of-range check-in:
{
"error": "Progress can go past 100% — that's what beating a target looks like — but it has to be between 0% and 1000%.",
"code": "VALIDATION_ERROR",
"docs": "https://empiraa.com/docs/api"
}

