API · v1

Programmable
payroll.

A JSON API for South African payroll. Run a cycle, pull payslips, and generate a bank-ready file with a handful of HTTP calls. No SOAP, no SDK lock-in, no ceremony.

  • REST + JSON
  • OpenAPI 3.1 spec
  • Signed webhooks
  • 99.95% uptime
run-payroll.sh
curl https://api.payloop.co.za/v1/payroll-runs \  -u "sk_live_pl_a37x9z2…:" \  -H "Idempotency-Key: 2026-10-monthly" \  -d period=2026-10 \  -d frequency=monthly
# → 200 OK{  "id":             "pr_01J9C2A8...",  "period":         "2026-10",  "status":         "compiling",  "employee_count": 42,  "totals":         null,  "created_at":     "2026-10-25T08:14:11Z"}
Foundations

Built like an API ought to be.

Predictable resources, sensible defaults, and the four things developers actually want.

01

Idempotent by default

Every mutating endpoint accepts an Idempotency-Key header. Replays are cheap; double-processing is impossible.

02

SARS-current tax engine

Calculations use the live 2026/27 brackets, rebates, MTC, UIF cap and SDL threshold — updated within hours of a SARS gazette.

03

Bank-format exports

FNB, Standard Bank, Capitec, Nedbank, Absa and Investec — choose a format, the API hands you the file ready to upload.

04

Signed webhooks

Every webhook is signed with an HMAC-SHA256 over the body and timestamp. Verify in three lines of any language.

A full cycle

Run a whole month in four calls.

Compile, calculate, export, dispatch. Every step returns a JSON resource you can poll or webhook on. Idempotent end to end — re-running an action is always safe.

  1. 01Create a draft run for the period.
  2. 02Calculate — engine returns line-by-line totals.
  3. 03Export for your bank — downloadable signed file URL.
  4. 04Pull payslip URLs — signed PDFs per employee.
import { Payloop } from "@payloop/sdk";
const pl = new Payloop({ apiKey: process.env.PAYLOOP_KEY! });
// 1. Compileconst run = await pl.payrollRuns.create({  period: "2026-10",  frequency: "monthly",});
// 2. Calculateawait pl.payrollRuns.calculate(run.id);
// 3. Export to FNBconst file = await pl.payrollRuns.export(run.id, { bank: "fnb" });console.log(file.download_url);
// 4. Fetch payslipsconst slips = await pl.payrollRuns.payslips(run.id);for (const p of slips) console.log(p.employee_name, p.url);
Webhooks

Real-time, signed.

Every event payloop produces — run created, calculation complete, export ready, payslip sent — is delivered to your endpoint with an HMAC-SHA256 signature over the body and a timestamp. Reject anything that doesn’t verify; the signature scheme is the same as Stripe and GitHub.

  • payroll_run.calculated
  • payroll_run.exported
  • payslip.dispatched
  • emp201.staged
verify-webhook.ts
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyPayloopSignature(  body: string,  signature: string, // header: payloop-signature  secret: string,) {  const [t, sig] = signature    .split(",")    .map((p) => p.split("=")[1]);  const expected = createHmac("sha256", secret)    .update(`${t}.${body}`)    .digest("hex");  return timingSafeEqual(    Buffer.from(sig, "hex"),    Buffer.from(expected, "hex"),  );}
Sandbox keys, free

Ship payroll,
programmatically.

Grab a sandbox key in 30 seconds. No credit card. Live keys when you’re ready to push your first run.