Using Payday

TypeScript SDK

A typed client with no runtime dependencies, for Node 18 and up or any runtime with fetch. Field names are the API's own snake_case, so the reference applies unchanged.

Shell
npm install @payday/sdk

There are two clients. PaydayClient holds a credential and is for your server. PaydayPayerClient holds nothing and reads the public routes behind a deposit link, so it can run in a browser.

The merchant client#

TypeScript
import { PaydayClient } from "@payday/sdk";

const payday = new PaydayClient({
  apiKey: process.env.PAYDAY_API_KEY!,
  // baseUrl: "https://api.sandbox.payday.sh",   // the sandbox, or a local gateway
});

const request = await payday.depositRequests.create(
  {
    amount: "10.00",
    payout_address: "0x1111111111111111111111111111111111111111",
    issuer: { name: "Acme LLC", email: "billing@acme.example" },
    payer: { name: "Customer Inc", details: "12 Main St, Springfield" },
    heading: "March retainer",
    reference: "INV-1042",
    payer_policy: { mode: "permissionless" },
    expires_in: 3600,
  },
  crypto.randomUUID(), // the idempotency key is required
);

await payday.depositRequests.get(request.id);
await payday.depositRequests.get(request.id, { waitForChange: true, timeout: 30 });
await payday.depositRequests.list({ status: "awaiting_deposit", limit: 20 });
NamespaceMethods
depositRequestscreate, get, list, cancel, transfers, attachment, requestPdf, proof, verification, createClientSecret, previewSession
customerscreate, get, list, update
issuerscreate, get, list, update, remove, startEmailVerification, confirmEmailVerification, setPayoutAddresses
payoutAddressescreate, list, remove
attachmentsupload (the whole exchange), create, finalize
webhooksadd, list, get, remove, test, deliveries
accountget, issueApiKey, revokeApiKey
statusChain, indexer, and settlement-queue health.

Every method maps to one route in the API reference, takes and returns the same fields, and is typed. attachments.upload is the one convenience: it reserves the slot, sends the bytes with the presigned headers, and polls finalization with backoff until the scan admits or rejects the file. Pass an AbortSignal to cancel any call.

Errors#

Every API failure throws PaydayError with the stable code, the HTTP status, the request id, and a message that names the problem.

TypeScript
import { PaydayError } from "@payday/sdk";

try {
  await payday.depositRequests.get("dr_does-not-exist");
} catch (error) {
  if (error instanceof PaydayError) {
    error.code;      // "deposit_request_not_found"
    error.status;    // 404
    error.message;   // names the problem, and the field when a body does not fit
    error.requestId; // quote this to support
  }
}

Helpers#

  • checkoutUrl(request, clientSecret) builds the merchant-session link with the secret in the URL fragment.
  • previewUrl(request, previewSession) builds the link that opens a request's payer view unlocked for its own issuer, for checking before sending.

The payer client#

For a checkout of your own. It reads what the hosted page reads and exposes the verification and wallet writes. See Building your own checkout for the flow.

TypeScript
import { PaydayPayerClient } from "@payday/sdk";

// No key. Safe in a browser: a deposit link is open by design.
const payer = new PaydayPayerClient();

const request = await payer.depositRequests.get("dr_0198f80c-…");
const qr = await payer.depositRequests.qr(request.id, payerSession);          // SVG blob
const pdf = await payer.depositRequests.attachment(request.id, payerSession); // descriptor + download_url

// Email verification, wallet attestation, and merchant-session exchange:
await payer.verification.startEmail(request.id);
await payer.verification.confirmEmail(request.id, "123456", payerSession);
await payer.verification.exchangeClientSecret(request.id, clientSecret);
await payer.wallet.challenge(request.id, walletAddress, { payerSession });
await payer.wallet.attest(request.id, walletAddress, signature, payerSession);

Dashboard sessions#

The client also accepts a dashboard session token in place of a key, which is how Payday's own dashboard calls the API from a browser without a key ever existing there. Exactly one of the two credentials is allowed.

TypeScript
// The dashboard's own way in: the signed-in merchant's session token instead of a key.
const payday = new PaydayClient({ accessToken: identityToken });
await payday.account.get();
await payday.account.issueApiKey(account.generation); // session only; a key is refused here