Developers

API overview

A high-level tour of the Performance Blocks REST API — base URL, versioning, conventions, and how to find the OpenAPI spec.

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

The Performance Blocks API is a JSON over HTTPS interface for programmatic access to the same data and actions that power the Performance Blocks application. Use it to build integrations with HRIS systems, BI dashboards, custom intake tools, internal slash commands, or any workflow that needs to read or write performance data outside the standard product UI.

This article gives you the conventions you need before you read any per-resource reference. Every endpoint in the API follows these rules unless explicitly noted.

What the API is for

The API is designed for three primary use cases:

  • Integrations — Sync employee records from an HRIS, post observations from a chat tool, send summaries to a data warehouse, or surface notifications in a third-party inbox.
  • Custom dashboards and reports — Pull observations, summaries, and objective data into a BI tool or internal report that goes beyond what the built-in analytics surface.
  • Custom workflows — Capture observations from a custom mobile app, trigger summary generation from a calendar event, or auto-create assignments when an objective changes status.

The API is not a UI delegation layer — there is no OAuth flow for end-user impersonation. Every request runs in the context of the API key's organization, and authorization decisions are based on the key's scopes (see Authentication and API keys).

Base URL

All API requests go to a single canonical base URL:

https://api.performanceblocks.com/v1

There are no regional endpoints. Requests are routed globally and the response is served from the closest healthy edge.

Append the resource path to the base URL. For example:

https://api.performanceblocks.com/v1/employees
https://api.performanceblocks.com/v1/observations/obs_01HXX...

All requests must use HTTPS. Plain HTTP is rejected at the edge.

REST principles

The API follows standard REST conventions:

  • Resources are nouns, identified by a plural URL segment (/employees, /observations).
  • Verbs are HTTP methods: GET to read, POST to create, PATCH to update, DELETE where applicable. State-changing actions that don't fit CRUD use a sub-path (POST /observations/{id}/archive).
  • JSON in, JSON out. Send Content-Type: application/json on writes; responses are always JSON.
  • Predictable URLs. Singular resources are accessed by ID (/observations/{id}); collections are accessed at the resource root (/observations).
  • Standard status codes200/201 for success, 4xx for client errors, 5xx for server errors. See Rate limits and errors for the full table.

Versioning

The API is versioned in the URL path. The current version is v1.

  • New fields, new endpoints, new optional parameters, and new event types are additive and do not constitute a breaking change. Clients should ignore unknown fields in responses.
  • Breaking changes — removed fields, renamed endpoints, changed required parameters, changed default behavior — get a new version prefix (/v2). The previous version remains available for at least 12 months after the next version ships.
  • Deprecations are announced in the changelog and surfaced via the Deprecation and Sunset HTTP response headers when applicable.

Pin to a specific version in your client by always including /v1 in the URL — never strip the version segment.

Resource list

The API exposes the following top-level resources. Each one has a dedicated reference article.

Resource Path Reference
Employees /employees Employees API
Observations /observations Observations API
Conversations /conversations
Summaries /summaries Summaries API
Team summaries /team-summaries
Employee objectives /employee-objectives Objectives API
Manager objectives /manager-objectives Objectives API
Feedback assignments /assignments
Review items /review-items
Notifications /notifications
User preferences /preferences
Search /search
Webhooks /webhooks Webhooks
Session /session
OpenAPI spec /openapi.json (see below)

Resources without a dedicated article follow the same conventions described here.

Pagination

Collection endpoints (anything that returns a list) are cursor-paginated. Two query parameters control the page:

Parameter Type Default Max Notes
limit integer 50 100 Number of items in the response.
cursor string Opaque token returned in the previous page's next_cursor.

The response envelope always includes a data array and a next_cursor string (or null when the collection is exhausted):

{
  "data": [ /* ... up to `limit` items ... */ ],
  "next_cursor": "eyJpZCI6Im9ic18wMUhYWC4uLiJ9",
  "has_more": true
}

To fetch the next page, send the same request with ?cursor=eyJpZCI6... appended. When next_cursor is null, you've reached the end.

Cursors are opaque and may change format without notice — never parse, decode, or store them long-term. Re-issue the original query if you need to resume from scratch.

Sorting

Use ?sort=field to sort ascending, or ?sort=-field to sort descending. The default sort for most collections is -created_at (newest first).

GET /v1/observations?sort=-observation_date
GET /v1/employees?sort=last_name

You can sort by multiple fields with a comma-separated list — the first field takes precedence:

GET /v1/employees?sort=last_name,first_name

Each resource documents which fields are sortable. Sorting by an unsupported field returns a 400 validation_error.

Filtering

Use bracketed filter syntax for collection queries:

GET /v1/observations?filter[employee_id]=emp_01HXX...
GET /v1/observations?filter[type]=strength&filter[archived]=false
GET /v1/observations?filter[observation_date][gte]=2026-01-01&filter[observation_date][lt]=2026-04-01

Comparison operators supported on date and numeric fields:

Operator Meaning
eq (default) Equals — omit the operator for shorthand.
gt Greater than.
gte Greater than or equal.
lt Less than.
lte Less than or equal.
in Comma-separated value list.

Each resource documents which fields are filterable. Filtering by an unsupported field is silently ignored — never rely on a filter you haven't verified in the per-resource reference.

Expanding relationships

By default, related resources are returned as IDs (e.g. manager_id on an employee). Use ?expand= with a comma-separated list of relationship paths to inline the full object:

GET /v1/employees/emp_01HXX...?expand=manager,department

The response replaces the ID field with a nested object:

{
  "id": "emp_01HXX...",
  "first_name": "Jordan",
  "last_name": "Reyes",
  "manager": {
    "id": "emp_01HYY...",
    "first_name": "Sam",
    "last_name": "Patel",
    "email": "sam.patel@example.com"
  },
  "department": { "id": "dep_...", "name": "Engineering" }
}

Nested expansions are supported for one level (e.g. expand=manager on an observation expands employee.manager). Deeper nesting requires a follow-up request.

Each resource documents the supported expand paths.

Idempotency keys for POSTs

Any POST request that creates a resource accepts an Idempotency-Key header. Send the same key on a retried request and the API returns the original response without creating a duplicate.

POST /v1/observations
Idempotency-Key: 6f4a4f64-7c2b-4f4e-a8e1-9a8e9d2c1e10

Recommended pattern:

  • Generate a UUID v4 client-side before issuing a write that you may need to retry.
  • Reuse the same key for any retry of the same logical request.
  • Do not reuse the key for a different payload — the API returns 409 conflict if the stored response was for different inputs.

Idempotency keys are remembered for 24 hours. After that window, the same key may produce a new resource if reused.

Authentication

Every request must include an Authorization: Bearer pb_live_... header. Unauthenticated requests return 401. See Authentication and API keys for key creation, scopes, and rotation.

Standard request headers

Header When to send Notes
Authorization Every request Bearer pb_live_... or Bearer pb_test_....
Content-Type Every POST/PATCH Always application/json.
Accept Optional Defaults to application/json.
Idempotency-Key POST writes that may retry UUID; see above.
X-Request-ID Optional Echoed back in the response and surfaced in error logs. Useful for support.

Standard response headers

Header Meaning
X-Request-ID Server-assigned (or echoed) request identifier. Include this when contacting support.
X-RateLimit-Limit The current quota for your key.
X-RateLimit-Remaining Requests remaining in the current window.
X-RateLimit-Reset Unix epoch seconds when the window resets.
Retry-After On 429/503, seconds to wait before retrying.

OpenAPI specification

The full machine-readable specification is published at:

GET https://api.performanceblocks.com/v1/openapi.json

The spec follows OpenAPI 3.0 and includes:

  • Every endpoint with its parameters, request bodies, and response shapes.
  • Reusable component schemas for every resource (Employee, Observation, Summary, etc.).
  • Authentication requirements declared via securitySchemes.

You can fetch the spec with no authentication. Use it to generate clients in any language with tools like openapi-generator, oapi-codegen, or openapi-typescript. There are no first-party SDKs maintained by Performance Blocks today — generated clients are the recommended approach.

Postman

Import the spec directly:

  1. In Postman, choose File → Import and paste https://api.performanceblocks.com/v1/openapi.json.
  2. Postman generates a collection with one folder per resource.
  3. Add a collection variable apiKey and use it as in the Authorization tab (set type to Bearer Token).
  4. Test against pb_test_ keys before switching to pb_live_.

TypeScript clients

A common pattern using openapi-typescript:

npx openapi-typescript https://api.performanceblocks.com/v1/openapi.json -o pb-types.ts

Then use the generated types with fetch:

import type { paths } from './pb-types';

type ListObservationsResponse =
  paths['/observations']['get']['responses']['200']['content']['application/json'];

const res = await fetch('https://api.performanceblocks.com/v1/observations?limit=10', {
  headers: { Authorization: `Bearer  

A minimal first request

The simplest request you can make is GET /session, which returns the authenticated user and organization context for your key:

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

A successful response looks like:

{
  "data": {
    "api_key": {
      "id": "key_01HXX...",
      "name": "Production HRIS sync",
      "scopes": ["employees:read", "employees:write"]
    },
    "organization": {
      "id": "org_01HXX...",
      "name": "Acme Inc",
      "plan": "agentic"
    }
  }
}

If you get a 200, your key is valid and your environment is wired up. If you get a 401, see Authentication and API keys. If you get a 403, your key lacks the necessary scope — keys created without session:read cannot call /session, but most keys include this by default.

Where to go next

  • Generate a key in Settings → API, then read Authentication and API keys.
  • Pick the resource you need from the table above and read its dedicated reference.
  • For event-driven integrations, set up a Webhook subscription so you don't have to poll.
  • When you ship to production, review Rate limits and errors for retry, backoff, and idempotency guidance.

© 2026 Performance Blocks. All rights reserved.