Subscriptions

A customer's recurring commitment to one or more prices, billed at the end of each period.

See the subscriptions guide for the billing model, trials and dunning.

The subscription object

idstring
Prefixed sub_.
objectstring
subscription
customerstring
statusenum
trialing, active, past_due, unpaid, canceled or paused.
collection_methodenum
charge_automatically (saved card) or send_invoice (bank invoice).
days_until_dueinteger | null
For send_invoice.
current_period_starttimestamp
current_period_endtimestamp
When the next invoice is issued.
cancel_at_period_endboolean
canceled_attimestamp | null
ended_attimestamp | null
trial_starttimestamp | null
trial_endtimestamp | null
pause_collectionobject | null
discountobject | null
amount_off or percent_off, duration (once, repeating, forever), and coupon and promotion_code when it came from one.
couponstring | null
The coupon behind discount.
promotion_codestring | null
The code it was redeemed with.
discount_endtimestamp | null
When a repeating discount stops applying.
metadataobject
created_attimestamp
updated_attimestamp

Quote a subscription

POST/v1/subscriptions/quoteSecret key

What one period of a subscription on these items would cost, VAT and an optional coupon or promotion code included, before anything is created or redeemed. The lines are at their prices with VAT inside them; the discount comes off their total and VAT is computed on what is paid. A metered price is listed at zero, since its usage is unknown. Use it to check a code, or to know what amount to collect on a checkout session that starts the subscription.

Body

itemsarrayrequired
{ price, quantity? }, as on create.
customerstring
For the customer's VAT status and customer-restricted codes.
couponstring
promotion_codestring
trial_period_daysinteger
Makes first_invoice_total zero and dates the first invoice at the trial's end.
Response
{
  "object": "subscription_quote",
  "currency": "ISK",
  "interval": "month",
  "interval_count": 1,
  "lines": [
    { "price": "price_2Ab4Cd6Ef8Gh0Ij2Kl4Mn6Op", "description": "Pro plan", "quantity": 2, "unit_amount": 4990, "amount": 9980, "tax_rate": 2400, "tax_amount": 1932 }
  ],
  "subtotal": 6438,
  "tax": 1546,
  "discount": 1996,
  "total": 7984,
  "tax_breakdown": [{ "tax_rate": 2400, "taxable_amount": 6438, "tax_amount": 1546 }],
  "coupon": "cpn_7Hs2Kq9LmW4xZc1Vb8Ny3Rt6",
  "promotion_code": "promo_3Vb8Ny1Rt6Hs2Kq9LmW4xZc7",
  "discount_duration": "repeating",
  "first_invoice_at": "2026-11-22T10:00:00.000Z",
  "first_invoice_total": 7984
}

Errors: resource_not_found for a price or customer, invalid_field for coupon and promotion_code together or a price listed twice, invalid_price_type for a price that is not recurring, invalid_subscription for items with mixed intervals or currencies, resource_in_wrong_state for a code or coupon that cannot be applied.

Create a subscription

POST/v1/subscriptionsSecret key

The customer needs a default payment method for charge_automatically, or a kennitala for send_invoice. All items must share an interval and a currency, and at least one must be recurring.

Body

customerstringrequired
itemsarrayrequired
One or more { price, quantity?, credit_rollover?, included_units?, trial_included_units?, metadata? }. trial_included_units is the item's allowance while trialing; without it the trial has the paid allowance.
collection_methodenumdefault: charge_automatically
charge_automatically or send_invoice.
trial_period_daysinteger
Free days before the first paid period.
days_until_dueinteger
For send_invoice: days the customer has to pay each invoice.
couponstring
A coupon to apply. Its discount is snapshotted onto the subscription.
promotion_codestring
A promotion code to redeem, as the customer typed it. Refused with resource_in_wrong_state when it no longer applies.
metadataobject
Node.jsts
// The customer already has a saved card (payment_method.attached fired).
const subscription = await borga.subscriptions.create({
  customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  items: [{ price: "price_2Ab4Cd6Ef8Gh0Ij2Kl4Mn6Op", quantity: 3 }],
  collection_method: "charge_automatically",
  trial_period_days: 14,
  metadata: { plan: "pro" },
});

console.log(subscription.status); // "trialing"
console.log(subscription.current_period_end);
Response
{
  "id": "sub_9Qw1Er3Ty5Ui7Op9As1Df3Gh",
  "object": "subscription",
  "customer": "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  "status": "trialing",
  "collection_method": "charge_automatically",
  "days_until_due": null,
  "current_period_start": "2026-09-07T12:00:00.000Z",
  "current_period_end": "2026-09-21T12:00:00.000Z",
  "cancel_at_period_end": false,
  "canceled_at": null,
  "ended_at": null,
  "trial_start": "2026-09-07T12:00:00.000Z",
  "trial_end": "2026-09-21T12:00:00.000Z",
  "pause_collection": null,
  "discount": null,
  "metadata": { "plan": "pro" },
  "items": [
    {
      "id": "si_5Zx7Cv9Bn1Mq3We5Rt7Yu9Io",
      "object": "subscription_item",
      "subscription": "sub_9Qw1Er3Ty5Ui7Op9As1Df3Gh",
      "price": "price_2Ab4Cd6Ef8Gh0Ij2Kl4Mn6Op",
      "quantity": 3,
      "credit_rollover": false,
      "included_units": null,
      "trial_included_units": null,
      "metadata": {},
      "created_at": "2026-09-07T12:00:00.000Z",
      "updated_at": "2026-09-07T12:00:00.000Z"
    }
  ],
  "created_at": "2026-09-07T12:00:00.000Z",
  "updated_at": "2026-09-07T12:00:00.000Z"
}

Errors: invalid_subscription, invalid_price_type, meter_already_subscribed, resource_not_found.

Retrieve a subscription

GET/v1/subscriptions/:idSecret key

Update a subscription

POST/v1/subscriptions/:idSecret key

Change items through subscription items.

Body

cancel_at_period_endboolean
couponstring | null
Apply a coupon, replacing the current discount; null removes the discount, whichever coupon or code it came from.
promotion_codestring
Redeem a promotion code, replacing the current discount.
collection_methodenum
days_until_dueinteger
metadataobject

Cancel a subscription

POST/v1/subscriptions/:id/cancelSecret key

Body

at_period_endbooleandefault: false
true keeps the subscription active until current_period_end, bills the final period, then cancels. false ends it now: a final invoice (billing_reason: subscription_cancel) bills metered usage so far and licensed items for the part of the period used.

Uncancel a subscription

POST/v1/subscriptions/:id/uncancelSecret key

Clears cancel_at_period_end. Fails with cannot_uncancel if no cancellation is pending.

Pause a subscription

POST/v1/subscriptions/:id/pauseSecret key

Stops billing. Fails with cannot_pause unless the subscription is active or trialing.

Resume a subscription

POST/v1/subscriptions/:id/resumeSecret key

Fails with not_paused if the subscription is not paused.

List subscriptions

GET/v1/subscriptionsSecret key

Query parameters

customerstring
statusenum
limitintegerdefault: 25
starting_afterstring
Node.jsts
const id = "sub_9Qw1Er3Ty5Ui7Op9As1Df3Gh";

// Change seats; Borga prorates the difference on the next invoice.
const [item] = (await borga.subscriptions.retrieve(id)).items;
await borga.subscriptionItems.update(item.id, {
  quantity: 5,
  proration_behavior: "create_prorations",
});

// Add a one-off charge to the next invoice.
await borga.invoiceItems.create({
  customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  subscription: id,
  amount: 15000,
  description: "Onboarding workshop",
});

// Cancel when the period ends (reversible with uncancel).
await borga.subscriptions.cancel(id, { at_period_end: true });
await borga.subscriptions.uncancel(id);

// Pause and resume billing.
await borga.subscriptions.pause(id);
await borga.subscriptions.resume(id);