> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ionicfi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Test subscription lifecycles with clocks

> Advance test time to verify trials, renewals, retries, cancellations, and your platform's response.

A test clock advances logical time for a test customer and its billing objects.
It lets you exercise months of subscription behavior in minutes without
changing production time or waiting for a real billing date.

## What a test clock proves

Ionic remains authoritative for subscription state, invoices, collection
schedules, and public events. You do not need to re-test Ionic's billing
calculations.

Your platform still owns what happens after Ionic reports a lifecycle change.
For example, an EHR platform may need to:

* keep the correct clinic active while collection retries are pending;
* restore access after a later collection succeeds;
* restrict only the affected account when retries are exhausted;
* notify the appropriate administrators or billing contacts;
* update its own reporting and account state exactly once; and
* handle retried or out-of-order events without duplicating changes.

A renewal can be completely correct in Ionic while the end-user experience is
incorrect because an integration changed the wrong account or reacted to the
wrong event. Test clocks let you verify that integration behavior through the
same Ionic objects and events used in production.

## How it works

<Steps>
  <Step title="Create a clock">
    Create a clock with a test-mode secret key and choose its initial Unix
    timestamp. The clock starts in `ready`.
  </Step>

  <Step title="Attach a test customer">
    Pass the clock ID as `test_clock` when creating a test customer. The
    relationship is permanent, and subscriptions plus billing records created
    for that customer inherit the same logical time.
  </Step>

  <Step title="Create the subscription scenario">
    Create prices, an active payment method, and a subscription through the
    normal test APIs. Arm a one-shot collection outcome when you need the next
    automatic attempt to succeed or decline deterministically.
  </Step>

  <Step title="Advance and poll">
    Request a later target. Advancement is asynchronous because Ionic processes
    every intervening lifecycle action in chronological order. Poll the clock
    until it returns to `ready`.
  </Step>

  <Step title="Assert both sides">
    Inspect Ionic subscription, invoice, collection-attempt, entitlement, and
    event state. Then assert that your own access control, notifications,
    reporting, and account state match.
  </Step>
</Steps>

Finish each customer and subscription creation request before advancing its
clock. Ionic serializes those writes with advancement, but waiting for each
response gives your test a clear creation-before-advancement order.

## Create a test clock

```bash theme={null}
curl https://api.ionicfi.com/v1/test_clocks \
  -H "Authorization: Bearer sk_v1_test_..." \
  -H "Idempotency-Key: clock_create_clinic_suite" \
  -H "Content-Type: application/json" \
  -d '{
    "frozen_time": 1788091200
  }'
```

```json theme={null}
{
  "id": "tclk_R7pA3nB9qK2mX5",
  "object": "test_clock",
  "status": "ready",
  "frozen_time": 1788091200,
  "target_time": null,
  "failure": null,
  "created_at": 1788091200,
  "updated_at": 1788091200
}
```

## Attach a customer

The customer and clock must belong to the same Ionic account. Test clocks
cannot be attached in live mode or reassigned later.

```bash theme={null}
curl https://api.ionicfi.com/v1/customers \
  -H "Authorization: Bearer sk_v1_test_..." \
  -H "Idempotency-Key: customer_clocked_clinic_123" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "billing@example-clinic.test",
    "test_clock": "tclk_R7pA3nB9qK2mX5"
  }'
```

## Advance the clock

### Arm the next collection outcome

Use a collection scenario when your test needs a specific result from the next
automatic attempt. The scenario belongs to one test-mode subscription and is
consumed by one newly recorded charge attempt.

```bash theme={null}
curl https://api.ionicfi.com/v1/test_helpers/subscriptions/sub_R7pA3nB9qK2mX5sJ4wF8tL6h/collection_scenarios \
  -H "Authorization: Bearer sk_v1_test_..." \
  -H "Idempotency-Key: renewal_decline_clinic_123" \
  -H "Content-Type: application/json" \
  -d '{
    "outcome": "declined",
    "decline_code": "insufficient_funds"
  }'
```

Arm the scenario before advancing the clock to the renewal or retry boundary.
After a failed attempt schedules a retry, arm `{"outcome":"succeeded"}` and
advance to the next attempt time to verify recovery. To verify retry exhaustion,
arm one declined scenario before each retry boundary.

Only one unconsumed scenario can be armed for a subscription. Retrying the
scenario-creation request with the same idempotency key replays its original
response. A different scenario conflicts until the current one is consumed.
When no scenario is armed, Ionic uses the normal test-mode collection path.

Collection scenarios require `charge_automatically`, reject ended
subscriptions, and cannot be created or consumed in live mode.

The target must be strictly later than `frozen_time` and no more than two
calendar years ahead. A clock cannot rewind or accept a second overlapping
advancement. After it returns to `ready`, you can advance it again.

```bash theme={null}
curl https://api.ionicfi.com/v1/test_clocks/tclk_R7pA3nB9qK2mX5/advance \
  -H "Authorization: Bearer sk_v1_test_..." \
  -H "Idempotency-Key: clock_advance_first_renewal" \
  -H "Content-Type: application/json" \
  -d '{
    "target_time": 1790769600
  }'
```

Poll `GET /v1/test_clocks/{id}` until `status` becomes `ready`. Do not assert
final subscription state while the clock is still `advancing`.

If the clock becomes `failed`, resolve the reported issue and call the advance
operation again with the same `target_time` and a new idempotency key. Ionic
resumes after the last committed checkpoint. A failed clock cannot be sent to
a different target because that would fork the original lifecycle timeline.

## Status values

| Status      | Meaning                                                                                                       |
| ----------- | ------------------------------------------------------------------------------------------------------------- |
| `ready`     | All billing work through `frozen_time` committed.                                                             |
| `advancing` | Ionic is processing ordered work toward `target_time`.                                                        |
| `failed`    | Processing stopped at the last committed checkpoint. The failure contains a stable code and safe explanation. |

Ionic limits concurrent advancement per account so one test suite cannot
monopolize shared processing. A limit response is safe to retry after existing
advancement work finishes.

## Recommended scenarios

* Trial completion with successful first collection.
* Successful renewal and unchanged access.
* Definite renewal failure followed by each retry boundary.
* Recovery on a later attempt.
* Retry exhaustion and the intended access restriction.
* Cancellation at period end without an extra renewal.
* A multi-period jump that produces every intervening invoice and event.
* Duplicate and out-of-order public event delivery.

<Warning>
  Test clocks work only in test mode. They never affect live customers,
  subscriptions, invoices, collection attempts, events, or money movement.
</Warning>

## Platforms and connected accounts

An authorized platform may include `Ionic-Account` on clock operations to
select a connected test account. The clock, customer, and subscription must all
belong to that selected account. A platform cannot advance one account's clock
while operating another account.
