Skip to content

Test clocks

A test clock is a sandbox clock that you control. Customers created on a test clock, and their subscriptions, read the clock's frozen_at as the current time instead of real time. Use a test clock to check what your integration does when a trial ends, a renewal charges, or a payment retry runs, without waiting days or weeks for it to happen.

Test clocks exist only in sandboxes. Every test clock route needs a test-mode credential: a live credential gets 403 FORBIDDEN with details[0].reason test_mode_required, whatever scopes it holds. Listing and retrieving clocks requires developer.test_clocks.read or developer.test_clocks.write. Creating and archiving them requires developer.test_clocks.write. Both scopes can be granted to live keys, so a live key can grant them to the sandbox keys it creates, but the routes themselves only accept test credentials.

Create a test clock#

POST /v1/test-clocks creates a clock with status ready, stopped at frozen_at. Both fields are optional.

cURL
curl -X POST https://api.withflintpay.com/v1/test-clocks \
  -H "Authorization: Bearer flint_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "frozen_at": "2026-11-01T00:00:00Z",
    "name": "Annual renewal test"
  }'

frozen_at defaults to the time of the request. Fractional seconds are dropped. It can be up to 5 years after the request time, and no more than 1 hour before it, to allow for clock skew on your side. A value outside that range returns 400 INVALID_REQUEST with param: "frozen_at". name is your label, from 1 to 255 characters.

A new clock has version 1 and sends test_clock.created. There's no limit on the number of clocks in a sandbox.

List and retrieve test clocks#

cURL
curl "https://api.withflintpay.com/v1/test-clocks?status=ready&status=archived" \
  -H "Authorization: Bearer flint_test_YOUR_KEY"

GET /v1/test-clocks returns the sandbox's clocks, newest first, then by test_clock_id descending. Set page_size from 1 to 100, with 20 by default, and pass next_page_token as the next request's page_token. Archived clocks are left out unless the status filter names archived. Repeat status, or send comma-separated values, to match several statuses.

cURL
curl https://api.withflintpay.com/v1/test-clocks/tclk_01K8ABCDEF0123456789ABCDEF \
  -H "Authorization: Bearer flint_test_YOUR_KEY"

GET /v1/test-clocks/{test_clock_id} returns a clock in this sandbox, including an archived one. A clock in another sandbox returns 404 RESOURCE_NOT_FOUND with param: "test_clock_id", the same as an unknown ID.

Archive a test clock#

cURL
curl -X DELETE "https://api.withflintpay.com/v1/test-clocks/tclk_01K8ABCDEF0123456789ABCDEF?expected_version=1" \
  -H "Authorization: Bearer flint_test_YOUR_KEY"

DELETE /v1/test-clocks/{test_clock_id} archives the clock and returns it with status archived. The clock stays readable by ID, never advances again, and no new customer can be created on it. Customers and subscriptions already on it don't change: they keep the clock's frozen_at as their current time, so their subscriptions don't bill again. Cancel those subscriptions first if you want them canceled, or reset the sandbox to remove everything.

Status before DELETEResult
ready or failed200 with status archived. version increases by 1 and Flint sends test_clock.archived.
archived200 with the clock unchanged. No event is sent.
advancing409 ACTION_NOT_ALLOWED with current_status: "advancing" and reason: "in_progress". Wait until the clock is ready or failed.

expected_version is optional. If it doesn't match the clock's version, the request returns 409 VERSION_CONFLICT with the current version and changes nothing.

The test clock resource#

FieldBehavior
test_clock_idThe clock's ID, beginning with tclk_.
nameYour label. Omitted when you didn't set one.
statusready, advancing, failed, or archived. See statuses.
frozen_atThe time the clock shows. Customers on the clock and their subscriptions treat it as the current time. During an advance it is already the time the clock is moving to.
created_at, updated_atWhen the clock was created and last changed, in real time.
versionIncreases when the clock is created, when an advance starts, when it finishes or fails, and when the clock is archived. Send it as expected_version to require the version you read.

Statuses#

StatusMeaning
readyStopped at frozen_at. You can advance it and create customers on it.
advancingFlint is moving the clock's customers and their subscriptions to frozen_at, running the billing that falls due along the way, in order. Until the clock is ready, resources on it can show any time between the previous frozen_at and the new one.
failedFlint couldn't finish an advance, and resources on the clock may have processed only part of it. The clock can't advance again: archive it and create a new one. Final.
archivedArchived with DELETE. Readable, but it never advances and takes no new customers. Final.

status is a closed set. Branch on it to decide whether to wait, continue, or start over with a new clock.

Events#

EventWhen
test_clock.createdA clock was created.
test_clock.advancingAn advance started.
test_clock.readyAn advance finished. Flint records it after every event the advance caused.
test_clock.failedAn advance failed.
test_clock.archivedA ready or failed clock was archived.

Each event carries the clock as data.object. Events go to the sandbox's webhook endpoints, like any other sandbox event.

What follows the clock#

Customers created on a test clock, their subscriptions, and those subscriptions' payment retries use the clock's time wherever Flint decides or records a point in their billing lifecycle. Everything else uses real time.

BehaviorFollows the clock
Trial end, then activation and the first chargeYes
subscription.trial_ending and subscription.renewal_upcoming noticesYes
Renewals at next_billing_atYes
Scheduled startsYes
Payment retries and the action when retries run outYes
Cancel at period end, and timed pauses that resume on their ownYes
Subscription _at fields other than created_at and updated_atYes
created_at and updated_at on every resourceNo
Webhook envelope times, request logs, and timelinesNo
Orders, payment intents, invoices, refunds, disputes, and payoutsNo
Card processing, 3D Secure, and bank debit settlement at StripeNo
The needs_attention subscription list filterNo

Sandbox emails go to real addresses, and webhooks are real sandbox deliveries, so expect them when billing runs on a clock.

Resetting the sandbox removes its test clocks with the rest of its data.

The Test clock object#

Every field on a test clock, as returned by retrieve and carried by the endpoints below.

Attributes

created_atstringRequired

When the test clock was created, in real time.

frozen_atstringRequired

The time the test clock shows. Customers created on the clock, and their subscriptions, treat it as the current time. After you advance the clock, `frozen_at` is the time you advanced it to, while `status` is `advancing` and after.

namestring

Your label for the test clock. Omitted when you didn't set one.

statusenumRequired

Where the test clock is in its lifecycle. Read it before advancing, and after an advance until it's `ready` or `failed`.

  • ready
  • advancing
  • failed
  • archived
test_clock_idstringRequired

Unique identifier for the test clock.

updated_atstringRequired

When the test clock last changed, in real time.

versionintegerRequired

Persisted version of this resource's owned state. Expanded resources and external observations can change independently.

JSON
{
  "created_at": "2026-10-28T12:00:00Z",
  "frozen_at": "2026-11-01T00:00:00Z",
  "name": "Annual renewal test",
  "status": "ready",
  "test_clock_id": "tclk_01K8ABCDEF0123456789ABCDEF",
  "updated_at": "2026-10-28T12:00:00Z",
  "version": 1
}

List test clocks#

GET/v1/test-clocks

Requires scope developer.test_clocks.read or developer.test_clocks.write

Sandbox only. Lists the sandbox's test clocks, newest first, then by `test_clock_id`. Archived clocks are listed only when the `status` filter names `archived`. Default order: created_at descending, then test_clock_id descending.

Query parameters

statusarray of enum

Filter by status. Repeat the parameter or send comma-separated values to match any of several. Omitted, the list leaves out archived rows; name archived to include them. Accepts repeated keys, comma-separated values, or mixtures. Values are trimmed and deduplicated in first-seen order; empty or unknown entries are rejected. Values within this filter are ORed; separate filters are ANDed.

  • ready
  • advancing
  • failed
  • archived
page_sizeinteger

Page size, default 20, max 100.

page_tokenstring

Opaque next_page_token from the previous response. Omit on the first request. Reuse it with the same endpoint, filters, sort, and request context. Do not parse or construct tokens. An invalid, expired, or mismatched token returns a validation error.

Response · 200

dataarray of objectRequired
metaobject

Metadata returned with a response: the `api_version`, `trace_id`, whether an idempotent request was replayed, and any warnings.

next_page_tokenstring
request_idstringRequired
curl https://api.withflintpay.com/v1/test-clocks \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"
JSON
{
  "data": [
    {
      "created_at": "2026-10-28T12:00:00Z",
      "frozen_at": "2026-11-01T00:00:00Z",
      "name": "Annual renewal test",
      "status": "ready",
      "test_clock_id": "tclk_01K8ABCDEF0123456789ABCDEF",
      "updated_at": "2026-10-28T12:00:00Z",
      "version": 1
    }
  ],
  "next_page_token": "opaque-next-page-token",
  "request_id": "req_01KWM6WAHG3N9Q8T2V5XRZ7C4D"
}

Create a test clock#

POST/v1/test-clocksIdempotent

Requires scope developer.test_clocks.write

Sandbox only. Creates a test clock stopped at `frozen_at`, with status `ready`. Customers created on it, and their subscriptions, read its time instead of real time.

Request body

frozen_atstring

The time the clock starts at. Defaults to now. It can be up to 5 years ahead, and no more than an hour behind now, to allow for clock skew on your side. Fractional seconds are truncated.

namestring

Your label for the test clock.

Response · 201

dataobjectRequired

A sandbox clock that customers created on it, and their subscriptions, read as the current time.

metaobject

Metadata returned with a response: the `api_version`, `trace_id`, whether an idempotent request was replayed, and any warnings.

request_idstringRequired
curl -X POST https://api.withflintpay.com/v1/test-clocks \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a-unique-key" \
  -d '{
    "frozen_at": "2026-11-01T00:00:00Z",
    "name": "Annual renewal test"
  }'

Get a test clock#

GET/v1/test-clocks/{test_clock_id}

Requires scope developer.test_clocks.read or developer.test_clocks.write

Sandbox only. Returns a test clock in this sandbox, including an archived one.

Path parameters

test_clock_idstringRequired

ID of the test clock.

Response · 200

Same response as Create a test clock.

curl https://api.withflintpay.com/v1/test-clocks/tclk_01K8ABCDEF0123456789ABCDEF \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"
JSON
{
  "data": {
    "created_at": "2026-10-28T12:00:00Z",
    "frozen_at": "2026-11-01T00:00:00Z",
    "name": "Annual renewal test",
    "status": "ready",
    "test_clock_id": "tclk_01K8ABCDEF0123456789ABCDEF",
    "updated_at": "2026-10-28T12:00:00Z",
    "version": 1
  },
  "request_id": "req_01KWM6WAHG3N9Q8T2V5XRZ7C4D"
}

Archive a test clock#

DELETE/v1/test-clocks/{test_clock_id}Idempotent

Requires scope developer.test_clocks.write

Sandbox only. Archives the test clock. It stays readable by ID, never advances again, and no new customer can be created on it. Customers and subscriptions already on it are not changed: they keep the clock's `frozen_at` as their current time, so their subscriptions don't bill again. Cancel those subscriptions first if you want them canceled, or reset the sandbox to remove everything. Deleting an archived clock returns it unchanged.

Path parameters

test_clock_idstringRequired

ID of the test clock.

Query parameters

expected_versioninteger

Optional version most recently read. A stale value returns 409 VERSION_CONFLICT with the current version.

Response · 200

Same response as Create a test clock.

curl -X DELETE https://api.withflintpay.com/v1/test-clocks/tclk_01K8ABCDEF0123456789ABCDEF \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

Was this helpful?