Skip to content

Team seats

The paying customer and the workspace receiving capacity can be different subjects. Your application still owns workspace membership, invitations, and RBAC.

import {
Access,
Billing,
Offer,
Purchase,
Seats,
assignSeat,
checkSeatAssignmentAccess,
createCheckout,
processPaymentNotification,
registerOffers,
unassignSeat,
} from "@byfungsi/sapa";
const teamAccess =
yield *
Access.entitlements({
entitlements: ["workspace.use"],
duration: "purchased_period",
});
const teamCapacity =
yield *
Seats.capacity({
pool: "workspace.members",
quantity: "purchase_quantity",
entitlement: "workspace.use",
});
const teamPurchase =
yield *
Purchase.parse({
key: "workspace.team.access",
revision: 1,
provides: [teamAccess, teamCapacity],
});
const teamBilling =
yield *
Billing.perSeatSubscription({
family: "workspace",
interval: "month",
price: { currency: "IDR", amount: 75_000n },
minimumQuantity: 1,
maximumQuantity: 100,
});
const team =
yield *
Offer.parse({
key: "workspace.team",
revision: 1,
purchase: teamPurchase,
billing: teamBilling,
});
yield * registerOffers([team]);
yield *
createCheckout({
type: "offer",
offerKey: team.key,
offerRevision: team.revision,
quantity: 10,
beneficiary: { type: "merchant_subject", subjectId: "workspace-42" },
provider: "midtrans",
customer: {
externalId: "billing-customer-42",
email: "owner@example.com",
name: "Workspace Owner",
},
successUrl,
cancelUrl,
clientIp: null,
idempotencyKey: "workspace-42-seats-10",
});
yield *
processPaymentNotification({
provider: "midtrans",
payload: rawProviderPayload,
});
const assignment =
yield *
assignSeat({
idempotencyKey: "workspace-42-member-7",
beneficiary: { type: "merchant_subject", subjectId: "workspace-42" },
pool: "workspace.members",
memberId: "member-7",
principal: "workspace-admin-3",
});
const access =
yield *
checkSeatAssignmentAccess({
beneficiary: { type: "merchant_subject", subjectId: "workspace-42" },
pool: "workspace.members",
memberId: "member-7",
entitlement: "workspace.use",
});
yield *
unassignSeat({
idempotencyKey: "workspace-42-member-7-remove",
assignmentId: assignment.assignmentId,
principal: "workspace-admin-3",
});

Authenticate and authorize the principal in your host before calling SAPA. Assignments are capacity accounting, not identity proof. To reduce quantity, first unassign enough members, then schedule a guarded next-period quantity change and settle the renewal. SAPA rejects a reduction below active occupancy.