BillKit/DocsConsole
Get started

Test mode

Test mode is the same API against separate data and a separate Mollie account. The key you send decides which one you are in.

Updated Oct 14, 2026·OpenAPI v1.1.0

What separates the two modes

Every object in BillKit belongs to one mode. A bk_test_ key can only read and write test objects, and a bk_live_ key can only read and write live ones. Asking for an object from the other mode returns 404 resource_missing, the same answer as for an object in another account.

Objects carry livemode: false in test mode and livemode: true in live mode, and so does every webhook event. Products, prices, customers, coupons, webhook endpoints and API keys all belong to one mode, so a test endpoint never receives live traffic and a test price cannot be sold live.

Mollie credentials are stored per mode as well. Test mode uses your Mollie test API key, so Mollie simulates the payments and no money moves. Connect it under Settings → Mollie in the console, or over the API as the Quickstart shows, before your first checkout.

Completing a test payment

BillKit does not simulate payments, and there are no BillKit test card numbers. Mollie owns that part. When you open the url from a test-mode checkout session, Mollie shows its test status selector rather than a real payment form: pick paid to complete the flow, or failed and expired to exercise your failure handling. Mollie’s own test card numbers work in the card form when the selector is not shown.

The console lists the ones you need under Developers → Test cards: Mollie’s card numbers, and the amounts that make a payment fail for a reason you pick, so you can exercise dunning without waiting for a real decline. Mollie owns those values and changes them from time to time, so if one stops working, check its Testing reference.

Everything downstream is real BillKit behaviour. The mandate is stored, the subscription is created, the invoice is written, the events fire and your webhook endpoint is called.

Customer email in test mode

Emails to your customers (invoices, receipts, payment failures, trial reminders, portal links, email-change approvals, activation links) are only sent in test mode when the customer’s email address belongs to a member of your team: someone who can sign in to your BillKit account and has not been removed. Any other address is skipped. The API call that would have sent it succeeds exactly as it would in live mode, and nothing tells the customer.

This keeps a test customer created with somebody else’s real address from receiving mail in your name. To see what your customers will get, create a test customer with your own address, or a teammate’s. Live mode sends to every customer as normal. Emails BillKit sends to your team, such as sign-in links, invitations and security notices, are sent in both modes.

To test the whole flow with real addresses outside your team, turn on Settings → Tenant → Customer emails → Email test-mode customers in the dashboard. Test-mode customers then get their emails at their own address, with [TEST] at the start of the subject and a note at the top saying no real payment was taken and nothing in the email is owed. A teammate’s copy stays unmarked, as the preview it always was. Only someone signed in to the dashboard can turn this on, never an API key, and they may be asked to confirm it is them first. Live mode is never affected.

Test clocks

A test clock is a virtual “now” that you move forward by hand. Attach customers to it, move the clock past a deadline, and BillKit’s time-driven work for those customers runs as if that moment had arrived.

shell
curl https://api.billkit.eu/v1/test_clocks \
-H "Authorization: Bearer $BILLKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"frozen_time": 1789344000, "name": "trial conversion"}'

frozen_time is Unix seconds. Attach a customer to the clock when you create it, by passing the clock’s tc_... id as test_clock_id on POST /v1/customers. A customer cannot be attached later, and the field is refused with a 400 on a live key. Then advance the clock. A new frozen_time at or before the current one is a 400 parameter_invalid.

shell
curl https://api.billkit.eu/v1/test_clocks/tc_9Yx2Rn6vQt4mLpKbdw/advance \
-H "Authorization: Bearer $BILLKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"frozen_time": 1791936000}'

The advance call finishes all the work it triggers before it returns, so you can read the results as soon as you get the response. Send an Idempotency-Key if your test harness retries: a repeat with the same key returns the first response instead of advancing again, while a repeat without one is refused with that 400, because the clock is already there.

Deadlines such as trial_end and current_period_end are set from real time when the subscription is created, not from the clock. Create the clock at or near the current time, then advance it past the deadline you want to test.

What a clock does and does not move

Advancing the clockEffect
Trial ending soonOnce the clock is within three days of trial_end, BillKit emits subscription.trial_will_end and sends the trial-ending email, once per trial.
Trial expiryOnce the clock passes trial_end, BillKit takes the first charge, and subscription.trial_ended fires when it is paid. The status stays trialing until then. If the charge has not settled once the clock is 24 hours past trial_end, subscription.trial_ended fires anyway, still trialing. If the customer cancelled or paused during the trial, nothing is charged, and once the clock passes trial_end the subscription is cancelled with cancellation_reason: "period_end", canceled_at at trial_end, and subscription.canceled.
Metered period closeElapsed periods are closed, the usage invoice is written, and the mandate is charged. The payment result arrives from Mollie as usual.
Dunning noticesPast-due subscriptions with a failed payment not yet notified get dunning.email_required and the dunning email.
End of a cancelled or paused subscriptionOnce the clock passes current_period_end, the subscription becomes canceled with subscription.canceled, unless a renewal charged before the cancel is still settling.
One-off payment expiryOne-off payments still open or pending more than an hour past expires_at become expired, with one_shot_payment.failed, unless Mollie already reports them settled.
Renewal noticeOnce the clock is within three days of current_period_end (seven for an annual plan), BillKit emits invoice.upcoming, once per period, and again on a later advance if the amount has changed. An advance that jumps straight past current_period_end skips the notice for that period and goes to the charge.
RenewalsOnce the clock passes current_period_end, BillKit charges the renewal on the stored payment method. The payment result arrives from Mollie as usual. A failed one is retried on the clock’s time: a retry fires on the first advance past next_payment_attempt. When the retries run out, or the unpaid limit passes, the subscription is cancelled with cancellation_reason: "payment_failed".
created / updated timestampsNot affected. They always record real time.

A clock only touches customers attached to it. BillKit’s normal time-driven processing skips those customers, so their deadlines move only when you advance the clock, and an advance never reaches anyone else’s data. What Mollie reports (a payment settling or failing) still arrives in real time, because a clock cannot move Mollie.

Clocks are test mode only. Every route under /v1/test_clocks refuses a live key with 403 mode_mismatch, whatever its scopes. Deleting a clock returns a deletion marker and detaches its customers, who go back to real time. The customers themselves are not deleted.

Going live

Your code does not change except for the key. Endpoints, field names and event types are identical. Live mode starts empty, though: create your products, prices, coupons and webhook endpoints again with a live key. Going live adds live mode; your test data and test keys keep working.

The console switches your account to live under Settings → Go live. Three requirements block the switch: a Mollie credential for live mode, at least one live price, and at least one test payment that succeeded. Connect with Mollie under Settings → Mollie covers both modes in one authorisation. Alternatively, paste your Mollie live key (live_...) there; a pasted key covers only its own mode. Three more are recommended but do not block: a verified domain, a live webhook endpoint, and your business address, which appears on your invoices. Verifying the domain is what counts here; you do not have to turn on sending or the portal hostname as well.