> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tagada.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Subscriptions & Rebilling

> Create subscriptions, handle trials, manual rebills, processor changes, and the full subscription lifecycle

# Subscriptions & Rebilling

**Time**: \~10 minutes | **Difficulty**: Intermediate

***

## What Are Subscriptions in TagadaPay?

TagadaPay manages the entire recurring billing lifecycle — creation, automatic rebills, retries on failure, pause/resume, cancellation, and processor changes. It's **PSP-agnostic**: the same subscription can be billed through Stripe today and NMI tomorrow, without touching your code.

```
┌────────────┐     ┌─────────────┐     ┌──────────────┐     ┌──────────┐
│  Customer   │────▶│  Subscribe  │────▶│  Auto-rebill │────▶│  Renew   │
│  pays once  │     │  (active)   │     │  every cycle │     │  forever │
└────────────┘     └─────────────┘     └──────────────┘     └──────────┘
                         │                    │
                         │              ┌─────┴──────┐
                         │              │  Declined?  │
                         │              │  → Retry    │
                         │              │  → Past due │
                         │              │  → Cancel   │
                         │              └────────────┘
                         │
                    ┌────┴────┐
                    │  Trial? │
                    │  7 days │
                    │  free   │
                    └─────────┘
```

<Info>
  **Subscriptions are vaulted.** The customer's card is tokenized once. TagadaPay charges it automatically each cycle — through whichever processor your payment flow selects. You never handle raw card data or PSP-specific recurring APIs.
</Info>

***

## Subscription Lifecycle

| Status     | Meaning                                    |
| ---------- | ------------------------------------------ |
| `trialing` | Free trial period, no charges yet          |
| `active`   | Recurring charges are running              |
| `past_due` | Last rebill failed, retrying automatically |
| `paused`   | Billing suspended, can be resumed          |
| `canceled` | Terminated (immediately or at period end)  |

```
create → trialing → active ⟲ (rebill each cycle)
                       ↓
                   past_due → retry → active (success)
                       ↓              → canceled (max retries)
                       ↓
                    paused → resume → active
                       ↓
                   canceled
```

***

## Create a Subscription

```ts theme={null}
import Tagada from '@tagadapay/node-sdk';

const tagada = new Tagada('your-api-key');

const { subscription } = await tagada.subscriptions.create({
  customerId: 'cus_abc123',
  priceId: 'price_monthly_2999',
  storeId: 'store_xxx',
  currency: 'USD',
  quantity: 1,
  defaultPaymentInstrumentId: 'pi_abc123',
});

console.log(subscription.id);             // 'sub_xyz789'
console.log(subscription.status);          // 'active'
console.log(subscription.nextBillingDate); // '2026-04-19T00:00:00Z'
```

| Parameter                    | Required | Description                                          |
| ---------------------------- | -------- | ---------------------------------------------------- |
| `customerId`                 | Yes      | The customer to bill                                 |
| `priceId`                    | Yes      | A recurring price (from a product variant)           |
| `storeId`                    | Yes      | Your store                                           |
| `currency`                   | Yes      | ISO currency code                                    |
| `defaultPaymentInstrumentId` | No       | Card to charge (uses customer's default if omitted)  |
| `quantity`                   | No       | Number of units (default: 1)                         |
| `paymentId`                  | No       | Link to the initial payment                          |
| `billingCycleAnchor`         | No       | ISO date — fix the billing day (e.g. always the 1st) |
| `initiatePayment`            | No       | Charge immediately on creation                       |

***

## Create with a Free Trial

```ts theme={null}
const { subscription } = await tagada.subscriptions.create({
  customerId: 'cus_abc123',
  priceId: 'price_monthly_2999',
  storeId: 'store_xxx',
  currency: 'USD',
  defaultPaymentInstrumentId: 'pi_abc123',
  trialSettings: {
    trialPeriodDuration: 7,
    trialPeriodDurationType: 'day',
  },
});

console.log(subscription.status); // 'trialing'
console.log(subscription.trial);  // true
```

The customer isn't charged until the trial ends. TagadaPay automatically transitions to `active` and triggers the first rebill.

| Trial Duration Type | Examples                |
| ------------------- | ----------------------- |
| `day`               | 3 days, 7 days, 14 days |
| `week`              | 1 week, 2 weeks         |
| `month`             | 1 month, 3 months       |

***

## Manual Rebill

Trigger an immediate charge on any `active` or `past_due` subscription — useful for dashboards, support tools, or retry scripts.

```ts theme={null}
const result = await tagada.subscriptions.rebill('sub_xyz789');

console.log(result.success); // true
console.log(result.message); // 'Rebill triggered successfully'
```

<Info>
  **What happens under the hood:**

  1. TagadaPay loads the subscription's price, currency, and payment instrument
  2. Routes the charge through your payment flow (cascade, weighted, fallback — all automatic)
  3. On success → updates `nextBillingDate`, status stays `active`
  4. On decline → enters retry logic (see below)
</Info>

### When to Use Manual Rebill

| Scenario                  | Example                                                                                         |
| ------------------------- | ----------------------------------------------------------------------------------------------- |
| **Customer updated card** | They fixed their card details, you want to charge immediately instead of waiting for auto-retry |
| **Support request**       | "Please charge me now"                                                                          |
| **Retry script**          | Batch-retry all `past_due` subscriptions                                                        |
| **Testing**               | Verify billing works end-to-end                                                                 |

### Batch Retry All Past-Due Subscriptions

```ts theme={null}
const { data: subscriptions } = await tagada.subscriptions.list({
  per_page: 100,
  filters: {
    'subscriptions.status': ['past_due'],
    storeIds: ['store_xxx'],
  },
});

console.log(`Found ${subscriptions.length} past-due subscriptions`);

for (const sub of subscriptions) {
  try {
    await tagada.subscriptions.rebill(sub.id);
    console.log(`Rebilled ${sub.id}`);
  } catch (err) {
    console.log(`Failed ${sub.id}: ${err.message}`);
  }
}
```

***

## Automatic Retry on Failure

When a rebill is declined, TagadaPay doesn't immediately cancel. It retries automatically:

```
Rebill declined
  → Wait 24h  → Retry #1
  → Wait 48h  → Retry #2
  → Wait 96h  → Retry #3
  → Wait 168h → Retry #4
  → Wait 168h → Retry #5
  → Cancel subscription
```

| Retry | Delay     | Total elapsed |
| ----- | --------- | ------------- |
| 1     | 24 hours  | 1 day         |
| 2     | 48 hours  | 3 days        |
| 3     | 96 hours  | 7 days        |
| 4     | 168 hours | 14 days       |
| 5     | 168 hours | 21 days       |

During retries, the subscription status is `past_due`. After max retries, it's automatically canceled.

<Tip>
  **Smart retry** is also available — it uses failure codes (e.g. "insufficient funds"), velocity limits, and even payroll-day heuristics to pick the optimal retry time. Contact support to enable it.
</Tip>

***

## Cancel a Subscription

### Cancel Immediately

```ts theme={null}
await tagada.subscriptions.cancel({
  subscriptionIds: ['sub_xyz789'],
});
// Status → 'canceled' right now
```

### Cancel at Period End

```ts theme={null}
await tagada.subscriptions.cancel({
  subscriptionIds: ['sub_xyz789'],
  cancelAtPeriodEnd: true,
});
// Stays 'active' until currentPeriodEnd, then auto-cancels
```

The customer keeps access until their billing period ends. TagadaPay handles the scheduled cancellation automatically.

### Bulk Cancel

```ts theme={null}
await tagada.subscriptions.cancel({
  subscriptionIds: ['sub_001', 'sub_002', 'sub_003'],
  cancelAtPeriodEnd: true,
});
```

***

## Resume a Subscription

Resume a `canceled`, `paused`, or `past_due` subscription:

```ts theme={null}
await tagada.subscriptions.resume({
  subscriptionIds: ['sub_xyz789'],
});
// Status → 'active', billing resumes
```

If the subscription had `cancelAtPeriodEnd: true` but hasn't reached the end yet, resuming clears the cancellation flag.

***

## Pause a Subscription

Pausing stops billing without canceling — the subscription stays on file and can be resumed at any time. Useful for customer-portal self-serve, seasonal accounts, or account-recovery flows where you don't want to lose the vaulted card.

```ts theme={null}
await tagada.subscriptions.pause({
  subscriptionIds: ['sub_xyz789'],
});
// Status → 'paused', billing suspended until resume
```

### Bulk Pause

```ts theme={null}
await tagada.subscriptions.pause({
  subscriptionIds: ['sub_001', 'sub_002', 'sub_003'],
});
```

Only subscriptions belonging to the authenticated account are affected — ids you don't own are silently ignored (no error). Response shape: `{ success: true }`.

### Pause vs. Cancel at Period End

| Scenario                                                                    | Use                                    |
| --------------------------------------------------------------------------- | -------------------------------------- |
| Customer explicitly wants to keep the sub, just skip billing for a while    | `pause`                                |
| Customer wants to end the sub but keep it running through the current cycle | `cancel({ cancelAtPeriodEnd: true })`  |
| Customer wants to end the sub immediately                                   | `cancel({ cancelAtPeriodEnd: false })` |

Resume works the same way for both `paused` and `canceled` states — `tagada.subscriptions.resume({ subscriptionIds: [...] })`.

***

## Change Processor

Move a subscription from one processor to another — for example, migrating from Stripe to Checkout.com, or clearing a stuck processor.

### Assign a Specific Processor

```ts theme={null}
const { subscription } = await tagada.subscriptions.changeProcessor(
  'sub_xyz789',
  'proc_cko',  // Checkout.com
);

console.log(subscription.processorId); // 'proc_cko'
```

The next rebill will use Checkout.com instead of the previous processor.

### Let TagadaPay Auto-Select

```ts theme={null}
const { subscription } = await tagada.subscriptions.changeProcessor('sub_xyz789');
// No processorId → TagadaPay uses the payment flow strategy
```

This removes the sticky processor, so the next rebill will be routed by your payment flow (weighted, cascade, etc.).

### Use Case: Migrate All Subscriptions from Stripe to NMI

```ts theme={null}
const { data: subscriptions } = await tagada.subscriptions.list({
  per_page: 100,
  filters: {
    'subscriptions.status': ['active'],
    processorIds: ['proc_s1'],  // current: Stripe
  },
});

for (const sub of subscriptions) {
  await tagada.subscriptions.changeProcessor(sub.id, 'proc_nmi');
  console.log(`Migrated ${sub.id} to NMI`);
}
```

<Tip>
  **This is a TagadaPay superpower.** With a traditional PSP, migrating subscriptions between processors means re-collecting card details. With TagadaPay's PSP-agnostic vault, you just change the processor ID — the vaulted card works across all processors.
</Tip>

***

## List & Filter Subscriptions

```ts theme={null}
const { data, total, hasMore } = await tagada.subscriptions.list({
  page: 1,
  per_page: 50,
  sort: 'createdAt',
  sort_order: 'desc',
  filters: {
    storeIds: ['store_xxx'],
    'subscriptions.status': ['active', 'trialing'],
  },
});

for (const sub of data) {
  console.log(`${sub.id} — ${sub.status} — next bill: ${sub.nextBillingDate}`);
}
```

### Available Filters

| Filter                 | Type                   | Example                                                         |
| ---------------------- | ---------------------- | --------------------------------------------------------------- |
| `subscriptions.status` | `string[]`             | `['active', 'past_due']`                                        |
| `subscriptions.trial`  | `boolean`              | `true`                                                          |
| `storeIds`             | `string[]`             | `['store_xxx']`                                                 |
| `processorIds`         | `string[]`             | `['proc_s1', 'proc_nmi']`                                       |
| `currency`             | `string`               | `'USD'`                                                         |
| `customerEmail`        | `string`               | `'jane@example.com'`                                            |
| `price`                | `{ condition, value }` | `{ condition: 'gte', value: [1000] }`                           |
| `date`                 | `{ condition, value }` | `{ condition: 'between', value: ['2026-01-01', '2026-03-01'] }` |
| `nextBillingDate`      | `{ condition, value }` | `{ condition: 'lte', value: ['2026-03-25'] }`                   |

***

## Retrieve a Subscription

```ts theme={null}
const { subscription } = await tagada.subscriptions.retrieve('sub_xyz789');

console.log(subscription.status);                // 'active'
console.log(subscription.currentPeriodStart);     // '2026-03-01T00:00:00Z'
console.log(subscription.currentPeriodEnd);       // '2026-04-01T00:00:00Z'
console.log(subscription.nextBillingDate);        // '2026-04-01T00:00:00Z'
console.log(subscription.processorId);            // 'proc_s1'
console.log(subscription.paymentInstrumentId);    // 'pi_abc123'
console.log(subscription.cancelAtPeriodEnd);      // false
```

***

## Webhook Events

Listen for subscription events via [webhooks](/api-reference/introduction) to keep your system in sync:

| Event                          | When it fires                                |
| ------------------------------ | -------------------------------------------- |
| `subscription/created`         | New subscription created                     |
| `subscription/rebillSucceeded` | Recurring charge succeeded                   |
| `subscription/rebillDeclined`  | Recurring charge failed                      |
| `subscription/pastDue`         | Subscription entered past\_due after decline |
| `subscription/paused`          | Subscription paused                          |
| `subscription/resumed`         | Subscription resumed                         |
| `subscription/canceled`        | Subscription canceled immediately            |
| `subscription/cancelScheduled` | Cancel scheduled for period end              |
| `subscription/rebillUpcoming`  | Upcoming rebill (advance notice)             |

***

## Full Example: Subscription with Trial, Rebill, and Processor Migration

```ts theme={null}
import Tagada from '@tagadapay/node-sdk';

const tagada = new Tagada('your-api-key');
const storeId = 'store_xxx';

// ── 1. Create a subscription with 14-day trial ──────────────────────
const { subscription } = await tagada.subscriptions.create({
  customerId: 'cus_abc123',
  priceId: 'price_monthly_2999',
  storeId,
  currency: 'USD',
  defaultPaymentInstrumentId: 'pi_abc123',
  trialSettings: {
    trialPeriodDuration: 14,
    trialPeriodDurationType: 'day',
  },
});

console.log(`Created: ${subscription.id} — ${subscription.status}`);
// → "Created: sub_xyz789 — trialing"

// ── 2. Later: trial ends, auto-rebill runs ──────────────────────────
// (TagadaPay handles this automatically — no code needed)

// ── 3. Manual rebill (e.g. customer updated their card) ─────────────
const rebillResult = await tagada.subscriptions.rebill(subscription.id);
console.log(`Rebill: ${rebillResult.message}`);

// ── 4. Move subscription from Stripe to Checkout.com ────────────────
const { subscription: updated } = await tagada.subscriptions.changeProcessor(
  subscription.id,
  'proc_cko',
);
console.log(`Processor changed to: ${updated.processorId}`);

// ── 5. Customer wants to cancel at period end ───────────────────────
await tagada.subscriptions.cancel({
  subscriptionIds: [subscription.id],
  cancelAtPeriodEnd: true,
});
console.log('Cancel scheduled at period end');

// ── 6. Customer changes their mind — resume ─────────────────────────
await tagada.subscriptions.resume({
  subscriptionIds: [subscription.id],
});
console.log('Subscription resumed!');
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Multi-PSP Routing & Vault" icon="route" href="/developer-tools/node-sdk/multi-stripe-routing">
    Connect Stripe, NMI, Checkout.com, Airwallex and route payments intelligently
  </Card>

  <Card title="Headless Payments (Frontend)" icon="credit-card" href="/developer-tools/payments/headless-payments">
    Build your own checkout UI with client-side card tokenization and 3DS
  </Card>

  <Card title="Node SDK Quick Start" icon="node-js" href="/developer-tools/node-sdk/quick-start">
    Get started with stores, products, and funnels
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Full REST API documentation
  </Card>
</CardGroup>
