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_id must reference an employee in your organization.
  • title is required.
  • period_start and period_end are required; period_end >= period_start.
  • status defaults to proposed. Setting approved or in_progress on create requires objectives:write and the caller must be authorized for that workflow step.
  • For /manager-objectives, scope must be manager or org. org requires the API key to belong to an admin context (typically created by an org admin).
  • For /employee-objectives, scope is always employee and 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

  • scope and owner_id cannot be changed after creation. Create a new objective and archive the old one.
  • Setting status directly via PATCH is allowed only between approved and in_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.

© 2026 Performance Blocks. All rights reserved.