Developers

Summaries API

Reference for creating, updating, sharing, and approving employee summaries via the Performance Blocks API.

Plan availability: The Performance Blocks API is available on the Agentic plan. Team plan customers can upgrade in Settings → Billing.

A Summary is a manager's narrative review for a single employee covering a defined period. It rolls up observations and objectives, captures the manager's analysis as rich text, and moves through a status lifecycle from draft to shared. The Summaries API lets you list, create, update, share, approve, and collect feedback on summaries programmatically.

This is useful for:

  • Pre-populating draft summaries from external systems (e.g. a calendar of past 1:1s, an outside review tool's notes).
  • Pulling completed summaries into a data warehouse or BI dashboard.
  • Triggering downstream workflows when a summary is shared (notify HRIS, archive to document storage, etc.).

Resource shape

{
  "id": "sum_01HXX9YK7Z8N2P3Q4R5S6T7U8V",
  "employee_id": "emp_01HXX...",
  "manager_id": "emp_01HYY...",
  "period_start": "2026-01-01",
  "period_end": "2026-03-31",
  "body": {
    "type": "doc",
    "content": [
      { "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "Strengths" }] },
      { "type": "paragraph", "content": [{ "type": "text", "text": "Jordan led the Q1 launch end-to-end..." }] }
    ]
  },
  "status": "draft",
  "included_observation_ids": ["obs_01HXX...", "obs_01HYY..."],
  "included_objective_ids": ["obj_01HXX..."],
  "shared_at": null,
  "approved_at": null,
  "created_at": "2026-04-01T09:12:00.000Z",
  "updated_at": "2026-04-12T15:42:18.000Z"
}

Field reference

Field Type Notes
id string ULID prefixed with sum_. Read-only.
employee_id string The subject of the summary.
manager_id string The author. Defaults to the manager-of-record at create time.
period_start string (date) ISO 8601. Required.
period_end string (date) ISO 8601. Required. Must be on or after period_start.
body object Rich text JSON (ProseMirror-style). See below.
status enum draft, pending_review, approved, shared, archived.
included_observation_ids string[] Observations the summary explicitly cites.
included_objective_ids string[] Objectives the summary explicitly cites.
shared_at string (datetime) | null Set when the summary transitions to shared.
approved_at string (datetime) | null Set when the summary transitions to approved.
created_at string (datetime) Read-only.
updated_at string (datetime) Read-only.

Body shape

body is a structured rich-text document. The top-level shape is:

{ "type": "doc", "content": [ /* block nodes */ ] }

Supported block nodes: paragraph, heading (level 1–3), bullet_list, ordered_list, list_item, blockquote, horizontal_rule. Inline marks: bold, italic, underline, strike, link (with href attr).

If you only need plain text, send a single paragraph:

{ "type": "doc", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "..." }] }] }

Status lifecycle

A summary moves through these states:

draft -> pending_review -> approved -> shared
   \____________________________________/
                  v
               archived (terminal)
From To Trigger
draft pending_review POST /summaries/{id}/submit (or app: "Submit for review").
draft or pending_review approved POST /summaries/{id}/approve. Only required if the org enables review workflow.
approved or draft shared POST /summaries/{id}/share.
any archived POST /summaries/{id}/archive.

Once shared, the body is locked. Updates after sharing require unsharing first (admin-only via the application), or creating a new summary.

List summaries

GET /summaries

Query parameters

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[employee_id] string Restrict to one employee.
filter[manager_id] string Summaries authored by this manager.
filter[status] enum One of the status values. Repeat for multiple.
filter[period_start][gte] date Inclusive.
filter[period_end][lte] date Inclusive.
expand string employee, manager, observations, objectives.

Example: curl

curl "https://api.performanceblocks.com/v1/summaries\
?filter[status]=shared\
&filter[period_end][gte]=2026-01-01\
&limit=50" \
  -H "Authorization: Bearer $PB_API_KEY"

Get a summary

GET /summaries/{id}

Returns the full summary including the body. Returns 404 if the ID is not in your organization.

curl https://api.performanceblocks.com/v1/summaries/sum_01HXX... \
  -H "Authorization: Bearer $PB_API_KEY"

Create a summary

POST /summaries

Required scope: summaries:write.

Request body

{
  "employee_id": "emp_01HXX...",
  "manager_id": "emp_01HYY...",
  "period_start": "2026-01-01",
  "period_end": "2026-03-31",
  "body": {
    "type": "doc",
    "content": [
      { "type": "paragraph", "content": [{ "type": "text", "text": "Initial draft from external tool import." }] }
    ]
  },
  "included_observation_ids": ["obs_01HXX..."],
  "included_objective_ids": ["obj_01HXX..."]
}

Validation rules

  • employee_id must reference an employee in your organization.
  • manager_id is optional. If omitted, defaults to the employee's current manager.
  • period_start and period_end are required. period_end >= period_start. Both must be valid ISO dates and not in the future.
  • body is required and must conform to the rich-text schema above. Maximum serialized size 256 KB.
  • included_observation_ids and included_objective_ids must reference resources in your organization. Each list is capped at 200 entries.

A successful create returns 201 with status: "draft".

Example: TypeScript

const res = await fetch('https://api.performanceblocks.com/v1/summaries', {
  method: 'POST',
  headers: {
    Authorization: `Bearer  

Update a summary

PATCH /summaries/{id}

Required scope: summaries:write.

You can update body, period_start, period_end, included_observation_ids, and included_objective_ids while the status is draft or pending_review. Once approved or shared, the body is locked.

curl -X PATCH https://api.performanceblocks.com/v1/summaries/sum_01HXX... \
  -H "Authorization: Bearer $PB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"included_observation_ids": ["obs_01HXX...", "obs_01HZZ..."]}'

Restrictions

  • employee_id and manager_id cannot be changed after creation.
  • Updates after shared return 409 conflict.

Submit for review

POST /summaries/{id}/submit

Required scope: summaries:write. Transitions draftpending_review. Returns 409 if the summary is not in draft.

curl -X POST https://api.performanceblocks.com/v1/summaries/sum_01HXX.../submit \
  -H "Authorization: Bearer $PB_API_KEY"

Approve a summary

POST /summaries/{id}/approve

Required scope: summaries:write. Transitions pending_review (or draft, depending on org workflow) → approved. Sets approved_at. Returns 409 if the summary is not in a state that can be approved.

curl -X POST https://api.performanceblocks.com/v1/summaries/sum_01HXX.../approve \
  -H "Authorization: Bearer $PB_API_KEY"

Share a summary

POST /summaries/{id}/share

Required scope: summaries:write. Transitions to shared, sets shared_at, locks the body, and notifies the employee through the standard application channels (email + in-app notification, if those are enabled in their preferences).

Optional body to customize delivery:

{
  "notify_employee": true,
  "message": "Hi Jordan — see the Q1 review attached. Happy to chat live tomorrow."
}
Field Type Default Notes
notify_employee boolean true Set false to share silently (e.g. when delivering via your own channel).
message string | null null Optional personal note included in the notification. Max 1000 chars.

Returns the updated summary with status: "shared" and shared_at set.

Archive a summary

POST /summaries/{id}/archive

Required scope: summaries:write. Soft-deletes the summary. Excluded from default lists. Audit-preserved. There is no public restore endpoint — restoration is admin-only via the application.

Submit feedback on a summary

POST /summaries/{id}/feedback

Records employee acknowledgment or a clarification request on a shared summary.

Request body

{
  "kind": "acknowledgment",
  "comment": "Thanks — landed well. One follow-up question on Goal #2."
}
Field Type Notes
kind enum acknowledgment or clarification_request.
comment string | null Optional. 0–2000 characters.

The summary must be in shared status. The endpoint records the feedback and, if kind is clarification_request, surfaces it to the manager in the application.

Webhook events

Event Fires when
summary.created A summary is created (draft).
summary.updated Body or included IDs change.
summary.submitted Transition to pending_review.
summary.approved Transition to approved.
summary.shared Transition to shared.
summary.archived Transition to archived.
summary.feedback_submitted Employee posts feedback on a shared summary.

The delivery data field contains the post-change resource (and, for feedback_submitted, includes a feedback sub-object with kind, comment, author_id, created_at). See Webhooks.

Common error responses

Status Code Cause
400 validation_error Missing field, invalid date range, body too large, malformed body schema.
401 authentication_required Missing or invalid API key.
403 permission_denied Key lacks summaries:write (or :read for GETs).
404 not_found Summary not in your organization.
409 conflict Status transition not allowed (e.g. share an archived summary, update a shared summary).
422 unprocessable Referenced employee, observation, or objective is not in your organization.

Use cases

Pre-populating draft summaries from notes

If your team uses an external 1:1 notes tool, run a weekly job that pulls notes from the last quarter, drops them into a summary body, and creates a draft summary linked to the manager. The manager opens the application and finishes the review with a head start.

const draft = await pb.request('/summaries', {
  method: 'POST',
  body: JSON.stringify({
    employee_id: report.employee_id,
    period_start: quarter.start,
    period_end: quarter.end,
    body: notesToProseMirror(report.notes),
    included_observation_ids: report.observation_ids
  })
});

Archiving completed summaries to your DMS

Subscribe to summary.shared, fetch the summary by ID, render the body to PDF (your renderer of choice), and upload to your document management system. Track the id so you can correlate later.

Auditing summary throughput

Schedule a daily GET /summaries?filter[status]=shared&filter[period_end][gte]=... and pipe results into your warehouse. Use cursors to page through; persist the latest period_end you've ingested as a watermark.

© 2026 Performance Blocks. All rights reserved.