Products & Pricing
Products and prices — methods, parameters, and examples for @easylabs/node.
Products are the catalog rows ("Pro plan", "Setup fee"); prices attach a currency, amount, and (for recurring prices) an interval to a product. Subscriptions and checkout line items reference prices, not products — a single product typically has several active prices (monthly vs. yearly, USD vs. EUR, etc.).
Methods
Products
easy.createProduct(body); // POST /products
easy.getProduct(productId); // GET /products/:id
easy.getProducts(params?); // GET /products
easy.updateProduct(productId, body); // PATCH /products/:id
easy.archiveProduct(productId); // PATCH /products/:id/archive
easy.getProductWithPrices(productId); // GET /products/:id/prices
easy.getProductWithPrice(productId, priceId); // GET /products/:id/prices/:priceIdcreateProduct payload:
await easy.createProduct({
name: "Pro plan",
active: true,
description: "Everything in Free, plus team seats.",
image_url: "https://cdn.example.com/pro.png",
statement_descriptor: "EXAMPLE PRO",
unit_label: "seat",
metadata: { tier: "pro" },
default_price_id: undefined, // set after you create a price
});updateProduct accepts Partial<CreateProduct> — including switching default_price_id once you have one. archiveProduct flips it to inactive without deleting historical orders.
Prices
easy.createPrice(body); // POST /product-prices
easy.getPrice(priceId); // GET /product-prices/:id
easy.getPrices(params?); // GET /product-prices
easy.updatePrice(priceId, body); // PATCH /product-prices/:id
easy.archivePrice(priceId); // PATCH /product-prices/:id/archiveCreatePrice is a discriminated union on recurring:
// Recurring
await easy.createPrice({
product_id: "PD_...",
active: true,
recurring: true,
currency: "USD",
unit_amount: 2900, // $29.00
interval: "month",
interval_count: 1,
tax_behavior: "exclusive",
pricing_model: "per_unit",
trial_period_days: 14,
});
// One-time
await easy.createPrice({
product_id: "PD_...",
active: true,
recurring: false,
currency: "USD",
unit_amount: 4999,
tax_behavior: "exclusive",
});updatePrice accepts only the fields the API treats as editable in flight: active, metadata, description. Everything else (amount, currency, interval) is immutable — create a new price and switch the product's default_price_id.
Object shape
ProductData
id, name, description, active, image_url, statement_descriptor, unit_label, default_price_id, metadata, created_at, updated_at, plus company_id (stripped on the nested getProductWithPrices response via PickExcept).
PriceData
| Field | Type | Notes |
|---|---|---|
id | string | |
product_id | string | |
currency | CurrencyCode | ISO 4217. |
unit_amount | number | Smallest currency unit. |
recurring | boolean | |
interval | "day" | "week" | "month" | "year" | null | Null on one-time prices. |
interval_count | number | null | E.g. 3 with interval: "month" for quarterly. |
pricing_model | "per_unit" | "tiered_volume" | "tiered_graduated" | "package" | "metered" | "flat_rate" | null | |
trial_period_days | number | null | |
tax_behavior | "exclusive" | "inclusive" | |
tax_rate_id | string | null | |
description | string | null | |
metadata | Record<string, unknown> | |
active | boolean |
Examples
Spin up a new monthly plan
const product = await easy.createProduct({ name: "Team plan", active: true });
const price = await easy.createPrice({
product_id: product.data.id,
active: true,
recurring: true,
currency: "USD",
unit_amount: 4900,
interval: "month",
interval_count: 1,
tax_behavior: "exclusive",
});
await easy.updateProduct(product.data.id, { default_price_id: price.data.id });Migrate to a new price (deprecate the old one)
const newPrice = await easy.createPrice({ /* … */ });
await easy.updateProduct(productId, { default_price_id: newPrice.data.id });
await easy.archivePrice(oldPriceId);Read product + every price in one round-trip
const { data } = await easy.getProductWithPrices(productId);
for (const p of data.prices) {
console.log(p.id, p.unit_amount, p.interval);
}