SDK · web components · framework-agnostic

Payroll UI you drop into any app.

Thirty-one production-ready components — onboarding wizards, pay-run dashboards, payslip viewers, governance trails — as standard web components. One script tag, Shadow-DOM isolated, works in React, Vue, Rails, or plain HTML — and in mobile apps via the WebView bridge (Flutter · iOS · Android). The components below are not screenshots: this page loads the real SDK bundle and they're running right now, in sample mode.

Installone script tag · npm @ledrapay/components
Components31 (ledrapay-* tags, lp-* aliases)
Modessample (no token) · live (embed token)
Bundle~60 KB gzipped, ESM

Quick start

One script tag

<!-- 1. Load the SDK -->
<script type="module" src="https://cdn.ledrapay.com/v1/ledrapay-components.js"></script>

<!-- 2. Use a component — no token: renders sample data for development -->
<ledrapay-payslip-viewer country="AU" show-ytd></ledrapay-payslip-viewer>

Or via npm: npm i @ledrapay/components, then import '@ledrapay/components'. Components register themselves; use the tags anywhere.

Running on this page

The SDK, in action

Everything in the frames below is the actual SDK bundle executing in your browser — interact with it. No token is set, so each component shows its sample-data banner; with an embed-token attribute these exact components render live payroll from the Ledra Pay API.

<ledrapay-payslip-viewer country="AU" show-ytd>RUNNING · SAMPLE MODE
<ledrapay-pay-runs-list country="AU">RUNNING · SAMPLE MODE
<ledrapay-employee-onboarding country="AU"> — multi-step wizard, try itRUNNING · SAMPLE MODE

Live mode

Same components, real payroll: the embed token

Partner API keys (lp_*) must never reach a browser. Instead, your backend mints a short-lived, tenant-scoped embed token and hands it to the page:

# Your server (only place your partner key lives)
curl -X POST $BASE/v1/tenants/$TENANT/embed/token \
  -H "Authorization: Bearer lp_live_..." \
  -d '{"components": ["payslip-viewer", "pay-runs-list"]}'
# → { "token": "eyJ…", "expires_in": 900 }
// Your page — create the component once the token is in hand
// (components fetch on connect, so credentials go on before mounting)
const el = document.createElement('ledrapay-pay-runs-list');
el.setAttribute('api-url', '/v1');
el.setAttribute('tenant-id', tenantId);
el.setAttribute('embed-token', token);
el.tokenProvider = () => fetch('/my-backend/refresh-embed-token').then(r => r.json()).then(d => d.token);
container.appendChild(el);
Scoping is enforced, not decorative: an embed token is pinned to exactly one tenant — pointing it at any other tenant returns 404. Expiry is short (15 min default); set tokenProvider and components refresh themselves on 401.

This is a real capture of the pay-runs component in live mode — rendering an actual previewed AU pay run (18 employees, engine-computed totals) next to a sample-mode component on the same page:

SDK components rendering a live AU pay run (18 employees, $6,130.96 gross) above a sample-mode payslip viewer

Try it live · sandbox

Paste a sandbox key, watch real payroll render

This panel talks to a live Ledra Pay sandbox through a same-origin bridge. Paste your lp_sandbox_* key — the page lists your tenants, mints a short-lived embed token, and mounts the component against real engine-computed AU data. Sandbox keys only — never paste a live key into any web page, including this one.

not connected — components below stay in sample mode until you connect

Under the hood: GET /partners/tenantsPOST /tenants/{id}/embed/token → component mounts with the scoped token. Your key stays in this browser tab and is used only for those two calls — the mounted component itself authenticates with the expiring embed token, exactly as your production pages would.

Component linking — what the View click above is doing. Components communicate through bubbling, composed DOM events; you wire them like any DOM:

// the list announces intent — it never navigates for you
container.addEventListener('ledrapay-view-pay-run', (e) => {
  // e.detail = { id: 'pr_…' }
  const detail = document.createElement('ledrapay-pay-run-detail');
  detail.setAttribute('pay-run-id', e.detail.id);
  detail.setAttribute('tenant-id', tenantId);   // same credentials
  detail.setAttribute('embed-token', token);
  container.appendChild(detail);                  // or route to your own page
});

Full input/output specs for all 31 components — attributes, events, theming variables, linking patterns — live in the component playground →

Universal schema, endpoints, and how each engine (myaccountant · Payroll Engine) maps onto it — with the data-flow learnings — in the schema & data-flow spec →

Make it yours, no fork: re-skin every component with --ledrapay-* CSS variables, translate the UI with a string catalogue (setLedraPayStrings() / per-instance .strings), and reshape multi-step flows with steps-config. See the brand showcase → (one component, four client brands) and the i18n + steps demo →.

Client methods · effective dating & capabilities

Effective dating, capabilities, and the DE extras

The client surfaces effective-dating as plain options and exposes the engine capability matrix so your UI can gate features instead of eating a 501. Entries and totals also carry the additive DE/PE extras (social, wage_types, total_social) — the components render them automatically when present.

// reads — point-in-time with as_of (default: today)
await api.getEmployee(id, { as_of: '2026-08-01' });   // value in force on that date
await api.listEmployees(1, { as_of: '2026-10-01' });

// writes — effective_from dates the change
await api.updateEmployee(id, { employment: { annual_salary: 90000 } }, { effective_from: '2026-09-01' });
await api.createEmployee(data, { effective_from: '2026-09-01' });

// capability gate — branch instead of failing
if (await api.supportsEffectiveDating()) showAsOfPicker();
const caps = await api.getCapabilities();   // { effective_dating: 'native' | 'unsupported', … }
Against an engine without effective-dating, a non-today as_of/effective_from returns 501 CAPABILITY_NOT_SUPPORTED — today/omitted always works. The ledrapay-employee-onboarding wizard already uses supportsEffectiveDating() to show its Effective From field only where it is honoured.

Try it live · effective dating

Watch a future-dated raise resolve by date — live

This runs the full round-trip against a live Payroll-Engine sandbox: create an employee at €60,000, post a raise to €90,000 effective 1 Sep 2026, then read the same record as_of three dates. No key needed — it talks to a dedicated demo backend. (This is the PE-native path; the AU engine reports effective_dating: unsupported, which the matrix and the 501 gate make explicit.)

idle — press run to create, raise, and read by date against the live engine
(no run yet)

Under the hood, each line is a real evidenced call: POST …/employeesPATCH …/employees/{id} with effective_from → three GET …/employees/{id}?as_of=…. Every write returns an evidence_id (cev_*).

Try it live · Germany

The same components, rendering a real German payslip — computed by payrollengine.ch

This mounts the actual SDK components against a finalised German pay run that was onboarded and computed through the governed rail: Ledra Pay → the Smartta evidenced executor → the real payrollengine.ch engine, each step leaving a cev_ evidence envelope. The partner lifecycle is preview → approve → finalise; finalise committed a real Payroll Engine payrun job, and its committed per-employee entries are read back through the payrun_entries resource composition. You'll see the German surface — Lohnsteuer, the social-contributions total (KV/RV/AV/PV), and the per-employee wage-type breakdown — the same components used for AU, just country="DE". The number is the engine's, not Ledra Pay's: net €2,344.25 from €3,500.00 gross.

idle — press to connect to the governed DE tenant and render the live components

Under the hood: connect → mint an embed token → mount <ledrapay-pay-run-detail country="DE"> + <ledrapay-payslip-viewer country="DE"> against the finalised governed pay run. Finalise committed a real payrollengine.ch payrun job through Smartta's evidenced executor; its committed per-employee entries — the 63-line statutory wage-type breakdown and the employee name — are read back through the payrun_entries resource composition and served from the evidenced control plane.

Catalog

All 31 components

Every component also registers a lp-* alias (lp-payslip-viewerledrapay-payslip-viewer). Common attributes everywhere: country (AU·DE·NZ·UK·US), api-url, tenant-id, embed-token.

Tag (ledrapay-…)What it doesNotable
payslip-viewerCompliant payslip with earnings, deductions, super, leave balancesshow-ytd · show-governance · print/download events
employee-onboardingMulti-step wizard: personal → tax → bank → super → employmentemits ledrapay-complete with the assembled employee
pay-runs-listPay run history with status, totals, latest-run stat cardsemits ledrapay-view-pay-run
employees-listEmployee directory — searchable roster of workersemits ledrapay-view-employee
employee-detailSingle employee profile: employment, pay, tax, super, bank (PII masked)employee-id attribute
pay-run-detailSingle run: entries, per-employee lines, action statespay-run-id attribute
pay-run-entryInline entry editor: pay items, hours × rates, gross/tax/netdraft-state editing
dashboardSummary stats: workers, MTD gross, compliance state
tax-declarationCountry-aware tax form (TFN declaration in AU, etc.)jurisdiction fields switch on country
super-formRetirement setup: fund selection, member number, salary sacrificeAU: USI lookup pattern
bank-detailsAccount capture with country routing formatsmasked display
leave-managementBalances, accrual rates, request workflow
pay-item-libraryEarnings/deduction catalogue with rate multipliers
stp-submissionLodgement status and authority responsesAU STP Phase 2 shapes
award-configAward rate tables and classificationsAU Modern Awards
payroll-calendarPay schedule builder with upcoming periods
org-setupCompany, sites, department hierarchy
cost-centresCost allocation management
reports-dashboardReport generation and download
governance-trailEvidence ledger view — receipts per actionpairs with /governance endpoints
requirementsCompliance/readiness checklist per employeepairs with pack validation

Events

Listening to components

document.querySelector('ledrapay-employee-onboarding')
  .addEventListener('ledrapay-complete', (e) => {
    // e.detail = the assembled employee — submit via YOUR backend with governance_reason
  });
EventEmitted by
ledrapay-completeonboarding wizard finish (employee payload in detail)
ledrapay-save / ledrapay-cancelforms (tax, super, bank)
ledrapay-view-pay-run / ledrapay-employee-selectlists (navigation intents)
ledrapay-print / ledrapay-downloadpayslip viewer actions
Writes stay governed: components collect and display; mutations go through your backend to the Ledra Pay API, where governance_reason, idempotency, and the evidence chain apply. The SDK never gives a browser an ungoverned write path.

Next

Where to go from here

Walk the full lifecycle in the AU end-to-end guide, browse the API reference, or request sandbox keys to point these components at real data.