Autumn gives you the data to handle your billing flows, which will automatically update when you make pricing changes. This page covers how to roll your own billing flows using our API or SDK. Alternatively, you can use our customizable shadcn/ui components to handle these out-of-the-box.

Pricing Table

Display a list of the products you offer by fetching your product data from Autumn’s API. This gives you a dynamic, single source of truth for your app’s pricing plans.
import { useCustomer, usePricingTable } from "autumn-js/react";

const { products, isLoading, error } = usePricingTable();
Each product in the array returned will contain a scenario enum, that can be used to determine its relation to the user’s currenct product. This can be used to control the button text of each pricing card you display. See our shadcn/ui pricing table texts for an example.
ScenarioDescription
upgradeThe product is an upgrade to the user’s current product
downgradeThe product is a downgrade to the user’s current product
cancelThe product is a free product which would cancel the user’s current subscription
renewThe product would be a renewal of a product that’s scheduled to downgrade
scheduledThe product is already scheduled to start at a future date (eg, for downgrades)
activeThe product is already active

Upgrades, Downgrades and Payment Confirmation

Typically when charging a customer’s card, you’ll want to display the payment information beforehand for the user to confirm. This is useful for upgrade, downgrade and add-on payment flows. The checkout function will return the data your customer will want to see, such as price information, billing cycle, etc.
If the customer’s payment details aren’t on file (eg, its a new payment), checkout will return a payment URL.
import { useCustomer } from "autumn-js/react";

const { checkout } = useCustomer();

const { data, error } = await checkout({
  productId: "pro",
});
{
    "customer_id": "user_1234",
    "code": "product_found",
    "product_id": "ultra",
    "allowed": false,
    "preview": {
        "title": "Upgrade to Ultra",
        "message": "By clicking confirm, you will upgrade your plan to Ultra and the following amount will be charged to your card immediately:",
        "scenario": "upgrade",
        "product_id": "ultra",
        "product_name": "Ultra",
        "recurring": true,
        "next_cycle_at": 1750604867000,
        "current_product_name": "Pro",
        "items": [
            {
                "price": "-$3.53",
                "description": "Unused time on Pro after 31 May 2025"
            },
            {
                "price": "$7.06",
                "description": "Remaining time on Ultra after 31 May 2025"
            }
        ],
        "options": [
            {
                "feature_id": "seats",
                "feature_name": "seats",
                "billing_units": 1,
                "included_usage": 0,
                "price": 4
            }
        ],
        "due_today": {
            "price": 3.53,
            "currency": "USD"
        },
        "due_next_cycle": {
            "price": 10,
            "currency": "USD"
        },
        "product": {
            "id": "ultra",
            "name": "Ultra",
            "group": null,
            "env": "sandbox",
            "is_add_on": false,
            "is_default": false,
            "version": 1,
            "created_at": 1747926427301,
            "items": [
                {
                    "type": "price",
                    "feature_id": null,
                    "interval": "month",
                    "price": 10
                },
                {
                    "type": "priced_feature",
                    "feature_id": "seats",
                    "feature_type": "continuous_use",
                    "included_usage": 0,
                    "interval": "month",
                    "price": null,
                    "tiers": [
                        {
                            "to": "inf",
                            "amount": 4
                        }
                    ],
                    "usage_model": "prepaid",
                    "billing_units": 1,
                    "reset_usage_when_enabled": true
                },
                {
                    "type": "feature",
                    "feature_id": "chat_messages",
                    "feature_type": "single_use",
                    "included_usage": 20,
                    "interval": "month",
                    "reset_usage_when_enabled": true
                }
            ],
            "free_trial": null
        }
    }
}
The preview object that is returned from the checkout function can be used to display the data you want to the user. Example implementation:
React
import { useCustomer } from "autumn-js/react";

export default function PurchaseButton() {
  const { checkout, attach } = useCustomer();

  return (
    <button
      onClick={async () => {
        const { data } = await checkout({
          productId: "ultra",
        });

        //throw a browser popup if no checkout URL
        if (!data?.url && window.confirm(data.preview.scenario)) {
          await attach({
            productId: "ultra",
          });
        }
      }}
    >
      Upgrade to Ulta
    </button>
  );
}
If preview is returned, it will contain a scenario enum. You can use this to control the messaging you display to the user. See our shadcn/ui dialog messaging for an example.
ScenarioDescription
upgradeThe customer is upgrading their product
downgradeThe customer is downgrading to another paid tier
cancelThe customer is downgrading to a free product
renewThe customer is reactivating a product that was previously cancelled
scheduledThe product is already scheduled to start at a future date (eg, for downgrades), so no changes are needed
activeThe customer already has the product active, so no changes are needed

Paywall

A paywall that prompts users to upgrade to the next tier when they hit a usage limit, or don’t have access to a feature. The check function has a with_preview parameter that can be used to get paywall preview information when a customer doesn’t have access to a feature. This will contain information about the next product tier (or an add-on) they should upgrade to, in order to access the feature.
import { useCustomer } from "autumn-js/react";
const { check } = useCustomer();
const { data, error } = await check({
  featureId: "chat_messages",
  withPreview: true,
});
{
  "customer_id": "user_1234",
  "feature_id": "chat_messages",
  "required_balance": 1,
  "code": "feature_found",
  "allowed": false,
  "unlimited": false,
  "balance": 0,
  "preview": {
    "title": "Upgrade to Pro",
    "message": "You have run out of messages. Please upgrade to Pro to continue using this feature.",
    "scenario": "usage_limit",
    "feature_id": "chat_messages",
    "feature_name": "messages",
    "products": [
      {
        "id": "pro",
        "name": "Pro",
        "group": null,
        "env": "sandbox",
        "is_add_on": false,
        "is_default": false,
        "version": 2,
        "created_at": 1748802907591,
        "items": [
          {
            "type": "price",
            "feature_id": null,
            "interval": "month",
            "price": 55
          },
          {
            "type": "feature",
            "feature_id": "chat_messages",
            "feature_type": "single_use",
            "included_usage": 300,
            "interval": "month",
            "reset_usage_when_enabled": true
          },
          {
            "type": "feature",
            "feature_id": "premium_support",
            "feature_type": "static"
          }
        ],
        "free_trial": null
      }
    ],
    "upgrade_product_id": "pro"
  }
}
The preview object that is returned from the check function can be used to control the paywall’s contents. preview will be null if the feature is allowed and no paywall should be shown. Example implementation:
import { useCustomer } from "autumn-js/react";

export default function FeatureButton() {
  const { check, attach } = useCustomer();

  return (
    <button
      onClick={async () => {
        const { data } = await check({
          featureId: "chat_messages",
          withPreview: true,
        });

        if (!data?.allowed && data?.preview) {
          if (
            window.confirm(`${data.preview.title}: ${data.preview.message}`)
          ) {
            if (data.preview.upgrade_product_id) {
              await attach({
                productId: data.preview.upgrade_product_id,
              });
            }
          }
        }
      }}
    >
      Send Message
    </button>
  );
}
If preview is returned, it will contain a scenario enum, which you can use this to control the paywall’s contents. See our shadcn/ui paywall dialog messaging for an example.
ScenarioDescription
usage_limitThe customer has hit a usage limit for the feature
feature_flagThe customer doesn’t have access to the feature