Skip to main content
Public demo Pulse is in demo mode. Open the demo
Pulse HR
Start free

Docs

REST API v1

Bearer auth, cursor pagination, webhooks on every event, maintained SDKs.

Base URL

https://api.pulsehr.it/v1

Self-host override

$ export PULSE_API_BASE=https://pulse.internal/v1

Authentication

All requests authenticate with a Bearer token. Mint keys in /developers inside the app. Keys carry a scope (read:*, write:leave, …) and an environment (pk_live_* or pk_test_*).

$ curl https://api.pulsehr.it/v1/employees \
    -H "Authorization: Bearer pk_live_a9f3…" \
    -H "Accept: application/json"

Pagination

Every list endpoint returns cursor pagination. Pass ?limit=50&cursor=…. Responses include next_cursor (null if there are no more pages) and total_count.

{
  "data": [ /* 50 employees */ ],
  "next_cursor": "eyJpZCI6ImVtcF8xOTgifQ==",
  "total_count": 312
}

Rate limits

1,000 requests per minute per API key on Standard. Self-host has no limit (your Postgres will be the bottleneck before this is). Every response carries three headers:

  • X-RateLimit-Limit: 1000
  • X-RateLimit-Remaining: 873
  • X-RateLimit-Reset: 2026-04-20T12:05:00Z

Error format

Every error returns the same envelope — an HTTP status, a stable code string, a human message, and (when relevant) a fields array pinpointing what's wrong.

HTTP/1.1 422 Unprocessable Entity
{
  "error": {
    "code": "unprocessable_entity",
    "message": "end_date must be after start_date",
    "fields": ["end_date"],
    "request_id": "req_01HR3…"
  }
}
HTTP code Meaning
400 invalid_request Payload failed schema validation. Response body lists the failing fields.
401 unauthenticated Missing or malformed Bearer token. Check the Authorization header.
403 permission_denied Authenticated, but the API key scope doesn't allow this operation.
404 not_found Resource ID unknown, or scoped to another tenant.
409 conflict Version mismatch on optimistic lock, or duplicate idempotency key.
422 unprocessable_entity Semantically invalid — e.g. end_date before start_date.
429 rate_limited 1,000 req/min exceeded. Retry-After header tells you when to try again.
500 internal_error Our bug. Every 5xx is paged and we'll email you with the incident ID within 24 h.

Resources

Partial list — the full spec is published as OpenAPI 3.1 at api.pulsehr.it/v1/openapi.json. Generated clients for TS / Python / Go drop on every merge.

/v1/employees

Active and former people in the workspace.

Key fields

  • id
  • email
  • full_name
  • role
  • department
  • manager_id
  • start_date
  • end_date
  • location
  • custom_fields

Methods

  • GET /v1/employees
  • GET /v1/employees/{id}
  • POST /v1/employees
  • PATCH /v1/employees/{id}
  • DELETE /v1/employees/{id}

Webhook events

  • employee.created
  • employee.updated
  • employee.offboarded
/v1/status_log

Async-standup entries. Three lines per person per day.

Key fields

  • id
  • employee_id
  • date
  • lines[]
  • topic
  • created_at

Methods

  • GET /v1/status-log
  • GET /v1/status-log/{id}
  • POST /v1/status-log

Webhook events

  • status_log.posted
/v1/kudos

Peer recognition. Feeds the Employee Score Recognition factor.

Key fields

  • id
  • from_employee_id
  • to_employee_id
  • coins
  • tag
  • note
  • created_at

Methods

  • GET /v1/kudos
  • POST /v1/kudos

Webhook events

  • kudos.awarded
/v1/workload_checkins

Weekly self-reported load — light, balanced, heavy, overloaded.

Key fields

  • id
  • employee_id
  • week_start
  • level
  • created_at

Methods

  • GET /v1/workload-checkins
  • POST /v1/workload-checkins

Webhook events

  • workload.recorded
/v1/leave

Personal leave journal. The IC logs their own days off.

Key fields

  • id
  • employee_id
  • kind
  • start_date
  • end_date
  • days
  • note

Methods

  • GET /v1/leave
  • GET /v1/leave/balance/{employee_id}
  • POST /v1/leave
  • DELETE /v1/leave/{id}

Webhook events

  • leave.logged
  • leave.cancelled

Webhooks

Configure endpoints in /developers → webhooks. Every delivery is HMAC-SHA256 signed with your endpoint's signing secret — verify via the X-Pulse-Signature header. Failed deliveries retry with exponential backoff for 72 hours; replay any event from the dashboard. See the full event catalogue on /ecosystem.

POST https://your.app/pulse/hook
Content-Type: application/json
X-Pulse-Event: leave.approved
X-Pulse-Signature: t=1745148720,v1=9f8a…
X-Pulse-Delivery: del_01HR3…

{
  "id": "evt_01HR3…",
  "type": "leave.approved",
  "created_at": "2026-04-20T10:12:03Z",
  "data": { /* full leave payload */ }
}

SDKs

TypeScript

@pulsehr/sdk

bun add @pulsehr/sdk

Python

pulsehr

pip install pulsehr

Go

pulsehr-go

go get github.com/pulsehr/pulsehr-go

Quick start — TypeScript

import { Pulse } from "@pulsehr/sdk";

const pulse = new Pulse({ apiKey: process.env.PULSE_API_KEY! });

// Post a status-log entry on behalf of an authenticated user
const entry = await pulse.statusLog.create({
  employee_id: "emp_01HR3…",
  lines: [
    "Shipped the OG localiser. Three locales live.",
    "Hit a CLS regression on /product; fix in 1742.",
    "Pairing with Niccolò on the workload trend curve.",
  ],
});

// Send a kudos
await pulse.kudos.create({
  to_employee_id: "emp_01HR4…",
  coins: 10,
  tag: "craft",
  note: "Picked up the failing migration without anyone asking.",
});