API Reference · v1 · Beta
The payroll layer, documented.
One REST API for the full embedded payroll lifecycle: tenants, certified country regulation packs, employees, pay runs, statutory lodgement, payslips, and an evidence chain on every write. Jurisdiction is a parameter — never a separate integration.
Getting started
Overview
Ledra Pay is provider-shaped: the API models what your platform ships — employees, pay runs, payslips, filings — not the internals of a calculation engine. Country logic lives in certified regulation packs attached per tenant; the request and response shapes are identical for every country.
All tenant-scoped routes are prefixed /v1/tenants/{tenantId}. Partner-scoped routes (tenant provisioning and lookup) are prefixed /v1/partners.
# Your first calculation — tenant-scoped, against a PE-native (DE) binding curl https://sandbox.ledrapay.com/v1/tenants/ten_8f3c…/calculate \ -H "Authorization: Bearer lp_sandbox_..." \ -H "Content-Type: application/json" \ -d '{ "frequency": "monthly", "employee": { "residency": "resident" }, "earnings": [ { "type": "ordinary", "amount": 3500.00 } ] }' # calculate is capability-gated (stateless_calculate): native on PE-native # (DE) bindings, 501 CAPABILITY_NOT_SUPPORTED otherwise. The unscoped # POST /v1/calculate is a 501 stub — always call the tenant-scoped route.
Getting started
Authentication
Every request carries a partner-scoped bearer key. Keys are issued per environment (lp_sandbox_*, lp_live_*; legacy ps_* keys remain valid) and grant access only to tenants registered to your partner account — strict partner-to-tenant isolation is enforced at the gateway, not in your code.
Embedded UI components authenticate with short-lived embed tokens minted server-side via POST /v1/tenants/{tenantId}/embed/token. Partner keys never touch the browser, and embed tokens cannot mint further tokens — a leaked token dies at its 15-minute TTL. Tokens are pinned to one tenant; reads within that tenant are allowed, while writes are gated by the component allowlist (components in the mint body — e.g. a payslip-viewer token cannot approve a pay run). Two optional mint fields narrow scope further: employee_id pins the token to a single employee for employee-facing surfaces, and origin binds it to one browser origin (requests presenting a different Origin header are rejected).
Components can call https://api.ledrapay.com/v1 directly from your pages — CORS is enabled, bearer-only (cookies are never accepted cross-origin). Direct cross-origin calls require an origin-bound token: mint with { "origin": "https://app.yourdomain.com" } and set api-url="https://api.ledrapay.com/v1" on the components. Alternatively, mount a same-origin reverse proxy on your own domain and skip CORS entirely — both integrations are first-class. The vendor embedding guide covers both topologies, token scoping, and the launch checklist.
Access control
User tokens & role-based access
For a full app where many users of one organisation each get restricted access (the enterprise model), mint a user token — an embed token plus a role and a data scope. Mint server-side with a partner key (or a tenant admin user token) via POST /v1/tenants/{tenantId}/users/token:
{ "role": "preparer",
"scope": { "cost_centres": ["OPS-ACS"], "sites": "*" },
"user_id": "usr_…", "user_name": "Priya P." }
Roles enforce segregation of duties (not a linear ladder). The API gate is the boundary; the SDK mirrors it by hiding/disabling buttons (query GET /v1/tenants/{id}/me for the caller's role + scope):
| viewer | reads only |
| preparer | create & edit pay runs, employees — cannot approve/finalise/submit/void |
| approver | approve · finalise · submit (STP) · void — cannot prepare (four-eyes) |
| admin | everything within the tenant, incl. offboard & config; may mint lower user tokens (never above its own role or wider than its own scope) |
Data scope restricts which rows a user sees by cost-centre/site. It fails closed: a scoped user querying a data source that can't attach the cost-centre dimension receives an empty result with scope_not_enforceable: true — never unfiltered data. (Cost-centres are a governed overlay, not engine-native; enforcement is full where the overlay is present and honest-empty where it isn't yet.) Unscoped users ("*") are unaffected.
Bureau
Portfolio API
For a payroll bureau / MSP running payroll across many client companies (the bureau model), the portfolio endpoints aggregate across every tenant on the partner key. Partner key only — tenant-scoped embed/user tokens are refused (they can't span a portfolio). First-party use behind an operator session; the partner key is never exposed to the browser.
| GET | /partners/portfolio/summary | Aggregate KPIs: total gross, active workers, runs in flight, risk flags, client count |
| GET | /partners/portfolio/pipeline | Pay runs across all clients, grouped by lifecycle (draft · approved · blocked · finalised) |
| GET | /partners/portfolio/clients | Per-client status rows — headcount, gross, in-flight runs, next pay date, health |
The bureau SDK components (ledrapay-portfolio-dashboard, ledrapay-client-list, ledrapay-pipeline-board) render these. Because they carry portfolio-wide access they authenticate via a same-origin session only and must never be embedded in an untrusted page — see the SDK docs.
Getting started
Conventions
Idempotency
All POST endpoints accept an Idempotency-Key header. Retries with the same key return the original response for 24 hours. Use it on everything that creates pay runs or submits to an authority.
Pagination
List endpoints return data, has_more, and next_cursor. Pass ?cursor= and ?limit= (max 200).
Money and dates
Amounts are decimal strings with an explicit currency (ISO 4217). Dates are YYYY-MM-DD; timestamps are UTC ISO 8601. Identifiers are prefixed: ten_, emp_, pr_, ps_, ldg_, evd_.
Versioning
The API is versioned in the path (/v1) and changes additively. Regulation packs are versioned independently (AU-2026.1) — statutory updates ship as new pack releases, never as silent behaviour changes under a pinned version.
Getting started
Core concepts
| Object | What it is |
|---|---|
| Partner | You — the platform embedding Ledra Pay. Holds API keys and a portfolio of tenants. |
| Tenant | An employer (your customer). Owns employees, pay runs, and attached regulation packs. Fully isolated. |
| Regulation pack | A certified, versioned country ruleset — tax scales, social contributions, statutory outputs. Attached to a tenant, pinned to a version. |
| Pay run | The lifecycle object: draft → previewed → approved → finalised. lodged ROADMAP (AU statutory rail), plus forecast / retro ROADMAP variants. |
| Shadow run ROADMAP | A pay run calculated for comparison only — returns a line-level variance report against incumbent results you supply. |
| Lodgement ROADMAP | A statutory submission (STP, RTI, payday filing) generated from a finalised pay run. |
| Evidence | An immutable, hash-chained governance record written on every state-changing call. |
Platform
Partners & tenants
Provision a tenant per employer. A tenant is created with its engine binding (pack + version) attached at provisioning time — the pack is pinned on the binding, not patched in later.
| POST | /partners/tenants | Provision a tenant (employer) + engine binding under your partner account. Pack pinned here (jurisdiction, engine, optional pack_version) |
| GET | /partners/tenants | List your tenant portfolio |
| GET | /partners/tenants/{tenantId} | Get tenant details and bindings (engine, pinned version, capabilities) |
| PATCH | /partners/tenants/{tenantId} | ROADMAP Update tenant registration |
| DELETE | /partners/tenants/{tenantId} | ROADMAP Unregister — revokes access, data retained per policy |
| POST | /partners/batch/pay-runs | ROADMAP Create pay runs across many tenants in one call |
| POST | /partners/batch/lodge | ROADMAP Batch statutory lodgement across tenants |
| GET | /partners/dashboard | ROADMAP Portfolio summary: upcoming runs, alerts, lodgement states |
Platform
Regulation packs
Packs are how Ledra Pay stays one integration across countries. Attach a pack to a tenant and pin a version; the pay run API never changes shape. Certification status, maintainer, and changelog are queryable — compliance is an API object, not a PDF.
| GET | /packs | List available packs with certification status and versions |
| GET | /packs/{packCode} | Pack detail: maintainer, statutory scope, changelog |
| GET | /tenants/{tenantId}/packs | Attached packs for a tenant with the live capability matrix (per binding) |
| POST | /tenants/{tenantId}/packs | ROADMAP Attach a pack post-hoc. Today the pack is pinned at tenant provisioning (POST /partners/tenants) |
| PATCH | /tenants/{tenantId}/packs/{packCode} | ROADMAP Move the pinned version (e.g. annual statutory update) |
{
"code": "AU",
"current_version": "AU-2026.1",
"status": "beta", // draft | beta | certified
"maintainer": "Ledra Pay Compliance (AU)",
"scope": ["paye_tax", "medicare_levy", "stsl", "super_guarantee", "awards", "payroll_tax_state"],
"lodgement_rails": [{ "type": "STP2", "channel": "ATO-SBR", "status": "in_build" }],
"versions": [{ "version": "AU-2026.1", "effective": { "from": "2026-07-01", "to": "2027-06-30" } }]
}
| Pack | Calculation | Lodgement rail |
|---|---|---|
| AU | Beta | STP Phase 2 — in build (sandbox simulation live) |
| NZ | Beta | Payday filing — planned next, with design partner |
| UK | Beta | RTI FPS/EPS — planned next, with design partner |
| DE | Beta | ELSTER + GKV/ITSG — design-partner project |
| FR · NL · BE · ES · PT · AT · CH · LU · US | Beta | Statutory outputs only |
Capability matrix
GET /tenants/{tenantId}/packs returns the attached pack(s) with a capability matrix per binding. Capabilities are how the one API stays honest across engines that genuinely differ: a feature is either native or unsupported, never silently faked. An unsupported call returns 501 CAPABILITY_NOT_SUPPORTED with the alternatives, not a wrong answer.
{
"data": [{
"binding_id": "bnd_…", "engine": "…", "pinned_version": "DE-2026.1",
"capabilities": {
"effective_dating": "native", // as_of reads / effective_from writes
"stateless_calculate": "native", // forecast /calculate with no pay run
"statutory_lodgement": "unsupported",
"payment_file": "unsupported",
"payslip_pdf": "unsupported"
}
}]
}
| Capability | AU pack | DE pack | What it gates |
|---|---|---|---|
| effective_dating | unsupported | native | ?as_of= reads · effective_from writes |
| stateless_calculate | unsupported | native | POST /calculate with no pay run |
| statutory_lodgement | native | unsupported | STP / lodgement rails |
| payment_file · payslip_pdf · retirement_batch | native | unsupported | ABA / payslip PDF / SuperStream |
getCapabilities() / supportsEffectiveDating()) so an unsupported feature is hidden, not a runtime 501.Platform
Organisation
Roadmap — not yet available
| GET | /tenants/{tenantId}/organisation | ROADMAP Company details, registration numbers (ABN, PAYE ref…) |
| PATCH | /tenants/{tenantId}/organisation | ROADMAP Update company details |
| GET | /tenants/{tenantId}/sites | ROADMAP List work sites / locations (drives state payroll tax, regional rules) |
| POST | /tenants/{tenantId}/sites | ROADMAP Create work site |
| GET | /tenants/{tenantId}/cost-centres | ROADMAP List cost centres |
| POST | /tenants/{tenantId}/cost-centres | ROADMAP Create cost centre |
| POST | /tenants/{tenantId}/validate | ROADMAP Payroll-readiness check: returns blocking issues before first run |
People
Employees
One employee model for every country. Country-specific fields (TFN declarations, UK tax codes, NI numbers) live under tax_profile, validated by the tenant's attached pack — your integration code stays identical.
| POST | /tenants/{tenantId}/employees | Create employee: personal, employment, tax profile, bank, retirement. Optional effective_from |
| GET | /tenants/{tenantId}/employees?as_of=YYYY-MM-DD | List with pagination and filters; optional as_of point-in-time read |
| GET | /tenants/{tenantId}/employees/{empId}?as_of=YYYY-MM-DD | Full record, optionally as of a past/future date |
| PATCH | /tenants/{tenantId}/employees/{empId} | Partial update; optional effective_from dates the change. Tax/bank/retirement fields are nested in the same payload (under tax_profile etc.) |
| POST | /tenants/{tenantId}/employees/{empId}/terminate | ROADMAP Terminate with final-pay calculation (unused leave, ETPs) |
| GET | /tenants/{tenantId}/employees/{empId}/tax-profile | ROADMAP Country tax settings as a sub-resource |
| PATCH | /tenants/{tenantId}/employees/{empId}/tax-profile | ROADMAP Update tax settings as a sub-resource (use the employee PATCH today) |
| PATCH | /tenants/{tenantId}/employees/{empId}/bank-accounts | ROADMAP Update bank account split |
| PATCH | /tenants/{tenantId}/employees/{empId}/retirement | ROADMAP Super fund / pension scheme details |
| POST | /tenants/{tenantId}/employees/bulk | ROADMAP Bulk import up to 500 employees |
| GET | /tenants/{tenantId}/employees/{empId}/documents | ROADMAP Statutory documents (payment summaries, P60, P45) |
Effective dating
Employee facts are effective-dated on engines that support it (capability effective_dating: native): a write carries an optional effective_from that stamps when the change takes effect, and a read carries an optional as_of that resolves the value in force on that date (default: today). A future-dated raise is the canonical case — it does not change "now", but it is already on file.
// DE / PE-native packs only — AU (myaccountant) returns 501 for a non-today date // future-dated raise — takes effect 2026-09-01, not today PATCH /tenants/{t}/employees/emp_8f3c… { "employment": { "annual_salary": 90000 }, "effective_from": "2026-09-01" } GET …/employees/emp_8f3c…?as_of=2026-08-01 → annual_salary 60000 // before GET …/employees/emp_8f3c…?as_of=2026-10-01 → annual_salary 90000 // after GET …/employees/emp_8f3c… → annual_salary 60000 // today (raise still future)
as_of/effective_from returns 501 CAPABILITY_NOT_SUPPORTED rather than silently ignoring the parameter (today/omitted always works). Check the capability matrix first. Effective-dating is scoped to the employee record: a finalised pay run already froze the values in force at its period-end, so its entries are point-in-time by construction.People
Timesheets
Roadmap — not yet available
| POST | /tenants/{tenantId}/timesheets | ROADMAP Create timesheet with daily entries |
| POST | /tenants/{tenantId}/timesheets/bulk | ROADMAP Bulk create for multiple employees |
| GET | /tenants/{tenantId}/timesheets | ROADMAP List with filters (employee, status, period) |
| POST | /tenants/{tenantId}/timesheets/{id}/approve | ROADMAP Approve for pay-run import |
Payroll
Pay runs
The pay run is a state machine: draft → previewed → approved → finalised. Preview never advances state — call it as often as you like; approve is a control-plane transition (an evidence envelope, no engine command); finalise commits to the engine. lodged, forecast and retro variants are roadmap.
| POST | /tenants/{tenantId}/pay-runs | Create pay run (period_start, period_end, optional payment_date; kind stored). Requires governance_reason |
| GET | /tenants/{tenantId}/pay-runs | List with status and totals (filter ?status=) |
| GET | /tenants/{tenantId}/pay-runs/{prId} | Pay run with totals and evidence refs |
| GET | /tenants/{tenantId}/pay-runs/{prId}/entries | Per-employee entries (persisted after preview/finalise; else read from engine) |
| POST | /tenants/{tenantId}/pay-runs/{prId}/entries | Add an entry — draft only. Requires governance_reason |
| POST | /tenants/{tenantId}/pay-runs/{prId}/preview | Calculate without advancing past previewed; persists entries + payslips |
| POST | /tenants/{tenantId}/pay-runs/{prId}/approve | Control-plane transition previewed → approved (evidence envelope) |
| POST | /tenants/{tenantId}/pay-runs/{prId}/finalise | Commit the approved run to the engine — a real, async Payroll Engine payrun job; persists committed entries (statutory wage-type lines). Does not generate bank/super files (AU-only, capability-gated) |
| DELETE | /tenants/{tenantId}/pay-runs/{prId} | Tear down a draft or previewed run (evidence-chained); finalised runs cannot be deleted |
| PATCH | /tenants/{tenantId}/pay-runs/{prId}/entries/{id} | ROADMAP Update hours, rates, pay items (re-add the entry today) |
| DELETE | /tenants/{tenantId}/pay-runs/{prId}/entries/{id} | ROADMAP Remove a single entry from draft |
| POST | /tenants/{tenantId}/pay-runs/{prId}/import-lines | ROADMAP Import approved timesheet lines |
| POST | /tenants/{tenantId}/pay-runs/{prId}/void | ROADMAP Void a finalised run (evidence-chained) |
| POST | /tenants/{tenantId}/pay-runs/{prId}/clone | ROADMAP Clone structure for the next period |
| POST | /tenants/{tenantId}/pay-runs/eoy | ROADMAP End-of-year finalisation for the attached pack's year-end |
# Preview response (abridged) — identical shape for every country { "pay_run": "pr_8f2c", "status": "previewed", "pack": "AU-2026.1", "entries": [{ "employee": "emp_31ad", "gross": { "amount": "3269.23", "currency": "AUD" }, "lines": [ { "type": "tax.paye", "amount": "-612.00" }, { "type": "tax.medicare_levy", "amount": "-65.38" }, { "type": "employer.super_guarantee", "amount": "392.31", "employer_cost": true } ], "net": { "amount": "2591.85", "currency": "AUD" } }], "evidence": "evd_a90b" // every calculation is receipted }
Entries — the cross-country shape
GET …/pay-runs/{prId}/entries returns one row per employee with a jurisdiction-stable core and pack-variant extras. The core is guaranteed identical in every country; extras are additive per pack and never replace core fields.
{
"data": [{
// core — every pack, always (amounts are 2dp decimal strings)
"id": "…", "employee_id": "emp_0e4c888c5987c068",
"employee_name": "Alex Chen",
"gross": "1140.00", "tax": "299.00", "net": "841.00",
// pack-variant extras (additive):
"super": "136.80", // AU/NZ: superannuation / KiwiSaver
"social": "1057.50", // DE: KV+RV+AV+PV employee share
"wage_types": [ "…" ] // PE-native packs: full statutory line breakdown
}],
"evidence_id": "cev_…"
}
Rows with all-zero amounts are engine truth, not errors: employees active in the period but with no pay items yet. Currency lives on the run's totals, never per entry.
Payroll
Shadow runs
Roadmap — not yet available
Prove the numbers before anything goes live. Post the inputs from a real payrun together with your incumbent provider's results; Ledra Pay calculates independently and returns a line-level variance report. Shadow runs never lodge, never pay, and never touch production state.
| POST | /tenants/{tenantId}/shadow-runs | ROADMAP Create shadow run with inputs and incumbent results |
| GET | /tenants/{tenantId}/shadow-runs/{id} | ROADMAP Variance report: matched, divergent, and unexplained lines |
{
"shadow_run": "sh_c41e",
"summary": { "entries": 640, "matched": 634, "divergent": 6, "max_variance": "4.85" },
"divergences": [{
"employee": "emp_99c2", "line": "tax.stsl",
"incumbent": "128.00", "ledrapay": "123.15",
"explanation": "Incumbent applied 2024-25 STSL thresholds after 1 July"
}]
}
Payroll
Calculation engine
Stateless gross-to-net for embedding calculations in your own UX — quote a net salary in onboarding — without creating payroll objects. Tenant-scoped and capability-gated: stateless_calculate is native on PE-native (DE) bindings and unsupported elsewhere (501). The unscoped POST /v1/calculate is a 501 stub that returns the capability matrix — always call the tenant-scoped route.
| POST | /tenants/{tenantId}/calculate | Gross-to-net: tax, social contributions, employer costs. 501 CAPABILITY_NOT_SUPPORTED on a non-native binding |
| POST | /calculate | STUB Unscoped — returns 501 with alternatives (use the tenant-scoped route) |
| POST | /calculate/award-interpret | ROADMAP AU: interpret award rates, penalties, loadings for a shift |
| POST | /calculate/leave | ROADMAP Leave entitlements and accruals |
| POST | /calculate/termination | ROADMAP Final pay including unused leave and termination payments |
Payroll
Statutory lodgement
finalised; lodged exists as a Phase-1 (AU) target. Australia (STP Phase 2 over ATO SBR) is the lead rail, in build with sandbox simulation planned. DE statutory rails (ELSTER + GKV/ITSG) are a design-partner build, advertised on the DE pack as design_partner_next and gated by the statutory_lodgement capability (unsupported on DE today).Roadmap — not yet available
| POST | /tenants/{tenantId}/pay-runs/{prId}/lodge | ROADMAP Lodge to the authority via the pack's rail (STP2 / RTI / payday) |
| GET | /tenants/{tenantId}/lodgements | ROADMAP List lodgements with authority responses |
| GET | /tenants/{tenantId}/lodgements/{ldgId} | ROADMAP Lodgement detail, receipt, and error remediation hints |
| GET | /tenants/{tenantId}/pay-runs/{prId}/statutory-outputs | ROADMAP Statutory output files for packs without a rail |
Payroll
Payslips & payments
| GET | /tenants/{tenantId}/pay-runs/{prId}/payslips | Per-employee payslips from the persisted entries — available after preview or finalise (gross, net, wage-type lines). pdf is null today |
| POST | /tenants/{tenantId}/embed/token | Mint a short-lived, tenant-scoped token for embeddable SDK components. Body: components (write allowlist, enforced), optional employee_id (employee pinning) and origin (origin binding). Partner keys only — embed tokens cannot mint |
| GET | /tenants/{tenantId}/payslips/{psId}/pdf | ROADMAP Download payslip PDF (white-label theming) — payslip_pdf is unsupported on PE today |
| GET | /tenants/{tenantId}/pay-runs/{prId}/payments | ROADMAP Payment instructions and bank files (e.g. ABA, Bacs) |
| POST | /tenants/{tenantId}/super-batches | ROADMAP AU: SuperStream contribution batches |
| GET | /embed/components | ROADMAP List embeddable components — the SDK ships the catalogue today |
finalise against the DE/PE engine commits the payrun and persists entries but does not emit payment files.Trust
Governance
Every state-changing call writes an immutable evidence record into a hash chain: actor, reason, inputs digest, pack version, result digest. When a regulator, auditor, or customer asks "why was this payslip this number", the answer is one API call.
| GET | /tenants/{tenantId}/governance/evidence/{evdId} | Single evidence record |
| GET | /tenants/{tenantId}/governance/audit-trail | Query by entity, actor, or date range |
| POST | /tenants/{tenantId}/governance/verify | Verify chain integrity over a range |
Trust
Reference data
Roadmap — not yet available
| GET | /reference/{packCode}/tax-tables | ROADMAP Current tax scales and thresholds for a pack version |
| GET | /reference/AU/awards | ROADMAP Modern Awards with classifications |
| GET | /reference/AU/awards/{awardCode} | ROADMAP Award rates, penalties, loadings |
| GET | /reference/AU/super-funds | ROADMAP Super fund directory (USI lookup) |
| GET | /reference/{packCode}/leave-types | ROADMAP Leave type definitions per jurisdiction |
| GET | /reference/{packCode}/income-types | ROADMAP Statutory income type codes (e.g. STP income types) |
GET /v1/packs and GET /v1/packs/{packCode} return statutory scope, versions and lodgement-rail status. The granular reference endpoints above are roadmap.Integrate
Webhooks
Roadmap — not yet available
Subscribe to events; deliveries are signed (Ledra Pay-Signature, HMAC-SHA256) and retried with backoff for 72 hours.
| POST | /webhooks | ROADMAP Create subscription with event filters |
| GET | /webhooks | ROADMAP List subscriptions |
| DELETE | /webhooks/{id} | ROADMAP Remove subscription |
| Event | Fires when |
|---|---|
| pay_run.previewed | Preview calculation completes |
| pay_run.finalised | Run committed to the engine; entries persisted (bank/super files AU-only) |
| lodgement.accepted / lodgement.rejected | Authority responds to a submission |
| shadow_run.completed | Variance report ready |
| employee.created / employee.terminated | Employee lifecycle changes |
| pack.version_released | A statutory update ships for a pack you use |
Integrate
MCP & agents
The REST write path is already governance-first: write operations require a governance_reason, supplied either as a body field or the x-governance-reason header — it lands in the evidence chain on every state-changing call. Omitting it returns 400 governance_reason_required.
Roadmap — MCP server not yet available
Ledra Pay will ship an MCP server so AI agents can operate payroll safely. Every tool carries a risk tier; all write tools require a governance_reason; high and critical tools support human-approval gates configured per partner. The tool catalogue below is the planned surface, not yet callable.
| Tool ROADMAP | Risk | Description |
|---|---|---|
| list_employees · get_employee · list_pay_runs · get_pay_run | LOW | Read operations with governance hashes |
| calculate_wage · interpret_award · employee_pay_summary | LOW | Stateless calculations |
| create_timesheet · approve_timesheet · preview_pay_run | MED | Reversible writes, no money movement |
| create_employee · update_employee · create_pay_run · update_pay_entry | HIGH | Writes requiring governance_reason |
| finalise_pay_run · submit_to_authority · terminate_employee · run_full_payroll | CRIT | Irreversible or outward-facing; approval-gated by default |
| get_audit_trail · verify_chain · payroll_readiness_check · run_shadow_run | LOW | Trust and verification surface |
Integrate
Errors
Errors are JSON with a stable code, human message, and — for pack validation failures — the statutory rule that rejected the input. code values are snake_case (pack_validation_failed, state_conflict, governance_reason_required), except the load-bearing CAPABILITY_NOT_SUPPORTED.
{
"error": {
"code": "pack_validation_failed",
"message": "tax_profile.tfn failed checksum validation",
"rule": "AU-2026.1/identity/tfn_algorithm",
"status": 422
}
}
| Status | Meaning |
|---|---|
| 400 / 422 | Malformed request (bad_request, governance_reason_required) / pack validation failure (pack_validation_failed) |
| 401 / 403 | Invalid key / tenant not in your partner portfolio |
| 404 | Object not found within your isolation boundary |
| 409 | state_conflict (e.g. finalising an unapproved run, adding entries outside draft); idempotency replay mismatch |
| 429 | Rate limited — honour Retry-After |
| 501 | CAPABILITY_NOT_SUPPORTED — the bound pack cannot do this (e.g. stateless_calculate on AU, statutory lodgement on DE, a non-today as_of/effective_from on a non-native engine). Body carries capability, pack, and alternatives — never a wrong answer |