Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.flexprice.io/llms.txt

Use this file to discover all available pages before exploring further.

Overview

A trial is a window where customers are on the paid plan schedule but billing has not begun yet in the recurring sense described below. Where trial_period_days comes from
  • Pass trial_period_days on POST /v1/subscriptions to override the plan for that subscription only.
  • Or omit it. Every recurring fixed price on the linked plan contributes the same trial_period_days (otherwise create fails validation).
Two invoices developers care about
  • Trial start (SUBSCRIPTION_TRIAL_START): $0, finalized at subscribe time with the same line-item shape as real advance billing for trial_starttrial_end, but all amounts forced to 0 (preview + integrations). collection_method controls SUCCEEDED vs PENDING here; details in Trial-start invoice.
  • Trial end (SUBSCRIPTION_TRIAL_END): first payable billing window trial_endtrial_end + billing_period. payment_behavior drives failure handling here; details in What happens at trial end.
Typical reasons to use trials
  • Product-led growth and self-serve upgrade paths
  • Freemium-style evaluation before committing
  • Time-boxed enterprise or partner pilots

How It Works

Trial subscriptions move through three statuses:
trialing -> (trial_end reached) -> incomplete -> (invoice paid / zero amount) -> active
StatusWhat it meansBilling
trialingTrial window is activeSUBSCRIPTION_TRIAL_START: $0 finalized invoice for trial_starttrial_end (preview line items); subscription stays trialing
incompleteTrial ended; first paid invoice issuedAwaiting SUBSCRIPTION_TRIAL_END payment (unless amount is $0)
activeTrial converted or non-trial startNormal billing cycle
Period fields while trialing
  • current_period_start = trial_start
  • current_period_end = trial_end
After trial_end, the billing anchor moves to trial_end. The first paid period is full-length: [trial_end, trial_end + billing_period]. There is no shortened first billing period after a trial. Automation: trial-end processing is handled by Flexprice workflows. No manual cron or API is required for the trial_end transition itself.

Trial-start invoice

Billing reason (API string): SUBSCRIPTION_TRIAL_START. Applies only when the subscription is created as non-draft trialing (trial window > 0 days). What you get on create:
  • Invoice period: period_start = trial_start (same as start_date), period_end = trial_end, matching current_period_* while subscription_status is trialing.
  • Amounts: subtotal, total, amount_due, and each line item amount are 0 after compute. Quantity, price linkage, and display metadata stay, so customers see what will charge later.
  • Subscription status: Remains trialing. SUBSCRIPTION_TRIAL_START does not gate activation. Moving to active still depends on SUBSCRIPTION_TRIAL_END (or a non-trial create path).

collection_method and payment_behavior

Use these fields deliberately: they clarify how trial start differs from trial end.
TopicRole
payment_behaviorApplies when generating / paying SUBSCRIPTION_TRIAL_END and broader subscription invoice payment flows (default_active, allow_incomplete, …). See What happens at trial end.
collection_methodAfter finalize, $0 trial-start invoices are SUCCEEDED under charge_automatically or left PENDING under send_invoice so outbound invoice sync can capture a card without active.
collection_methodTrial-start invoice payment_status (typical)
charge_automaticallySUCCEEDED at $0
send_invoicePENDING until host / PSP completes the synced $0 flow
On POST /v1/subscriptions, latest_invoice is this document when subscription_status is trialing.

Configuring a Trial Period

  1. Define trial_period_days on the plan’s recurring FIXED prices or
  2. Override on POST /v1/subscriptions.
There is no trial_enabled flag. If trial_period_days > 0 after resolve (inherit vs override), the subscription enters trialing; 0 at subscription level disables trial even if the plan carries trial days.

Step 1: Set trial_period_days on the price

Constraints:
  • Only billing_cadence: RECURRING + price_type: FIXED combinations accept trial_period_days. Usage-only or unsupported tier geometries fail validation upstream.
  • Omitted ⇒ 0. That price contributes no inherited trial.
curl -X POST https://api.flexprice.io/v1/prices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4900,
    "currency": "USD",
    "billing_cadence": "RECURRING",
    "billing_period": "MONTHLY",
    "billing_period_count": 1,
    "price_type": "FIXED",
    "trial_period_days": 14
  }'
Response (abbreviated):
{
  "id": "price_01abc123",
  "billing_cadence": "RECURRING",
  "price_type": "FIXED",
  "trial_period_days": 14
}

Step 2: Create a subscription (trial inherited automatically)

When all recurring fixed prices on a plan share the same trial_period_days, that value is inherited by the subscription automatically. No trial fields are required in the subscription request.
curl -X POST https://api.flexprice.io/v1/subscriptions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cust_01xyz",
    "plan_id": "plan_01abc",
    "currency": "USD",
    "billing_cadence": "RECURRING",
    "billing_period": "MONTHLY",
    "billing_period_count": 1,
    "start_date": "2025-05-01T00:00:00Z"
  }'
Response (abbreviated; real payloads also include plan, customer, line items with full pricing metadata, and other subscription fields):
{
  "id": "sub_01def456",
  "subscription_status": "trialing",
  "customer_id": "cust_01xyz",
  "plan_id": "plan_01abc",
  "trial_start": "2025-05-01T00:00:00Z",
  "trial_end": "2025-05-15T00:00:00Z",
  "current_period_start": "2025-05-01T00:00:00Z",
  "current_period_end": "2025-05-15T00:00:00Z",
  "collection_method": "charge_automatically",
  "latest_invoice": {
    "id": "inv_trial_start_abc",
    "billing_reason": "SUBSCRIPTION_TRIAL_START",
    "invoice_type": "SUBSCRIPTION",
    "invoice_status": "FINALIZED",
    "payment_status": "SUCCEEDED",
    "currency": "USD",
    "period_start": "2025-05-01T00:00:00Z",
    "period_end": "2025-05-15T00:00:00Z",
    "subtotal": "0",
    "total": "0",
    "amount_due": "0",
    "amount_remaining": "0",
    "line_items": [
      {
        "display_name": "Pro · monthly (trial preview)",
        "amount": "0",
        "quantity": "1"
      }
    ]
  }
}
Notes on the abbreviated response:
  • subscription_status: "trialing" is the canonical lifecycle enum; the payload may also expose a separate status field (SubscriptionResponse).
  • latest_invoice.billing_reason is SUBSCRIPTION_TRIAL_START on this $0 opening invoice. It stays visible / finalized, not SKIPPED, unlike typical zero-amount SUBSCRIPTION_CREATE invoices.
  • If you use send_invoice instead of charge_automatically, payment_status on that same invoice will usually be PENDING until PSP / outbound sync settles the $0 path.

Overriding trial days at subscription creation

Pass trial_period_days in the subscription request to override the plan-level value.
curl -X POST https://api.flexprice.io/v1/subscriptions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cust_01xyz",
    "plan_id": "plan_01abc",
    "currency": "USD",
    "billing_cadence": "RECURRING",
    "billing_period": "MONTHLY",
    "billing_period_count": 1,
    "start_date": "2025-05-01T00:00:00Z",
    "trial_period_days": 0
  }'
ValueEffect
OmittedInherits trial_period_days from plan prices
0Disables trial; subscription starts as active immediately
> 0Overrides plan-level value with this number of days
To shorten or lengthen the plan-level trial, pass a positive integer:
curl -X POST https://api.flexprice.io/v1/subscriptions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cust_01xyz",
    "plan_id": "plan_01abc",
    "currency": "USD",
    "billing_cadence": "RECURRING",
    "billing_period": "MONTHLY",
    "billing_period_count": 1,
    "start_date": "2025-05-01T00:00:00Z",
    "trial_period_days": 30
  }'

What Happens at Trial End

Trial end is detected automatically. When trial_end <= now:
  1. Subscription status changes to incomplete.
  2. Billing anchor resets to trial_end.
  3. A SUBSCRIPTION_TRIAL_END invoice is created covering [trial_end, trial_end + billing_period].
  4. If the invoice amount is zero: subscription immediately converts to active. No payment event needed.
  5. If the invoice amount is non-zero: subscription stays incomplete until the invoice is paid.
An incomplete subscription does not auto-expire. It remains incomplete until the trial-end invoice is paid or the subscription is cancelled.
payment_behavior at trial end:
ValueBehavior if payment fails
default_active (default)Subscription activates regardless of payment result
allow_incompleteSubscription stays incomplete if payment fails
error_if_incompleteSubscription stays incomplete if payment fails; wallet fallback is not attempted
default_incompleteSubscription stays incomplete; only valid with send_invoice collection method
Credit grants held pending activation are applied automatically when the subscription becomes active.

API Reference

Price fields

FieldTypeRequiredDescription
trial_period_daysintegerNoTrial length inherited by subscriptions linking this recurring fixed price. Default 0 (no trial). Only valid for billing_cadence: RECURRING + price_type: FIXED. Must be >= 0.

Subscription create fields

FieldTypeRequiredDescription
trial_period_daysintegerNoOverride plan-level trial length in days. 0 disables trial. Omit to inherit from plan prices (all recurring fixed plan prices must share the same value). Must be >= 0.
payment_behaviorenumNoSUBSCRIPTION_TRIAL_END and broader subscription payment semantics when attempts fail (default_active, allow_incomplete, error_if_incomplete, default_incomplete). API normalizes unspecified creates toward default_active (not all combinations are valid with every collection_method).
collection_methodenumNocharge_automatically vs send_invoice. For SUBSCRIPTION_TRIAL_START, controls whether the $0 invoice is succeeded immediately or left pending for outbound invoicing/card capture.
FieldTypeDescription
subscription_statusstringtrialing while the trial window is open. Prefer this for branching; status may also exist on SubscriptionResponse.
trial_starttimestampTrial start (= start_date on create).
trial_endtimestampWhen paid billing anchors; first SUBSCRIPTION_TRIAL_END period starts here.
current_period_starttimestampDuring trial = trial_start.
current_period_endtimestampDuring trial = trial_end.
latest_invoiceobjectOn non-draft trialing creates, populated with SUBSCRIPTION_TRIAL_START.
latest_invoice.billing_reasonstringSUBSCRIPTION_TRIAL_START, the $0 trial preview invoice.

Webhooks & Events

Trial → paid conversion:
  • subscription.activated when subscription_status flips trialingactive.
  • Raised after SUBSCRIPTION_TRIAL_END settles (paid) or auto-completes at amount_due = 0 (no PSP event required).
  • SUBSCRIPTION_TRIAL_START emits no subscription.activated. subscription_status remains trialing until SUBSCRIPTION_TRIAL_END finishes, even if the opening invoice shows payment_status: SUCCEEDED.
There is no dedicated subscription.trialing, subscription.trial_ended, or subscription.trial_ending webhook. Use subscription.activated (and your own trial clock if needed) to detect conversion.
Example payload shape (truncated; your handler may receive more fields):
{
  "subscription": {
    "id": "sub_01def456",
    "status": "active",
    "trial_start": "2025-05-01T00:00:00Z",
    "trial_end": "2025-05-15T00:00:00Z",
    "current_period_start": "2025-05-15T00:00:00Z",
    "current_period_end": "2025-06-15T00:00:00Z"
  }
}
For information on configuring webhook endpoints, see Webhooks.

Edge Cases

ScenarioBehavior
trial_period_days = 0 at subscription levelTrial disabled even if plan price has trial_period_days > 0
Plan prices have mismatched trial_period_daysSubscription creation fails: "all recurring fixed plan prices must have the same trial_period_days"
Cancellation during trialStandard cancellation applies. Trial-end processing skips subscriptions not in trialing status.
Paused subscription when trial_end is reachedTrial-end processing is skipped. Runs when the subscription is unpaused.
Zero-amount trial-end invoiceSubscription auto-activates immediately without waiting for a payment event.
Trial-start $0 invoiceAlways created for trialing creates; SUBSCRIPTION_TRIAL_START stays visible (not SKIPPED) unlike other $0 subscription invoices
Inherited (child) subscriptionsTrial status cascades from parent. Children are not processed independently.
Re-trialingNot supported. Each subscription has one trial window. To extend a trial, cancel and recreate the subscription.

Best Practices

  • Payment ahead of trial end. You can onboard trialing subscriptions without storing a card, but SUBSCRIPTION_TRIAL_END still expects a collectible path whenever amount_due > 0.
  • send_invoice + outbound PSP sync. Keeps SUBSCRIPTION_TRIAL_START in PENDING until the hosted $0 handshake finishes. Prefer this pattern when the gateway captures cards solely behind invoice artifacts.
  • Pin payment_behavior in production. SUBSCRIPTION_TRIAL_END obeys it when autopay retries fail (allow_incomplete vs default_active, etc.).
  • Split entitlements cleanly. Fire subscription.activated once SUBSCRIPTION_TRIAL_END succeeded (or $0 short-circuit). SUBSCRIPTION_TRIAL_START leaves subscription_status at trialing even when payment_status reads SUCCEEDED.
  • Cheap staging loops. Truly $0 plan prices accelerate trialing → active because SUBSCRIPTION_TRIAL_END can finalize with nothing to capture.
  • Single trial episode. No built-in replay; cancel + recreate is the sanctioned reset.
Configure webhook destinations in Webhooks (events: Webhooks & Events above).