> ## 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.

# Node.js

# Installing the SDK

The first step is to install the Stigg SDK as a dependency in your application using your application's dependency manager:

<CodeGroup>
  ```shell shell theme={null}
  npm install @stigg/node-server-sdk
  ```
</CodeGroup>

# Retrieving the full access key

In the Stigg app, go to **Integrations > API keys**.

Copy the **Full access key** of the relevant environment.

# Initializing the SDK

Import the Stigg client and initialize it with the API key:

<CodeGroup>
  ```javascript initializeStiggClient.js theme={null}
  import { Stigg } from '@stigg/node-server-sdk';

  const stiggClient = Stigg.initialize({ apiKey: 'YOUR_FULL_ACCESS_KEY' });

  export default stiggClient;
  ```
</CodeGroup>

<Warning>
  The Stigg SDK instance has an internal cache and supports retries for certain API calls on network errors. In general, you should initialize it once per process (for example: using a singleton design-pattern) and use the same instance throughout the codebase.
</Warning>

To find out when the client is ready, you can use `waitForInitialization` which return a promise that resolves when the client is ready to use, or rejects when the initialization failed.

<CodeGroup>
  ```javascript waitForInitialization.js theme={null}
  try {
    await stiggClient.waitForInitialization();
    // initialization succeeded, entitlements values are now available
  } catch (err) {
    // initialization failed
  }
  ```
</CodeGroup>

# Provisioning customers

When a new customer is provisioned within your application (for example: as part of your registration or onboarding process), you should also provision them in Stigg.

You can optionally pass `subscriptionParams` to create a subscription for that customer using a single operation. The actual subscription that is created for the customer can be overriden by Stigg admin from the [Stigg app](https://app.stigg.io), according to the [product's Customer Journey settings](/documentation/modeling-your-pricing-in-stigg/products/defining-customer-journey).

<CodeGroup>
  ```javascript provisionCustomer.js theme={null}
  const customer = await stiggClient.provisionCustomer({  
      customerId: 'customer-test-id',  
      name: 'My very first customer', // optional
      email: 'john@example.com',      // optional - billing email address
      couponId: 'coupon-test-id',     // optional
      subscriptionParams: {           // optional - pass null to skip initial subscription
        planId: 'plan-basic',
        resourceId: 'resource-01',      // optional, required for multiple subscription for same product
        billableFeatures: [{ 						// optional, required for subscriptions with per unit pricing
          featureId:'feature-01-templates',
          quantity: 2
        }],
        billingPeriod: 'MONTHLY', 			// optional, relevant only for paid subscriptions  
        addons: [{  										// optional
          addonId: 'addon-extra-stuff',  
          quantity: 1,  
        }],  
        promotionCode: 'STIGG30', 			// optional
        billingCountryCode:'DK',     		// optional, required for price localization, must be in the ISO-3166-1 format
        metadata: { 									  // optional
          key: 'value',  
        },
        billingInformation: {
          taxPercentage: 17,// optional. taxRate will be created if not exists
        }
      },
      billingInfo: {                  // optional
          language: 'en',  
          timezone: 'America/New_York',
          customerName: "The name of the customer for the billing provider",
          billingAddress: {  
            country: 'US',  				// must be in the ISO-3166-1 format
            city: 'New York',  
            state: 'NY',  
            addressLine1: '123 Main Street',  
            addressLine2: 'Apt. 1',  
            phoneNumber: '+1 212-499-5321',  
            postalCode: '10164',  
          },  
          shippingAddress: {  
            country: 'US', 					// must be in the ISO-3166-1 format
            city: 'New York',  
            state: 'NY',  
            addressLine1: '123 Main Street',  
            addressLine2: 'Apt. 1',  
            phoneNumber: '+1 212-499-5321',  
            postalCode: '10164',  
          },  
          taxIds: [  
            { type: "au_abn", value: "12345678912" },  
            { type: "us_ein", value: "12-3456789" }  
          ],  
          invoiceCustomFields: {  
            lorem: "ipsum",  
            acme: "co"  
          },  
          paymentMethodId: "pm_1LcBMDAnAO1PFouUusJSmaPu",  
          currency: "usd",
        	metadata: { 		// optional - metadata that will be stored in the billing solution
            hello: "world"
          }
      },  
      metadata: { 				// optional - metadata that will be stored in Stigg
          key: "value",  
      }
  });
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  {
    "customer": {
      "id": "customer-test-id",
      "experimentInfo": {
        "name": "should we use freemium?",
        "id": "should-we-use-freemium",
        "groupName": "Variant group",
        "groupType": "VARIANT",
      }
    },
    "subscriptionDecisionStrategy": "REQUESTED_PLAN",
    "subscription": {
      "id": "subscription-plan-basic-ed5dac",
      "planId": "plan-basic",
      "status": "ACTIVE",
      "addons": [],
      "addonIds": [],
      "customerId": "customer-test-id",
      "metadata": null,
      "price": null,
      "pricingType": "FREE",
      "resource": {
        "id": "resource-01",
      },
    }
  }
  ```
</Expandable>

<Note>
  In order to control customers' initial access to the product according to [product's Customer Journey setting](/documentation/modeling-your-pricing-in-stigg/products/defining-customer-journey), the `subscriptionParams` must **not** be `null`.

  In order to provision a customer without any default subscription, simply pass `subscriptionParams: null` in the argument object.
</Note>

<Note>
  The customer's billing information can also be passed to the Stigg SDK when a customer is created or updated.

  When Stigg is integrated with additional service, this information will be propagated to the active integrations.

  Passing the billing information will cause the customer to be synced to the integrated billing solution even if it doesn't have any subscriptions that are synced to the billing solution (i.e. free, trial, or custom).

  Customers' billing information does not persist on Stigg's servers.
</Note>

# Updating customers

Customer information can also be updated whenever you like assuming they already exists, for example: in order to update the customer's email, you only need to send the new email value without the rest of the customer.

<Warning>
  Due to the fact that the customer's billing information is not persisted on Stigg's servers, in order to update it the entire `billingInfo` objected must be passed each time.
</Warning>

<CodeGroup>
  ```javascript updateCustomer.js theme={null}
  const customer = await stiggClient.updateCustomer({  
      customerId: 'customer-test-id',  
      email: 'john@example.com', // optional, use to update the customer's email address
      couponId: 'coupon-test-id',   // optional, use to apply a coupon to a customer
      billingInfo: {  							// optional, use to update the customer's billing information
          ...  
      },  
      metadata: {  									// optional, use to update the customer's metadata
        key: 'value',  
      }  
  });
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  {
    "id": "customer-test-id"
  }
  ```
</Expandable>

# Getting customer data

<CodeGroup>
  ```javascript getCustomer.js theme={null}
  const customer = await stiggClient.getCustomer('customer-demo-01');
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  {
    "id": "customer-demo-01",
    "name": "Test Name",
    "email": "john@example.com",
    "createdAt": "2022-09-20T09:59:22.324Z",
    "updatedAt": "2022-09-20T10:00:32.750Z",
    "hasPaymentMethod": false,
    "promotionalEntitlements": [],
    "metadata": {
      "lorem": "ipsum"
    }
  }
  ```
</Expandable>

# Provisioning subscriptions

When a customer subscribes to a new plan (for example: during an upgrade from a free plan to a paid plan, or downgrade from a higher tier to a lower tier), a subscription needs to be provisioned in Stigg.

When provisioning of a paid subscription is attempted, Stigg is integrated with a billing solution and payment details have not been previously provided by the customer, Stigg will auto-magically redirect customers to a the billing solution's checkout page. After the customer enters the required payment details in the presented checkout page, the relevant subscription will be created in both Stigg and the billing solution.

When no payment is required or when Stigg is not integrated with a billing solution, the subscription will be immediately created in Stigg.

When a customer subscribes to a new plan (free, paid, trial, etc.), provision a subscription in Stigg. The provision result can be a successfully created subscription, or a redirect link to checkout page of the integrated billing solution in case payment details are required in order to complete the provisioning of the subscription.

<CodeGroup>
  ```javascript provisionSubscription.js theme={null}
  const subscription = await stiggClient.provisionSubscription({  
      // Mandatory, subscription customer ID
      customerId: 'customer-demo-01',  
    
      // Mandatory, subscription plan ID
      planId: 'plan-basic',
    
      // Optional, required only for paid subscriptions  
      billingPeriod: 'MONTHLY',
      
      // Optional, required for multiple subscription for same product
      resourceId: 'resource-01',      

      // Optional, required for subscriptions with per unit pricing
      billableFeatures: [{ 						
        featureId:'feature-01-templates',
        quantity: 2
      }],
     	
      // Optional, list of subscription addons and quantities
      addons: [{
          addonId: 'addon-extra-stuff',
          quantity: 1,
      }],

      // Optional, entitlements for custom plans (feature + credit)
      entitlements: [
        {
          feature: {
            featureId: 'feature-seats',
            usageLimit: 50,
          },
        },
        {
          credit: {
            customCurrencyId: 'currency-api-credits',
            amount: 50000,
            cadence: 'MONTH',  // MONTH or YEAR
          },
        },
      ],

      // Optional, used for discounts
      promotionCode: 'STIGG30',
    
      // Optional, required for price localization, must be in the ISO-3166-1 format
      billingCountryCode:'DK',     		
    
      // Optional
      billingInformation: {
        // Optional, list of tax rate IDs to apply in Stripe
        taxRateIds: [  
          "txr_1LcTSRE1gVT2zwZV07MIRKdf",  
          "txr_1LcTSRE1gVT2zwZV07MIRKdf"  
        ],
        
        // Optional, metadata that is being passed-through to the billing integration
        metadata: {
        	lorem: "ipsum"
        },
        
        // Optional, used to create a taxRate with the provided percent if not exists
        taxPercentage: 17,						
      },
    
      // Optional, parameters used for the hosted checkout
      checkoutOptions: {
        // Mandatory, redirect URL after the checkout is complete
        successUrl: "https://your-success-url.com",
        
        // Mandatory, redirect URL after the checkout is cancelled
      	cancelUrl: "https://your-cancel-url.com",
        
        // Optional, allow customer to use promo codes on checkout
        allowPromoCodes: true, 				
        
        // Optional, collect billing address for invoice on checkout
        collectBillingAddress: true, 	
        
        // Optional, pass an ID that can be used to reconcile the subscription with internal systems, such as referral programs
        referenceId: 'your-id-for-the-checkout-session', 
        
        // Optional, allow customer to insert taxId to be shown on the invoice
        allowTaxIdCollection: true, 	
        
        // Optional, collect phone number during checkout
        collectPhoneNumber: true
      },
      
      // Optional, can be used to store extra data
      metadata: {
        key: 'value',
      },

      // Optional, controls the billing cycle anchor on subscription change
      // SubscriptionBillingCycleAnchor.UNCHANGED - keep the existing billing cycle (default)
      // SubscriptionBillingCycleAnchor.NOW - reset the billing cycle to the current timestamp
      billingCycleAnchor: SubscriptionBillingCycleAnchor.UNCHANGED,
  });
  ```
</CodeGroup>

<Expandable title="Success result">
  ```json theme={null}
  {
    "status": "Success",
    "checkoutUrl": "https://your-success-url.com",
    "subscription": {
      "id": "subscription-plan-basic",
      "planId": "plan-basic",
      "status": "ACTIVE",
      "addons": [
        
      ],
      "addonIds": [
        
      ],
      "customerId": "customer-demo-01",
      "resource": {
        "id": "resource-01",
      },
      "metadata": {
        "lorem": "ipsum"
      },
      "price": null,
      "pricingType": "FREE",
      "paymentCollection": "NOT_REQUIRED",
      "latestInvoice": {
        "billingId": "invoice-01",
        "status": "PAID",
      },
      "experimentInfo": {
        "name": "should we use freemium?",
        "id": "should-we-use-freemium",
        "groupName": "Variant group",
        "groupType": "VARIANT",    
      }
    }
  }
  ```
</Expandable>

<Expandable title="Payment required action">
  ```json theme={null}
  {
    "status": "Success",
    "subscription": {
      "paymentCollectio"n: "ACTION_REQUIRED",
      "latestInvoice": {
        "paymentUrl": "https...",
        "paymentSecret": "secret"
      }
    }
  }
  ```
</Expandable>

<Expandable title="Payment required failed">
  ```json theme={null}
  {
    "status": "Success",
    "subscription": {
      "paymentCollection": "FAILED",
      "latestInvoice": {
        "paymentUrl": "https...",
        "errorMessage": "no sufficient funds"
      }
    }
  }
  ```
</Expandable>

<Expandable title="Payment required checkout">
  ```json theme={null}
  {
    "status": "PaymentRequired",
    "checkoutUrl": "https:///documentation/native-integrations/billing/stripe/integration-when-using-stripe-elements-for-checkout-checkout-page.com",
  }
  ```
</Expandable>

<Warning>
  1. A customer can have both a non-trial (free or paid) subscription and trial subscription to different plans of the same product in parallel - this logic allows customers to trial a higher tier plan, while still paying for an existing plan.
  2. When the customer has a trial subscription for plan X of product A and a new subscription is created for the same plan, the new subscription will be created as as non-trial (paid) subscription - this logic follows an upgrade flow of a trial subscription to a paid subscription of a specific plan.
  3. Except in the above mentioned cases, when a customer has an active subscription for product X, and another subscription for the same product is created with start date S, the existing subscription will automatically be cancelled and the new subscription will start on start date S.
  4. It's also possible to explicitly skip the trial period of the selected plan by providing the `skipTrial: true` parameter to the `provisionSubscription()` method.
</Warning>

<Warning>
  1. Passing of promo codes requires an integration with a billing solution, such as: [Stripe](/documentation/native-integrations/billing/stripe/integration-when-using-stripe-elements-for-checkout).
  2. Promo codes should be generated in the integrated billing solution.
  3. When providing the optional `promotionCode` parameter, the promo code and associated coupon will validated against the defined restrictions to ensure that they're applicable for the subscription. If validation fails, a [relevant error code](https://node-sdk-docs.stigg.io/enums/errorcode) would be returned; otherwise, the subscription will include the discounted price.
  4. When providing the optional `promotionCode` parameter and payment method is required, the promotion will be applied to the checkout session automatically.
</Warning>

<Note>
  This method replaces the `createSubscription() `method, which has been deprecated.
</Note>

# Updating subscriptions

It's possible to update an existing subscription without creating a new one, keeping the original start date and billing cycle. Using this function you can:

1. Update subscription metadata
2. Update unit quantity, if the subscription has per-unit pricing (i.e. when customers add or removes a seat)
3. Add or remove add-ons, only if they are compatible with the subscription's plan.
4. Update subscription promotion code.
5. Update subscription billing period

When integrated with a billing solution (for example: [Stripe](/documentation/native-integrations/billing/stripe/integration-when-using-stripe-elements-for-checkout)), whenever the unit quantity or add-ons are modified, a new invoice with prorated amount will be immediately generated in the integrated billing solution.

<CodeGroup>
  ```javascript updateSubscription.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'
    },
    billingInformation: {
      // Optional, metadata that is being passed-through to the billing integration
      metadata: {
        lorem: "ipsum"
      },
    },
    billingPeriod: BillingPeriod.Monthly,

    // Optional, controls the billing cycle anchor on subscription change
    // SubscriptionBillingCycleAnchor.UNCHANGED - keep the existing billing cycle (default)
    // SubscriptionBillingCycleAnchor.NOW - reset the billing cycle to the current timestamp
    billingCycleAnchor: SubscriptionBillingCycleAnchor.UNCHANGED,
  });
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  {
    "id": "subscription-plan-basic",
    "planId": "plan-basic",
    "status": "ACTIVE",
    "addons": [],
    "addonIds": [],
    "customerId": "customer-demo-01",
    "resource": {
      "id": "resource-01",
    },
    "metadata": {
      "key": "newValue"
    },
    "price": null,
    "pricingType": "FREE",
    "experimentInfo": {
        "name": "should we use freemium?",
        "id": "should-we-use-freemium",
        "groupName": "Variant group",
        "groupType": "VARIANT",
    }
  }
  ```
</Expandable>

<Warning>
  1. Passing of promo codes requires an integration with a billing solution, such as: [Stripe](/documentation/native-integrations/billing/stripe/integration-when-using-stripe-elements-for-checkout).
  2. Promo codes should be generated in the integrated billing solution.
  3. When providing the optional `promotionCode` parameter, the promo code and associated coupon will validated against the defined restrictions to ensure that they're applicable for the subscription. If validation fails, a [relevant error code](https://node-sdk-docs.stigg.io/enums/errorcode) would be returned; otherwise, the subscription will include the discounted price.
</Warning>

# Canceling subscriptions

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

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

<Warning>
  Subscription cancellation will take place according to the defined product behavior.
</Warning>

# Canceling subscriptions scheduled updates

Will cancel all the scheduled updates of the given subscription

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

<Expandable title="Result">
  ```json theme={null}
  {
      "subscriptionId": "subscription-plan-essentials"
  }
  ```
</Expandable>

# Canceling subscriptions pending payment updates

Will cancel all the pending payment updates of the given subscription

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

<Expandable title="Result">
  ```json theme={null}
  {
      "subscriptionId": "subscription-plan-essentials"
  }
  ```
</Expandable>

# Listing customers active subscriptions

The `getActiveSubscriptionsList` method returns a list of slim subscription data, for extended data of a specific subscription use `getSubscription` method.

### Products with a single active subscription

To retrieve subscriptions for products that only allow customers to have a single active subscription:

<CodeGroup>
  ```javascript getActiveSubscriptionsList.js theme={null}
  const subscriptions = await stiggClient.getActiveSubscriptionsList({
    customerId: 'customer-demo-01',
  });
  ```
</CodeGroup>

### Products with multiple active subscriptions

To retrieve subscriptions for products that allow customers to have multiple active subscriptions, include the [resource ID](/guides/i-want-to/add-multiple-subscriptions#resource-id) of the relevant resource:

<CodeGroup>
  ```javascript getActiveSubscriptionsList.js theme={null}
  const subscriptions = await stiggClient.getActiveSubscriptionsList({
    customerId: 'customer-demo-01',
    resourceId: 'resource-01'
  });
  ```
</CodeGroup>

Passing more than one (and up to 100) [resource ID](/guides/i-want-to/add-multiple-subscriptions#resource-id) will return the active subscriptions for all of the provided resources IDs.

<CodeGroup>
  ```javascript getActiveSubscriptionsList.js theme={null}
  const subscriptions = await stiggClient.getActiveSubscriptionsList({
    customerId: 'customer-demo-01',
    resourceId: ['resource-01', 'resource-02', 'resource-03' ]
  });
  ```
</CodeGroup>

<Expandable title="Result">
  ```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

To retrieve extended subscription:

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

# Getting the entitlement of a customer to a specific feature

### Generic entitlement check (recommended)

The `getEntitlement` method provides a unified way to check any type of feature entitlement. It automatically returns the appropriate entitlement type based on the feature configuration.

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

  if (entitlement.hasAccess) {  
    // Customer has access to the feature
  } else {  
    // Access denied
    console.log('Access denied reason:', entitlement.accessDeniedReason);
  }

  // Entitlement contains the specific entitlement details
  // based on feature type (boolean, numeric, or metered)
  switch (entitlement.type) {
    case "NUMERIC":
      // Numeric feature - contains a value
      console.log('Numeric value:', entitlement.value);
      break;
    case "METERED":
      // Metered feature - contains usage and limits
      console.log('Usage limit:', entitlement.usageLimit);
      console.log('Current usage:', entitlement.currentUsage);
      console.log('Is unlimited:', entitlement.isUnlimited);
      break;
    case "BOOLEAN":
      // Boolean feature - no additional configuration
      break;
  }
  ```
</CodeGroup>

<Expandable title="Result - boolean feature">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": true,
    "entitlement": {
      type: "BOOLEAN",
      "feature": {
          "id": "feature-demo-01",
          "featureType": "Boolean",
          "meterType": "None",
          "units": "",
          "unitsPlural": "",
          "isMetered": false
      }
    }
  }
  ```
</Expandable>

<Expandable title="Result - numeric feature">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": true,
    "entitlement": {
      type: "NUMERIC",
      "feature": {
        "id": "feature-demo-02",
        "featureType": "Numeric",
        "meterType": "None",
        "units": "request",
        "unitsPlural": "requests",
        "isMetered": false
      },
      "value": 100,
      "isUnlimited": false
    }
  }
  ```
</Expandable>

<Expandable title="Result - metered feature">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": true,
    "entitlement": {
      "type": "METERED"
      "feature": {
        "id": "feature-demo-03",
        "featureType": "Numeric",
        "meterType": "Incremental",
        "units": "request",
        "unitsPlural": "requests",
        "isMetered": true
      },
      "usageLimit": 2,
      "isUnlimited": false,
      "currentUsage": 0,
      "resetPeriod": "MONTH",
      "usagePeriodAnchor": "2023-07-21T00:00:00Z",
      "usagePeriodStart": "2023-11-21T00:00:00Z",
      "usagePeriodEnd": "2023-12-21T00:00:00Z"
    }
  }
  ```
</Expandable>

<Note>
  The generic `getEntitlement` method is the recommended approach for checking feature entitlements as it provides a unified interface and automatically handles all entitlement types. For metered features, you can also pass a `options.requestedUsage` parameter to proactively check if additional usage would be allowed.
</Note>

### Checking the entitlement of a boolean feature

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

  if (entitlement.hasAccess) {  
    // customer has access to the feature  
  } else {  
    // access denied  
  }
  ```
</CodeGroup>

<Expandable title="Result - has access">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": true,
    "feature": {
      "id": "feature-demo-01",
      "featureType": "Boolean",
      "meterType": "None",
      "units": "",
      "unitsPlural": "",
      "isMetered": false
    }
  }
  ```
</Expandable>

<Expandable title="Result - no access">
  ```json theme={null}
  {
    "hasAccess": false,
    "accessDeniedReason": "CustomerNotEntitledForFeature",
    "isFallback": false
  }
  ```
</Expandable>

### Checking the entitlement of a numeric configuration feature

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

  if (entitlement.hasAccess) {  
    // get the entitlement numeric value that is defined for the feature  
    console.log(entitlement.value);  
  }
  ```
</CodeGroup>

<Expandable title="Result - has access">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": true,
    "isUnlimited": false,
    "value": 100,
    "feature": {
      "id": "feature-demo-02",
      "featureType": "Numeric",
      "meterType": "None",
      "units": "request",
      "unitsPlural": "requests",
      "isMetered": false
    }
  }
  ```
</Expandable>

<Expandable title="Result - no access">
  ```json theme={null}
  {
    "hasAccess": false,
    "accessDeniedReason": "CustomerNotEntitledForFeature",
    "isFallback": false,
    "isUnlimited": false
  }
  ```
</Expandable>

### Checking if a customer has access to a metered feature

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

  if (entitlement.hasAccess) {  
    // has access and the requested usage is within the allowed quota  
  } else {  
    // the customer has exceeded his quota for this feature  
  }
  ```
</CodeGroup>

<Expandable title="Result - has access">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": true,
    "isUnlimited": false,
    "usageLimit": 2,
    "currentUsage": 0,
    "requestedUsage": 0,
    "feature": {
      "id": "feature-demo-03",
      "featureType": "Numeric",
      "meterType": "Incremental",
      "units": "request",
      "unitsPlural": "requests",
      "isMetered": true
    }
  }
  ```
</Expandable>

<Expandable title="Result - no access">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": false,
    "accessDeniedReason": "RequestedUsageExceedsLimit",
    "isUnlimited": false,
    "usageLimit": 2,
    "currentUsage": 0,
    "requestedUsage": 0,
    "feature": {
      "id": "feature-demo-03",
      "featureType": "Numeric",
      "meterType": "Incremental",
      "units": "request",
      "unitsPlural": "requests",
      "isMetered": true
    }
  }
  ```
</Expandable>

### Proactively checking if a customer will have access to X units of a metered feature

<CodeGroup>
  ```javascript getMeteredEntitlement.js theme={null}
  const requestedUsage = 10;

  const entitlement = await stiggClient.getMeteredEntitlement({  
    customerId: 'customer-demo-01',  
    featureId: 'feature-demo-03',  
    options: {  
      requestedUsage
    },
    resourceId: 'resource-01',      // optional, pass it to get entitlement of specific resource
  });

  if (entitlement.hasAccess) {  
    // has access and the requested usage is within the allowed quota  
  } else {  
    // the customer has exceeded his quota for this feature  
  }
  ```
</CodeGroup>

<Expandable title="Result - has access">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": true,
    "isUnlimited": false,
    "usageLimit": 2,
    "currentUsage": 0,
    "requestedUsage": 0,
    "feature": {
      "id": "feature-demo-03",
      "featureType": "Numeric",
      "meterType": "Incremental",
      "units": "request",
      "unitsPlural": "requests",
      "isMetered": true
    }
  }
  ```
</Expandable>

<Expandable title="Result - no access">
  ```json theme={null}
  {
    "isFallback": false,
    "hasAccess": false,
    "accessDeniedReason": "RequestedUsageExceedsLimit",
    "isUnlimited": false,
    "usageLimit": 2,
    "currentUsage": 0,
    "requestedUsage": 10,
    "feature": {
      "id": "feature-demo-03",
      "featureType": "Numeric",
      "meterType": "Incremental",
      "units": "request",
      "unitsPlural": "requests",
      "isMetered": true
    }
  }
  ```
</Expandable>

# Getting credit entitlements

Use `getCreditEntitlement()` to retrieve a customer's current credit balance for a specific credit type. This method reads directly from the SDK's local cache, providing low-latency access to credit data without API rate-limit concerns.

This is the recommended approach for checking credit balance before gating credit-consuming actions.

<CodeGroup>
  ```javascript getCreditEntitlement.js theme={null}
  const creditEntitlement = await stiggClient.getCreditEntitlement({
    customerId: 'customer-demo-01',
    currencyId: 'currency-ai-credits',
    resourceId: 'resource-01', // optional
  });

  // Check remaining balance before allowing a credit-consuming action
  if (creditEntitlement.remaining > 0) {
    // Customer has credits available — proceed with the action
    console.log('Remaining credits:', creditEntitlement.remaining);
    console.log('Total granted:', creditEntitlement.totalGranted);
    console.log('Total consumed:', creditEntitlement.totalConsumed);
  } else {
    // No credits remaining — deny or prompt for top-up
  }
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  {
    "currentBalance": 4750,
    "remaining": 4750,
    "totalGranted": 5000,
    "totalConsumed": 250,
    "currency": {
      "id": "currency-ai-credits",
      "displayName": "AI Credits",
      "symbol": "AIC"
    }
  }
  ```
</Expandable>

<Note>
  `getCreditEntitlement()` replaces the deprecated `getCreditBalance()` method. It returns the same data (`currentBalance`, `totalConsumed`, `totalGranted`, and `currency`) with significantly lower latency via the local cache.
</Note>

Credit entitlements are surfaced when customers subscribe to plans or add-ons that include recurring credit grants. The balance reflects all active grants — from plan entitlements, add-on entitlements, manual top-ups, and promotional grants — minus total consumption.

# Getting all of the entitlements for a specific customer

<CodeGroup>
  ```javascript getEntitlements.js theme={null}
  const entitlementsState = await stiggClient.getEntitlementsState(
    'customer-demo-01',
    'resource-01',       // optional, required for multiple subscription for same product
  );

  // all the entitlements the customer is entitled to  
  entitlementsState.entitlements.forEach((entitlement) => {  
    console.log(JSON.stringify(entitlement));  
  });
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  [
    {
      "isFallback": false,
      "hasAccess": true,
      "isUnlimited": false,
      "value": 100,
      "feature": {
        "id": "feature-demo-01",
        "featureType": "Numeric",
        "meterType": "None",
        "units": "request",
        "unitsPlural": "requests",
        "isMetered": false
      }
    },
    {
      "isFallback": false,
      "hasAccess": true,
      "feature": {
        "id": "feature-demo-02",
        "featureType": "Boolean",
        "meterType": "None",
        "units": "",
        "unitsPlural": "",
        "isMetered": false
      }
    },
    {
      "isFallback": false,
      "hasAccess": true,
      "isUnlimited": false,
      "usageLimit": 2,
      "currentUsage": 0,
      "requestedUsage": 0,
      "feature": {
        "id": "feature-demo-03",
        "featureType": "Numeric",
        "meterType": "Incremental",
        "units": "request",
        "unitsPlural": "requests",
        "isMetered": true
      }
    }
  ]
  ```
</Expandable>

# Getting paywall data

Useful for rendering the public pricing page or customer paywall.

<CodeGroup>
  ```javascript getPaywall.js theme={null}
  const paywallData = await stiggClient.getPaywall();
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  {
    "plans": [
      {
        "id": "plan-stigg-starter",
        "order": 0,
        "displayName": "Starter",
        "description": "For getting started",
        "entitlements": [
          {
            "usageLimit": 100,
            "feature": {
              "id": "feature-demo-01",
              "featureType": "Numeric",
              "description": "",
              "meterType": "None",
              "units": "request",
              "unitsPlural": "requests",
              "displayName": "Feature #1",
              "isMetered": false,
              "metadata": {
                "key": "value"
              }
            },
            "hasUnlimitedUsage": false,
            "displayNameOverride": "Awesome feature #1",
            "hiddenFromWidgets": []
          },
          {
            "usageLimit": 0,
            "feature": {
              "id": "feature-demo-02",
              "featureType": "Boolean",
              "description": "",
              "meterType": "None",
              "displayName": "Feature #2",
              "isMetered": false
            },
            "hasUnlimitedUsage": false,
            "displayNameOverride": null,
            "hiddenFromWidgets": ["PAYWALL"]
          }
        ],
        "inheritedEntitlements": [],
        "pricePoints": [],
        "pricingType": "FREE",
        "defaultTrialConfig": null,
        "compatibleAddons": [],
        "product": {
          "id": "product-stigg",
          "displayName": "Stigg",
          "description": null,
          "metadata": null
        },
        "metadata": null
      }
    ]
  }
  ```
</Expandable>

# Subscribing to entitlement and usage updates

The server SDK use a streaming connection to receive updates with low latency, and can notify you when changes to customer entitlements and usage take place.

An additional parameter is sent to the subscribed event listeners with data that is relevant for the specific event, for example: for `entitlementsUpdated` event the extra parameter has information about the customer and the updated entitlements.

<CodeGroup>
  ```javascript listeners.js theme={null}
  // listen to customer entitlement updates
  stiggClient.addListener('entitlementsUpdated', (data) => {  
    console.log('entitlements updated: ', data.customerId, data.entitlements);  
  });  

  // listen to customer usage updates
  stiggClient.addListener('usageUpdated', (data) => {  
    console.log('entitlement usage updated', data.entitlement, data.usage);  
  });
  ```
</CodeGroup>

# Reporting usage measurements to Stigg

The Stigg SDK allows you to report usage measurements for [metered features](/documentation/modeling-your-pricing-in-stigg/features/overview). The reported usage will be used to track, limit and bill the customer's usage of metered features.

Stigg supports metering of usage from the following data sources:

1. [Calculated usage](#calculated-usage) - usage that has been aggregated and calculated by **your application**. This type is useful for features, such as: seats.
2. [Raw events](#raw-events) - raw events from your application, which **Stigg** filters and aggregates aggregate to calculate customer usage. This type is useful for features, such as: monthly active users (MAUs).

More details about Stigg's metering and aggregation capabilities can be found here:

<Card title="Stigg's metering and aggregation capabilities" icon="gauge" horizontal href="/documentation/getting-usage-data-into-stigg/overview" />

<Warning>
  1. Reporting of measurements to Stigg should be done only after the relevant resources have been provisioned in your application.
  2. To ensure that the provisioned resources are aligned with the measurements that are reported to Stigg, ensure that customers are not allowed to allocate new resources until an acknowledgement about the processed measurement is received from the Stigg backend.
</Warning>

<Note>
  Validating that a measurement was successfully reported to Stigg is also possible in the [customer details section](/documentation/managing-customers-and-subscriptions/customers/viewing-customers-entitlement-usage) of the relevant customer in the Stigg app.
</Note>

### Calculated usage

<CodeGroup>
  ```javascript reportUsage.js theme={null}
  // increment usage of a metered feature by 10
  await stiggClient.reportUsage({  
    customerId: 'customer-test-id',  
    featureId: 'feature-seats',  
    value: 10,
    resourceId: 'resource-01',      // optional, pass it to report usage for specific resource
  });
  ```
</CodeGroup>

By default, the value reported should represent the **change** in usage of the feature, for example user added 2 more seats then the report usage call should have the value of 2.

In some cases, it's more useful to set the usage to some value instead of reporting the change value, it's possible by passing the `updateBehavior` parameter:

<CodeGroup>
  ```avascript reportUsage.js theme={null}
  import { UsageUpdateBehavior } from '@stigg/node-server-sdk';

  // set usage of a metered feature to 10
  await stiggClient.reportUsage({  
    customerId: 'customer-test-id',  
    featureId: 'feature-seats',  
    value: 10,
    updateBehavior: UsageUpdateBehavior.Set,
    resourceId: 'resource-01',      // optional, pass it to report usage for specific resource
  });
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  {
    "measurementId": "e7293df0-2d69-4713-9913-10ed06b9b777"
  }
  ```
</Expandable>

<Note>
  Calculated usage measurements can also be reported in [bulks](https://node-sdk-docs.stigg.io/classes/stigg#reportusagebulk)
</Note>

### Raw events

<CodeGroup>
  ```javascript reportEvent.js theme={null}
  // report event of a user login
  await stiggClient.reportEvent({
    customerId: 'customer-test-id',
    resourceId: 'resource-01',      // optional, pass it to report event for specific resource
    eventName: 'user_login',
    idempotencyKey: '227c1b73-883a-457b-b715-6ba5a2c69ce4',
    dimensions: {
      user_id: 'user-01',
      user_email: 'john@example.com'
      },
    timestamp: '2022-10-26T15:01:55.768Z' // optional, pass it to report event with specific timestamp
  });
  ```
</CodeGroup>

It's also possible to send a batch of events in one invocation. To do so, simply pass an array of events to the `reportEvent` method.

<Warning>
  The `dimensions` field support key value pairs, while keys are strictly of type `string` values can be `string | number | boolean`
</Warning>

# Granting promotional entitlements

You can grant [promotional entitlements](/documentation/managing-customers-and-subscriptions/customers/managing-customers-promotional-entitlements) for customers using the [grantPromotionalEntitlements](https://node-sdk-docs.stigg.io/classes/stigg#grantpromotionalentitlements) method.

# Revoking promotional entitlements

You can revoke [promotional entitlements](/documentation/managing-customers-and-subscriptions/customers/managing-customers-promotional-entitlements) from customer using the [revokePromotionalEntitlements](https://node-sdk-docs.stigg.io/classes/stigg#revokepromotionalentitlements) method.

# Getting available coupons

<CodeGroup>
  ```javascript getCoupons.js theme={null}
  const coupons = await stiggClient.getCoupons();
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  [
    {
      "id": "coupon-16e5c7",
      "name": "Test coupon",
      "description": null,
      "metadata": null,
      "discountValue": 5
    }
  ]
  ```
</Expandable>

# Getting all products

<CodeGroup>
  ```javascript getProducts.js theme={null}
  const products = await stiggClient.getProducts();
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  [
    {
      "id": "product-revvenu",
      "displayName": "Revvenu",
      "description": "Example description",
      "metadata": { "recommendedPlan": "plan-revvenu-essentials" },
      "downgradePlan": { "id": "plan-revvenu-basic", "displayName": "Basic" }
    }
  ]
  ```
</Expandable>

# Estimate subscription cost

You can estimate the subscription cost using the `estimateSubscription` method. This can help the customer to understand the costs before paying.

<CodeGroup>
  ```javascript estimateSubscription.js theme={null}
  // increment usage of a metered feature  
  await stiggClient.estimateSubscription({  
  	customerId: "customer-demo-01",
    billingPeriod: "MONTHLY",
    planId: "plan-revvenu-growth",
    billableFeatures: [{ 						// optional, required for plans with per unit pricing
      featureId:'feature-01-templates',
      quantity: 2
    }],
    addons: [{  								// optional
    	addonId: 'addon-10-campaigns',  
      quantity: 1  
    }],
    billingInformation: {  					// optional
        taxRateIds: [  
          "txr_1LcTSRE1gVT2zwZV07MIRKdf",  
          "txr_1LcTSRE1gVT2zwZV07MIRKdf"  
        ],  
        taxPerentage: 17,// optional. taxRate will be created if not exists
    },
    promotionCode: "STIGG50",		// optional
    startDate: new Date(), 			// optional
    billingCountryCode: 'US' 		// optional, required for price localization, must be in the ISO-3166-1 format
    resourceId: 'resource-01',  // optional, required for multiple subscription for same product
  });
  ```
</CodeGroup>

**Return fields**

<CodeGroup>
  ```javascript estimateSubscriptionResult.js theme={null}
  {
    total: 'Total after discounts and taxes.',
    totalExcludingTax: 'Total including discounts but excluding tax.',
    subTotal: 'Total before any discount or exclusive tax is applied.',
    tax: 'Tax amount',
    taxDetails: {
      displayName: 'Tax display name.',
      inclusive: 'Specifies if the tax rate is inclusive or exclusive.',
      percentage: 'Tax rate percentage out of 100.',
    },
    discount: {
      type: 'Discount type - `PERCENTAGE` / `FIXED`.',
      value: 'Discount value out of 100.',
      durationType:
        'The duration of the discount - `FOREVER` / `REPEATING` / `ONCE`',
      durationInMonths: 'The duration of the discount in months',
    },
    billingPeriodRange: {
      start: 'Billing period start date',
      end: 'Billing period end date',
    },
    proration: {
      prorationDate: 'The date when the proration occurred',
      credit:
        'The prorated amount of credits to refund the customer in the upcoming invoice',
      debit:
        'The prorated amount of debit to charge the customer in the upcoming invoice',
      netAmount: 'The sum of `debit` and `credit`',
    },
    // Not prorated subscription prices
    subscription: {
      total: 'Total after discounts and taxes.',
      totalExcludingTax: 'Total including discounts but excluding tax.',
      subTotal: 'Total before any discount or exclusive tax is applied.',
      tax: 'Tax amount',
    },
  };
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}

  {
    "total": {
      "amount": 10,
      "currency": "USD"
    },
    "subTotal": {
      "amount": 10,
      "currency": "USD"
    },
    "billingPeriodRange": {
      "start": "2022-10-26T00:00:00.000Z",
      "end": "2022-11-26T00:00:00.000Z"
    },
    "discount": {
      "type": "PERCENTAGE",
      "value": 50,
      "durationType": "REPEATING",
      "durationInMonths": 3
    },
    "proration": {
      "credit": {
        "amount": 0,
        "currency": "USD"
      },
      "debit": {
        "amount": 10,
        "currency": "USD"
      },
      "netAmount": {
        "amount": 10,
        "currency": "USD"
      },
      "prorationDate": "2022-10-26T00:00:00.000Z"
    },
    "subscription": {
      "total": {
        "amount": 10,
        "currency": "USD"
      },
      "subTotal": {
        "amount": 20,
        "currency": "USD"
      }
    }
  }
  ```
</Expandable>

You can also estimate the cost of updating an existing subscription using the `updateSubscription` method, which also returns a breakdown of the proration amounts:

<CodeGroup>
  ```javascript estimateSubscriptionUpdate.js theme={null}
  await stiggClient.estimateSubscriptionUpdate({  
    subscriptionId: "subscription-plan-revvenu-growth-da6324",
    billableFeatures: [{ 						// optional, required for subscriptions with per unit pricing
      featureId:'feature-01-templates',
      quantity: 3
    }],
    addons: [{  								// optional
      addonId: 'addon-10-campaigns',  
      quantity: 1  
    }],
    promotionCode: "STIGG50"		// optional
  });
  ```
</CodeGroup>

*Return fields*

<CodeGroup>
  ```javascript estimateSubscriptionUpdateResult.js theme={null}
  {
    total: 'Total after discounts and taxes.',
    totalExcludingTax: 'Total including discounts but excluding tax.',
    subTotal: 'Total before any discount or exclusive tax is applied.',
    tax: 'Tax amount',
    taxDetails: {
      displayName: 'Tax display name.',
      inclusive: 'Specifies if the tax rate is inclusive or exclusive.',
      percentage: 'Tax rate percentage out of 100.',
    },
    discount: {
      type: 'Discount type - `PERCENTAGE` / `FIXED`.',
      value: 'Discount value out of 100.',
      durationType:
        'The duration of the discount - `FOREVER` / `REPEATING` / `ONCE`',
      durationInMonths: 'The duration of the discount in months',
    },
    billingPeriodRange: {
      start: 'Billing period start date',
      end: 'Billing period end date',
    },
    proration: {
      prorationDate: 'The date when the proration occurred',
      credit:
        'The prorated amount of credits to refund the customer in the upcoming invoice',
      debit:
        'The prorated amount of debit to charge the customer in the upcoming invoice',
      netAmount: 'The sum of `debit` and `credit`',
    },
    // Not prorated subscription prices
    subscription: {
      total: 'Total after discounts and taxes.',
      totalExcludingTax: 'Total including discounts but excluding tax.',
      subTotal: 'Total before any discount or exclusive tax is applied.',
      tax: 'Tax amount',
    },
  };
  ```
</CodeGroup>

<Expandable title="Result">
  ```json theme={null}
  {
    "total": {
      "amount": 2.5,
      "currency": "USD"
    },
    "subTotal": {
      "amount": 5,
      "currency": "USD"
    },
    "billingPeriodRange": {
      "start": "2022-10-26T00:00:00.000Z",
      "end": "2022-11-26T00:00:00.000Z"
    },
    "discount": {
      "type": "PERCENTAGE",
      "value": 50,
      "durationType": "REPEATING",
      "durationInMonths": 3
    },
    "proration": {
      "credit": {
        "amount": -9.99,
        "currency": "USD"
      },
      "debit": {
        "amount": 14.99,
        "currency": "USD"
      },
      "netAmount": {
        "amount": 5,
        "currency": "USD"
      },
      "prorationDate": "2022-10-26T00:00:00.000Z"
    },
    "subscription": {
      "total": {
        "amount": 2.5,
        "currency": "USD"
      },
      "subTotal": {
        "amount": 5,
        "currency": "USD"
      }
    }
  }
  ```
</Expandable>

<Warning>
  1. Passing of promo codes requires an integration with a billing solution, such as: [Stripe](/documentation/native-integrations/billing/stripe/integration-when-using-stripe-elements-for-checkout).
  2. Promo codes should be generated in the integrated billing solution.
  3. When providing the optional `promotionCode` parameter, the promo code and associated coupon will validated against the defined restrictions to ensure that they're applicable for the subscription. If validation fails, a [relevant error code](https://node-sdk-docs.stigg.io/enums/errorcode) would be returned; otherwise, the subscription will include the discounted price.
</Warning>

# Migrating subscriptions to the latest plan and add-on version

When changes to plans and add-ons are rolled out only to new subscriptions, grandfathering takes place. In order to prevent a SKU sprawl, Stigg allows you to manually migrate subscriptions to the latest plan and add-on version on a subscription-by-subscription basis.

<Warning>
  When the current price of the subscription is different than the latest published package version, during the migration the customer will be charged or credited the prorated amount until the end of the current billing period depending on whether the latest price is more expensive or cheaper than the current subscription price.
</Warning>

To migrate a customer to the latest plan and add-on version:

<CodeGroup>
  ```javascript migrateSubscriptionToLatest.js theme={null}
  await stiggClient.MigrateSubscriptionToLatest("subscriptionId", 
                                                SubscriptionMigrationTime.EndOfBillingPeriod);
  ```
</CodeGroup>

# Archiving customers

When a customer is archived:

1. Its PII (name and email address) will be nullified.
2. It will no longer appear in the Stigg app UI.
3. It will no longer be returned by the Stigg API and SDKs.

When Stigg is integrated with a billing solution, customers that are archived in Stigg will still exist in the billing solution to allow upcoming invoices to be finalized. Archiving customers in the billing solution can be done manually in the billing solution.

<Warning>
  New customers cannot use the customer ID of archived customers.
</Warning>

To archive a customer:

<CodeGroup>
  ```javascript archiveCustomer.js theme={null}
  await stiggClient.archiveCustomer("customer-a18f01");
  ```
</CodeGroup>

# Persistent cache

To configure a persistent cache store, you can read about it [in the persistent cache section](/documentation/high-availability-and-scale/persistent-caching).

# Offline mode

During local development or testing, you might want to avoid making network requests to the Stigg API. To do this, you can run the Node SDK service in offline mode by enabling the offline option. When enabled, API key validation will always succeed, regardless of the key provided.

<CodeGroup>
  ```javascript initializeOfflineClient.js theme={null}
  const stiggClient = Stigg.initialize({
    apiKey: 'local',
    offline: true
  });
  ```
</CodeGroup>

In offline mode, the Node SDK respects the [global fallback strategy](/documentation/high-availability-and-scale/local-caching-and-fallback-strategy#global-fallback-configuration)g, and entitlement evaluations are limited to the values defined as fallback entitlements.

<CodeGroup>
  ```javascript initializeOfflineClientWithFallback.js theme={null}
  const stiggClient = Stigg.initialize({
    apiKey: 'local',
    offline: true,
    entitlementsFallback: {
      'feature-number-of-seats': {
       	hasAccess: true,
      },
      'feature-templates': {
        hasAccess: true,
        usageLimit: 10
      }
    }
  });
  ```
</CodeGroup>

<Warning>
  Only the following methods support offline mode:

  * getEntitlements
  * getBooleanEntitlement
  * getNumericEntitlement
  * getMeteredEntitlement
  * reportUsage (noop)
  * reportEvents (noop)
  * reloadEntitlements (noop)

  All other SDK methods will attempt to send requests to the Stigg API over the network, even when offline mode is enabled
</Warning>

# Credit balance

## Get credit entitlement (low-latency)

Use `getCreditEntitlement()` to instantly retrieve a customer's current credit balance. This method reads directly from the SDK's local cache, providing sub-millisecond latency for in-app credit balance checks. When integrated with Stigg's [persistent cache](/documentation/high-availability-and-scale/persistent-caching), requests are served from the Edge with **\< 10ms latency**.

The response includes the customer's `currentBalance`, `totalConsumed`, `totalGranted`, `currency`, and — when recurring grants are included in plans or add-ons — the `nextResetDate`.

<CodeGroup>
  ```javascript getCreditEntitlement.js theme={null}
  const entitlement = await stiggClient.getCreditEntitlement({
    customerId: 'customer-demo-01',
    currencyId: 'currency-ai-tokens',
  });

  // entitlement.currentBalance  — available credits right now
  // entitlement.totalConsumed   — credits used in the current period
  // entitlement.totalGranted    — total credits granted in the current period
  // entitlement.currency        — the credit currency details
  // entitlement.nextResetDate   — next recurring grant reset date (if applicable)
  ```
</CodeGroup>

<Note>
  `getCreditEntitlement()` replaces the deprecated `getCreditBalance()`. It reads from the SDK's local cache with no additional API call, eliminating rate-limit concerns and reducing latency.
</Note>

# Credit auto-recharge

## Get automatic recharge configuration

<CodeGroup>
  ```javascript initializeOfflineClientWithFallback.js theme={null}
  const config = await stiggClient.getAutomaticRechargeConfiguration({
    customerId: 'customer-123',
    currencyId: 'tokens',
    environmentId: 'env-production'  // optional
  });

  console.log(config);
  // {
  //   customerId: 'customer-123',
  //   currencyId: 'tokens',
  //   isEnabled: true,
  //   thresholdType: 'CREDIT_AMOUNT',
  //   thresholdValue: 50,
  //   targetBalance: 100,
  //   maxSpendLimit: 250,
  //   grantExpirationPeriod: 'ONE_MONTH',
  //   currentMonthlySpend: 125,
  //   createdAt: Date,
  //   updatedAt: Date
  // }
  ```
</CodeGroup>

## Save/update automatic recharge configuration

<CodeGroup>
  ```javascript initializeOfflineClientWithFallback.js theme={null}
  const config = await stiggClient.saveAutomaticRechargeConfiguration({
    customerId: 'customer-123',
    currencyId: 'tokens',
    environmentId: 'env-production',  // optional
    isEnabled: true,
    thresholdType: ThresholdType.CreditAmount,  // or 'CREDIT_AMOUNT'
    thresholdValue: 50,
    targetBalance: 100,
    maxSpendLimit: 250,  // optional, null for unlimited
    grantExpirationPeriod: GrantExpirationPeriod.OneMonth  // optional
  });
  ```
</CodeGroup>

## TypeScript types

<CodeGroup>
  ```javascript initializeOfflineClientWithFallback.js theme={null}
  import {
    ThresholdType,
    GrantExpirationPeriod
  } from '@Stigg/node-server-sdk';

  // ThresholdType enum values:
  ThresholdType.CreditAmount  // Trigger based on credit balance
  ThresholdType.DollarAmount  // Trigger based on dollar value

  // GrantExpirationPeriod enum values:
  GrantExpirationPeriod.OneMonth
  GrantExpirationPeriod.ThreeMonths
  GrantExpirationPeriod.SixMonths
  GrantExpirationPeriod.OneYear
  ```
</CodeGroup>

## Configuration object schema

<CodeGroup>
  ```javascript initializeOfflineClientWithFallback.js theme={null}
  interface AutoRechargeSettings {
    customerId: string;
    currencyId: string;
    isEnabled: boolean;
    thresholdType: 'CREDIT_AMOUNT' | 'DOLLAR_AMOUNT';
    thresholdValue: number;
    targetBalance: number;
    maxSpendLimit?: number | null;  // null = unlimited
    grantExpirationPeriod?: 'ONE_MONTH' | 'THREE_MONTHS' | 'SIX_MONTHS' | 'ONE_YEAR';
    currentMonthlySpend?: number;  // Read-only, returned by GET
    createdAt: Date;
    updatedAt: Date;
  }
  ```
</CodeGroup>

## Use cases

### Backend admin panel

<CodeGroup>
  ```javascript initializeOfflineClientWithFallback.js theme={null}
  // Allow admins to configure auto-recharge for customers
  app.post('/api/customers/:customerId/auto-recharge', async (req, res) => {
    const config = await stiggClient.saveAutomaticRechargeConfiguration({
      customerId: req.params.customerId,
      currencyId: req.body.currencyId,
      isEnabled: req.body.isEnabled,
      thresholdValue: req.body.thresholdValue,
      targetBalance: req.body.targetBalance,
      maxSpendLimit: req.body.maxSpendLimit,
    });

    res.json(config);
  });
  ```
</CodeGroup>

### Check current configuration

<CodeGroup>
  ```javascript initializeOfflineClientWithFallback.js theme={null}
  // Get current auto-recharge settings
  const config = await stiggClient.getAutomaticRechargeConfiguration({
    customerId: 'customer-123',
    currencyId: 'tokens'
  });

  if (config.isEnabled) {
    console.log(`Auto-recharge active: ${config.currentMonthlySpend}/${config.maxSpendLimit}`);
  }
  ```
</CodeGroup>

# Governance

Stigg Governance lets your enterprise customers control AI usage and credit spend across their organizations. Install the governance client separately from the main SDK:

<CodeGroup>
  ```shell npm theme={null}
  npm install @stigg/governance-client
  ```

  ```shell yarn theme={null}
  yarn add @stigg/governance-client
  ```
</CodeGroup>

## Initializing the client

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { createGovernanceClient } from '@stigg/governance-client';

  const governance = createGovernanceClient({
    baseUrl:       process.env.GOVERNANCE_API_URL,
    accessToken:   process.env.GOVERNANCE_ACCESS_TOKEN,
    accountId:     process.env.STIGG_ACCOUNT_ID,
    environmentId: process.env.STIGG_ENVIRONMENT_ID,
    caller:        'my-app',
  });
  ```
</CodeGroup>

## Check

Call `check` before allowing consumption. Returns `hasAccess: true` when every budget in the hierarchy allows the request.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const { data: report } = await governance.check.checkControllerCheck('cus-acme', {
    entityIds:       ['team-eng'],
    capabilityId:    'ai-tokens',
    requestedAmount: 1000,
  });

  if (!report.hasAccess) {
    throw new Error('Usage limit reached');
  }
  ```
</CodeGroup>

## Ingest

Call `ingest` after consuming to increment the usage counter. Returns `204 No Content` immediately.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await governance.ingest.ingestControllerIngest('cus-acme', {
    events: [
      { entityIds: ['team-eng'], capabilityId: 'ai-tokens', amount: 1250 },
    ],
  });
  ```
</CodeGroup>

For the full check-then-ingest pattern, provisioning entities, setting budgets, and querying the governance tree, see [Governance](/documentation/governance/overview).

# Full SDK reference

<Card title="Node.js SDK reference" icon="code" horizontal href="https://node-sdk-docs.stigg.io/classes/stigg" />

# SDK changelog

<Card title="Node.js SDK changelog" icon="list-timeline" horizontal href="/api-and-sdks/changelog/backend-graphql/node" />
