Pulse API
A REST API to read and manage your assets, people, and licenses, plus webhooks to receive events in real time. All endpoints return JSON and are scoped to your organization.
Authentication
Every request needs a bearer token. Create one under Configure → API (available on the Obsidian plan and above). The token is shown once — store it securely.
curl https://<your-workspace>.onyx-labs.io/api/v1/assets \ -H "Authorization: Bearer pulse_your_token_here"
The base URL is your workspace subdomain followed by /api/v1. Requests are rate-limited per IP.
Scopes
Each token carries scopes. A read-only token gets every :read scope; a read-and-write token also gets the :write scopes.
- assets:read
- assets:write
- people:read
- people:write
- licenses:read
Responses
Successful responses wrap the result in a data key; errors return an error message with an appropriate status code.
{ "data": { "id": "…", "assetTag": "PL-0001" } }
{ "error": "Invalid or revoked API token." }Assets
curl -X POST https://<your-workspace>.onyx-labs.io/api/v1/assets \
-H "Authorization: Bearer pulse_…" \
-H "Content-Type: application/json" \
-d '{
"serialNumber": "C02X1234JGH7",
"make": "Apple",
"model": "MacBook Pro 14",
"status": "IN_STOCK"
}'People
curl -X POST https://<your-workspace>.onyx-labs.io/api/v1/people \
-H "Authorization: Bearer pulse_…" \
-H "Content-Type: application/json" \
-d '{ "name": "Jane Doe", "email": "jane@acme.com", "role": "EMPLOYEE" }'Sensitive fields (permissions, 2FA secrets) are never returned, and the API can’t grant the Superadmin role.
Licenses
Webhooks
Register an endpoint under Configure → Webhooks (Onyx plan). Pulse sends a POST with a JSON body when a subscribed event fires. A webhook with no events selected receives all of them. Delivery is best-effort and retried on the next matching event.
Events
- asset.created
- asset.updated
- asset.checked_out
- asset.checked_in
- person.created
- person.updated
- person.deleted
- ping
Payload
{
"id": "3f2a…", // unique delivery id
"event": "person.created",
"createdAt": "2026-07-05T21:00:00.000Z",
"organizationId": "…",
"data": { "personId": "…" }
}Verifying the signature
Each request carries X-Pulse-Event and X-Pulse-Signature. The signature is sha256= followed by an HMAC-SHA256 of the raw request body keyed with your webhook secret. Compare it against your own computation to confirm authenticity.
import { createHmac, timingSafeEqual } from "crypto";
function verify(rawBody, signature, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && timingSafeEqual(a, b);
}