---
title: "Trial lifecycle"
url: "https://trialvalid.com/docs/trial-lifecycle.md"
canonical_url: "https://trialvalid.com/docs/trial-lifecycle"
last_updated: "2026-10-17"
type: "reference"
summary: "The states a trial moves through, the jobs that drive it, and how cancel and failure paths resolve."
related: ["https://trialvalid.com/docs/webhooks.md", "https://trialvalid.com/product/trial-holds.md"]
---

# Trial lifecycle

> **For AI agents:** the complete index of TrialValid pages is [llms.txt](https://trialvalid.com/llms.txt). Every page is also served as markdown at its URL plus .md.

The states a trial moves through, the jobs that drive it, and how cancel and failure paths resolve.

## States

| State | Means | Can move to |
| --- | --- | --- |
| `awaiting_card` | The hold is created and the buyer has not confirmed a card | `held`, `abandoned`, `canceled` |
| `held` | The card is confirmed, the trial is running, the hold is open | `converted`, `canceled`, `capture_failed`, `hold_lost` |
| `converted` | The hold was captured and the credit written | `refunded` |
| `canceled`, `abandoned`, `refunded`, `capture_failed`, `hold_lost` | Terminal | Nothing |

## Jobs

Four job kinds drive a trial: `capture`, `renew_hold`, `remind` and `abandon`. A runner claims due jobs with a 5 minute lease, runs each one and marks it done, rescheduled or failed. One job failing never stops the others. Every handler starts from Stripe's current state, so running a job twice is safe.

- `abandon` runs 24 hours after the hold is created and cancels the intent if the buyer never confirmed a card.
- `remind` sends the reminder: 7 days before the end for trials longer than 7 days, at the start otherwise.
- `capture` runs 2 hours before the trial ends, with up to 11 attempts 10 minutes apart.
- `renew_hold` places a fresh hold when the capture falls after the current hold would lapse (a standard online hold lasts about 7 days). A failed renewal is retried up to 3 times, 1 hour apart.

## Cancel paths

| When | What happens |
| --- | --- |
| Before the card is confirmed | The trial is canceled and the hold released |
| While `held` | The subscription is canceled and the hold released. If a capture landed in that instant, it is refunded and the credit reversed |
| After capture, before the trial ends | The subscription is canceled, the capture refunded and the credit reversed |
| After the trial ended | Refused with `conflict`. You refund the invoice in Stripe |

## Failure policies

- Every capture attempt failed: the trial becomes `capture_failed`, the hold is released and you are alerted once. Your `on_capture_failure` setting decides the subscription: `leave_to_stripe` (the default) leaves it, and Stripe keeps retrying the first invoice under your own billing settings, which may recover the payment; `cancel_subscription` cancels it.
- Capture window missed: recorded as `capture_window_missed`. The subscription is not canceled and the buyer is charged once by the invoice.
- Hold lapsed or unrenewable: the trial becomes `hold_lost`, you are alerted once, and the same `on_capture_failure` setting applies as for `capture_failed`.
- You disconnect Stripe: held trials become `hold_lost` and unconfirmed ones end. Nothing is canceled, because TrialValid can no longer reach your account; the subscriptions keep billing in Stripe.
- The setting is read when a trial fails, not when it starts, and a change never alters a trial that already failed. Each failed trial records what was done as `failure_policy_applied`.

## Related

- [Webhooks](https://trialvalid.com/docs/webhooks.md)
- [Card holds for free trials](https://trialvalid.com/product/trial-holds.md)
