Sandboxes and Test Clocks
A Sandbox is a tenant isolated from a LIVE account. It has its own TEST keys, objects, activity, simulated balances, and webhook endpoints. A Sandbox key can never read IDs from LIVE or another Sandbox, and simulations never call a payment processor, bank, blockchain, or tax provider.
Rollout is enabled per account. Once enabled, every public operation runs inside the isolated tenant: some use the normal model and others persist a deterministic local simulation. None call real providers.
Create and authenticate a Sandbox
An administrator can create up to five environments under Settings → Sandboxes. A Sandbox can start empty or copy branding, safe checkout settings, products, prices, custom fields, and coupons. Customers, payment methods, subscriptions, movements, credentials, and balances are never copied.

The TEST key is shown once. Store it outside your code and use it against the same base URL as LIVE:
Every response and webhook created with this key belongs only to that Sandbox.

After entering the environment, a persistent banner keeps the Sandbox name visible and reminds the developer that its data, keys, and money are simulated. On mobile it sits below the fixed navigation so the environment context is never obscured.
Sandboxes do not appear in the global account selector. To enter one, return to the LIVE account and use Settings → Sandboxes → Enter, making every environment switch explicit.


Distinguish LIVE, legacy TEST, and named Sandboxes
A LIVE key never changes tenants. A legacy TEST key routes to the default Sandbox only after Recurrente provisions it, verifies its keys and webhooks, and enables migration for that account. Disabling routing restores legacy behavior without deleting Sandbox data. New integrations should use a key created directly inside a named Sandbox.
What you can test
The public API is available with Sandbox keys, but it cannot send funds. Catalog and configuration resources live naturally inside the tenant. Charges, refunds, conversions, connected accounts, terminals, and tax configuration persist deterministic local states and balances. A platform’s children form a connected graph inside that same Sandbox and never mix with LIVE accounts.
A Sandbox balance is simulated, non-transferable, and non-redeemable. The dashboard does not offer Send. The API rejects account and phone transfers, checkout and subscription splits, stablecoin sends, and bank withdrawals. Internal currency conversions remain available because they do not move funds outside the ledger.
These simulations preserve the observable contract without contacting payment processors, banks, Bridge, blockchain, POS hardware, INFILE, or other real providers. See the parity matrix to distinguish native operations from local simulations.
Choose how time moves
A Sandbox subscription can use one of two timelines:
A Test Clock belongs to one Sandbox and is attached to a customer. Subscriptions created afterward for that customer inherit the clock. Effective time applies only to that customer during the request or job; other clocks, customers, and LIVE keep their own time.
Attach the customer to the clock before creating the subscription. A customer with an existing subscription cannot change clocks; create another customer to start a different timeline.
Tutorial: test a complete subscription lifecycle
1. Register the test webhook
Register the endpoint with the Sandbox key, not a LIVE key:
Store the signingSecret: it is returned only when the endpoint is created. This
endpoint receives only TEST events from the authenticated Sandbox.
2. Create the clock and customer
Save the returned clock_... ID and use it when creating the customer:
Save the customer ID (cus_...). Customer metadata is separate from the metadata
that will persist on the subscription.
3. Create a recurring product
Save prices[0].id from the response (price_...).
4. Create and complete the initial checkout
Open the returned checkout_url and use:
After a successful payment, assert:
- the checkout is
paid; - the subscription is
activeand hastest_clock_id,current_period_start, andcurrent_period_end; - one invoice and payment intent succeeded;
integration_idandscenariometadata are present on the subscription and its webhooks;payment_intent.succeededandsubscription.createincludelive_mode: falseand the expectedsandbox_id.
Checkout metadata survives renewals, retries, and cancellation. Metadata sent when creating the customer stays on the customer and is not copied automatically to the subscription.
5. Advance to the first renewal
Fetch the subscription and use its current_period_end as the clock destination:
Advance responds with 202 and runs asynchronously. Poll the clock until it moves
from advancing to ready:
When it is ready, assert a new paid invoice, another
payment_intent.succeeded, an updated period, and a simulated balance credit. If it
is failed, inspect last_error before rerunning the scenario.
6. Test a decline and recovery
The outcome supplied while advancing applies to one charge and then resets to
success:
After completion, assert payment_intent.failed, subscription.past_due,
payment_retries: 1, and a next_payment_attempt_at. To test recovery, advance the
same clock to that next_payment_attempt_at without next_charge_outcome:
The pending invoice should become paid, the subscription should return to active,
and another payment_intent.succeeded should be emitted.
7. Test a refund and cancellation
Use the pa_... ID from a payment_intent.succeeded event to issue a full refund:
Assert a succeeded refund, refund.create, and the simulated balance reversal.
Then cancel the subscription:
Assert subscription.cancel, a cancelled status, and no new invoices after
advancing past the next period.
Expected sequence
Each message includes eventType, an idempotent eventId, and data.event_type.
Verify its signature with the endpoint secret, deduplicate by eventId, and confirm
that data.sandbox_id matches the environment under test.
Test a webhook without creating its domain flow
The tutorial above is the recommended subscription test because it validates natural objects, states, and events. If you only need to test signature verification, routing, retries, or idempotency for an event that is difficult to trigger, emit a fixture:
The helper accepts any event_type in the public catalog, persists an Activity,
and delivers it through the same Svix endpoint. The 201 response contains the
normalized base payload. Recurrente generates and replaces data.id and
data.created_at; Svix delivery adds data.event_type and data.sandbox_id.
A fixture validates the delivery contract, but it does not create a dispute, withdrawal, or other domain graph. Use the real endpoint and assert its objects when you need an end-to-end behavior test.
Natural payloads include fields such as live_mode, metadata, and test_clock_id
when that domain serializer publishes them. The helper does not invent those fields:
it delivers the submitted fields plus event_type, sandbox_id, id, and
created_at.
Test Clock rules
- Each Sandbox can have up to ten clocks, and a customer can belong to at most one.
- A clock can move only forward.
- The maximum advance is twice the shortest billing interval among its subscriptions; without subscriptions, it is two years.
- An advance processes renewals, retries, and resumptions before the destination in chronological order.
- Clock states are
ready,advancing,failed, anddeleted. - A clock cannot be deleted while advancing or while it has subscriptions.
- LIVE endpoints and endpoints from another Sandbox never receive its events.
Automation checklist
- Use a dedicated Sandbox and TEST key per test suite or team.
- Create unique data per case; never reuse LIVE IDs.
- Register the webhook before starting checkout and store its signing secret.
- Wait for
readybefore advancing the same clock again. - Use dates returned by the API (
current_period_endandnext_payment_attempt_at) instead of assuming fixed dates. - Assert objects and webhooks: status, amount, currency, metadata,
sandbox_id, andtest_clock_id. - Treat webhooks as retryable and idempotent.
- Use fixtures only for consumer tests; use real flows for behavior tests.
- End each scenario by canceling the subscription and archive the Sandbox when its data is no longer needed.
Archiving and retention
Archiving a Sandbox revokes its keys, blocks new requests, and marks its clocks as deleted. It does not purge customers, subscriptions, invoices, activities, or historical webhooks; those records are retained for audit and debugging. Create a new Sandbox for a clean suite and never use real customer data as fixtures.
Differences from Stripe Test Clocks
Recurrente follows customer attachment, asynchronous forward-only advances, and the two-interval maximum. It intentionally differs because:
- clock state is observed by polling; separate clock-status webhooks are not published;
- a clock with subscriptions cannot be deleted;
- deleting or archiving retains the historical graph instead of deleting it;
- clocks are not automatically deleted after a fixed retention period;
- objects and event names follow Recurrente’s public contract.
See Stripe’s documentation on subscription simulations and advanced Test Clock API usage to compare the contracts.
See the endpoint reference and Sandbox parity matrix to learn which operations are native and which persist a local simulation.

