Getting started
The OKRs Tool API gives you read and write access to your workspace's OKRs, KPIs, and review summaries. Use it to wire up Slack bots, scheduled jobs, BI dashboards, or anything else that needs your team's progress data.
The API is JSON over HTTPS. Every endpoint is scoped to the organisation that owns the API key — you cannot access another workspace's data.
https://go.okrstool.com/api/v1- All requests must be authenticated with a Bearer API key in the
Authorizationheader. - All responses are JSON, UTF-8 encoded.
- Timestamps use ISO 8601 in UTC.
- Each key is rate-limited to 60 requests per minute.
okrs:read— list OKRs and Key Resultswrite:okrs— create or update OKRs and KRsokr-checkins:write— submit KR check-ins
cycle_id— callGET /api/v1/cycleskr_id— read the nestedkey_resultsarray onGET /api/v1/okrs
curl https://go.okrstool.com/api/v1/okrs \
-H "Authorization: Bearer okrt_your_api_key_here"# 1. Find your active cycle id
curl https://go.okrstool.com/api/v1/cycles \
-H "Authorization: Bearer okrt_your_api_key_here"
# 2. Read OKRs and pick a Key Result id from the
# nested key_results array
curl https://go.okrstool.com/api/v1/okrs \
-H "Authorization: Bearer okrt_your_api_key_here"
# 3. Submit a check-in for that Key Result
curl -X POST https://go.okrstool.com/api/v1/key-results/KR_ID/checkins \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "value": 70, "status": "on_track" }'Authentication
All requests must include an API key in the Authorization header as a Bearer token. Keys start with the okrt_ prefix and are tied to a single workspace.
Generate keys from Settings → API Keys. The full key is shown only once at creation — copy it immediately and store it somewhere safe (a password manager, a secrets vault, or your CI environment).
Keep keys server-side. Never commit them to git, never paste them into client-side JavaScript, and never share them in chat. Compromised keys can be revoked instantly from the Settings page.
| Scope | Allows |
|---|---|
okrs:read | List OKRs with progress, status, and health. |
write:okrs | Create, update, and delete OKRs and Key Results. |
kpis:read | List KPIs with current value, target, and status. |
kpis:write | Create new KPIs and submit KPI value updates. |
okr-checkins:write | Submit Key Result check-ins. |
reviews:read | Read review cycles and shared review answers. |
Any workspace admin can create keys and grant any combination of scopes.
Authorization: Bearer okrt_your_api_key_hereRate limits
Each API key is allowed 60 requests per minute. Limits reset at the start of every minute (UTC).
When you exceed the limit, the API returns a 429 response with a Retry-After header (in seconds) telling you when to try again.
| Header | Description |
|---|---|
Retry-After | Seconds until the next request will be accepted. Returned only on 429. |
For higher limits, batch your requests where possible (e.g. a single GET /okrs already returns up to 500 records) or open an issue describing your use case.
{
"error": "rate_limit_exceeded",
"retry_after_seconds": 30
}Pagination
List endpoints return up to 500 records per request and support keyset (cursor) pagination via the optional limit and after parameters.
The list endpoints — GET /okrs, GET /kpis, GET /cycles, GET /initiatives, GET /review-cycles and GET /review-cycles/:id/reviews — accept two optional query parameters:
limit— page size, 1–500 (default 500)./reviewskeeps its historical 1000 default and maximum.after— an opaque cursor returned by the previous page.
Every list response carries pagination metadata in headers: X-Has-More (true/false), X-Next-Cursor (present only when there is a next page) and a Link: <…&after=…>; rel="next" header. GET /initiatives additionally returns next_cursor and has_more alongside data.
To read every record, keep calling the endpoint with after=<X-Next-Cursor> until X-Has-More is false.
Response bodies are unchanged: if you send no pagination parameters you get exactly the same payload, order and 500-record default as before.
Caveat: GET /review-cycles/:id/reviews is ordered by updated_at. A review edited while you are paging can shift position, so a row may be seen twice or missed. De-duplicate by id if that matters to you.
records per request, sorted by created_at desc.
Postman collection
Download the official Postman collection to start making requests in seconds — no manual setup required.
- Open Postman → Import → select the downloaded file
- Set the
api_keycollection variable to your key from Settings → API Keys - All requests are pre-configured and ready to run
Errors
All errors follow the same JSON shape so they're easy to handle in client code.
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | The request body or parameters are malformed. |
| 401 | unauthorized | Missing or invalid API key. |
| 403 | forbidden | The key does not have the required scope. |
| 404 | not_found | The resource does not exist or is not accessible to this key. |
| 429 | rate_limit_exceeded | You exceeded 60 requests in the current minute. |
| 500 | server_error | Something went wrong on our end. Safe to retry. |
{
"error": "forbidden",
"message": "This key does not have kpis:write permission"
}OKRs
Read your organisation's OKRs and submit Key Result check-ins.
/api/v1/okrsrequiresokrs:readReturns the most recent 500 OKRs in your workspace, with computed progress, status, and a 0–100 health score derived from check-in recency, velocity, and progress gap. Each OKR also includes a nested key_results array — use the id of each Key Result to call the check-in and update endpoints.
401Invalid or missing API key.403Key is missing the okrs:read scope.
curl https://go.okrstool.com/api/v1/okrs \
-H "Authorization: Bearer okrt_your_api_key_here"[
{
"id": "f4b1a2c8-…",
"title": "Improve onboarding conversion",
"type": "org",
"owner": "Alice Nguyen",
"progress": 0.65,
"status": "on_track",
"health_score": 82,
"cycle": "Q2 2026",
"key_results": [
{
"id": "kr-2c8f…",
"title": "Lift signup → activation rate to 50%",
"metric_type": "percentage",
"start_value": 32,
"current_value": 41,
"target_value": 50,
"progress": 0.50,
"status": "on_track",
"owner_id": "u9f8e7d6-…"
}
]
}
]/api/v1/key-results/:id/checkinsrequiresokr-checkins:writeRecords a Key Result check-in. The :id in the URL is the Key Result UUID (find it in the key_results array on GET /api/v1/okrs). Required: value (number, or 'yes'/'no'/boolean for binary KRs). Optional: comment (string), status (on_track | at_risk | off_track | blocked — defaults to on_track), blocker_note (required when status is 'blocked').
400value is missing or not a number/yes/no/boolean, or status is invalid.404Key Result does not exist in this workspace.
curl -X POST https://go.okrstool.com/api/v1/key-results/KR_ID/checkins \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"value": 70,
"comment": "New flow shipped",
"status": "on_track"
}'{
"id": "9e2b…",
"okr_id": "f4b1a2c8-…",
"key_result_id": "KR_ID",
"value": 70,
"status": "on_track",
"comment": "New flow shipped",
"blocker_note": null,
"created_at": "2026-04-19T12:00:00Z"
}/api/v1/okrs/:id/checkinsrequiresokr-checkins:writeDeprecated: use POST /api/v1/key-results/:id/checkins instead. Despite the path, the :id is the Key Result UUID, not the OKR UUID — this is what caused the rename. Behaviour is identical to the new endpoint and will continue to work.
400value is missing or not a number.404Key Result does not exist in this workspace.
curl -X POST https://go.okrstool.com/api/v1/okrs/KR_ID/checkins \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"value": 70,
"comment": "New flow shipped",
"status": "on_track"
}'{
"id": "9e2b…",
"okr_id": "f4b1a2c8-…",
"key_result_id": "KR_ID",
"value": 70,
"status": "on_track",
"created_at": "2026-04-19T12:00:00Z"
}/api/v1/okrsrequireswrite:okrsCreate a new OKR. Required: title (string), type (one of company, department, team — alias 'org' is also accepted for company), cycle_id (UUID — see GET /api/v1/cycles). Optional: description (string), owner_id (UUID), department_id (UUID), team_id (UUID), company_id (UUID — group-hierarchy workspaces only; omit or null for a group-level goal), visibility (one of organisation, department, team — defaults to organisation).
400title, type, or cycle_id missing or invalid.403Key is missing the write:okrs scope.
curl -X POST https://go.okrstool.com/api/v1/okrs \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Improve onboarding conversion",
"description": "Lift activation in the first 7 days",
"type": "company",
"cycle_id": "c1a2b3c4-…",
"owner_id": "u9f8e7d6-…",
"team_id": null,
"company_id": null,
"visibility": "organisation"
}'{
"id": "f4b1a2c8-…",
"title": "Improve onboarding conversion",
"description": "Lift activation in the first 7 days",
"type": "org",
"cycle_id": "c1a2b3c4-…",
"owner_id": "u9f8e7d6-…",
"department_id": null,
"team_id": null,
"company_id": null,
"visibility": "organisation",
"created_at": "2026-04-19T12:00:00Z"
}/api/v1/okrs/:idrequireswrite:okrsUpdate an existing OKR. Only the fields you include are changed. Returns the full updated OKR.
400No updatable fields provided or values invalid.404OKR does not exist in this workspace.
curl -X PUT https://go.okrstool.com/api/v1/okrs/OKR_ID \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Improve onboarding conversion (revised)",
"owner_id": "u9f8e7d6-…",
"visibility": "team"
}'{
"id": "f4b1a2c8-…",
"title": "Improve onboarding conversion (revised)",
"type": "org",
"owner_id": "u9f8e7d6-…",
"visibility": "team",
"updated_at": "2026-04-19T12:05:00Z"
}/api/v1/okrs/:idrequireswrite:okrsDelete an OKR and all its Key Results. Returns 204 with an empty body on success.
404OKR does not exist in this workspace.
curl -X DELETE https://go.okrstool.com/api/v1/okrs/OKR_ID \
-H "Authorization: Bearer okrt_your_api_key_here"(empty body)/api/v1/okrs/:id/key-resultsrequireswrite:okrsAdd a Key Result to an OKR. Required: title (string), metric_type (one of number, percentage, currency, boolean), start_value (number), target_value (number). Optional: current_value (number — defaults to start_value), owner_id (UUID), unit (string, e.g. '%'). Stored KR status is one of on_track, at_risk, off_track, not_started — set via the check-in endpoint.
400title, metric_type, start_value, or target_value missing or invalid.404OKR does not exist in this workspace.
curl -X POST https://go.okrstool.com/api/v1/okrs/OKR_ID/key-results \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Lift signup → activation rate to 50%",
"metric_type": "percentage",
"start_value": 32,
"target_value": 50,
"current_value": 35,
"owner_id": "u9f8e7d6-…",
"unit": "%"
}'{
"id": "kr-2c8f…",
"okr_id": "f4b1a2c8-…",
"title": "Lift signup → activation rate to 50%",
"metric_type": "percentage",
"start_value": 32,
"target_value": 50,
"current_value": 35,
"owner_id": "u9f8e7d6-…",
"unit": "%",
"status": "on_track",
"created_at": "2026-04-19T12:00:00Z"
}/api/v1/key-results/:idrequireswrite:okrsUpdate an existing Key Result. Only the fields you include are changed. Returns the full updated Key Result.
400No updatable fields provided or values invalid.404Key Result does not exist in this workspace.
curl -X PUT https://go.okrstool.com/api/v1/key-results/KR_ID \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Lift activation rate to 55%",
"target_value": 55,
"current_value": 38,
"owner_id": "u9f8e7d6-…"
}'{
"id": "kr-2c8f…",
"okr_id": "f4b1a2c8-…",
"title": "Lift activation rate to 55%",
"metric_type": "percentage",
"start_value": 32,
"target_value": 55,
"current_value": 38,
"owner_id": "u9f8e7d6-…",
"unit": "%",
"status": "on_track",
"updated_at": "2026-04-19T12:05:00Z"
}/api/v1/key-results/:idrequireswrite:okrsDelete a Key Result and all its check-ins. Returns 204 with an empty body on success.
404Key Result does not exist in this workspace.
curl -X DELETE https://go.okrstool.com/api/v1/key-results/KR_ID \
-H "Authorization: Bearer okrt_your_api_key_here"(empty body)Cycles
List the OKR cycles in your workspace. Use the returned id values as cycle_id when creating OKRs via POST /api/v1/okrs.
/api/v1/cyclesrequiresokrs:readReturns all cycles in your workspace ordered by start_date (newest first). status is computed from today's date: active (today is between start_date and end_date inclusive), upcoming (start_date is in the future), or ended (end_date is in the past).
401Invalid or missing API key.403Key is missing the okrs:read scope.
curl https://go.okrstool.com/api/v1/cycles \
-H "Authorization: Bearer okrt_your_api_key_here"[
{
"id": "c1a2b3c4-…",
"name": "Q2 2026",
"start_date": "2026-04-01",
"end_date": "2026-06-30",
"status": "active"
},
{
"id": "c0e7…",
"name": "Q1 2026",
"start_date": "2026-01-01",
"end_date": "2026-03-31",
"status": "ended"
}
]KPIs
Read tracked KPIs and post new measurement values.
/api/v1/kpisrequireskpis:readReturns up to 500 KPIs with their current value, target, unit, and computed status.
401Invalid or missing API key.403Key is missing the kpis:read scope.
curl https://go.okrstool.com/api/v1/kpis \
-H "Authorization: Bearer okrt_your_api_key_here"[
{
"id": "a812…",
"name": "Weekly Active Users",
"current": 3721,
"target": 4500,
"unit": "users",
"status": "at_risk",
"last_updated": "2026-04-01T09:30:00Z"
}
]/api/v1/kpisrequireskpis:writeCreates a new KPI in the workspace. Required: name. Optional: description, category (one of acquisition, conversion, retention, revenue, custom — defaults to custom), group_name (free-text grouping label), owner_id (profile UUID in the same org), team_id (team UUID in the same org), current_value (number), target_value (number), unit (e.g. '%', '$', 'users'), update_frequency (weekly | monthly | quarterly — defaults to monthly), direction (higher | lower — defaults to higher).
400Missing required field 'name', or invalid value (e.g. owner_id/team_id not in this org, unknown category, bad enum).401Invalid or missing API key.403Key is missing the kpis:write scope.
curl -X POST https://go.okrstool.com/api/v1/kpis \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Weekly Active Users",
"description": "Unique users who logged in this week",
"category": "retention",
"group_name": "North Star metrics",
"current_value": 3721,
"target_value": 4500,
"unit": "users",
"update_frequency": "weekly",
"direction": "higher"
}'{
"id": "a812…",
"organisation_id": "org-uuid",
"name": "Weekly Active Users",
"description": "Unique users who logged in this week",
"owner_id": null,
"team_id": null,
"category": "retention",
"group_name": "North Star metrics",
"current_value": 3721,
"target_value": 4500,
"unit": "users",
"status": "on_track",
"update_frequency": "weekly",
"direction": "higher",
"last_updated_at": null,
"created_by": "user-uuid",
"created_at": "2026-04-30T09:30:00Z",
"updated_at": "2026-04-30T09:30:00Z"
}/api/v1/kpis/:id/updatesrequireskpis:writeRecords a new KPI measurement. The previous value is captured automatically for trend tracking.
400value is missing or not a number.404KPI does not exist in this workspace.
curl -X POST https://go.okrstool.com/api/v1/kpis/KPI_ID/updates \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"value": 3810,
"comment": "Small uptick after release"
}'{
"id": "u-7c2…",
"kpi_id": "KPI_ID",
"previous_value": 3721,
"new_value": 3810,
"created_at": "2026-04-19T12:00:00Z"
}Initiatives
Manage initiatives — work items linked to OKRs and Key Results.
/api/v1/initiativesrequiresokrs:readList initiatives in your workspace. Optional query params: status (todo, in_progress, done, blocked), priority (p0, p1, p2, p3), okr_id (UUID — filter by linked OKR), kr_id (UUID — filter by linked Key Result). Initiative responses include a 'priority' field (p0 | p1 | p2 | p3 | null).
400Invalid query parameter (unknown status, malformed UUID).401Invalid or missing API key.403Key is missing the okrs:read scope.
curl https://go.okrstool.com/api/v1/initiatives?status=in_progress \
-H "Authorization: Bearer okrt_your_api_key_here"{
"data": [
{
"id": "init-uuid",
"title": "Launch onboarding redesign",
"description": "Ship the new onboarding flow",
"status": "in_progress",
"start_date": "2026-04-01",
"end_date": "2026-05-15",
"owner_id": "user-uuid",
"kr_id": "kr-uuid",
"okr_id": "okr-uuid",
"created_at": "2026-03-15T09:00:00Z",
"updated_at": "2026-04-10T11:00:00Z"
}
]
}/api/v1/initiatives/:idrequiresokrs:readFetch a single initiative by ID. Must belong to the authenticated workspace.
401Invalid or missing API key.404Initiative not found in this workspace.
curl https://go.okrstool.com/api/v1/initiatives/INIT_ID \
-H "Authorization: Bearer okrt_your_api_key_here"{
"data": {
"id": "init-uuid",
"title": "Launch onboarding redesign",
"description": "Ship the new onboarding flow",
"status": "in_progress",
"start_date": "2026-04-01",
"end_date": "2026-05-15",
"owner_id": "user-uuid",
"kr_id": "kr-uuid",
"okr_id": "okr-uuid",
"created_at": "2026-03-15T09:00:00Z",
"updated_at": "2026-04-10T11:00:00Z"
}
}/api/v1/initiativesrequireswrite:okrsCreate an initiative. Required: title. Optional: description, status (todo | in_progress | done | blocked, default todo), priority (p0 | p1 | p2 | p3 — null/omitted means no priority), start_date (YYYY-MM-DD), end_date (YYYY-MM-DD), owner_id (member UUID in this org), okr_id (UUID — informational), kr_id (UUID — links the initiative to a Key Result).
400Missing title, invalid status, or referenced owner/okr/kr does not belong to this org.401Invalid or missing API key.403Key is missing the write:okrs scope.
curl -X POST https://go.okrstool.com/api/v1/initiatives \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Launch onboarding redesign",
"description": "Ship the new onboarding flow",
"status": "in_progress",
"start_date": "2026-04-01",
"end_date": "2026-05-15",
"owner_id": "user-uuid",
"kr_id": "kr-uuid"
}'{
"data": {
"id": "init-uuid",
"title": "Launch onboarding redesign",
"description": "Ship the new onboarding flow",
"status": "in_progress",
"start_date": "2026-04-01",
"end_date": "2026-05-15",
"owner_id": "user-uuid",
"kr_id": "kr-uuid",
"okr_id": "okr-uuid",
"created_at": "2026-04-30T09:30:00Z",
"updated_at": "2026-04-30T09:30:00Z"
}
}/api/v1/initiatives/:idrequireswrite:okrsUpdate an initiative. All fields are optional; provide at least one. Updatable fields include title, description, status, priority (p0 | p1 | p2 | p3 | null), start_date, end_date, owner_id, kr_id. Passing kr_id replaces the existing KR link (pass null to clear).
400Invalid field value or no fields provided.404Initiative not found in this workspace.
curl -X PUT https://go.okrstool.com/api/v1/initiatives/INIT_ID \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"status": "done",
"end_date": "2026-05-10"
}'{
"data": {
"id": "init-uuid",
"title": "Launch onboarding redesign",
"status": "done",
"end_date": "2026-05-10",
"...": "other fields"
}
}/api/v1/initiatives/:idrequireswrite:okrsDelete an initiative and its KR links.
401Invalid or missing API key.404Initiative not found in this workspace.
curl -X DELETE https://go.okrstool.com/api/v1/initiatives/INIT_ID \
-H "Authorization: Bearer okrt_your_api_key_here"{ "success": true }Reviews
Read review cycles and the raw answers from shared reviews.
/api/v1/review-cyclesrequiresreviews:readLists all review cycles in the workspace with the count of distinct reviewees.
403Key is missing the reviews:read scope.
curl https://go.okrstool.com/api/v1/review-cycles \
-H "Authorization: Bearer okrt_your_api_key_here"[
{
"id": "rc-2c8…",
"name": "Q2 2026 Mid-Year Review",
"status": "active",
"due_date": "2026-05-03",
"reviewee_count": 2
}
]/api/v1/reviews/:id/summaryrequiresreviews:readReturns the raw question/answer pairs from a shared review. Only reviews that have been shared (status: shared) are accessible — drafts return 404. We intentionally return raw answers rather than a curated summary so you can run your own AI summarisation downstream.
404Review does not exist or has not been shared yet.
curl https://go.okrstool.com/api/v1/reviews/REVIEW_ID/summary \
-H "Authorization: Bearer okrt_your_api_key_here"{
"review_id": "REVIEW_ID",
"employee": "Sarah Chen",
"cycle": "Q2 2026 Mid-Year Review",
"type": "self",
"status": "shared",
"answers": [
{
"question": "What went well this cycle?",
"answer": "Shipped the onboarding redesign on time"
},
{
"question": "What are your growth areas?",
"answer": "Delegation and prioritisation"
}
],
"shared_at": "2026-04-19T12:00:00Z"
}Webhooks
Subscribe to workspace events and receive signed HTTPS POSTs as things happen. Use webhooks to keep external systems in sync instead of polling the REST API.
Registering a webhook
Webhooks are managed from Settings → Webhooks. You must be an organisation admin to create or edit a webhook.
- Endpoint URL must use
https://. - Pick only the events your endpoint needs.
- The signing secret is shown once at creation. Store it securely — it cannot be retrieved later.
There is no public API to create webhooks today. If you need to rotate a compromised secret, delete the webhook and create a new one.
Use the Send test action on a saved webhook to verify your receiver. Test payloads carry event:"test".
POST https://your-service.com/webhooks/okrs-tool
Headers:
Content-Type: application/json
X-OKRs-Event: okr.created
X-OKRs-Signature: <hex HMAC-SHA256>Live events
These are the events the app currently emits. Each webhook subscription can select any combination of the events it needs.
| Event | When it fires |
|---|---|
| okr.created | A new OKR is created in the workspace. |
| okr.updated | An existing OKR's fields are saved. |
| okr.completed | An OKR is marked as completed. |
| kr.checked_in | A Key Result receives a check-in value/note. |
| kpi.updated | A KPI's current value is updated. |
| review.completed | A performance review is submitted. |
| cycle.started | A new OKR cycle is started/activated. |
| cycle.ended | An OKR cycle is closed. |
Payload envelope
Every delivery uses the same envelope. The data object is event-specific and mirrors the shape you would receive from the REST API for the affected resource.
{
"event": "okr.created",
"timestamp": "2026-08-31T14:32:01.123Z",
"organisation_id": "550e8400-e29b-41d4-a716-446655440000",
"data": {
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"title": "Improve onboarding",
"type": "individual"
}
}event— one of the live event names.timestamp— ISO 8601 UTC string set at dispatch time.organisation_id— the workspace the event belongs to.data— event-specific payload object.
Signature verification
Verify every payload before acting on it. The signature is computed as HMAC-SHA256 over the raw JSON body, rendered as lowercase hex, and delivered in the X-OKRs-Signature header. The event name is also repeated in X-OKRs-Event.
const crypto = require('crypto');
const secret = 'whsec_...'; // your webhook signing secret
const signature = req.headers['x-okrs-signature'];
const body = await getRawBody(req); // the RAW request body as a string
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
throw new Error('Invalid signature');
}- Read the raw body before parsing JSON.
- Use a constant-time comparison to avoid timing attacks.
- Do not trust the
X-OKRs-Eventheader alone; always verify the signature.
Delivery behaviour
The app makes a single POST attempt per event per subscribed webhook.
- Timeout: 10 seconds.
- Redirects: not followed. A 3xx response is treated as a failed delivery.
- Retries: none. A non-2xx response or network failure is recorded but not retried.
- Ordering: not guaranteed across events; design your receiver to be idempotent.
Because deliveries are best-effort with no retry, reconcile important state through the REST API rather than assuming every webhook will land. The webhook's Last status column in Settings shows the most recent HTTP response code.
MCP Server
Connect OKRs Tool to Claude, Cursor, and any MCP-compatible AI assistant. The Model Context Protocol (MCP) lets LLMs read OKRs, KPIs, risks, and check in on Key Results on your behalf.
MCP access is included on every plan (Launch, Scale, and Expand) — no add-on required.
Claude Desktop / Cursor config
Add this block to your claude_desktop_config.json (or the equivalent MCP config in Cursor / Claude Code):
{
"mcpServers": {
"okrstool": {
"url": "https://go.okrstool.com/api/public/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}Available tools
| Tool | Description | Parameters |
|---|---|---|
| get_okrs | List OKRs for the active cycle with progress and status | cycle_id?, status? |
| get_okr | Get a single OKR with all its Key Results | okr_id |
| create_checkin | Log a check-in on a Key Result | kr_id, value, note? |
| get_kpis | List KPIs with current values and targets | status? |
| get_cycle | Get the active cycle details and overall health | — |
| get_risks | OKRs and KRs at risk of missing their targets | severity? |
| get_insights | AI-generated insights for the organisation | — |
| get_team_performance | Team health and OKR performance summary | cycle_id? |
| search_okrs | Search OKRs and Key Results by keyword | query, cycle_id? |
Test it from the CLI
curl -X POST https://go.okrstool.com/api/public/mcp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"method":"tools/list"}'curl -X POST https://go.okrstool.com/api/public/mcp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"method":"tools/call",
"params":{
"name":"get_okrs",
"arguments":{"status":"at_risk"}
}
}'Guides
End-to-end patterns for the most common API workflows.
Push OKR progress from an external system
This guide shows how to use the API to sync OKR progress from an external source — for example, a BI tool, a product analytics platform, or a spreadsheet pipeline.
- An API key with okrs:read and okr-checkins:write scopes
- The Key Result IDs you want to update (fetched in step 1)
Step 1 — Fetch your OKRs and Key Result IDs
curl https://go.okrstool.com/api/v1/okrs \
-H "Authorization: Bearer okrt_your_api_key_here"Each OKR in the response includes an id field. Note the id of the Key Result you want to update — you’ll use it in step 2.
Step 2 — Post a check-in to a Key Result
curl -X POST https://go.okrstool.com/api/v1/okrs/KR_ID/checkins \
-H "Authorization: Bearer okrt_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"value": 82,
"comment": "Pulled from analytics pipeline — weekly active users up 12%",
"status": "on_track"
}'Step 3 — Automate with a scheduled job
import requests
API_KEY = "okrt_your_api_key_here"
KR_ID = "your-kr-uuid"
def push_progress(value: float, comment: str, status: str):
response = requests.post(
f"https://go.okrstool.com/api/v1/okrs/{KR_ID}/checkins",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"value": value, "comment": comment, "status": status}
)
response.raise_for_status()
return response.json()
push_progress(82, "Weekly sync from analytics pipeline", "on_track")Status values
| Value | Meaning |
|---|---|
on_track | Progress is on pace to hit the target |
at_risk | Progress is lagging but recoverable |
off_track | Progress is significantly behind target |
Pull OKR health data into a BI tool
Use this pattern to extract OKR health data into Power BI, Tableau, Looker, or any BI tool that can consume a REST API or JSON file.
Step 1 — Fetch OKRs
curl https://go.okrstool.com/api/v1/okrs \
-H "Authorization: Bearer okrt_your_api_key_here" \
-o okrs.jsonEach record includes progress (0–1 float), status, health_score (0–100), owner, cycle, and type. These fields map cleanly to BI dimensions and measures.
Step 2 — Fetch KPIs
curl https://go.okrstool.com/api/v1/kpis \
-H "Authorization: Bearer okrt_your_api_key_here" \
-o kpis.jsonStep 3 — Load into your BI tool
- Power BI: Web connector → paste the endpoint URL → authenticate with the API key as a header
- Tableau: Web Data Connector or JSON file import
- Looker / Metabase: Custom API connector or JSON dataset
Generate OKRs and KPIs with AI and sync via API
Combine an LLM (ChatGPT, Claude, Gemini, or your own model) with the OKRs Tool API to turn a strategic plan into structured OKRs and KPIs inside your workspace — no manual entry required.
- An API key with okrs:write and kpis:write scopes
- Your strategic plan, OKR draft, or annual goals as text
- An AI tool you can prompt (web UI or API)
Step 1 — Prompt the AI to draft structured OKRs and KPIs
Paste your strategy doc into ChatGPT or Claude and ask for structured JSON. A prompt like the one below produces output that maps directly to the API field names.
You are an OKR coach. From the strategic plan below, produce JSON with two arrays:
1. "okrs": each item has { title, type ("company"|"department"|"team"), key_results: [{ title, metric_type, start_value, target_value, unit }] }
2. "kpis": each item has { name, description, category ("acquisition"|"conversion"|"retention"|"revenue"|"custom"), update_frequency ("weekly"|"monthly"|"quarterly"), direction ("higher"|"lower"), target_value, unit }
Return only valid JSON. No prose, no markdown fences.
Strategic plan:
<paste your plan here>Step 2 — Find the IDs you need (cycle, owner)
For OKRs you’ll need a cycle_id. Fetch your active cycles with GET /api/v1/cycles and copy the id of the cycle you want the new goals to land in. Owner UUIDs come from your org’s profiles — pass them as owner_id when creating each KPI.
curl https://go.okrstool.com/api/v1/cycles \
-H "Authorization: Bearer okrt_your_api_key_here"Step 3 — Create each KPI from the AI output
Loop over the kpis array and POST each one. The same pattern applies to OKRs via POST /api/v1/okrs.
const response = await fetch('https://go.okrstool.com/api/v1/kpis', {
method: 'POST',
headers: {
'Authorization': 'Bearer okrt_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Member Retention Rate',
description: 'Percentage of members who renew year-over-year',
category: 'custom',
owner_id: 'your-owner-uuid',
update_frequency: 'quarterly',
direction: 'higher',
target_value: 85,
unit: '%'
})
});
const kpi = await response.json();
console.log('Created KPI:', kpi.id);Step 4 — Bulk import from a JSON file
import fs from 'node:fs';
const API_KEY = 'okrt_your_api_key_here';
const BASE = 'https://go.okrstool.com/api/v1';
const plan = JSON.parse(fs.readFileSync('./ai-output.json', 'utf8'));
for (const kpi of plan.kpis) {
const res = await fetch(`${BASE}/kpis`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(kpi)
});
if (!res.ok) {
console.error('Failed', kpi.name, await res.text());
continue;
}
const created = await res.json();
console.log('✓', created.name, created.id);
}400 response. Stick to the documented enums: category ∈ acquisition | conversion | retention | revenue | custom, update_frequency ∈ weekly | monthly | quarterly, direction ∈ higher | lower.