Developers
Objectives API
Reference for managing employee, manager, and org objectives via the Performance Blocks API — CRUD, lifecycle, and webhook events.
Plan availability: The Performance Blocks API is available on the Agentic plan. Team plan customers can upgrade in Settings → Billing.
Objectives in Performance Blocks come in three scopes — employee, manager, and org. They're exposed as two top-level resources in the API:
/employee-objectives— owned by an individual employee./manager-objectives— owned by a manager. Manager objectives can be team-scoped (scope: "manager") or organization-wide (scope: "org"); the convention is described below.
Both resources share the same field shape and lifecycle. The split exists because the application surfaces them in different places — IC review screens vs. manager / admin planning screens — and because permission checks differ.
This article covers fields, status transitions, CRUD endpoints, state actions, and the convention for org-scoped objectives.
Resource shape
{
"id": "obj_01HXX9YK7Z8N2P3Q4R5S6T7U8V",
"scope": "employee",
"owner_id": "emp_01HXX...",
"title": "Ship the new onboarding flow by end of Q2",
"description": "Reduce activation friction for new admins...",
"status": "in_progress",
"period_start": "2026-04-01",
"period_end": "2026-06-30",
"linked_summary_ids": ["sum_01HXX..."],
"created_at": "2026-04-01T09:30:00.000Z",
"updated_at": "2026-04-12T15:42:18.000Z",
"submitted_at": "2026-04-02T10:00:00.000Z",
"approved_at": "2026-04-03T14:00:00.000Z",
"completed_at": null
}
Field reference
| Field | Type | Notes |
|---|---|---|
id |
string | ULID prefixed with obj_. Read-only. |
scope |
enum | employee, manager, or org. Determines which top-level resource owns it. |
owner_id |
string | The employee responsible. For org-scoped objectives, the owner is typically a manager / org admin who acts as DRI. |
title |
string | 1–200 characters. Required. |
description |
string | null | 0–4000 characters. Plain text. |
status |
enum | proposed, approved, in_progress, completed, missed, archived. |
period_start |
string (date) | ISO 8601. Required. |
period_end |
string (date) | ISO 8601. Required. Must be on or after period_start. |
linked_summary_ids |
string[] | Summaries that explicitly cite this objective. Read-only — managed via the Summaries API. |
created_at |
string (datetime) | Read-only. |
updated_at |
string (datetime) | Read-only. |
submitted_at |
string (datetime) | null | Set on transition proposed → review. |
approved_at |
string (datetime) | null | Set on transition to approved or in_progress. |
completed_at |
string (datetime) | null | Set on transition to completed. |
Resource convention by scope
| Scope | Resource | Owner | Visibility |
|---|---|---|---|
employee |
/employee-objectives |
The individual contributor. | Owner, owner's managers, org admins. |
manager |
/manager-objectives |
A manager. Applies to their team. | Owner, owner's managers, org admins. |
org |
/manager-objectives (with scope: "org") |
An org admin. Applies organization-wide. | All employees can read; only org admins can write. |
For org objectives, set scope: "org" on create against /manager-objectives. The endpoint validates that the caller's key has admin-level scope and that the owner_id references an employee with role: "org_admin".
Status lifecycle
Both resources share this state machine:
proposed -> approved -> in_progress -> completed
\
-> missed
any -> archived (terminal)
| From | To | Endpoint |
|---|---|---|
proposed |
(review pending) | POST /…-objectives/{id}/submit |
proposed or review |
approved |
POST /…-objectives/{id}/approve |
proposed or review |
proposed (with reason) |
POST /…-objectives/{id}/reject |
approved |
in_progress |
Auto on period_start, or explicit PATCH to status: "in_progress". |
in_progress |
completed |
POST /…-objectives/{id}/complete |
in_progress |
missed |
POST /…-objectives/{id}/miss |
| any | archived |
POST /…-objectives/{id}/archive |
Objectives created without an explicit approval workflow can skip directly from proposed to approved via the approve action. Whether submit/reject are used depends on org configuration.
List objectives
GET /employee-objectives
GET /manager-objectives
Query parameters (both resources)
| Parameter | Type | Notes |
|---|---|---|
limit |
integer | Default 50, max 100. |
cursor |
string | Pagination. |
sort |
string | period_end, -period_end, created_at, -created_at. Default -period_end. |
filter[owner_id] |
string | Restrict to one owner. |
filter[scope] |
enum | employee, manager, or org. (/employee-objectives always returns employee; /manager-objectives returns manager and org by default.) |
filter[status] |
enum | Repeat for multiple. |
filter[period_end][gte] |
date | Inclusive. |
filter[period_start][lte] |
date | Inclusive. |
expand |
string | owner (Employee), linked_summaries (Summary[]). |
Example: curl
# All in-progress employee objectives for one report
curl "https://api.performanceblocks.com/v1/employee-objectives\
?filter[owner_id]=emp_01HXX...\
&filter[status]=in_progress" \
-H "Authorization: Bearer $PB_API_KEY"
# All org objectives for the current quarter
curl "https://api.performanceblocks.com/v1/manager-objectives\
?filter[scope]=org\
&filter[period_end][gte]=2026-04-01\
&filter[period_end][lte]=2026-06-30" \
-H "Authorization: Bearer $PB_API_KEY"
Get an objective
GET /employee-objectives/{id}
GET /manager-objectives/{id}
curl https://api.performanceblocks.com/v1/employee-objectives/obj_01HXX... \
-H "Authorization: Bearer $PB_API_KEY"
Returns 404 if the objective is not in your organization or not accessible at the given resource (e.g. fetching a manager objective from /employee-objectives).
Create an objective
POST /employee-objectives
POST /manager-objectives
Required scope: objectives:write.
Request body — employee objective
{
"owner_id": "emp_01HXX...",
"title": "Ship the new onboarding flow by end of Q2",
"description": "Reduce activation friction for new admins...",
"period_start": "2026-04-01",
"period_end": "2026-06-30",
"status": "proposed"
}
Request body — manager objective
{
"owner_id": "emp_01HYY...",
"title": "Cut median PR review time by 30% in Q2",
"description": "Implement weekly review-day rotation...",
"scope": "manager",
"period_start": "2026-04-01",
"period_end": "2026-06-30",
"status": "proposed"
}
Request body — org objective
{
"owner_id": "emp_01HZZ...",
"scope": "org",
"title": "Hit 90% NRR for the SMB segment by year end",
"description": "Cross-functional initiative...",
"period_start": "2026-01-01",
"period_end": "2026-12-31",
"status": "approved"
}
Validation rules
owner_idmust reference an employee in your organization.titleis required.period_startandperiod_endare required;period_end >= period_start.statusdefaults toproposed. Settingapprovedorin_progresson create requiresobjectives:writeand the caller must be authorized for that workflow step.- For
/manager-objectives,scopemust bemanagerororg.orgrequires the API key to belong to an admin context (typically created by an org admin). - For
/employee-objectives,scopeis alwaysemployeeand is set automatically.
A successful create returns 201 with the new resource.
Example: TypeScript
const res = await fetch(
'https://api.performanceblocks.com/v1/employee-objectives',
{
method: 'POST',
headers: {
Authorization: `Bearer
Update an objective
PATCH /employee-objectives/{id}
PATCH /manager-objectives/{id}
Required scope: objectives:write.
You can update title, description, period_start, and period_end while the status is proposed, approved, or in_progress. After completed, missed, or archived, the objective is read-only — create a new one if intent changes.
curl -X PATCH https://api.performanceblocks.com/v1/employee-objectives/obj_01HXX... \
-H "Authorization: Bearer $PB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"description": "Refined scope after Q2 planning."}'
Restrictions
scopeandowner_idcannot be changed after creation. Create a new objective and archive the old one.- Setting
statusdirectly viaPATCHis allowed only betweenapprovedandin_progress. All other transitions go through their state action endpoint.
State actions
Each action returns the updated resource. All require objectives:write.
Submit
POST /{resource}/{id}/submit
Transitions proposed to internal review (still surfaces as proposed in the API; the difference is recorded in the audit log and the application surfaces the "awaiting approval" state).
Approve
POST /{resource}/{id}/approve
Body optional:
{ "comment": "Approved with minor scope tweak — see chat." }
Transitions to approved. Sets approved_at. Returns 409 if the objective is in a state that can't be approved.
Reject
POST /{resource}/{id}/reject
Body required:
{ "reason": "Scope is too broad for one quarter — split into two objectives and resubmit." }
Returns the objective to proposed and notifies the owner with the reason.
Complete
POST /{resource}/{id}/complete
Body optional:
{ "outcome": "Shipped on May 28. Activation increased 18%." }
Transitions to completed. Sets completed_at. Allowed from in_progress or approved.
Miss
POST /{resource}/{id}/miss
Body optional:
{ "reason": "Deprioritized after re-org in May." }
Transitions to missed. Counterpart to complete for objectives that didn't land. Used for honest end-of-period closeout.
Archive
POST /{resource}/{id}/archive
Soft-deletes the objective. Excluded from default lists. Audit-preserved. Allowed from any state.
Linking to summaries
Objectives are linked to summaries via the Summary resource — set included_objective_ids on a summary to cite specific objectives. The reverse link linked_summary_ids on an objective is read-only and updated automatically.
To find every summary that cites a given objective:
GET /summaries?filter[included_objective_id]=obj_01HXX...
(See Summaries API.)
Webhook events
| Event | Fires when |
|---|---|
objective.created |
Any objective is created. The payload includes scope. |
objective.updated |
Title, description, or period changes. |
objective.status_changed |
Status transitions through any state action. |
objective.archived |
Convenience event mirroring status_changed to archived. |
Subscribe once and filter by data.scope to handle employee / manager / org events differently. See Webhooks.
Common error responses
| Status | Code | Cause |
|---|---|---|
| 400 | validation_error |
Missing field, invalid date range, unknown status. |
| 401 | authentication_required |
Missing or invalid API key. |
| 403 | permission_denied |
Key lacks objectives:write; caller can't create org-scoped objectives. |
| 404 | not_found |
Objective not in your org, or fetched from the wrong resource. |
| 409 | conflict |
Status transition not allowed; objective already archived. |
Use cases
Importing goals from a planning tool
Map each row in your planning tool to either /employee-objectives or /manager-objectives based on owner role. Set status: "approved" if your planning tool already represents an approved goal — skip the submit/approve flow.
End-of-period closeout automation
After period_end, run a job that lists in_progress objectives and prompts each owner to mark complete or miss. Surface the prompts in your team's chat tool with action buttons that call the appropriate state-action endpoint.
Reporting on org-objective progress
Subscribe to objective.status_changed filtered by data.scope == "org". Push each event into your BI warehouse keyed by objective ID and timestamp. Build a downstream report showing how many org objectives are still on track at each week of the quarter.
Related
- API overview
- Summaries API — linking objectives into summaries.
- Webhooks