The SDK wraps the REST API for Node.js 18 and later with no runtime dependencies: a typed client per resource, for await over every page of a list, an Idempotency-Key on every write so its own retries never act twice, typed errors, and webhook signature verification.
Create a client
import { AnytimeCapital } from "@anytimecapital/node";
const client = new AnytimeCapital({
apiKey: process.env.ANYTIME_API_KEY!, // ak_live_…, on your server only
maxRetries: 2, // retries after the first attempt, on 429, 5xx and network errors
timeoutMs: 30_000, // per attempt
baseUrl: "https://anytime-capital.com/api/v1",
});| Resource | Methods |
|---|---|
client.payments | list, retrieve |
client.refunds | list, create, retrieve |
client.paymentLinks | list, create, retrieve, update, deactivate |
client.invoices | list, create, retrieve, send |
client.customers | list, create, retrieve, update |
client.products | list, create, retrieve, update, archive |
client.subscriptionPlans | list, create, retrieve |
client.subscriptions | list, retrieve, cancel, pause, resume |
client.discountCodes | list, create, retrieve, deactivate |
client.payouts | list, retrieve |
client.balance | retrieve |
client.disputes | list, retrieve |
client.webhookEndpoints | list, create, retrieve, update, del |
client.events | list, retrieve |
client.testHelpers.events | create (test keys only: Test helpers) |
Meters and usage events aren't in the SDK yet: send them with fetch as on Meters & usage events.
Lists and pages
// One page…
const page = await client.payments.list({ status: "paid", limit: 50 });
// …or every payment, each page fetched as you reach it.
for await (const payment of client.payments.list({ createdFrom: "2026-10-01" })) {
console.log(payment.id, payment.amountCents, payment.metadata.orderId);
}
// Or collect them (stops at 10,000 by default).
const all = await client.refunds.list({ paymentId: "pay_7Hq2LmX9cR4tVb" }).toArray();Writes and retries
Every write sends an Idempotency-Key: yours when you pass one, otherwise one made per call. The SDK retries 429, 5xx and network errors with backoff that honours Retry-After, always with the same key, so a retry never acts twice. Pass your own key, stored with your order, to stay safe across process restarts too.
const refund = await client.refunds.create(
{ paymentId: "pay_7Hq2LmX9cR4tVb", amountCents: 2500, reason: "requested_by_customer" },
{ idempotencyKey: `refund-${orderId}` },
);
if (refund.status === "pending_approval") {
// Over your refund approval limit: an owner approves it in the dashboard.
}Errors
| Class | When |
|---|---|
InvalidRequestError | 400 or 422: fix the request; the message names the field. |
AuthenticationError | 401: missing, unknown or revoked key. |
PermissionError | 403: PERMISSION_DENIED or NOT_ENROLLED. |
NotFoundError | 404. |
ConflictError | 409: the object's state doesn't allow it. |
RateLimitError | 429, after its retries. |
ApiError | 5xx, after its retries. |
ConnectionError | No answer, after its retries (TIMEOUT or CONNECTION_ERROR). |
SignatureVerificationError | A webhook that didn't verify. |
All extend AnytimeCapitalError, with code, message, status and retryAfterSeconds. Branch on instanceof or on code; every code is in the error code catalogue.
Webhooks
import express from "express";
import { AnytimeCapital } from "@anytimecapital/node";
const app = express();
app.post("/webhooks/anytime", express.raw({ type: "application/json" }), (req, res) => {
let event;
try {
event = AnytimeCapital.webhooks.constructEvent(
req.body, // the raw Buffer, not parsed JSON
req.header("Anytime-Signature"),
process.env.ANYTIME_WEBHOOK_SECRET!,
);
} catch {
return res.sendStatus(400);
}
if (event.type === "payment.succeeded") {
// event.data is the payment: match your order with data.paymentLinkId or data.metadata.
}
res.sendStatus(200);
});constructEvent rejects signatures older than 5 minutes ({ toleranceSeconds } changes it), and generateTestHeader(body, secret) signs a body for your own tests. Without the SDK, verify as on Verify webhooks.