Create a subscription
Start a recurring charge agreement against a saved payment instrument.
Goal
Create an active (or trialing) subscription that charges a customer's saved payment instrument on a fixed cadence. Use this whenever you sell a recurring plan — SaaS seats, monthly memberships, repeat-fulfillment products. For one-off invoices, see Send an invoice. For self-serve plan changes after the subscription exists, see Launch the customer portal.
Prerequisites
- A sandbox or production API key
@easylabs/nodeinstalled- A Customer and a saved payment instrument belonging to that customer
- A recurring Price (
recurring: true)
Implementation
1. Resolve or create the price
Subscriptions charge against a price ID, not a product ID. If you don't already have one, create the price (or look it up) before continuing:
import { createClient } from "@easylabs/node";
const easy = await createClient({ apiKey: process.env.EASY_API_KEY! });
const { data: price } = await easy.createPrice({
product_id: "prod_01HXXXXXXXXXXX",
active: true,
recurring: true,
currency: "USD",
unit_amount: 4900, // $49.00
interval: "month",
interval_count: 1,
tax_behavior: "exclusive",
});2. Create the subscription
Pass identity_id, instrument_id, and one or more items. The first invoice is generated synchronously; the call resolves once the subscription is active (or trialing if you supply a trial).
const { data: subscription } = await easy.createSubscription({
identity_id: "cus_01HXXXXXXXXXXX",
instrument_id: "pi_01HXXXXXXXXXXX",
items: [{ price_id: price.id, quantity: 3 }],
metadata: { workspace_id: "ws_42" },
});To start with a free trial, omit instrument_id and supply trial_period_days (or an explicit trial_end ISO datetime):
await easy.createSubscription({
identity_id: "cus_01HXXXXXXXXXXX",
items: [{ price_id: price.id, quantity: 1 }],
trial_period_days: 14,
});3. React to lifecycle events
Wire your webhook endpoint to act on subscription.created, subscription.updated, subscription.trial_will_end, invoice.paid, and invoice.payment_failed. See the webhooks reference for signature verification with EasyWebhooks.constructEvent.
4. Mutate later
Add a seat: await easy.addSubscriptionItem(subscription.id, { price_id, quantity: 1 }). Change quantity: updateSubscriptionItem(subscription.id, itemId, { quantity: 5 }). Cancel at period end: updateSubscription(subscription.id, { cancel_at_period_end: true }). Cancel immediately: cancelSubscription(subscription.id). Preview the proration impact of a change first with getSubscriptionProrationPreview.
Tradeoffs
- Trial without an instrument is fine, but the subscription will move to
incompleteat trial end if noinstrument_idhas been attached. Schedule a reminder beforetrial_endand patch the subscription withupdateSubscription({ instrument_id }). - Proration behavior defaults to
create_prorationson item changes. Passproration_behavior: "none"for grandfathered customers, or"always_invoice"to bill the proration immediately rather than rolling into the next cycle. - Skip the subscription engine entirely when billing is per-event or quote-style — use
createInvoiceinstead and avoid the cycle bookkeeping.