BlogMetering

Test mode: break your pricing in the sandbox, not on your customers

You changed the free tier from 10 to 25 generations and somehow locked out every Pro user for an hour. Nobody tested it, because testing it meant tracking fake events into production analytics. There is a mode for this.

Why pricing logic goes untested

Nobody ships a payment flow without testing it, yet metering changes go out blind constantly. The reason is friction: testing a limit means generating events, and generating events into production pollutes the analytics you make decisions with, skews the very counters you are trying to verify, and - if you meter your own usage - costs real quota. So devs test pricing changes by squinting at the diff. The fix is the same one Stripe taught everyone a decade ago: a test mode that is a real, full-fidelity environment, not a mock.

The key is the switch

Every Vevee app has four keys: pk_live / sk_live and pk_test / sk_test. The prefix is the entire mode mechanism - same SDK, same endpoints, same plans and limit groups, but test keys read and write an isolated set of runtime tables. End-user state (events, counters, subscriptions, reservations) is fully partitioned; configuration (apps, plans, match rules) is shared. That split is exactly what you want for pricing tests: you exercise the real plan you are about to ship, against throwaway users.

// The only line that differs between prod and test
const vevee = createClient({
  apiKey: process.env.NODE_ENV === 'production'
    ? process.env.VEVEE_SECRET_KEY!       // sk_live_...
    : process.env.VEVEE_TEST_SECRET_KEY!, // sk_test_...
});

The pre-ship ritual, scripted

Because test mode is just the SDK with a different key, a pricing change gets an integration test like any other code. Subscribe a fake user to the changed plan, walk them to the limit, assert the wall appears exactly at the new number, assert the premium sub-cap still binds. Run it in CI if you like - test events are isolated, so the suite is rerunnable. This is the test you could never write against production.

// pricing-change.test.ts - runs against sk_test
const u = 'test_user_' + Date.now();
await vevee.upsertSubscription({ userId: u, planId: 'free' });

for (let i = 0; i < 25; i++) {
  expect(await vevee.can(u, 'image_generation')).toBe(true);
  await vevee.track(u, 'image_generation');
}
// the 26th must hit the new wall
expect(await vevee.can(u, 'image_generation')).toBe(false);

The dashboard follows you

Test mode is not headless. Flip the workspace toggle and the entire dashboard - usage charts, event feeds, subscription lists - reads the sandbox universe, with a yellow TEST MODE banner so you always know which reality you are looking at. This is how you demo the product to a stakeholder with realistic-looking data, debug a customer-reported limit issue by reproducing it safely, or check that your seeded staging environment looks right, all without a single fake row in production.

The fine print that keeps it honest

Two boundaries worth knowing. Test data older than 30 days is hard-deleted by a daily sweep - the sandbox is a workbench, not an archive, so never point anything you care about at a test key. And test-mode operations count toward your workspace’s plan cap just like live ones; the sandbox is isolated for data, not unmetered, so routing production traffic through a test key buys you nothing except missing analytics. Use live keys for users, test keys for everything else - staging, CI, demos, and every pricing change before it meets a customer.

See your own numbers in two minutes.Add the SDK and your users, costs and revenue fill in on the next request.
Join waitlist

Keep reading

ShareCopy a link to this post.

Set your first limit in about five minutes.

50k operations free every month. No card required.
Join waitlist