Tracking free trials

This tutorial describes how to track free trials in ChartMogul using the API.

Tracking trials lets you measure how many trials start, understand which plans customers are trialing, and analyze how effectively trials convert into free or paid subscriptions.

The recommended way to track a free trial is to create a trial line item associated with a plan.

To track a free trial:

  1. Create or identify the plan being trialed
  2. Create an invoice containing a trial line item
  3. When the customer converts, import their paid subscription

Gradual rollout. We introduced the trial line item type on October 2, 2025, and are making it gradually available to all accounts. Learn more.

Creating a plan

Every trial must be associated with a plan.

If the plan does not already exist in ChartMogul, create it using the Create a Plan endpoint:

curl -X POST "https://api.chartmogul.com/v1/plans" \
     -u YOUR_API_KEY: \
     -H "Content-Type: application/json" \
     -d '{
           "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430",
           "name": "Pro",
           "interval_count": 1,
           "interval_unit": "month",
           "external_id": "plan_pro"
         }'
ChartMogul::Import::Plan.create!(
  data_source_uuid: "ds_fef05d54-47b4-431b-aed2-eb6b9e545430",
  name: "Pro",
  interval_count: 1,
  interval_unit: "month",
  external_id: "plan_pro",
)
ChartMogul.Plan.create(config, {
  data_source_uuid: "ds_fef05d54-47b4-431b-aed2-eb6b9e545430",
  name: "Pro",
  interval_count: 1,
  interval_unit: "month",
  external_id: "plan_pro",
});
ChartMogul\Import\Plan::create([
  "data_source_uuid" => "ds_fef05d54-47b4-431b-aed2-eb6b9e545430",
  "name" => "Pro",
  "interval_count" => 1,
  "interval_unit" => "month",
  "external_id" => "plan_pro"
]);
api.CreatePlan(&cm.Plan{
  DataSourceUUID: "ds_fef05d54-47b4-431b-aed2-eb6b9e545430",
  Name:           "Pro",
  IntervalCount:  1,
  IntervalUnit:   "month",
  ExternalID:     "plan_pro",
})
chartmogul.Plan.create(
  config,
  data={
    "data_source_uuid": "ds_fef05d54-47b4-431b-aed2-eb6b9e545430",
    "name": "Pro",
    "interval_count": 1,
    "interval_unit": "month",
    "external_id": "plan_pro",
  },
)

Use the plan_uuid from the response when creating the trial. If the plan already exists, use its existing plan_uuid.

Creating a free trial

Use the Import Invoices endpoint to create an invoice containing a trial line item:

If the customer belongs to a Stripe, Chargebee, Recurly, Braintree, Google Play, App Store Connect or SaaSync source, set the handle_as_user_edit query parameter to true. Without it, the invoice may be deleted during a reprocess or reimport. This is not required for custom sources. Learn more.

For example, the following creates a 14-day free trial of the Pro plan:

curl -X POST "https://api.chartmogul.com/v1/import/customers/cus_f466e33d-ff2b-4a11-8f85-417eb02157a7/invoices" \
     -u YOUR_API_KEY: \
     -H "Content-Type: application/json" \
     -d '{
            "invoices": [
              {
                "external_id": "inv_trial_001",
                "date": "2026-01-10",
                "currency": "USD",
                "line_items": [
                  {
                    "type": "trial",
                    "external_id": "trial_001",
                    "subscription_external_id": "sub_pro_001",
                    "plan_uuid": "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
                    "service_period_start": "2026-01-10",
                    "service_period_end": "2026-01-24",
                    "amount_in_cents": 0,
                    "quantity": 1
                  }
                ]
              }
            ]
        }'
# The Ruby library has no dedicated trial line item class yet,
# so override the type on a subscription line item.
line_item = ChartMogul::LineItems::Subscription.new(
  external_id: "trial_001",
  subscription_external_id: "sub_pro_001",
  plan_uuid: "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  service_period_start: Time.utc(2026, 1, 10),
  service_period_end: Time.utc(2026, 1, 24),
  amount_in_cents: 0,
  quantity: 1,
)
line_item.type = "trial"

invoice = ChartMogul::Invoice.new(
  external_id: "inv_trial_001",
  date: Time.utc(2026, 1, 10),
  currency: "USD",
  line_items: [line_item],
)

ChartMogul::CustomerInvoices.create!(
  customer_uuid: "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  invoices: [invoice],
)
ChartMogul.Invoice.create(config, "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", {
  invoices: [
    {
      external_id: "inv_trial_001",
      date: "2026-01-10",
      currency: "USD",
      line_items: [
        {
          type: "trial",
          external_id: "trial_001",
          subscription_external_id: "sub_pro_001",
          plan_uuid: "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
          service_period_start: "2026-01-10",
          service_period_end: "2026-01-24",
          amount_in_cents: 0,
          quantity: 1,
        },
      ],
    },
  ],
});
// The PHP library has no dedicated trial line item class yet,
// so override the type on a subscription line item.
$line_item = new ChartMogul\LineItems\Subscription([
  "type" => "trial",
  "external_id" => "trial_001",
  "subscription_external_id" => "sub_pro_001",
  "plan_uuid" => "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  "service_period_start" => "2026-01-10",
  "service_period_end" => "2026-01-24",
  "amount_in_cents" => 0,
  "quantity" => 1
]);

$invoice = new ChartMogul\Invoice([
  "external_id" => "inv_trial_001",
  "date" => "2026-01-10",
  "currency" => "USD",
  "line_items" => [$line_item]
]);

ChartMogul\CustomerInvoices::create([
  "customer_uuid" => "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  "invoices" => [$invoice]
]);
lineItem := &cm.LineItem{
  Type:                   "trial",
  ExternalID:             "trial_001",
  SubscriptionExternalID: "sub_pro_001",
  PlanUUID:               "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  ServicePeriodStart:     "2026-01-10",
  ServicePeriodEnd:       "2026-01-24",
  AmountInCents:          0,
  Quantity:               1,
}

invoice := &cm.Invoice{
  ExternalID: "inv_trial_001",
  Date:       "2026-01-10",
  Currency:   "USD",
  LineItems:  []*cm.LineItem{lineItem},
}

api.CreateInvoices(
  []*cm.Invoice{invoice},
  "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
)
chartmogul.Invoice.create(
  config,
  uuid="cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  data={
    "invoices": [
      {
        "external_id": "inv_trial_001",
        "date": datetime(2026, 1, 10),
        "currency": "USD",
        "line_items": [
          {
            "type": "trial",
            "external_id": "trial_001",
            "subscription_external_id": "sub_pro_001",
            "plan_uuid": "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
            "service_period_start": datetime(2026, 1, 10),
            "service_period_end": datetime(2026, 1, 24),
            "amount_in_cents": 0,
            "quantity": 1,
          }
        ],
      }
    ]
  },
)

ChartMogul now records the customer as having started a free trial of the Pro plan on January 10.

A customer can have multiple trials. Each trial can be associated with a different plan and have its own start and end dates.

Paid trials. You can also use trial line items to track paid trials. Set amount_in_cents to a value greater than zero.

Converting a trial to a paid subscription

When the customer converts, use the Import Invoices endpoint to import their first paid invoice in the same way you normally import subscription data:

curl -X POST "https://api.chartmogul.com/v1/import/customers/cus_f466e33d-ff2b-4a11-8f85-417eb02157a7/invoices" \
     -u YOUR_API_KEY: \
     -H "Content-Type: application/json" \
     -d '{
            "invoices": [
              {
                "external_id": "inv_paid_001",
                "date": "2026-01-24",
                "currency": "USD",
                "line_items": [
                  {
                    "type": "subscription",
                    "external_id": "line_paid_001",
                    "subscription_external_id": "sub_pro_001",
                    "plan_uuid": "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
                    "service_period_start": "2026-01-24",
                    "service_period_end": "2026-02-24",
                    "amount_in_cents": 5000,
                    "quantity": 1
                  }
                ]
              }
            ]
        }'
line_item = ChartMogul::LineItems::Subscription.new(
  external_id: "line_paid_001",
  subscription_external_id: "sub_pro_001",
  plan_uuid: "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  service_period_start: Time.utc(2026, 1, 24),
  service_period_end: Time.utc(2026, 2, 24),
  amount_in_cents: 5000,
  quantity: 1,
)

invoice = ChartMogul::Invoice.new(
  external_id: "inv_paid_001",
  date: Time.utc(2026, 1, 24),
  currency: "USD",
  line_items: [line_item],
)

ChartMogul::CustomerInvoices.create!(
  customer_uuid: "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  invoices: [invoice],
)
ChartMogul.Invoice.create(config, "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", {
  invoices: [
    {
      external_id: "inv_paid_001",
      date: "2026-01-24",
      currency: "USD",
      line_items: [
        {
          type: "subscription",
          external_id: "line_paid_001",
          subscription_external_id: "sub_pro_001",
          plan_uuid: "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
          service_period_start: "2026-01-24",
          service_period_end: "2026-02-24",
          amount_in_cents: 5000,
          quantity: 1,
        },
      ],
    },
  ],
});
$line_item = new ChartMogul\LineItems\Subscription([
  "external_id" => "line_paid_001",
  "subscription_external_id" => "sub_pro_001",
  "plan_uuid" => "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  "service_period_start" => "2026-01-24",
  "service_period_end" => "2026-02-24",
  "amount_in_cents" => 5000,
  "quantity" => 1
]);

$invoice = new ChartMogul\Invoice([
  "external_id" => "inv_paid_001",
  "date" => "2026-01-24",
  "currency" => "USD",
  "line_items" => [$line_item]
]);

ChartMogul\CustomerInvoices::create([
  "customer_uuid" => "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  "invoices" => [$invoice]
]);
lineItem := &cm.LineItem{
  Type:                   "subscription",
  ExternalID:             "line_paid_001",
  SubscriptionExternalID: "sub_pro_001",
  PlanUUID:               "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  ServicePeriodStart:     "2026-01-24",
  ServicePeriodEnd:       "2026-02-24",
  AmountInCents:          5000,
  Quantity:               1,
}

invoice := &cm.Invoice{
  ExternalID: "inv_paid_001",
  Date:       "2026-01-24",
  Currency:   "USD",
  LineItems:  []*cm.LineItem{lineItem},
}

api.CreateInvoices(
  []*cm.Invoice{invoice},
  "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
)
chartmogul.Invoice.create(
  config,
  uuid="cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  data={
    "invoices": [
      {
        "external_id": "inv_paid_001",
        "date": datetime(2026, 1, 24),
        "currency": "USD",
        "line_items": [
          {
            "type": "subscription",
            "external_id": "line_paid_001",
            "subscription_external_id": "sub_pro_001",
            "plan_uuid": "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
            "service_period_start": datetime(2026, 1, 24),
            "service_period_end": datetime(2026, 2, 24),
            "amount_in_cents": 5000,
            "quantity": 1,
          }
        ],
      }
    ]
  },
)

ChartMogul treats the trial and paid subscription as a continuous subscription history and includes the conversion in trial conversion reporting.

When the paid subscription comes from Stripe or another billing system

In many cases, you may track the free trial using the API while the customer's paid subscription is later imported automatically from a billing system such as Stripe or Recurly.

Because the trial and paid subscription come from different sources, ChartMogul initially creates a separate customer record for the paid subscription.

Set up an automation to merge the new customer record into the customer's existing trial record:

Converting a trial to a free subscription

A trial can also convert to a free subscription. In this case, use the Import Invoices endpoint to create a subscription line item with amount_in_cents set to 0:

curl -X POST "https://api.chartmogul.com/v1/import/customers/cus_f466e33d-ff2b-4a11-8f85-417eb02157a7/invoices" \
     -u YOUR_API_KEY: \
     -H "Content-Type: application/json" \
     -d '{
            "invoices": [
              {
                "external_id": "inv_free_001",
                "date": "2026-01-24",
                "currency": "USD",
                "line_items": [
                  {
                    "type": "subscription",
                    "external_id": "line_free_001",
                    "subscription_external_id": "sub_pro_001",
                    "plan_uuid": "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
                    "service_period_start": "2026-01-24",
                    "amount_in_cents": 0,
                    "quantity": 1
                  }
                ]
              }
            ]
        }'
line_item = ChartMogul::LineItems::Subscription.new(
  external_id: "line_free_001",
  subscription_external_id: "sub_pro_001",
  plan_uuid: "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  service_period_start: Time.utc(2026, 1, 24),
  amount_in_cents: 0,
  quantity: 1,
)

invoice = ChartMogul::Invoice.new(
  external_id: "inv_free_001",
  date: Time.utc(2026, 1, 24),
  currency: "USD",
  line_items: [line_item],
)

ChartMogul::CustomerInvoices.create!(
  customer_uuid: "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  invoices: [invoice],
)
ChartMogul.Invoice.create(config, "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7", {
  invoices: [
    {
      external_id: "inv_free_001",
      date: "2026-01-24",
      currency: "USD",
      line_items: [
        {
          type: "subscription",
          external_id: "line_free_001",
          subscription_external_id: "sub_pro_001",
          plan_uuid: "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
          service_period_start: "2026-01-24",
          amount_in_cents: 0,
          quantity: 1,
        },
      ],
    },
  ],
});
$line_item = new ChartMogul\LineItems\Subscription([
  "external_id" => "line_free_001",
  "subscription_external_id" => "sub_pro_001",
  "plan_uuid" => "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  "service_period_start" => "2026-01-24",
  "amount_in_cents" => 0,
  "quantity" => 1
]);

$invoice = new ChartMogul\Invoice([
  "external_id" => "inv_free_001",
  "date" => "2026-01-24",
  "currency" => "USD",
  "line_items" => [$line_item]
]);

ChartMogul\CustomerInvoices::create([
  "customer_uuid" => "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  "invoices" => [$invoice]
]);
lineItem := &cm.LineItem{
  Type:                   "subscription",
  ExternalID:             "line_free_001",
  SubscriptionExternalID: "sub_pro_001",
  PlanUUID:               "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
  ServicePeriodStart:     "2026-01-24",
  AmountInCents:          0,
  Quantity:               1,
}

invoice := &cm.Invoice{
  ExternalID: "inv_free_001",
  Date:       "2026-01-24",
  Currency:   "USD",
  LineItems:  []*cm.LineItem{lineItem},
}

api.CreateInvoices(
  []*cm.Invoice{invoice},
  "cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
)
chartmogul.Invoice.create(
  config,
  uuid="cus_f466e33d-ff2b-4a11-8f85-417eb02157a7",
  data={
    "invoices": [
      {
        "external_id": "inv_free_001",
        "date": datetime(2026, 1, 24),
        "currency": "USD",
        "line_items": [
          {
            "type": "subscription",
            "external_id": "line_free_001",
            "subscription_external_id": "sub_pro_001",
            "plan_uuid": "pl_3eb4efb2-d101-4dce-a664-be271b0da4de",
            "service_period_start": datetime(2026, 1, 24),
            "amount_in_cents": 0,
            "quantity": 1,
          }
        ],
      }
    ]
  },
)

See Tracking free subscriptions for more information about importing and managing free subscriptions.

Trial reporting in ChartMogul

Once imported, trial data can be used throughout ChartMogul. You can use it to:

Because each trial is associated with a subscription and plan, ChartMogul can distinguish between different trials for the same customer and provide plan-level trial reporting.

Tracking trials using free_trial_started_at

ChartMogul also supports a simpler customer-level method for recording the start of a free trial. Set free_trial_started_at when creating or updating a customer:

curl -X PATCH "https://api.chartmogul.com/v1/customers/cus_ab223d54-75b4-431b-adb2-eb6b9e234571" \
     -u YOUR_API_KEY: \
     -H "Content-Type: application/json" \
     -d '{
           "free_trial_started_at": "2026-01-10T00:00:00Z"
         }'
customer = ChartMogul::Customer.retrieve("cus_ab223d54-75b4-431b-adb2-eb6b9e234571")

customer.free_trial_started_at = Time.utc(2026, 1, 10)

customer.update!
ChartMogul.Customer.modify(config, "cus_ab223d54-75b4-431b-adb2-eb6b9e234571", {
  free_trial_started_at: "2026-01-10T00:00:00Z",
});
ChartMogul\Customer::update(
  ["customer_uuid" => "cus_ab223d54-75b4-431b-adb2-eb6b9e234571"],
  ["free_trial_started_at" => "2026-01-10T00:00:00Z"]
);
api.UpdateCustomer(&cm.Customer{
  FreeTrialStartedAt: "2026-01-10T00:00:00Z",
}, "cus_ab223d54-75b4-431b-adb2-eb6b9e234571")
chartmogul.Customer.modify(
  config,
  uuid="cus_ab223d54-75b4-431b-adb2-eb6b9e234571",
  data={"free_trial_started_at": "2026-01-10T00:00:00Z"},
)

This records a single trial start date for the customer. In the same way, set lead_created_at to record when the customer became a lead.

For new implementations, we recommend using trial line items instead. Trial line items allow ChartMogul to track:

Use free_trial_started_at when you only need to record that a customer started a trial and don't need subscription-level trial data.