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

# Subscription management

> Provision, update, cancel, list, and migrate subscriptions using Stigg backend SDKs.

## Subscription management

### Provisioning subscriptions

When a customer subscribes to a new plan (upgrade, downgrade, or initial subscription), a subscription needs to be provisioned in Stigg.

You provide the customer ID and target plan ID. For paid subscriptions, the billing period (monthly or annually) is required. Additional optional parameters include:

* **Resource ID** - required when a product supports [multiple active subscriptions](/guides/i-want-to/add-multiple-subscriptions)
* **Billable feature quantities** - required for per-unit pricing
* **Add-ons** - list of add-ons and their quantities
* **Promotion code** - a promo code from your billing provider
* **Billing country code** - ISO-3166-1 format, required for price localization
* **Checkout options** - configuration for hosted checkout (success/cancel URLs, promo code collection, etc.)
* **Metadata** - arbitrary key-value pairs

**Behavior:**

* When payment details are required and not yet provided, Stigg redirects the customer to the billing solution's hosted checkout page.
* When no payment is required, the subscription is created immediately.
* The result includes the subscription details or a checkout URL if payment is needed.

<CodeGroup>
  ```javascript Node.js theme={null}
  const subscription = await stiggClient.provisionSubscription({
      customerId: 'customer-demo-01',
      planId: 'plan-basic',
      billingPeriod: 'MONTHLY',                   // optional, required for paid subscriptions
      resourceId: 'resource-01',                  // optional, required for multiple subscriptions
      billableFeatures: [{
        featureId:'feature-01-templates',
        quantity: 2
      }],
      addons: [{
          addonId: 'addon-extra-stuff',
          quantity: 1,
      }],
      promotionCode: 'STIGG30', 		              // optional
      billingCountryCode:'DK',     		            // optional, required for price localization
      checkoutOptions: {                          // optional
        successUrl: "https://your-success-url.com",
        cancelUrl: "https://your-cancel-url.com",
        allowPromoCodes: true,
        collectBillingAddress: true,
      },
      metadata: {
        key: 'value',
      }
  });
  ```

  ```python Python theme={null}
  data = await client.provision_subscription(ProvisionSubscriptionInput(
      **{
          "customerId": "customer-id",
          "planId": "plan-revvenu-basic",
          "resourceId": "resource-01",  # optional, required for multiple subscriptions
          "billingPeriod": "MONTHLY",   # optional, required for paid subscriptions
          "billableFeatures": [{                # optional, required for per-unit pricing
            "featureId": "feature-01-templates",
            "quantity": 2
          }],
          "addons": [                                       # optional
              {
                  "addonId": "addon-extra-stuff",
                  "quantity": 2
              }
          ],
          "billingCountryCode":"DK",     # optional, required for price localization
          "checkoutOptions": {				   # optional
              "successUrl": "https://your-success-url.com",
              "cancelUrl": "https://your-cancel-url.com",
          },
          "additionalMetaData": {               # optional
              "key": "value"
          }
      }
  ))
  ```

  ```go Go theme={null}
  result, err := client.ProvisionSubscription(context.Background(), stigg.ProvisionSubscriptionInput{
    CustomerID: "customer-demo-01",
    PlanID: "plan-revvenu-essentials",
    BillingPeriod: Ptr(stigg.BillingPeriodMonthly),     // optional, required for paid subscriptions
    ResourceID: Ptr("resource-01"),                      // optional, required for multiple subscriptions
    BillableFeatures: []*stigg.BillableFeatureInput{     // optional, required for per-unit pricing
      {
        FeatureID: "feature-01-templates",
        Quantity:  20,
      },
    },
    Addons: []*stigg.SubscriptionAddonInput{             // optional
      {
        AddonID:  "addon-revvenu-extra-campaigns",
        Quantity: Ptr(int64(3)),
      },
    },
    BillingCountryCode: Ptr("DK"),   					           // optional, required for price localization
    CheckoutOptions: &stigg.CheckoutOptions{             // optional
      SuccessURL: "https://your-success-url.com",
      CancelURL:  "https://your-cancel-url.com",
    },
  })
  ```

  ```ruby Ruby theme={null}
  resp = client.request(Stigg::Mutation::ProvisionSubscription, {
    "input": {
      "customerId": "customer-demo-02",
      "planId": "plan-revvenu-essentials",
      "resourceId": "resource-01", # optional, required for multiple subscriptions
      "billingPeriod": "MONTHLY",  # optional, required for paid subscriptions
      "billingCountryCode":"DK",   # optional, required for price localization
      "checkoutOptions": {				 # optional
        "successUrl": "https://your-success-url.com",
        "cancelUrl": "https://your-cancel-url.com",
      },
      "billableFeatures": [{       # optional, required for per-unit pricing
        "featureId": "feature-01-templates",
        "quantity": 2
      }],
    }
  })
  ```

  ```java Java theme={null}
  var resp = stigg.mutation(
    ProvisionSubscriptionMutation.builder()
    .input(ProvisionSubscriptionInput.builder()
           .customerId("customer-demo-01")
           .planId("plan-revvenu-basic")
           .resourceId("resource-01")                     // optional
           .billingPeriod(BillingPeriod.MONTHLY)           // optional, required for paid plans
           .billableFeatures(List.of(                      // optional, required for per-unit pricing
             BillableFeatureInput.builder()
             .featureId("feature-01-templates")
             .quantity(2.0)
             .build()
           ))
           .addons(List.of(                                // optional
             SubscriptionAddonInput.builder()
             .addonId("addon-extra-stuff")
             .quantity(5)
             .build()
           ))
           .billingCountryCode("DK")                       // optional, required for price localization
           .checkoutOptions(                               // optional
             CheckoutOptions.builder()
             .successUrl("https://your-success-url.com")
             .cancelUrl("https://your-cancel-url.com")
             .allowPromoCodes(true)
             .collectBillingAddress(true)
             .build()
           )
           .additionalMetaData(new HashMap<>() {{
             put("key", "value");
           }})
           .build())
    .build()
  );
  ```

  ```c# .NET theme={null}
  var res = await stigg.ProvisionSubscription.ExecuteAsync(new ProvisionSubscriptionInput()
  {
    CustomerId = "customer-demo-01",
    PlanId = "plan-revvenu-essentials",
    BillingPeriod = BillingPeriod.Monthly,                 // optional, required for paid plans
    BillableFeatures = new List<BillableFeatureInput>()    // optional, required for per-unit pricing
    {
      new()
      {
        FeatureId = "feature-01-templates",
        Quantity = 5
      }
    },
    Addons = new List<SubscriptionAddonInput>()            // optional
    {
      new()
      {
        AddonId = "addon-extra-stuff",
        Quantity = 5
      }
    },
  });
  ```
</CodeGroup>

<Expandable title="Result (Node.js)">
  ```json theme={null}
  {
    "status": "Success",
    "checkoutUrl": "https://your-success-url.com",
    "subscription": {
      "id": "subscription-plan-basic",
      "planId": "plan-basic",
      "status": "ACTIVE",
      "addons": [],
      "customerId": "customer-demo-01",
      "resource": {
        "id": "resource-01"
      },
      "metadata": {
        "lorem": "ipsum"
      },
      "pricingType": "FREE",
      "paymentCollection": "NOT_REQUIRED",
      "latestInvoice": {
        "billingId": "invoice-01",
        "status": "PAID"
      }
    }
  }
  ```
</Expandable>

<Warning>
  1. A customer can have both a non-trial and a trial subscription to different plans of the same product in parallel.
  2. When a customer with a trial subscription for plan X creates a new subscription for the same plan, it will be created as a non-trial (paid) subscription.
  3. When a customer already has an active subscription for a product and a new one is created, the existing subscription is automatically cancelled.
  4. You can explicitly skip the trial period by providing a skip-trial flag.
</Warning>

### Updating subscriptions

Update an existing subscription without creating a new one, keeping the original start date and billing cycle. You can:

1. Update subscription metadata
2. Update unit quantity for per-unit pricing (e.g., adding or removing seats)
3. Add or remove add-ons (only if compatible with the plan)
4. Update the promotion code
5. Update the billing period

When integrated with a billing solution, modifying quantities or add-ons generates a prorated invoice.

<CodeGroup>
  ```javascript Node.js theme={null}
  const subscription = await stiggClient.updateSubscription({
    subscriptionId: 'subscription-plan-basic',
    billableFeatures: [{
      featureId:'feature-01-templates',
      quantity: 3
    }],
    promotionCode: "STIGG30", // optional
    addons: [
      { addonId: 'extra-api-calls', quantity: 5 }
    ],
    metadata: {
        key: 'newValue'
    },
  });
  ```

  ```python Python theme={null}
  data = await client.update_subscription(UpdateSubscriptionInput(
    **{
      "refId": "subscription-plan-revvenu-growth-589879",
      "billableFeatures": [{
        "featureId": "feature-01-templates",
        "quantity": 2
      }],
    }
  ))
  ```

  ```go Go theme={null}
  result, err := client.UpdateSubscription(context.Background(), stigg.UpdateSubscriptionInput{
    SubscriptionID: Ptr("subscription-plan-revvenu-essentials-dc847c"),
    BillingPeriod: Ptr(stigg.BillingPeriodMonthly),
    BillableFeatures: []*stigg.BillableFeatureInput{
      {
        FeatureID: "feature-01-templates",
        Quantity:  6,
      },
    },
    Addons: []*stigg.SubscriptionAddonInput{
      {
        AddonID:  "addon-revvenu-extra-campaigns",
        Quantity: Ptr(int64(2)),
      },
    },
  })
  ```

  ```ruby Ruby theme={null}
  resp = client.request(Stigg::Mutation::UpdateSubscription, {
    "input": {
      "subscriptionId": "subscription-plan-revvenu-growth-589879",
      "billableFeatures": [{
        "featureId": "feature-01-templates",
        "quantity": 2
      }],
    }
  })
  ```

  ```java Java theme={null}
  var resp = stigg.mutation(
    UpdateSubscriptionMutation.builder()
    .input(
      UpdateSubscriptionInput.builder()
      .subscriptionId("subscription-plan-revvenu-essentials-bum2flivze")
      .billableFeatures(List.of(
        BillableFeatureInput.builder()
        .featureId("feature-01-templates")
        .quantity(5.0)
        .build()
      ))
      .addons(List.of(                    // optional
        SubscriptionAddonInput.builder()
        .addonId("addon-extra-stuff")
        .quantity(5)
        .build()
      ))
      .build()
    )
    .build()
  );
  ```

  ```c# .NET theme={null}
  var res = await stigg.UpdateSubscription.ExecuteAsync(new UpdateSubscriptionInput()
  {
    SubscriptionId = "subscription-plan-revvenu-essentials-cpzi7p2g09",
    BillableFeatures = new List<BillableFeatureInput>()
    {
      new()
      {
        FeatureId = "feature-01-templates",
        Quantity = 7
      }
    },
    Addons = new List<SubscriptionAddonInput>()
    {
      new()
      {
        AddonId = "addon-extra-stuff",
        Quantity = 5
      }
    },
  });
  ```
</CodeGroup>

<Expandable title="Result (Node.js)">
  ```json theme={null}
  {
    "id": "subscription-plan-basic",
    "planId": "plan-basic",
    "status": "ACTIVE",
    "addons": [],
    "customerId": "customer-demo-01",
    "resource": {
      "id": "resource-01"
    },
    "metadata": {
      "key": "newValue"
    },
    "pricingType": "FREE"
  }
  ```
</Expandable>

### Canceling subscriptions

Cancel a subscription by its ID. Cancellation behavior (immediate or end of billing period) is determined by the product's configuration.

<CodeGroup>
  ```javascript Node.js theme={null}
  await stiggClient.cancelSubscription({
      subscriptionId: 'subscription-plan-basic'
  });
  ```

  ```python Python theme={null}
  data = await client.cancel_subscription(SubscriptionCancellationInput(
    **{
      "subscriptionRefId": "subscription-plan-revvenu-growth-589879",
      "subscriptionCancellationTime":  "IMMEDIATE"
    }
  ))
  ```

  ```go Go theme={null}
  result, err := client.CancelSubscription(context.Background(), stigg.SubscriptionCancellationInput{
    SubscriptionRefID: "subscription-plan-revvenu-essentials-dc847c",
    SubscriptionCancellationTime: Ptr(stigg.SubscriptionCancellationTimeImmediate),
  })
  ```

  ```ruby Ruby theme={null}
  resp = client.request(Stigg::Mutation::CancelSubscription, {
    "input": {
      "subscriptionRefId": "subscription-plan-revvenu-growth-589879",
      "subscriptionCancellationTime":  "IMMEDIATE"
    }
  })
  ```

  ```java Java theme={null}
  var resp =
    stigg.mutation(
      CancelSubscriptionMutation.builder()
      .input(
        SubscriptionCancellationInput.builder()
        .subscriptionRefId("subscription-plan-revvenu-essentials-bum2flivze")
        .subscriptionCancellationAction(SubscriptionCancellationAction.DEFAULT)       // optional
        .subscriptionCancellationTime(SubscriptionCancellationTime.END_OF_BILLING_PERIOD) // optional
        .build())
      .build());
  ```

  ```c# .NET theme={null}
  var res = await stigg.CancelSubscription.ExecuteAsync(new SubscriptionCancellationInput()
  {
    SubscriptionRefId = "subscription-plan-revvenu-essentials-cpzi7p2g09",
    SubscriptionCancellationTime = SubscriptionCancellationTime.END_OF_BILLING_PERIOD, // optional
  });
  ```
</CodeGroup>

<Expandable title="Result (Node.js)">
  ```json theme={null}
  {
    "id": "subscription-plan-basic",
    "planId": "plan-basic",
    "status": "CANCELED",
    "customerId": "customer-demo-01",
    "metadata": {
      "key": "newValue"
    },
    "pricingType": "FREE",
    "effectiveEndDate": "2022-09-20T10:18:06.842Z"
  }
  ```
</Expandable>

### Canceling scheduled updates

Cancel all scheduled updates or pending payment updates for a given subscription.

<CodeGroup>
  ```javascript Node.js theme={null}
  await stiggClient.cancelSubscriptionScheduledUpdates({
    subscriptionId: 'subscription-plan-essentials',
    status: SubscriptionScheduleStatus.Scheduled, // or SubscriptionScheduleStatus.PendingPayment
  });
  ```
</CodeGroup>

### Listing active subscriptions

Retrieve a list of a customer's active subscriptions. For products that support multiple active subscriptions, pass one or more resource IDs to filter by resource.

<CodeGroup>
  ```javascript Node.js theme={null}
  const subscriptions = await stiggClient.getActiveSubscriptionsList({
    customerId: 'customer-demo-01',
    resourceId: 'resource-01', // optional, pass it to get subscription of specific resource
  });
  ```

  ```python Python theme={null}
  data = await client.get_active_subscriptions_list(GetActiveSubscriptionsInput(
    **{
      "customerId": "customer-demo-01",
      "resourceId": "resource-01",  # optional, pass it to get subscription of specific resource
    }
  ))
  ```

  ```go Go theme={null}
  result, err := client.GetActiveSubscriptions(context.Background(), stigg.GetActiveSubscriptionsInput{
    CustomerID: "customer-demo-01",
    ResourceID: Ptr("resource-01"), // optional, pass it to get subscription of specific resource
  })
  ```

  ```ruby Ruby theme={null}
  resp = client.request(Stigg::Query::GetActiveSubscriptionsList, {
    "input": {
      "customerId": "customer-demo-01",
      "resourceId": "resource-01",  # optional, pass it to get subscription of specific resource
    }
  })
  ```

  ```java Java theme={null}
  var resp = stigg.query(
    GetActiveSubscriptionsListQuery.builder()
    .input(GetActiveSubscriptionsInput.builder()
           .customerId("customer-demo-01")
           .resourceId("resource-01") // optional
           .build())
    .build()
  );
  ```

  ```c# .NET theme={null}
  var res = await stigg.GetActiveSubscriptionsList.ExecuteAsync(new GetActiveSubscriptionsInput()
  {
    CustomerId = "customer-demo-01",
    ResourceId = "resource-01", // optional
  });
  ```
</CodeGroup>

<Expandable title="Result (Node.js)">
  ```json theme={null}
  [
    {
      "subscriptionId": "subscription-plan-pro-abc123",
      "status": "ACTIVE",
      "startDate": "2024-01-15T00:00:00.000Z",
      "endDate": null,
      "effectiveEndDate": "2025-01-15T00:00:00.000Z",
      "currentBillingPeriodStart": "2024-01-15T00:00:00.000Z",
      "currentBillingPeriodEnd": "2024-02-15T00:00:00.000Z",
      "billingPeriod": "MONTHLY",
      "additionalMetaData": {
        "internalTierId": "tier-gold"
      },
      "customer": {
        "customerId": "customer-demo-01",
        "email": "user@example.com"
      },
      "payingCustomer": {
        "customerId": "customer-demo-01",
        "email": "user@example.com"
      },
      "plan": {
        "refId": "plan-pro",
        "displayName": "Pro Plan",
        "description": "Full access to all Pro features",
        "product": {
          "refId": "product-main",
          "displayName": "Main Product",
          "downgradePlan": {
            "refId": "plan-free",
            "displayName": "Free Plan"
          }
        }
      },
      "addons": [],
      "prices": [
        {
          "billingModel": "FLAT_FEE",
          "price": {
            "billingPeriod": "MONTHLY",
            "price": {
              "amount": 99,
              "currency": "USD"
            }
          }
        }
      ],
      "totalPrice": {
        "subTotal": {
          "amount": 99,
          "currency": "USD"
        },
        "total": {
          "amount": 99,
          "currency": "USD"
        }
      },
      "trialEndDate": null
    }
  ]
  ```
</Expandable>

### Getting a specific subscription

Retrieve extended details for a single subscription, including plan details, add-ons, pricing, payment collection status, and the latest invoice.

<CodeGroup>
  ```javascript Node.js theme={null}
  const subscription = await stiggClient.getSubscription({
    subscriptionId: 'subscription-01',
  });
  ```

  ```python Python theme={null}
  data = await client.get_subscription(GetSubscriptionInput(
    **{
      "subscriptionId": "subscription-01",
    }
  ))
  ```

  ```go Go theme={null}
  // Not available in the Go SDK — use the GraphQL API directly
  ```

  ```ruby Ruby theme={null}
  resp = client.request(Stigg::Query::GetSubscription, {
    "subscriptionId": "subscription-01",
  })
  ```

  ```java Java theme={null}
  var resp = stigg.query(
    GetSubscriptionQuery.builder()
    .input(GetSubscriptionInput.builder()
           .subscriptionId("subscription-01")
           .build())
    .build()
  );
  ```

  ```c# .NET theme={null}
  var res = await stigg.GetSubscription.ExecuteAsync(new GetSubscriptionInput()
  {
    SubscriptionId = "subscription-01",
  });
  ```
</CodeGroup>

### Listing subscriptions with filtering

Query subscriptions with rich filtering, sorting, and cursor-based pagination. Useful for admin dashboards, audits, and reporting.

Supported filter operators include equality, inequality, inclusion, and exclusion for strings and enums, as well as comparison operators for dates. Filters support logical AND/OR composition.

Sortable fields include creation date, customer ID, environment ID, product ID, resource ID, and status.

<CodeGroup>
  ```javascript Node.js theme={null}
  const subscriptions = await stiggClient.getSubscriptions({
    filter: {
      and: [
        { customerId: { eq: "customer-demo-01" } },
        { status: { in: ["ACTIVE", "IN_TRIAL"] } }
      ]
    },
    sorting: [{ field: "createdAt", direction: "DESC", nulls: "LAST" }],
    paging: { first: 25 }
  });
  ```

  ```python Python theme={null}
  data = await client.get_subscriptions(GetSubscriptionsInput(
    **{
      "filter": {
        "and": [
          { "customerId": { "eq": "customer-demo-01" } },
          { "status": { "in": ["ACTIVE", "IN_TRIAL"] } }
        ]
      },
      "sorting": [{ "field": "createdAt", "direction": "DESC", "nulls": "LAST" }],
      "paging": { "first": 25 }
    }
  ))
  ```

  ```go Go theme={null}
  result, err := client.GetSubscriptions(context.Background(), stigg.GetSubscriptionsInput{
    Filter: &stigg.SubscriptionsFilterInput{
      And: []*stigg.SubscriptionsFilterInput{
        {CustomerID: &stigg.StringFieldComparison{Eq: Ptr("customer-demo-01")}},
        {Status: &stigg.SubscriptionStatusFilterComparison{In: []string{"ACTIVE", "IN_TRIAL"}}},
      },
    },
    Sorting: []*stigg.SubscriptionSort{{Field: "createdAt", Direction: "DESC", Nulls: Ptr("LAST")}},
    Paging:  &stigg.CursorPaging{First: Ptr(int64(25))},
  })
  ```

  ```ruby Ruby theme={null}
  resp = client.request(Stigg::Query::GetSubscriptions, {
    "filter": {
      "and": [
        { "customerId": { "eq": "customer-demo-01" } },
        { "status": { "in": ["ACTIVE", "IN_TRIAL"] } }
      ]
    },
    "sorting": [{ "field": "createdAt", "direction": "DESC", "nulls": "LAST" }],
    "paging": { "first": 25 }
  })
  ```

  ```java Java theme={null}
  var resp = stigg.query(
    GetSubscriptionsQuery.builder()
    .filter(SubscriptionsFilterInput.builder()
      .and(List.of(
        SubscriptionsFilterInput.builder()
          .customerId(StringFieldComparison.builder().eq("customer-demo-01").build())
          .build(),
        SubscriptionsFilterInput.builder()
          .status(SubscriptionStatusFilterComparison.builder()
            .in(List.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.IN_TRIAL))
            .build())
          .build()
      ))
      .build())
    .sorting(List.of(SubscriptionSort.builder()
      .field(SubscriptionSortFields.CREATED_AT)
      .direction(SortDirection.DESC)
      .nulls(QuerySortNulls.LAST)
      .build()))
    .paging(CursorPaging.builder().first(25).build())
    .build()
  );
  ```

  ```c# .NET theme={null}
  var resp = await stigg.GetSubscriptions.ExecuteAsync(new GetSubscriptionsInput()
  {
    Filter = new SubscriptionsFilterInput()
    {
      And = new List<SubscriptionsFilterInput>()
      {
        new() { CustomerId = new StringFieldComparison() { Eq = "customer-demo-01" } },
        new() { Status = new SubscriptionStatusFilterComparison() { In = new List<SubscriptionStatus> { SubscriptionStatus.Active, SubscriptionStatus.InTrial } } }
      }
    },
    Sorting = new List<SubscriptionSort>() { new() { Field = SubscriptionSortFields.CreatedAt, Direction = SortDirection.Desc, Nulls = QuerySortNulls.Last } },
    Paging = new CursorPaging() { First = 25 }
  });
  ```
</CodeGroup>

## Subscription migration

Migrate subscriptions to the latest plan and add-on version on a subscription-by-subscription basis, to prevent SKU sprawl from grandfathering.

<Warning>
  When the current subscription price differs from the latest published version, the customer will be charged or credited the prorated difference.
</Warning>

<CodeGroup>
  ```javascript Node.js theme={null}
  await stiggClient.migrateSubscriptionToLatest({
    subscriptionId: "subscription-id",
    subscriptionMigrationTime: SubscriptionMigrationTime.EndOfBillingPeriod, // optional
  });
  ```
</CodeGroup>
