In this example we’ll create the pricing for a premium AI chatbot. We’re going to have:
  • A Free plan that gives users 5 chat messages per month for free
  • A Pro plan that gives users 100 chat messages per month for $20 per month.

1. Create your products

Create a product for each plan that your app offers. In our example we’ll create a “Free” and “Pro” product.
Run the following command in your root directory:
npx atmn init
This will prompt you to login or create an account, and create an autumn.config.ts file. Paste in the code below, or view our config schema to build your own.
autumn.config.ts
import {
  feature,
  product,
  featureItem,
  pricedFeatureItem,
  priceItem,
} from "atmn";

// Features
export const messages = feature({
  id: "messages",
  name: "Messages",
  type: "single_use",
});

// Products
export const free = product({
  id: "free",
  name: "Free",
  items: [
    // 5 messages per month
    featureItem({
      feature_id: messages.id,
      included_usage: 5,
      interval: "month",
    }),
  ],
});

export const pro = product({
  id: "pro",
  name: "Pro",
  items: [
    // 100 messages per month
    featureItem({
      feature_id: messages.id,
      included_usage: 100,
      interval: "month",
    }),
    // $20 per month
    priceItem({
      price: 20,
      interval: "month",
    }),
  ],
});
Then, push your changes to Autumn’s sandbox environment.
npx atmn push
If you already have products created in the dashboard, run npx atmn pull to pull them into your local config.

2. Setup Autumn

Install autumn-js
npm install autumn-js
If you’re using a separate backend and frontend, make sure to install the library in both.
Then create an Autumn Secret API key from the dashboard, and add it to your backend .env file. If you’re using the CLI this should already be done for you.
.env
AUTUMN_SECRET_KEY=am_sk_1234567890

Add Endpoints Server-side

Server-side, mount the Autumn handler. This will create endpoints in the /api/autumn/* path, which will be called by Autumn’s frontend React library. These endpoints in turn call Autumn’s API. The handler takes in an identify function where you should pass in the user ID or organization ID from your auth provider.
// app/api/autumn/[...all]/route.ts

import { autumnHandler } from "autumn-js/next";
import { auth } from "@/lib/auth";

export const { GET, POST } = autumnHandler({
  identify: async (request) => {
    // get the user from your auth provider (example: better-auth)
    const session = await auth.api.getSession({
      headers: request.headers,
    });

    return {
      customerId: session?.user.id, //or org ID
      customerData: {
        name: session?.user.name,
        email: session?.user.email,
      },
    };
  },
});
Autumn will automatically create a customer if the customer ID is new.

Add Provider Client-side

Client side, wrap your application with the <AutumnProvider> component. If your backend is hosted on a separate URL (eg, when using Vite), pass it into the backendUrl prop.
//layout.tsx
import { AutumnProvider } from "autumn-js/react";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode,
}) {
  return (
    <html>
      <body>
        <AutumnProvider>
          {children}
        </AutumnProvider>
      </body>
    </html>
  );
}
The Provider component can also take in a getBearerToken function, which should be used for auth frameworks with separate backend and frontend, eg using Clerk with Express.This will pass the auth token in the Authorization header from the request to the backend, which can be used in the autumnHandler identify function.
CORS issues? Please reach out and we’ll help you ASAP. Or try a backend-only setup.

3. Stripe Payments

Call checkout to redirect the customer to a Stripe checkout page when they want to purchase the Pro plan. Once they’ve paid, Autumn will grant them access to the features we defined. If their payment details are already on file, CheckoutDialog will open instead to let the customer confirm their upgrade, downgrade or new purchase, then handle the payment.
You can alternatively use our PricingTable component, or roll your own flows using our API.
React
import { useCustomer, CheckoutDialog } from "autumn-js/react";

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

  return (
    <button
      onClick={async () => {
        await checkout({
          productId: "pro",
          dialog: CheckoutDialog,
        });
      }}
    >
      Upgrade to Pro
    </button>
  );
}
Make sure you’ve pasted in your Stripe test secret key sk_test_... in the Autumn dashboard.

4. Build Pricing Logic

There are 3 key functions you need to handle the billing logic once the customer has purchased the Pro plan:
  • check for feature access to see if the customer is allowed to send a message.
  • track a usage event in Autumn
  • customer to display usage data in the UI.

Server-side

The server-side check function makes an API call to Autumn’s DB to fetch the latest information.
You should always handle access checks server-side for security.
send-message.ts
import { Autumn as autumn } from "autumn-js";

// same user id from auth as in the autumn handler
const { data } = await autumn.check({
  customer_id: "user_123",
  feature_id: "messages",
});
if (!data?.allowed) {
  console.log("No more messages for you buddy!");
  return;
}

// ... your own function to send the message

// then record the usage in Autumn
await autumn.track({
  customer_id: "user_123",
  feature_id: "messages",
});

Client-side

The customer object is a single state that contains all the billing data for your user (subscription states, feature permissions, usage balances, etc). Use the client-side check function to gate features and show paywalls. This is determined by reading the local customer state, so no API call is made.
send-message.tsx
import { useCustomer } from "autumn-js/react";

export default function SendChatMessage() {
  const { customer, check, refetch } = useCustomer();

  return (
    <>
      <button
        onClick={async () => {
          const { data } = check({ featureId: "messages" });

          if (!data?.allowed) {
            alert("You're out of messages");
          } else {
            //send chatbot message server-side, then
            await refetch(); // refetch customer usage data

            //display remaining messages
            const messages = customer?.features.messages;
            console.log("Remaining messages: " + messages?.balance);
          }
        }}
      >
        Send AI Message
      </button>
    </>
  );
}
Congrats 🎉 Nice! You’ve now integrated Autumn into your application. You can make pricing changes through the dashboard/CLI and it’ll automatically update in your app. Next steps:
  • Add Stripe’s billing portal
  • Handle failed payments
  • Use our UI components to show a pricing table, and handle upgrades, downgrades and paywalls.