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.commiddleware.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.