Skip to main content
Free trials give customers temporary access to a paid plan before they’re charged. Autumn supports two trial modes: card required (collect payment info upfront, bill when trial ends) and card not required (no payment info needed, access expires automatically).
Example
A SaaS tool offers a 14-day free trial of their Pro plan. If the customer doesn’t cancel, billing begins on day 15.

Setting up

Add a freeTrial object to your plan:
autumn.config.ts
import { feature, item, plan } from 'atmn';

export const messages = feature({
  id: 'messages',
  name: 'Messages',
  type: 'metered',
  consumable: true,
});

export const pro = plan({
  id: 'pro',
  name: 'Pro',
  group: 'main',
  price: { amount: 20, interval: 'month' },
  freeTrial: {
    durationLength: 14,
    durationType: 'day',
    cardRequired: true,
  },
  items: [
    item({
      featureId: messages.id,
      included: 1000,
      reset: { interval: 'month' },
    }),
  ],
});
Trial duration types: day, month, year.Push changes with atmn push.

Card required trials

When cardRequired is true, the customer must provide payment information to start the trial. Stripe creates a subscription with a trial period — no charge occurs until the trial ends.
import { Autumn } from "autumn-js";

const autumn = new Autumn({ secretKey: "am_sk_..." });

const { data } = await autumn.checkout({
  customer_id: "user_123",
  plan_id: "pro",
});

// Returns Stripe Checkout URL — customer adds card and starts trial
If the customer doesn’t cancel before the trial ends, their card is automatically charged.

Card not required trials

When cardRequired is false, no checkout is needed. You can attach the plan directly:
const { data } = await autumn.attach({
  customer_id: "user_123",
  plan_id: "pro",
});
When the trial expires, the customer loses access unless they add a payment method. If a free plan with autoEnable exists in the same group, it’s activated as a fallback.
You can combine autoEnable with cardRequired: false to create an auto-trial plan. The trial starts automatically when a customer is created, and expires after the trial period — no API call needed.

Checking trial status

The customer’s subscription includes a trial_ends_at timestamp when a trial is active. You can also expand trials_used to see which trials a customer has consumed:
const { data } = await autumn.customers.get("user_123");

for (const sub of data.subscriptions) {
  if (sub.trialEndsAt) {
    console.log(`Trialing until ${new Date(sub.trialEndsAt)}`);
  }
}

Trial deduplication

Each customer can only use a plan’s trial once. If they try to attach the same plan again, the trial is skipped and they’re billed immediately. To prevent trial abuse across multiple accounts, set a fingerprint when creating a customer (e.g., device ID, browser fingerprint). Autumn checks whether any customer with the same fingerprint has already used the trial.
await autumn.customers.create({
  id: "user_456",
  name: "Jane Doe",
  email: "jane@example.com",
  fingerprint: "device_abc123",
});
Custom trials passed via customize.freeTrial always bypass deduplication. Use this for support cases where you want to grant a second trial.
You can check which trials a customer has already used by expanding trials_used on the customer object:
const customer = await autumn.customers.getOrCreate({
  customerId: "user_123",
  expand: ["trials_used"],
});

Upgrades and Downgrades

When upgrading to a plan with a trial, the trial behavior depends on the customer’s current state and whether the new plan has an unused trial:
Current stateUnused trial?Result
TrialingYesCurrent trial ends. Fresh trial starts on new plan.
TrialingNoCurrent trial ends. Billing starts immediately.
Active (not trialing)YesTrial starts. Current cycle refunded.
Active (not trialing)NoNo trial. Billing starts at new price.
When a customer downgrades during a trial, the lower plan is scheduled to activate when the trial ends. The lower plan’s own trial is not applied - you cannot get a new trial on a downgrade.
You can override any of these behaviors by passing customize.freeTrial on the attach call. See Overriding trial behavior below.

Overriding trial behavior

You can override the default trial behavior on any /attach or /update-subscription call by passing customize.freeTrial:
Pass a freeTrial object to start a trial with a custom duration. This bypasses deduplication — the customer always gets the trial, even if they’ve trialed this plan before.
await autumn.attach({
  customerId: "user_123",
  planId: "pro",
  customize: {
    freeTrial: {
      durationLength: 30,
      durationType: "day",
      cardRequired: true,
    },
  },
});

Trials with shared subscriptions

When using entities or add-ons, trial state is shared across the same Stripe subscription. This is because Stripe manages trials at the subscription level.
You can pass in newBillingSubscription: true to create a new subscription for each plan, rather than merging into the existing subscription.
Here are some principles to keep in mind when using trials with shared subscriptions:

First entity gets the trial

When the first entity is attached with a trial plan, the trial starts on the shared subscription. Any subsequent entities attached to the same subscription inherit the existing trial state — they don’t start their own independent trial.

Adding plans to a non-trialing subscription

If the subscription is not trialing, new plans are charged immediately — even if the product they’re being attached to has a trial configured. The product’s trial config is ignored for merges into an active subscription.

Shared trial state affects all plans

Because entities (by default) share a subscription, trial state changes affect all entities:
  • Entity upgrade to a plan with a trial: a fresh trial starts, and all other entities on the subscription inherit the new trial end date.
  • Entity upgrade to a plan without a trial: the trial ends for all entities, and they’re all billed immediately.
  • Entity downgrade during trial: the downgrade is scheduled for when the trial ends.
Passing customize.freeTrial on an entity attach or upgrade affects the shared subscription, so all entities are affected. Similarly, passing freeTrial: null ends the trial for all entities on the subscription.

Resetting usage after trial

This feature is coming soon.
By default, feature usage during a trial carries over into the paid period. If you want usage to reset when billing starts, pass transition_rules.reset_after_trial_end with the feature IDs to reset:
await autumn.attach({
  customerId: "user_123",
  planId: "pro",
  transitionRules: {
    resetAfterTrialEnd: ["messages"],
  },
});
This sets the feature’s reset cycle to begin when the trial ends rather than when the trial starts, so the customer gets a full fresh allowance once they start paying.