@payslice/sdk
v0.1.0
Published
Official TypeScript SDK for the Payslice Earned Wage Access (EWA) partner API.
Maintainers
Readme
@payslice/sdk
Official TypeScript SDK for the Payslice Earned Wage Access (EWA) partner API (/v1).
It handles the parts that are easy to get wrong — HMAC request signing, idempotency keys, cursor pagination, typed errors, and webhook signature verification — so you write business logic, not crypto.
- Universal runtime — Node 18+, Deno, Bun, edge, and browsers (built on Web Crypto +
fetch, zero dependencies). - Fully typed — request/response types mirror the OpenAPI contract 1:1.
- Drop-in webhooks — verify and narrow inbound events with one call.
Install
npm install @payslice/sdkQuick start
import { Payslice } from "@payslice/sdk";
const payslice = new Payslice({
keyId: process.env.PAYSLICE_KEY_ID!,
secret: process.env.PAYSLICE_SECRET!,
baseUrl: "https://sandbox-api.payslice.com", // prod: https://api.payslice.com
});
const quote = await payslice.quotes.create({
user_id: "employee_42",
company_id: "employer_7",
contract: { id: "contract_1", start_date: "2024-01-15" },
salary: { amount: 500_000, currency: "USD", frequency: "monthly" },
});
if (quote.approved) {
const advance = await payslice.advances.create({
quote_id: quote.id,
user_id: quote.user_id,
amount: 20_000,
currency: quote.currency!,
destination_account_id: "partner_ledger_acct_99",
due_date: "2026-07-31",
});
}Authentication (the three X-Payslice-* signature headers and the request timestamp) is applied automatically on every call.
All monetary amounts are integers in minor units (cents).
Resources
| Call | Endpoint |
| --- | --- |
| quotes.create() / quotes.get(id) | POST/GET /v1/quotes |
| advances.create() | POST /v1/advances |
| advances.get(id) / advances.list() / advances.listPage() | GET /v1/advances |
| advances.confirmDisbursement(id, …) | POST /v1/advances/{id}/disbursement |
| collections.listDue() / collections.listDuePage() | GET /v1/collections/due |
| collections.confirm(…) | POST /v1/collections |
| vault.get() | GET /v1/vault |
Idempotency
advances.create() and collections.confirm() require an idempotency key. The SDK generates a UUID per call automatically; pass your own to make a retry replay the original result instead of creating a duplicate:
await payslice.advances.create(req, { idempotencyKey: orderId });Pagination
List methods return an async iterator that fetches pages lazily:
for await (const advance of payslice.advances.list({ company_id: "employer_7" })) {
console.log(advance.id, advance.status);
}Use listPage() / listDuePage() for manual cursor control (and to read per-page totals on collections).
Errors
Every non-2xx response throws a typed subclass of PaysliceError, selected on the API error code:
import { QuoteExpiredError, InsufficientVaultBalanceError } from "@payslice/sdk";
try {
await payslice.advances.create(req);
} catch (err) {
if (err instanceof QuoteExpiredError) { /* request a fresh quote */ }
else if (err instanceof InsufficientVaultBalanceError) { /* top up */ }
else throw err;
}Each error exposes code, message, status, details, and requestId.
Webhooks
Verify the signature and get a typed, narrowed event. Always verify against the raw request body — re-serialized JSON will not match the signature.
import { constructEvent } from "@payslice/sdk";
const event = await constructEvent({
payload: rawBody, // string or Buffer, exactly as received
headers: req.headers,
secret: process.env.PAYSLICE_WEBHOOK_SECRET!,
endpointUrl: "https://partner.example/webhooks/payslice", // your REGISTERED URL
});
switch (event.type) {
case "advance.released": /* event.data: Advance */ break;
case "collection.due": /* event.data: CollectionsDue */ break;
}See examples/webhook-express.ts for a full Express handler.
Development
npm install
npm run generate # regenerate src/generated/types.ts from openapi.yaml
npm test
npm run buildopenapi.yaml is vendored from the API repo. CI runs generate:check to fail on drift between the spec and the generated types, and typecheck runs a type-level conformance test (test/conformance.test-d.ts) asserting the public types stay interchangeable with the generated ones.
baseUrlmust be the host without a/v1suffix (the SDK adds it). Usehttps://sandbox-api.payslice.com, not.../v1.
Releasing
Publishing is automated. Bump the version, tag, and push — CI runs the full gate (spec drift-check, typecheck, tests, build) and publishes to npm:
npm version patch # or minor / major — updates package.json and creates the tag
git push --follow-tagsThe npm credential lives only in the repo secret NPM_TOKEN; nobody publishes
from a laptop.
