Sandboxes & Test Mode
Flint has one API and one hostname for both test and live traffic. Your key decides which mode a request runs in: a flint_test_... key runs in test mode against a sandbox, and a flint_live_... key runs in live mode against real money. There is no separate test hostname and no per-request mode flag. Everything in this guide happens against https://api.withflintpay.com.
This is the second thing to understand after Accept Your First Payment, because it explains where your test data lives and why a bare test key sometimes gets rejected.
Hit SANDBOX_SELECTION_REQUIRED? Your test key is not bound to a sandbox. Jump to the fix.
Test mode vs live mode#
| Test mode | Live mode | |
|---|---|---|
| Key prefix | flint_test_... | flint_live_... |
| Money | Nothing moves; card charges are simulated | Real charges |
| Cards | Test cards only | Real cards |
| Data | Lives in a sandbox, isolated from live | Your live merchant data |
Test and live data never mix. An order created with a test key is invisible to a live key, and the reverse. You can build and tear down test data freely without touching anything real.
Sandboxes#
A sandbox is an isolated test environment with its own orders, customers, payments, and settings. Every developer merchant gets one default sandbox automatically, and every test key is permanently bound to exactly one sandbox at creation. Because the binding is baked into the key, there is no sandbox header to send: the key you authenticate with selects the sandbox.
Create additional sandboxes when you want isolation between uses, for example one for local development, one for CI, and one for partner QA, so their data never collides.
curl -X POST https://api.withflintpay.com/v1/developer/sandboxes \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"name": "CI",
"issue_test_key": true,
"test_key_name": "ci-runner"
}'
{
"data": {
"sandbox_id": "test_01KWJS7ZW5YAC86HMHWTCV93GX",
"name": "CI",
"status": "active",
"is_default": false,
"secret_key": "flint_test_2686d60a...",
"api_key": {
"api_key_id": "key_01KWJS8006FVHT8CRWDKBWA1T9",
"name": "ci-runner",
"key_prefix": "flint_test_2686d60a",
"sandbox_id": "test_01KWJS7ZW5YAC86HMHWTCV93GX",
"status": "active"
}
}
}
The sandbox ID (test_...) is what you pass to the reset, archive, and test-key endpoints below. The secret_key at data.secret_key is your new sandbox-bound key.
The secret_key is shown only in this response, at creation. Flint never returns it again. Capture it now; if you lose it, issue a new key with the test-key endpoint below.
Set that key as your Authorization: Bearer header and any /v1 call now runs in this sandbox; a quick GET /v1/orders (expect an empty list on a fresh sandbox) confirms it works. To mint another key for an existing sandbox, use its test-key endpoint:
curl -X POST https://api.withflintpay.com/v1/developer/sandboxes/SANDBOX_ID/test-key \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"name": "local-dev"}'
{
"data": {
"api_key_id": "key_01KWJS80E4G3CYMKRA955524FF",
"name": "local-dev",
"sandbox_id": "test_01KWJS7ZW5YAC86HMHWTCV93GX",
"secret_key": "flint_test_ca7f32e8...",
"status": "active"
}
}
The same rule applies: this secret_key is returned only here, so capture it now.
The sandbox-bound key requirement#
Test mode requires a key that is bound to a sandbox. A test key that is not sandbox-bound cannot authenticate public API requests, and Flint returns a 401 rather than guessing which sandbox you meant:
{
"error": {
"type": "authentication_error",
"code": "SANDBOX_SELECTION_REQUIRED",
"message": "Test mode API keys must be sandbox-bound before they can authenticate public API requests."
}
}
If you hit this, you are using a test key that was minted outside a sandbox. The fix is to use a sandbox-bound key: create a key on the dashboard API keys page (dashboard keys come sandbox-bound automatically), or mint one from a sandbox with the test-key endpoint above. The error's remediation.next_actions points at the same step.
Resetting and archiving#
A sandbox accumulates test data as you work. Two lifecycle actions keep it manageable:
- Reset clears all data in the sandbox while keeping the same sandbox ID and key bindings. This is the fastest way to return a QA or CI environment to a clean slate without re-issuing keys.
curl -X POST https://api.withflintpay.com/v1/developer/sandboxes/SANDBOX_ID/reset \
-H "Authorization: Bearer YOUR_API_KEY"
- Archive retires a sandbox you no longer need. Its
statusbecomesarchived.
The default sandbox cannot be reset or archived: those calls return a 409 with DEFAULT_SANDBOX_CANNOT_BE_RESET or DEFAULT_SANDBOX_CANNOT_BE_ARCHIVED. Keep throwaway work in a sandbox you created.
Next Steps#
- Testing: test cards and simulating declines, 3D Secure, and other payment outcomes.
- Sandboxes API reference: every endpoint, field, and response shape.
- Accept Your First Payment: the end-to-end payment flow these sandboxes are for.
- Going Live: when you are ready for real charges, swap your
flint_test_key for a live one.
