VerityPay documentation

Developer and user guide

Everything you need to set up, run, and integrate VerityPay — from first payroll to full API integration.

Introduction to VerityPay

VerityPay is a South African multi-tenant payroll SaaS. Every organisation is completely isolated. All DB queries scope to organisationId — no cross-tenant data leakage is possible by design.

The system calculates PAYE using the SARS annualised method, enforces the UIF R177.12/month cap, applies SDL at 1% for organisations above the R500k annual payroll threshold, and tracks BCEA leave cycles automatically.

Organisation setup

Create an account at /register. During onboarding you will capture:

  • Company registered name and trading name
  • SARS PAYE reference number
  • UIF reference number
  • SDL reference (if applicable)
  • Banking details for EFT file generation

Your first payroll run

After adding employees, navigate to Payroll and click New run. Select the pay period, confirm employees, and click Calculate. The system shows the full gross-to-net breakdown before you approve.

Two-person rule: the same user who calculated the run cannot approve it. calculatedById must differ from approvedById.

Payroll processing

VerityPay processes a full gross-to-net pipeline per employee per run. Each run is immutable once approved. Corrections create a new correction run — the original is never modified.

PAYE annualised method (SARS 2025/26)

The annualised method applies monthly gross to annual brackets, then divides by 12:

monthly_gross × 12
  → find bracket
  → apply: base_tax + rate × (annual - lower_bound)
  → subtract rebates
  → ÷ 12 = monthly PAYE

Rebates (2025/26):
  Primary:    R17,235  (all taxpayers)
  Secondary:  R9,444   (age 65+)
  Tertiary:   R3,145   (age 75+)

Thresholds:
  Under 65:   R95,750
  65–74:      R148,217
  75+:        R165,689

Correction runs

Completed payroll runs are immutable. To correct an error, create a new correction run referencing the original. Both runs are retained for SARS audit purposes. Overpayments and underpayments are captured as separate line items on the correction run payslip.

Payslip PDF export

Payslips are generated as branded PDFs after every approved run. Employees can download them from the self-service portal immediately. HR can bulk-download all payslips for a run from the Payroll dashboard.

Tax calculations

VerityPay's tax engine is implemented as pure functions in lib/engines/tax/. No Prisma, no fetch calls, no side effects — only deterministic math you can unit-test in isolation.

UIF

UIF is 1% employee and 1% employer, capped at R177.12/month (salary ceiling R17,712/month).

Excluded from UIF gross: bonuses, overtime, retrenchment pay, retirement lump sums.

SDL

SDL is 1% employer-only. It applies if the annual payroll exceeds R500,000. SDL gross includes everything — no exclusions.

ETI (Employment Tax Incentive)

ETI reduces the employer's PAYE liability for qualifying employees (18–29 years, earning below the threshold). VerityPay calculates the ETI reduction and applies it before the EMP201 is generated. See SARS ETI guide for current qualifying criteria.

Leave management

Leave tracking is fully BCEA-compliant. Balances update in real time after each leave transaction. The system supports annual, sick, and family responsibility leave.

Annual leave

Employees accrue 15 working days per year (1.25 days/month). Leave taken deducts from the accrued balance. Unused annual leave is paid out on termination (taxable, IRP5 code 3606).

Sick leave cycles

30 days per rolling 3-year cycle. The system tracks the cycle start date per employee and resets automatically. Family responsibility leave: 3 days per year, no carry-over.

Termination leave payout

When an employee is terminated, all outstanding annual leave is automatically added to the final pay run as a taxable lump sum. The IRP5 code 3606 is applied. No manual calculation needed.

Exports and compliance

EMP201 XML

After each payroll run, navigate to Exports and click Generate EMP201. The file is SARS-formatted XML ready for eFiling. The system aggregates PAYE, UIF, SDL, and ETI across all employees in the run.

IRP5 certificates

IRP5s are generated at year-end for all employees who received income during the tax year. Each certificate includes all taxable income, deductions, and fringe benefits correctly coded per SARS requirements.

Bank EFT files

VerityPay generates EFT files for ABSA, FNB, Nedbank, and Standard Bank. Each file contains one record per employee with their net pay amount and bank account details (AES-256 decrypted at export time, never stored in plain text).

Employment letters

Salary confirmation letters can be generated on demand from the Employee portal or the HR dashboard. Letters are signed with the organisation name and include current salary, employment date, and leave balance.

Developer API

The VerityPay REST API allows you to integrate payroll data into your own systems. All endpoints require an API key passed as a bearer token.

Authentication

Authorization: Bearer vp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

All requests must be HTTPS.
API keys are scoped to one organisation.
Rotate keys from: Settings > API Keys

Payroll run endpoints

GET  /api/v1/{orgSlug}/payroll
     List all payroll runs

GET  /api/v1/{orgSlug}/payroll/{runId}
     Get a single run with full line items

POST /api/v1/{orgSlug}/payroll
     Create a new payroll run (draft)

POST /api/v1/{orgSlug}/payroll/{runId}/approve
     Approve a draft run (different user required)

Employee data endpoints

GET  /api/v1/{orgSlug}/employees
     List employees (paginated)

GET  /api/v1/{orgSlug}/employees/{id}
     Get employee record

POST /api/v1/{orgSlug}/employees
     Create employee

PATCH /api/v1/{orgSlug}/employees/{id}
     Update employee details

Webhooks

VerityPay fires webhook events to your registered endpoint for key lifecycle events:

  • payroll.run.approved — run approved and payslips generated
  • employee.created — new employee added
  • leave.approved — leave request approved
  • leave.balance.low — employee leave balance below 3 days

Errors and rate limits

Rate limit: 120 requests per minute per API key. All errors return JSON with code, message, and details fields.

{
  "code": "PAYROLL_ALREADY_APPROVED",
  "message": "This payroll run has already been approved.",
  "details": { "runId": "clx..." }
}

Security and POPIA

VerityPay encrypts all PII at the application layer before writing to the database. Use encryptField() and decryptField() from lib/crypto.ts for all SA ID numbers and bank account numbers.

POPIA compliance

VerityPay acts as an Operator under POPIA for the personal information it processes on behalf of your organisation (the Responsible Party). We apply purpose limitation, data minimisation, and retention policies as required by POPIA.

Data retention

Payslip and payroll run data is retained for 5 years after the tax year in which it was created, in line with SARS record-keeping requirements. Employee records are retained for 3 years after employment ends. Deletion requests processed within 30 days where legally permissible.