@forjio/serront
v0.1.0
Published
Serront SDK — typed JS/TS client for the serront.com REST API. Sister to the Python + Go SDKs.
Maintainers
Readme
@forjio/serront
Typed JS/TS client for the serront.com services-storefront REST API.
npm install @forjio/serrontimport fs from "node:fs";
import { SerrontClient } from "@forjio/serront";
// Bearer token from `token` or the SERRONT_TOKEN env var — use an
// sk_live_… API key from Dashboard → Settings → API keys.
const client = new SerrontClient({ token: "sk_live_xxx" });
// Storefront — the seller's public profile
const settings = await client.storefront.get(); // null until first put
await client.storefront.put({
slug: "studio-renata",
displayName: "Studio Renata",
bio: "Brand & logo design studio.",
whatsappNumber: "+628120000000",
manualBankAccounts: [{ bankName: "BCA", accountNumber: "1234567890", accountHolder: "Renata" }],
published: true,
});
// Services — the catalog (fixed / hourly / package pricing)
const services = await client.services.list();
const svc = await client.services.create({
slug: "logo-design",
name: "Logo design",
pricingType: "package",
packages: [
{ name: "Basic", priceIdr: 500_000 },
{ name: "Full identity", priceIdr: 2_500_000 },
],
});
await client.services.update(svc.id, { sortOrder: 1 });
// Orders — the order desk
const { orders, counts, cursor, hasMore } = await client.orders.list({
status: "requested",
paymentStatus: "payment_claimed",
q: "wedding",
});
const order = await client.orders.get(orders[0].id); // + full thread
await client.orders.reply(order.id, { body: "On it!", isInternal: false });
await client.orders.update(order.id, { status: "confirmed", quotedPriceIdr: 750_000 });
await client.orders.confirmPayment(order.id);
const proof = await client.orders.downloadProof(order.id); // { data, contentType }
// Modules — Payment (Plugipay) / Marketing (Ripllo) integrations
const modules = await client.modules.get();
await client.modules.set({ payment: true });
// Ledger + payouts (Plugipay-held; Payment module required)
const balance = await client.ledger.balance();
const entries = await client.ledger.entries({ limit: 20 });
const payouts = await client.payouts.list({ status: "pending" });
const payout = await client.payouts.create({ amount: 1_000_000 });
await client.payouts.cancel(payout.id);
// API keys + webhook subscriptions
const { apiKeys } = await client.apiKeys.list();
const created = await client.apiKeys.create({ name: "ci" }); // created.key shown ONCE
const hook = await client.webhookSubscriptions.create({
url: "https://example.com/serront-hook",
events: ["serront.order.created.v1"],
}); // hook.secret shown ONCE
// Billing — current plan + upgrade checkout (tiers: free / starter / growth / business)
const { subscription, effectiveTier, tiers } = await client.billing.get();
const { hostedUrl } = await client.billing.checkout("starter"); // redirect the browser here
// Public (buyer) surface — no token required
const view = await client.public.getStorefront("studio-renata");
const placed = await client.public.createOrder("studio-renata", {
serviceSlug: "logo-design",
packageName: "Basic",
buyerName: "Budi",
buyerEmail: "[email protected]",
discountCode: "WELCOME10", // Marketing module
});
const check = await client.public.validateDiscount("studio-renata", {
code: "WELCOME10",
priceIdr: 500_000,
});
const myOrder = await client.public.getOrder(placed.accessToken);
await client.public.replyOrder(placed.accessToken, { body: "Any update?" });
await client.public.uploadProof(placed.accessToken, {
data: await fs.promises.readFile("transfer.png"),
contentType: "image/png",
});
await client.public.claimPayment(placed.accessToken); // "I have transferred"
const pay = await client.public.pay(placed.accessToken); // Payment module → hostedUrlEndpoints covered
| Resource | Methods |
| --- | --- |
| storefront | get, put (slug / display / bio / WhatsApp / bank accounts / branding / publish) |
| services | list, create (403 LIMIT_REACHED past the tier's service limit), get, update, delete (409 while orders reference it) |
| orders | list (status / paymentStatus / serviceId / q / limit / cursor + per-status counts), get, update (status and/or quotedPriceIdr), reply (with isInternal), confirmPayment, downloadProof |
| modules | get, set (payment / marketing toggles; first enable provisions the partner workspace) |
| ledger | balance, entries (cursor-paged; 409 PAYMENT_MODULE_DISABLED when off) |
| payouts | list, create, cancel, balance, getBankAccount, updateBankAccount, markInTransit, markPaid, markFailed |
| apiKeys | list, create (plaintext returned once), delete |
| webhookSubscriptions | list, create (secret returned once), update (active), delete |
| billing | get (subscription + effectiveTier + tier table), checkout(tier) → hosted checkout URL |
| public | getStorefront, createOrder, validateDiscount, getOrder, replyOrder, claimPayment, pay, uploadProof (no token) |
Errors throw SerrontError carrying the API envelope's error.code
(NOT_FOUND, VALIDATION_ERROR, LIMIT_REACHED, UPGRADE_REQUIRED,
INVALID_TRANSITION, …), the HTTP status, and the meta.requestId.
See serront.com/docs/sdk/js for the full method reference.
Family
Sister to:
forjio-serront(Python)hachimi-cat/serront-go(Go)
