Test subscriptions
A synthetic subscription in Stripe test mode — sign-up with a test card, your real emails at Checkbox's inbox, a test clock through trial end and renewal, and online cancellation — so every lifecycle check is observed, like a CMS preview.
A test run is a preview of your whole subscription lifecycle before your customers see it. Checkbox subscribes a test customer in your billing platform's test mode, receives your emails at its own inbox, fast-forwards a Stripe test clock through the trial end and the first renewal, cancels online, and reports every step with evidence, check results and fixes your agent can apply. Nothing is charged and nobody has to log in.
1. Give Checkbox a Stripe test key
Create a restricted key in test mode (Stripe Dashboard → Developers → API keys) with:
| Permission | Access | Used for |
|---|---|---|
| Customers | Write | The test subscriber (attached to the run's test clock) |
| Checkout Sessions | Write | The mirrored test-mode Checkout Session |
| Subscriptions | Write | Reading trial, period and cancellation; creating the subscription if the hosted page cannot be completed |
| Test clocks | Write | Creating, advancing and deleting the run's clock |
| Customer portal | Write | A portal session for the online cancellation |
| Prices, Products | Read | Resolving the mirrored prices |
| Billing portal configuration | Read | Comparing the test portal with live |
Store it for the site. Live keys are refused (test_key_not_test_mode); the key is verified with Stripe, kept encrypted and never returned (only a hint such as rk_test_…a1b2):
curl https://app.checkbox.site/v1/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/test_credentials \
-u "$CHECKBOX_SECRET_KEY:" \
-H "Content-Type: application/json" \
-d '{ "type": "stripe_test", "secret_key": "rk_test_..." }'checkbox init and setup_checkbox do this for you when the human ticks "use my Stripe test key" on the approval page.
2. Start a run
curl -X POST https://app.checkbox.site/v1/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/test_runs \
-u "$CHECKBOX_SECRET_KEY:" \
-H "Content-Type: application/json" \
-d '{}'Checkbox copies your checkout into a test-mode Checkout Session. The source is, in order: a config you registered with createStripeCheckoutConfig (exactly what your code passes to stripe.checkout.sessions.create), a test price you pass, or your latest live Checkout Session read through the read-only connection. It also tells you whether the copy matches live (stripe.matches_live).
3. Follow it
A run takes about 5 to 20 minutes, mostly waiting for emails. Poll GET /v1/test_runs/{run} every 15–30 seconds, or subscribe to the test_run.completed / test_run.failed webhooks. List a site's runs:
curl -G https://app.checkbox.site/v1/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/test_runs \
-u "$CHECKBOX_SECRET_KEY:" \
-d limit=3import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const runs = await checkbox.testRuns.list('site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b', { limit: 3 });
for (const run of runs.data) console.log(run.id, run.status, run.next_action?.type);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
runs = checkbox.test_runs.list("site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b", limit=3)
for run in runs.data:
print(run["id"], run["status"], (run.get("next_action") or {}).get("type"))What a run does
| Step | What happens | Evidence for |
|---|---|---|
credentials | The test key works | |
checkout_config | Mirror the checkout and compare it with live | |
checkout_page | The synthetic shopper scans the hosted test-mode page | signup.* |
subscribe | Complete the page with test card 4242; if the page cannot be completed, the subscription is created through the API and flagged | |
acknowledgment_email | Wait for your email at the run's inbox | confirmation.* |
trial_reminder | Move the test clock to every notice-window edge before the trial ends (from the legal catalog), collect mail after each stop | reminder.trial_ending |
renewal | The same around the first renewal | reminder.annual_renewal, reminder.charge_notice |
cancel_online | Open the customer portal (or your own cancel page) and cancel with the shopper | cancel.* |
cancellation_email | Wait for the confirmation | cancel.confirmation |
cleanup | Delete the test clock (Stripe deletes its customer and subscription with it) |
Exact timing. The clock stops on window edges, so an email that arrives between two stops was sent inside, before or after each state's window — timing is in_window, too_early or too_late, exactly, even though the run takes minutes. The run is judged in test-clock time: a 35-day trial is evaluated as 35 days.
The acknowledgment email
Stripe sends no customer emails in test mode, so a run only sees emails your code or email provider sends. If your acknowledgment comes from Stripe's own receipts, the run reports it as a warning with a fix — send the acknowledgment from your checkout.session.completed webhook handler (or turn on Checkbox Notices) — and it is not counted against your site. Each step's fix has summary, ordered steps, where, and the operation_id to call to try again.
What counts for monitoring
Everything a run observes is labelled provenance: "test_run". Your emails always count (they are your real emails); the hosted checkout counts only when the mirrored config matches live; the cancellation flow counts when it is your own cancel page or a test portal configured like live. Missing evidence that is only missing because of test mode is never recorded as a failure of your site.
Each response's next_action (wait, fix_and_retry, apply_fixes, review_warnings, none) tells your agent what to do next. After applying fixes, start another run to confirm them.
Checkbox is compliance software, not a law firm; nothing in these docs is legal advice (details). For AI agents: every page is available as Markdown at the same URL plus .md; the index is /llms.txt.