Developer

Levvr API

REST API for workforce, payroll, compliance, and incident data held in your Levvr tenant. Bearer-token authentication; tenant-scoped reads and writes; HMAC-signed webhooks for the three high-value lifecycle events.

Authentication

Every request includes an Authorization: Bearer lvr_live_… header. Issue and rotate keys at Settings → API keys. Plaintext keys are shown once on creation; we store only the SHA-256 hash.

Each key carries a scope set. Scopes follow theentity:actionshape — for example workers:read,payroll:read. A request that the key's scopes don't cover returns403.

Rate limits

Every endpoint is rate-limited per-tenant:

  • Read endpoints — 600 requests / minute (per tenant).
  • Write endpoints — 60 requests / minute (per tenant).
  • Exceeding the limit returns 429 with a Retry-After header (seconds).

Endpoints

More endpoints will land as customer demand surfaces. Tell us what you need at hello@levvr.com.au.

GET/api/v1/workersscope: workers:read

List active workers in your tenant. Cursor-paginated.

Request

curl -H 'Authorization: Bearer lvr_live_xxxxxxxxxxxxxxxxxxxxxxxx' \
  https://levvr.com.au/api/v1/workers?limit=50&cursor=cuid1

Response (200)

{
  "workers": [
    {
      "id": "wkr_xxx",
      "firstName": "Sam",
      "lastName": "Jones",
      "email": "sam@example.com",
      "role": "WORKER",
      "employmentType": "FULL_TIME",
      "active": true
    }
  ],
  "nextCursor": "wkr_yyy"
}

Webhooks

Configure endpoints at Settings → Webhooks. Each delivery includes:

  • X-Levvr-Signature — HMAC-SHA256 of the raw body, keyed on your endpoint's secret.
  • X-Levvr-Event — the event name (e.g. payroll.run.approved).
  • X-Levvr-Delivery — unique delivery id; safe to use for idempotency.
  • Retries on 5xx / timeout: 5 attempts with exponential backoff (1s, 4s, 30s, 5m, 1h).

Verify the signature in Node:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyLevvr(rawBody: string, signature: string, secret: string): boolean {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  if (a.length !== b.length) return false;
  return timingSafeEqual(a, b);
}

Event reference

payroll.run.approved

When a Pay Run transitions DRAFT → APPROVED.

{
  "event": "payroll.run.approved",
  "occurredAt": "2026-05-17T03:14:22Z",
  "data": {
    "payRunId": "pr_xxx",
    "periodStart": "2026-05-04",
    "periodEnd": "2026-05-10",
    "payDate": "2026-05-17",
    "totalGross": "184320.50",
    "lineItemCount": 42
  }
}
document.signed

When a competent-person sign-off completes on a SWMS / JSA / Risk Assessment.

{
  "event": "document.signed",
  "occurredAt": "2026-05-17T04:08:11Z",
  "data": {
    "documentId": "doc_xxx",
    "type": "SWMS",
    "jurisdiction": "NSW",
    "competentPersonName": "J. Smith",
    "competentPersonLicence": "WHS-NSW-12345"
  }
}
incident.notifiable-flagged

When an Incident Investigation toggles the notifiable flag false → true.

{
  "event": "incident.notifiable-flagged",
  "occurredAt": "2026-05-17T05:21:01Z",
  "data": {
    "incidentId": "inc_xxx",
    "severity": "MAJOR",
    "jurisdiction": "VIC",
    "occurredAt": "2026-05-16T18:30:00+10:00"
  }
}

Versioning & backwards-compatibility

Endpoint path includes the major version (/api/v1/...). Within v1 we will only add fields, not remove or rename them. Breaking changes will land on a new path (/api/v2/...) with at least 6 months of overlap. Webhook event names are stable. New events will use new names; existing events' payloads may grow but never shrink.

Need an endpoint that isn't here?

Email hello@levvr.com.au — most additions take a week or two once we've seen the use case.

Developer API — Levvr | Levvr