Server SDK
@guapocado/sdk is the core package. Framework packages are thin wrappers
around it.
Use the server SDK anywhere you have trusted server code:
- Hono routes
- Express routes
- Next.js route handlers
- server actions
- background workers
- queues
- cron jobs
Install
Section titled “Install”npm install @guapocado/sdkCreate a Client
Section titled “Create a Client”import { createGuapocadoClient } from "@guapocado/sdk";
const guap = createGuapocadoClient({ apiKey: process.env.GUAPOCADO_API_KEY!, customerId: "org_123",});customerId is optional at client creation. If you do not provide it, pass it
per call.
await guap.has("advanced-analytics", { customerId: "org_123",});Server Key Required
Section titled “Server Key Required”The server SDK should use a server key:
sk_guap_test_...sk_guap_live_...Server keys can read and mutate billing state. Never expose them to browser code.
Feature Checks
Section titled “Feature Checks”Use has() for boolean feature gates.
const allowed = await guap.has("advanced-analytics");
if (!allowed) { throw new Response("Upgrade required", { status: 403 });}Meters are read and consumed through usage.
const balance = await guap.usage.balance("api-calls");Consume when billable work happens:
await guap.usage.consume("api-calls", 1);Refund if the work fails after consumption:
await guap.usage.consume("exports", 1);
try { await runExport();} catch (error) { await guap.usage.refund("exports", 1); throw error;}Enable or disable customer-controlled overage:
await guap.usage.configure("api-calls", { overageEnabled: true,});Limits
Section titled “Limits”Limits return the numeric allowance. Your app compares it with local state.
const seats = await guap.limit("seats");const activeSeats = await db.user.count({ where: { orgId } });
if (activeSeats >= seats.limit) { throw new Response("Seat limit reached", { status: 403 });}Configure purchased expansion:
await guap.limits.configure("seats", { purchased: 3, autoExpansionEnabled: false,});Context
Section titled “Context”Use context() when a page or route needs several billing answers at once.
const context = await guap.context({ features: ["advanced-analytics", "exports"], usage: ["api-calls"], limits: ["seats"], includePlans: true, includeSubscription: true,});
context.features["advanced-analytics"];context.usage["api-calls"]?.balance;context.limits.seats?.limit;Customers
Section titled “Customers”Create or update a customer record before checkout or when syncing app identity:
await guap.customers.create({ id: "org_123", name: "Acme Inc.", email: "owner@example.com",});The id should match the customerId you use for checks.
Checkout
Section titled “Checkout”Create a checkout session for recurring or one-time products:
const checkout = await guap.checkout.create({ productKey: "pro", successUrl: "https://app.example.com/billing/success", cancelUrl: "https://app.example.com/billing",});
return Response.redirect(checkout.url);Guapocado reads the product config and chooses the correct Stripe Checkout mode.
Plans, Subscriptions, and Purchases
Section titled “Plans, Subscriptions, and Purchases”const plans = await guap.plans.list();const currentSubscription = await guap.subscription.current();const changedSubscription = await guap.subscription.change("pro");const purchases = await guap.purchases.list();subscription.change() is useful after a customer already has a subscription.
New customers normally start with checkout.
Webhook Registration
Section titled “Webhook Registration”Framework integrations usually handle receiver registration. The server SDK also exposes the primitive:
await guap.webhooks.register({ url: "https://app.example.com/api/guap-webhook", events: "*", integration: "custom", registrationKey: "custom:primary",});Registration requires a server key (this call is server-side only), so new receivers go active immediately — no dashboard approval step. A server key is the tenant’s own highest-trust credential, so a tenant registering a destination for their own data doesn’t need platform review.
Read-only Client
Section titled “Read-only Client”For browser-safe reads, use a client key with createReadOnlyGuapocadoClient().
Most React apps should use @guapocado/react, which wraps this client.
import { createReadOnlyGuapocadoClient } from "@guapocado/sdk";
const guap = createReadOnlyGuapocadoClient({ apiKey: "ck_guap_test_...", customerId: "org_123",});
await guap.has("advanced-analytics");await guap.limit("seats");await guap.usage.balance("api-calls");The read-only client cannot start checkout, consume usage, or mutate state.
Error Types
Section titled “Error Types”The SDK throws typed errors:
import { GuapocadoAuthError, GuapocadoError, GuapocadoRateLimitError, GuapocadoValidationError,} from "@guapocado/sdk";Use them in framework error handlers to return the right status code.
API Target
Section titled “API Target”By default the SDK calls:
https://api.guapocado.devEnd-user integrations should normally leave this alone. Sandbox versus production is selected by API key.