Vatly
JavaScript Fluent

Subscriptions

Change an existing subscription with vatly.subscription(localRecord).

Change an existing subscription with vatly.subscription(localRecord). Load and authorize the record from your repository; wire that repository into Vatly so swap, resume, and sync can save changes.

Change plan or quantity

import type { LocalSubscription, Vatly } from '@getvatly/fluent';

export async function schedulePlan(vatly: Vatly, local: LocalSubscription, planId: string) {
  return vatly.subscription(local).swap(planId, { quantity: 2 });
}

export async function changeAndInvoice(vatly: Vatly, local: LocalSubscription, planId: string) {
  return vatly.subscription(local).swapAndInvoice(planId, { quantity: 2 });
}

export async function changeWithoutCharge(vatly: Vatly, local: LocalSubscription, planId: string) {
  return vatly.subscription(local).swapWithoutCharge(planId);
}

Choose one action:

MethodOptions sent to the API
swap(planId, body?, requestOptions?)Your update options; without applyImmediately, changes are scheduled.
swapAndInvoice(...)Forces applyImmediately: true, invoiceImmediately: true.
swapWithoutCharge(...)Forces applyImmediately: true, prorationCharge: false.

Local plan and quantity follow the API response. A scheduled swap leaves the current plan unchanged. scheduledUpdate() fetches the pending change; hasScheduledUpdate() checks whether one exists. See the API reference below for proration rules.

Cancel and resume

import type { LocalSubscription, Vatly } from '@getvatly/fluent';

export async function cancelNow(vatly: Vatly, local: LocalSubscription, operationId: string) {
  await vatly.subscription(local).cancel({
    immediately: true,
    idempotencyKey: `cancel:${operationId}`,
  });
}

export async function resume(vatly: Vatly, local: LocalSubscription) {
  const handle = await vatly.subscription(local).resume();
  return handle.model();
}

cancel() uses the API's default timing; { immediately: true } cancels now. Cancellation waits for webhooks to set local endsAt. Resume clears it after the API succeeds. cancellationReason() fetches the remote reason.

model() returns the local record. sync() fetches and saves the API resource. state() derives active, valid, recurring, cancelled, onGracePeriod, and ended from endsAt. Check Vatly's status separately for states such as paused.

Grace-period limitation in this alpha

This alpha's sync() and cancellation reactions set endsAt to endedAt ?? canceledAt. During a grace period, endedAt can be null and canceledAt is the cancellation request time, not the access deadline. The local flags can therefore report an ended subscription while access is still owed. Keep entitlement decisions separate from state() until this is corrected; confirm the grace deadline before changing access. Do not use canceledAt as an expiry.

Update billing details

import type { LocalSubscription, Vatly } from '@getvatly/fluent';

export async function billingUpdateUrl(vatly: Vatly, local: LocalSubscription) {
  return vatly.subscription(local).updateBilling({
    redirectUrlSuccess: 'https://app.example.com/billing/updated',
    redirectUrlCanceled: 'https://app.example.com/billing/canceled',
  });
}

Redirect to the returned URL; do not log or persist it. This hosted flow updates billing details, rather than writing address fields directly. Both redirects are required here or in configuration. Process subscription.billing_updated to update local payment details.

For all options, eligibility, and fields, see the Subscriptions API reference.

Copyright © 2026