Skip to content

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
Terminal window
npm install @guapocado/sdk
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",
});

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.

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 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,
});

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;

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.

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.

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.

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.

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.

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.

By default the SDK calls:

https://api.guapocado.dev

End-user integrations should normally leave this alone. Sandbox versus production is selected by API key.