JSPM

@foundryco/sdk

0.4.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 26
  • Score
    100M100P100Q67526F
  • License MIT

Official Foundry SDK for Node.js — billing, checkout sessions, subscriptions and webhooks.

Package Exports

  • @foundryco/sdk

Readme

Foundry SDK for Node.js

Status: alpha — published under the next tag. API may change before 1.0.0.

Official SDK for Foundry — billing, checkout sessions, subscriptions and webhooks for SaaS products.

Install

npm install @foundryco/sdk@next

Requires Node.js 20+.

Quickstart

import { Foundry } from "@foundryco/sdk";

const foundry = new Foundry({
  apiKey: process.env.FOUNDRY_API_KEY!, // sk_live_... or sk_test_...
});

// Create a customer
const customer = await foundry.customers.create({
  name: "Tienda Don Pepe",
  email: "pepe@tienda.com",
  external_owner_id: "user_abc123",
});

// Open a checkout session for a price
const session = await foundry.checkoutSessions.create({
  customer: customer.id,
  price: "price_01h...",
  success_url: "https://recompry.com/billing/success",
  cancel_url: "https://recompry.com/pricing",
});

// session.url is the hosted page where your user pays
console.log(session.url);

Resources

Resource Methods
customers list, retrieve, create, update
products list, retrieve
prices list, retrieve
checkoutSessions create, retrieve
subscriptions list, retrieve, cancel
entitlements get
billingPortalSessions create
webhookEndpoints list, retrieve, create, update, delete

Billing portal (Stripe-style)

Mint a hosted self-service URL for the customer:

const session = await foundry.billingPortalSessions.create({
  customer: "cus_...",
  return_url: "https://yourapp.com/account",
});

// session.url expires in 24h. Redirect the customer to it.

The hosted page lets the customer see their subscription, download invoices (PDF/XML, including Colombia DIAN when applicable), and return to your app.

API key scopes

Keys are issued per workspace and can optionally be scoped to a single product. The SDK transparently honors the scope — same code, the server filters responses:

  • Workspace-wide key (default): full access to every product, plan, price, subscription and checkout session in the workspace.
  • Product-scoped key: products, prices, subscriptions and checkoutSessions only see resources for that product. Creating a checkout session against a price that belongs to a different product returns a validation error.

customers and webhookEndpoints remain workspace-wide regardless of scope — contacts are shared and webhooks are workspace-level by design.

Generate keys (and pick a product scope) under Settings → API keys in the console.

Webhooks

Verify and parse webhook deliveries from Foundry:

import { Foundry, WebhookSignatureError } from "@foundryco/sdk";

const foundry = new Foundry({ apiKey: process.env.FOUNDRY_API_KEY! });

// In your HTTP handler, with the raw request body:
try {
  const event = await foundry.webhooks.constructEvent(
    rawBody,
    request.headers["foundry-signature"],
    process.env.FOUNDRY_WEBHOOK_SECRET!, // whsec_...
  );

  switch (event.type) {
    case "subscription.created":
      // event.data.object is the Subscription resource
      break;
    case "checkout_session.completed":
      // event.data.object is the CheckoutSession resource
      break;
    // ...
  }
} catch (err) {
  if (err instanceof WebhookSignatureError) {
    return new Response("invalid signature", { status: 401 });
  }
  throw err;
}

Errors

All API errors inherit from FoundryAPIError and carry status, code, message, requestId:

import {
  AuthenticationError,
  NotFoundError,
  RateLimitError,
  ValidationError,
} from "@foundryco/sdk";

try {
  await foundry.customers.retrieve("cus_doesnotexist");
} catch (err) {
  if (err instanceof NotFoundError) {
    // 404 — resource doesn't exist or doesn't belong to your workspace
  } else if (err instanceof RateLimitError) {
    // 429 — back off (err.retryAfter has the seconds, when present)
  } else if (err instanceof ValidationError) {
    // 422 — body/query failed validation; err.details has per-field info
  }
}

Configuration

const foundry = new Foundry({
  apiKey: "sk_live_...",
  baseURL: "https://api.foundry.greenstudio.vc", // default
  timeout: 30_000,    // ms, default 30s
  maxRetries: 3,      // default — only 408/429/5xx
  fetch: customFetch, // optional — defaults to globalThis.fetch
});

Per-request:

await foundry.customers.create(
  { name: "..." },
  {
    idempotencyKey: crypto.randomUUID(),
    signal: abortController.signal,
  },
);

Modes

API keys carry their environment in the prefix:

  • sk_live_... → operates against live customers and real charges
  • sk_test_... → operates against test data; charges go to Wompi sandbox

The same SDK code works for both — switch by setting a different apiKey.

License

MIT — see LICENSE.