Package Exports
- @foundryco/sdk
Readme
Foundry SDK for Node.js
Status: alpha — published under the
nexttag. API may change before1.0.0.
Official SDK for Foundry — billing, checkout sessions, subscriptions and webhooks for SaaS products.
Install
npm install @foundryco/sdk@nextRequires 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 |
webhookEndpoints |
list, retrieve, create, update, delete |
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 chargessk_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.