OGSM API
The API is organized around REST. Our API has predictable resource-oriented URLs, accepts JSON encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.
All endpoints live under your own subdomain:
https://yoursubdomain.ogsm.online/api/v2/
The API has to be switched on for your organization. Admins find it under Settings -> API. If you’re missing that menu option, contact our support team to enable the API for you.
Authentication
Authentication is done at organization level: one set of credentials for the whole organization, giving read access to all of its data. It also lets you add, change and delete measurements on goals and KPIs.
Because these credentials belong to the organization rather than to a person, everything you add, change or delete through them shows up as “System” in the OGSM history, and it does not send any notifications to your team.
API token (bearer)
Admins find the token under Settings -> API -> Settings. Copy it and send it as a bearer token on every request:
Authorization: Bearer <your token>
The token does not expire, but it is regenerated whenever API access is switched off and on again, which invalidates the previous one.
Username and password (basic)
Organizations that set up the API before tokens existed authenticate using the HTTP “Basic” schema, with the API username and password from Settings -> API. It gives exactly the same access as the token above.
New organizations get a bearer token instead of a username and password. If you’re still on basic authentication and want to move over, contact our support team.
Errors
Conventional HTTP response codes are used to indicate the success or failure of an API request. In general: codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed given the information provided, for example a missing required field. Codes in the 5xx range indicate an error with our servers.
| Code | Meaning |
|---|---|
401 |
Missing or invalid credentials |
404 |
The resource does not exist, or is not visible to this user |
422 |
The body was understood but rejected. The response is an object of field names and their error messages |
A successful delete answers with 204 and an empty body.
Body formats
All body information you send needs to be encoded in JSON format. Don’t forget to send the correct header with it:
Content-Type: application/json
Field values
A few fields come back as fixed sets of values:
traffic_lightis"green","amber","red"ornullwhen nothing has been reported yet. Cards report the traffic light of their most recent progress report.statuson an action is an object naming the status column the action sits in:{ "id": "doing", "name": "In progress" }, ornullwhen the action has no status yet. If your organization uses custom action statuses, you get those names and ids, so read the available statuses from/api/v2/statuses.jsoninstead of hard-coding them.statuson an action progress report holds only the name of the standard status:"Icebox","To do","In progress"or"Done".priorityis"none","low","normal","high"or"very_high".aggregation_methodis"sum","average","min","max","median","none","sum_per_quarter","average_per_quarter","sum_per_year"or"average_per_year".chart_typeis"individual","quarter_average","quarter_sum","year_average"or"year_sum".relationsholds one entry per place the card appears on an OGSM. Itsidis the relation id used in the card’s detail URL in the app, which is what you need if you want to link back from your own system.
Resources
The following resources are available through the API. Most of them currently support read access only. Let our support team know if you need to be able to create or update these resources so we can extend the API with it.
Teams
The team object looks like this:
{
id: 123,
name: "team name"
}
All API calls regarding teams use this structure.
List all teams
GET /api/v2/teams.json
This will return an array of team objects.
OGSMs
The OGSM object looks like this:
{
team: { "id": 123 },
id: 123,
name: "OGSM name",
status: "active"
}
All API calls regarding OGSMs use this structure.
List all OGSMs
GET /api/v2/ogsm_canvases.json
This will return an array of OGSM objects.
Objectives
The objective object looks like this:
{
id: 123,
title: "Objective title",
labels: [{ name: "label 1" }, { name: "Label 2" }],
description: "<p>Description</p>",
assignee: { name: "Firstname Lastname" },
ogsm_canvases: [{ id: 789, row_order: 91 }],
relations: [{ id: "8ebcb715-b51d-45b3-9271-5beb9b329472", ogsm_canvas_id: 789, parent_id: null }],
traffic_light: "green",
archived: false
}
All API calls regarding objectives use this structure.
List all objectives
GET /api/v2/objectives.json
This will return an array of objective objects.
Objective progress report
The objective progress report object looks like this:
{
id: 123,
objective: { "id": 123 },
at: "2024-01-01",
traffic_light: "green",
description: "<p>Description</p>",
decision: "<p>Decision</p>"
}
All API calls regarding objective progress reports use this structure.
List all objective progress reports
GET /api/v2/objective_reports.json
This will return an array of objective progress report objects.
Goals
The goal object looks like this:
{
id: 123,
title: "Goal title",
labels: [{ name: "label 1" }, { name: "Label 2" }],
description: "<p>Description</p>",
target_unit: "EUR",
assignee: { name: "Firstname Lastname" },
ogsm_canvases: [{ id: 789, row_order: 91 }],
relations: [{ id: "7ebcb715-b51d-45b3-9271-5beb9b329472", ogsm_canvas_id: 789, parent_id: null }],
traffic_light: "red",
archived: false,
chart_vertical_min: null,
aggregation_method: "sum",
chart_type: "individual"
}
All API calls regarding goals use this structure.
List all goals
GET /api/v2/goals.json
This will return an array of goal objects.
Goal measurements
The goal measurement object looks like this:
{
id: 123,
goal: { id: 456 },
at: "2024-01-01",
current_value: 78.9,
target_value: null,
traffic_light: "green",
show_in_ogsm: false,
description: "Example",
decision: "<p>Decision</p>"
}
All API calls regarding goal measurements use this structure.
List all goal measurements
GET /api/v2/goal_reports.json
This will return an array of goal measurement objects.
Create a measurement for a goal
POST /api/v2/goal_reports.json
{ "goal_report": { "goal": { "id": 123 }, "at": "2022-12-14", "current_value": 45.6 } }
Send a JSON encoded goal measurement object as the body to create the measurement. Besides at and current_value you can set target_value, traffic_light and show_in_ogsm. Leave out the id, it will be generated by the system. When the creation is successful you’ll receive the complete goal measurement object as response including the newly generated id.
For example, to create a measurement with value 45.6 for the goal with id 123:
curl -H "content-type: application/json" \
-H "authorization: Bearer <your token>" \
-d '{"goal_report": {"goal": {"id": 123}, "at": "2024-10-09", "current_value": 45.6}}' \
https://yoursubdomain.ogsm.online/api/v2/goal_reports.json
Change a measurement for a goal
PATCH /api/v2/goal_reports/123.json
{ "goal_report": { "current_value": 47.1 } }
Send only the fields you want to change: at, current_value, target_value, traffic_light and show_in_ogsm. Anything you leave out keeps the value it has, so there is no need to send the whole measurement back. When the update is successful you’ll receive the complete goal measurement object as response.
For example, to correct the value of the measurement with id 123:
curl -X PATCH -H "content-type: application/json" \
-H "authorization: Bearer <your token>" \
-d '{"goal_report": {"current_value": 47.1}}' \
https://yoursubdomain.ogsm.online/api/v2/goal_reports/123.json
Delete a measurement for a goal
DELETE /api/v2/goal_reports/123.json
A successful delete answers with 204 and an empty body. The measurement disappears from the OGSM and from the list of measurements, and the totals it counted towards are recalculated. Nothing is destroyed straight away: a deleted measurement lands in the Trash, where you can restore it yourself as long as it has not been permanently cleaned up.
For example, to delete the measurement with id 123:
curl -X DELETE \
-H "authorization: Bearer <your token>" \
https://yoursubdomain.ogsm.online/api/v2/goal_reports/123.json
Change many measurements of goals at once
POST /api/v2/goal_report_batches.json
{ "goal_report_batch": { "operations": [
{ "op": "create", "goal": { "id": 123 }, "at": "2024-10-09", "current_value": 45.6 },
{ "op": "update", "id": 456, "current_value": 47.1 },
{ "op": "delete", "id": 789 }
] } }
If you feed measurements in from another system, you rarely have a single change to make. Send them together as a batch instead of making a call per measurement. Every entry in operations says what it does in op:
createneeds thegoalthe measurement belongs to and takes the same fields as a single create:at,current_value,target_value,traffic_lightandshow_in_ogsm.updateneeds theidof the measurement plus the fields you want to change. Anything you leave out keeps the value it has.deleteneeds theidof the measurement and nothing else.
A batch holds at most 500 operations. Send more than that, or leave out an id or a goal an operation needs, and the whole batch is refused with 422 before anything is changed.
You can queue 5 batches per 5 minutes. Goals and KPIs are counted separately, so that is up to 2500 goal measurements every 5 minutes. Anything beyond it is answered with 429 and { "error": "rate_limited" }, and nothing is queued, so wait and send the batch again. Asking for the result of a batch you already queued does not count towards this.
The batch is not applied while you wait. You get 202 straight away, with a batch object and a Location header pointing at the address to ask for the result:
{
id: "9c4ab0e2-6e29-4a5f-9d9e-70b11a0f2f81",
status: "queued",
operation_count: 3,
succeeded: 0,
failed: 0,
created_at: "2024-10-09T07:21:44.000Z",
completed_at: null,
results: []
}
For example, to create one measurement and delete another in a single call:
curl -H "content-type: application/json" \
-H "authorization: Bearer <your token>" \
-d '{"goal_report_batch": {"operations": [{"op": "create", "goal": {"id": 123}, "at": "2024-10-09", "current_value": 45.6}, {"op": "delete", "id": 789}]}}' \
https://yoursubdomain.ogsm.online/api/v2/goal_report_batches.json
Ask for the result of a batch of goal measurements
GET /api/v2/goal_report_batches/9c4ab0e2-6e29-4a5f-9d9e-70b11a0f2f81.json
status is queued as long as the batch is waiting, processing while it is being applied and completed once every operation has had its turn. Keep asking until you see completed; a few seconds between attempts is plenty. failed means something went wrong with the batch itself rather than with one of its operations; whatever was applied before that stays applied.
Operations are applied one by one, and each one stands on its own, so one bad row does not cost you the rest of the batch. results holds an entry per operation, in the order you sent them, each with the response that operation would have received as a call of its own:
{
id: "9c4ab0e2-6e29-4a5f-9d9e-70b11a0f2f81",
status: "completed",
operation_count: 3,
succeeded: 2,
failed: 1,
created_at: "2024-10-09T07:21:44.000Z",
completed_at: "2024-10-09T07:21:46.000Z",
results: [
{ index: 0, op: "create", status: 201, id: 901, goal_report: { id: 901, goal: { id: 123 }, at: "2024-10-09", current_value: 45.6 } },
{ index: 1, op: "update", status: 404, id: 456, error: "Measurement Not Found" },
{ index: 2, op: "delete", status: 204, id: 789 }
]
}
| Status | Meaning |
|---|---|
200 |
The measurement was changed |
201 |
The measurement was created |
204 |
The measurement was deleted |
403 |
These credentials are not allowed to change this measurement |
404 |
The goal or the measurement does not exist, or is not visible to these credentials |
422 |
The operation was rejected. errors names the fields and what is wrong with them |
A batch is only visible to the credentials that sent it.
Strategies
The strategy object looks like this:
{
id: 123,
title: "Strategy title",
labels: [{ name: "label 1" }, { name: "Label 2" }],
description: "<p>Description</p>",
assignee: { name: "Firstname Lastname" },
ogsm_canvases: [{ id: 789, row_order: 91 }],
relations: [{ id: "6ebcb715-b51d-45b3-9271-5beb9b329472", ogsm_canvas_id: 789, parent_id: null }],
traffic_light: "green",
archived: false
}
All API calls regarding strategies use this structure.
List all strategies
GET /api/v2/strategies.json
This will return an array of strategy objects.
Strategy progress report
The strategy progress report object looks like this:
{
id: 123,
strategy: { "id": 123 },
at: "2024-01-01",
traffic_light: "green",
description: "<p>Description</p>",
decision: "<p>Decision</p>"
}
All API calls regarding strategy progress reports use this structure.
List all strategy progress reports
GET /api/v2/strategy_reports.json
This will return an array of strategy progress report objects.
KPI
The KPI object looks like this:
{
id: 123,
title: "KPI title",
labels: [{ name: "label 1" }, { name: "Label 2" }],
strategies: [{ id: 456 }],
description: "<p>Description</p>",
target_unit: "EUR",
traffic_light: "red",
assignee: { name: "Firstname Lastname" },
ogsm_canvases: [{ id: 789, row_order: 91 }],
relations: [{ id: "5ebcb715-b51d-45b3-9271-5beb9b329472", ogsm_canvas_id: 789, parent_id: 456 }],
archived: false,
chart_vertical_min: null,
aggregation_method: "sum",
chart_type: "individual"
}
All API calls regarding KPIs use this structure.
List all KPIs
GET /api/v2/indicators.json
This will return an array of KPI objects.
KPI measurements
The KPI measurement object looks like this:
{
id: 123,
indicator: { id: 456 },
at: "2024-01-01",
current_value: 78.9,
target_value: null,
traffic_light: "green",
show_in_ogsm: false,
description: "Example",
decision: "<p>Decision</p>"
}
All API calls regarding KPI measurements use this structure.
List all KPI measurements
GET /api/v2/indicator_reports.json
This will return an array of KPI measurement objects.
Create a measurement for a KPI
POST /api/v2/indicator_reports.json
{ "indicator_report": { "indicator": { "id": 123 }, "at": "2022-12-14", "current_value": 45.6 } }
Send a JSON encoded KPI measurement object as the body to create the measurement. Besides at and current_value you can set target_value, traffic_light and show_in_ogsm. Leave out the id, it will be generated by the system. When the creation is successful you’ll receive the complete KPI measurement object as response including the newly generated id.
For example, to create a measurement with value 45.6 for the KPI with id 123:
curl -H "content-type: application/json" \
-H "authorization: Bearer <your token>" \
-d '{"indicator_report": {"indicator": {"id": 123}, "at": "2024-10-09", "current_value": 45.6}}' \
https://yoursubdomain.ogsm.online/api/v2/indicator_reports.json
Change a measurement for a KPI
PATCH /api/v2/indicator_reports/123.json
{ "indicator_report": { "current_value": 47.1 } }
Send only the fields you want to change: at, current_value, target_value, traffic_light and show_in_ogsm. Anything you leave out keeps the value it has, so there is no need to send the whole measurement back. When the update is successful you’ll receive the complete KPI measurement object as response.
For example, to correct the value of the measurement with id 123:
curl -X PATCH -H "content-type: application/json" \
-H "authorization: Bearer <your token>" \
-d '{"indicator_report": {"current_value": 47.1}}' \
https://yoursubdomain.ogsm.online/api/v2/indicator_reports/123.json
Delete a measurement for a KPI
DELETE /api/v2/indicator_reports/123.json
A successful delete answers with 204 and an empty body. The measurement disappears from the OGSM and from the list of measurements, and the totals it counted towards are recalculated. Nothing is destroyed straight away: a deleted measurement lands in the Trash, where you can restore it yourself as long as it has not been permanently cleaned up.
For example, to delete the measurement with id 123:
curl -X DELETE \
-H "authorization: Bearer <your token>" \
https://yoursubdomain.ogsm.online/api/v2/indicator_reports/123.json
Change many measurements of KPIs at once
POST /api/v2/indicator_report_batches.json
{ "indicator_report_batch": { "operations": [
{ "op": "create", "indicator": { "id": 123 }, "at": "2024-10-09", "current_value": 45.6 },
{ "op": "update", "id": 456, "current_value": 47.1 },
{ "op": "delete", "id": 789 }
] } }
If you feed measurements in from another system, you rarely have a single change to make. Send them together as a batch instead of making a call per measurement. Every entry in operations says what it does in op:
createneeds theindicatorthe measurement belongs to and takes the same fields as a single create:at,current_value,target_value,traffic_lightandshow_in_ogsm.updateneeds theidof the measurement plus the fields you want to change. Anything you leave out keeps the value it has.deleteneeds theidof the measurement and nothing else.
A batch holds at most 500 operations. Send more than that, or leave out an id or an indicator an operation needs, and the whole batch is refused with 422 before anything is changed.
You can queue 5 batches per 5 minutes. Goals and KPIs are counted separately, so that is up to 2500 KPI measurements every 5 minutes. Anything beyond it is answered with 429 and { "error": "rate_limited" }, and nothing is queued, so wait and send the batch again. Asking for the result of a batch you already queued does not count towards this.
The batch is not applied while you wait. You get 202 straight away, with a batch object and a Location header pointing at the address to ask for the result:
{
id: "9c4ab0e2-6e29-4a5f-9d9e-70b11a0f2f81",
status: "queued",
operation_count: 3,
succeeded: 0,
failed: 0,
created_at: "2024-10-09T07:21:44.000Z",
completed_at: null,
results: []
}
For example, to create one measurement and delete another in a single call:
curl -H "content-type: application/json" \
-H "authorization: Bearer <your token>" \
-d '{"indicator_report_batch": {"operations": [{"op": "create", "indicator": {"id": 123}, "at": "2024-10-09", "current_value": 45.6}, {"op": "delete", "id": 789}]}}' \
https://yoursubdomain.ogsm.online/api/v2/indicator_report_batches.json
Ask for the result of a batch of KPI measurements
GET /api/v2/indicator_report_batches/9c4ab0e2-6e29-4a5f-9d9e-70b11a0f2f81.json
status is queued as long as the batch is waiting, processing while it is being applied and completed once every operation has had its turn. Keep asking until you see completed; a few seconds between attempts is plenty. failed means something went wrong with the batch itself rather than with one of its operations; whatever was applied before that stays applied.
Operations are applied one by one, and each one stands on its own, so one bad row does not cost you the rest of the batch. results holds an entry per operation, in the order you sent them, each with the response that operation would have received as a call of its own:
{
id: "9c4ab0e2-6e29-4a5f-9d9e-70b11a0f2f81",
status: "completed",
operation_count: 3,
succeeded: 2,
failed: 1,
created_at: "2024-10-09T07:21:44.000Z",
completed_at: "2024-10-09T07:21:46.000Z",
results: [
{ index: 0, op: "create", status: 201, id: 901, indicator_report: { id: 901, indicator: { id: 123 }, at: "2024-10-09", current_value: 45.6 } },
{ index: 1, op: "update", status: 404, id: 456, error: "Measurement Not Found" },
{ index: 2, op: "delete", status: 204, id: 789 }
]
}
| Status | Meaning |
|---|---|
200 |
The measurement was changed |
201 |
The measurement was created |
204 |
The measurement was deleted |
403 |
These credentials are not allowed to change this measurement |
404 |
The KPI or the measurement does not exist, or is not visible to these credentials |
422 |
The operation was rejected. errors names the fields and what is wrong with them |
A batch is only visible to the credentials that sent it.
Actions
The action object looks like this:
{
id: 123,
title: "Action title",
labels: [{ name: "label 1" }, { name: "Label 2" }],
strategies: [{ id: 456 }],
description: "<p>Description</p>",
assignee: { name: "Firstname Lastname" },
involved: [{ type: "user", name: "Another name" }, { type: "team", name: "Marketing" }],
start: "2024-01-01",
due: "2024-12-31",
status: { id: "doing", name: "In progress" },
ogsm_canvases: [{ id: 789, row_order: 91 }],
relations: [{ id: "3ebcb715-b51d-45b3-9271-5beb9b329472", ogsm_canvas_id: 789, parent_id: 456 }],
traffic_light: "green",
progress: 50,
archived: false,
effort: 3,
added_value: 8,
recurring: "weekly",
priority: "high",
budget_time_available: 400,
budget_money_available: 2000,
field_configuration: { name: "Project" },
project-sponsor: "Firstname Lastname",
results: "<p>results</p>"
}
All API calls regarding actions use this structure.
Everything after field_configuration depends on the card type. field_configuration names the card type the action uses (“Action”, “Project”, or one you defined yourself), and each of its text, number and person fields is added to the object under its own name, lowercased and with dashes instead of spaces. A field called “Project sponsor” becomes project-sponsor. Person fields come back as { name: "Firstname Lastname" }, the rest as plain values. So the exact set of keys differs per action.
List all actions
GET /api/v2/actions.json
This will return an array of action objects. Add canvas_id to limit the list to one OGSM:
GET /api/v2/actions.json?canvas_id=789
Action comments
The comment object looks like this:
{
id: 123,
content: "Comment text",
user: { id: 789, name: "Firstname Lastname" },
created_at: "2026-01-15T10:22:31.000Z",
updated_at: "2026-01-15T10:22:31.000Z"
}
List the comments on an action
GET /api/v2/actions/123/comments.json
This will return an array of comment objects, oldest first.
Action progress report
The action progress report object looks like this:
{
id: 123,
action: { "id": 123 },
at: "2024-01-01",
traffic_light: "green",
description: "<p>Description</p>",
progress: 50,
status: { name: "In progress" },
budget_time_used: 120,
budget_money_used: 750,
decision: "<p>Decision</p>"
}
All API calls regarding action progress reports use this structure.
List all action progress reports
GET /api/v2/action_reports.json
This will return an array of action progress report objects.
Action statuses
The status object looks like this:
{
id: "doing",
name: "In progress",
base_list: "doing"
}
All API calls regarding statuses use this structure.
List all statuses
GET /api/v2/statuses.json
This will return an array of status objects, in the same order as the columns on the Actions page.
An organization without custom statuses gets the four standard statuses, whose id is "backlog", "todo", "doing" or "done". An organization with custom action statuses gets those instead: id is a uuid, name is the name your administrator gave the column, and base_list is the standard status it is linked to. Read the list once and use it to interpret the status of an action, which refers to one of these objects by id. base_list is what tells you what a custom status means, for example whether an action is done.
Users
The user object looks like this:
{
id: 789,
name: "user name",
email_address: "email@example.com"
}
All API calls regarding users use this structure.
List all users
GET /api/v2/users.json
This will return an array of user objects.