Sandbox and Production

Every account has both environments from the moment it is created. They run the same code against the same schema, so anything that works in Sandbox works in Production — but they never share a single row of data. Sandbox is free and needs no plan, which means there is no reason to develop against live data and no cost to not doing so.

One base URL, two environments

There is no separate sandbox hostname and no ?test=true parameter. The key you send determines which environment you are in, and nothing else about the request changes.

The only difference

# Sandbox
curl https://countdownshare.com/api/v1/timers \
  -H "Authorization: Bearer cs_test_..."

# Production — same URL, same path, different key
curl https://countdownshare.com/api/v1/timers \
  -H "Authorization: Bearer cs_live_..."

This is deliberate. A separate sandbox host is a second thing that can drift, and drift between test and live environments is how integrations pass every test and then fail on launch day. Here there is one deployment, one codebase, and one contract.

What actually differs

The behaviour of every endpoint is identical. What changes is the ceiling on volume and whether a timer can become public.

SandboxProduction
Key prefixcs_test_cs_live_
CostFree with any accountStarter, Growth, or Custom plan
New timers per month1001,000 – 10,000+
Requests per minute30300
API keys13 – 10+
Webhook destinations15 – 25+
Webhook deliveries per month100Unlimited
Webhook rulesUnlimitedUnlimited
Publish public pages and embedsNot availableAvailable
Destination URL schemehttp:// or https://https:// only
The rate limit gap is the one that surprises people. 30 requests per minute in Sandbox is low enough that a naïve load test will trip it — that is intentional, because it surfaces missing retry handling while the consequences are zero. See rate limits and usage.

How strict the isolation is

Complete. A Sandbox key presented with a Production timer ID does not return a permission error — it returns not_found, because from that key's perspective the timer does not exist. There is no query the API can be asked that crosses the boundary.

Separate rows

Timers, keys, destinations, rules, and deliveries all carry an environment. Nothing is shared between the two.

Separate meters

Sandbox usage never counts toward your Production allowance, and the two billing cycles are tracked independently.

Enforced below the code

The constraint lives in the database, so a bug in a route handler cannot leak across it. Application logic is the second line of defence, not the first.

A practical consequence: you cannot promote a Sandbox timer to Production. There is no copy operation, and there should not be — a timer that was created for a test should not quietly become one your customers see. Recreate it with a Production key.

Testing webhooks locally

Sandbox accepts http:// destination URLs specifically so you can point a destination at a local tunnel — ngrok, Cloudflare Tunnel, or your own — while you develop the handler. Production requires HTTPS.

Private, loopback, and link-local addresses are rejected in both environments, so http://localhost:3000 will not be accepted. Use the public tunnel URL the tool gives you rather than the address it forwards to.

POST /webhook-destinations/{destination_id}/test sends a real signed delivery on demand. Use it to confirm your signature verification works before any timer exists, which separates “my handler is wrong” from “my rule never matched”. See verify a delivery.

Going live

The code change is one environment variable. Everything below is the part people forget.

Configuration

Boot-time guard

// Environment is a deployment concern, not a code branch.
// One variable changes everything; nothing else in your code differs.
const key = process.env.COUNTDOWNSHARE_API_KEY;

// A guard worth having: fail loudly at boot rather than
// quietly writing test data to a live account.
if (process.env.NODE_ENV === "production" && !key?.startsWith("cs_live_")) {
  throw new Error("Production build is holding a non-production API key");
}

Checklist

  • Swap the key

    A cs_live_ key in your production secret store, and the Sandbox key removed from it.

  • Add publish: true

    Production timers are still drafts by default. A timer nobody can see is the most common launch-day surprise.

  • Re-register destinations

    Destinations do not cross environments. Create the Production one with its public HTTPS URL and store the new signing secret.

  • Re-create webhook rules

    Rules belong to a timer, so Production timers need their own rules attached.

  • Recheck rate limits

    Production allows ten times the Sandbox rate. If you built throttling around 30 rpm, you are now leaving throughput unused.

  • Confirm the monthly allowance

    GET /usage reports the limit and what remains on the current plan. Check it matches the volume you expect.

  • Handle quota_exhausted

    It is a 429, like the rate limit, but it does not clear on its own. Make sure your code distinguishes them.

Sandbox stays available after you go live, and it stays free. Keep using it for CI, for reproducing a customer's bug, and for trying a schema change — none of which should ever touch a live countdown.