There is no sandbox API key. Your API key is a single production
credential (krnl_...), so every API-key endpoint — provider-initiated
provisioning, bulk provisioning, billing reports — acts on your live
account and can run a real provisioning job. There is no krnl_sb_ key and
no separate sandbox host.
What the sandbox covers
- The in-dashboard sandbox — an operator-facing safe mode you enter from
the dashboard. It shows mirrored sample data and simulated network/billing,
and never touches real subscribers or switch ports. It is driven by a
dashboard login, not by a partner API key. See the operator guide:
Use the dashboard sandbox.
- The webhook-secret surface CAN target the sandbox — sessions,
/complete, and /subscription-status work against your sandbox provider.
GET /api/v1/sandbox/provider-info (dashboard JWT) returns the
sandbox_provider_id and sandbox_webhook_secret to use. Creating a
checkout session for a sandbox provider redirects to a kurnl-internal
fake-payment page instead of your store — so the store-redirect leg itself
cannot be sandbox-tested, but the callbacks can.
- The API-key surface is live-only — a sandbox provider has no API key, so
provider-initiated, bulk, and report calls always hit production data.
A legacy no-session direct-provision /complete against a sandbox
provider still queues a real SSH provisioning job against the fake
sandbox switch — there is no sandbox branch in the provisioning worker. The
job will hang and end as provisioning.failed. Treat that path as a
failure-path test only; this is a current platform limitation.
(Session-based sandbox /complete calls can never direct-provision: sandbox
seed buildings have no line type, so they route to an install job or 422.)
Testing your live integration safely
For the API-key surface (no sandbox), the pragmatic approach:
- Point
webhook_url at a throwaway receiver (e.g. webhook.site)
while wiring up your webhook handler — this exercises the full event flow
without needing a finished endpoint.
- Use a test location/unit you control for end-to-end runs, and clean up the
resulting test subscriber afterward from Customers in the dashboard.
- Rely on idempotency: retries are genuinely safe — the same subscriber
email + plan version + location returns the existing subscription with
"Provisioning already in progress" (while a job is running) or
"Subscription already exists (idempotent replay)", never a duplicate.
Sandbox webhooks
Sandbox webhooks are a separate pipeline from production webhooks, with
different event names and no retry machinery:
- Events:
checkout.session.completed (a raw Stripe-shaped payload) and
kurnl.sandbox.ping.
- Delivery: one attempt, 10 second timeout, no retries and no
dead-letter queue. The signature header is identical to production
(
X-Webhook-Signature: sha256=<hmac>, signed with the sandbox webhook
secret).
- Configure the receiver with
POST /api/v1/sandbox/webhook-config — the URL
must be https://, http://localhost, or http://127.0.0.1 (anything else is rejected). Saving
a URL fires a synchronous kurnl.sandbox.ping test event and returns its
delivery status.
- Inspect events with
GET /api/v1/sandbox/webhook-events (newest first,
default 20; limit accepts 1–200 but values above 100 are clamped to 100,
and >200 is rejected with 422) and replay one with
POST /api/v1/sandbox/webhook-events/{id}/replay.
Going live on the production domain
If you integrated against the demo host and are moving to the kurnl.ca
production domain, swap the host in your endpoint URLs
(middleware.bso-projects.com → middleware.kurnl.ca) and use the API key +
webhook secret + service_provider_document_id for that environment.
Production location_hash values come from real Marketplace redirects — read
location_hash dynamically from the redirect URL rather than hardcoding it.