Skip to main content
← All Guides
beginner 8 min 100 XP

Test Mode vs Live Mode

Use a separate data scope for development without polluting your live help center. Covers the four key prefixes, how the SDK and dashboard signal mode, and the common pitfalls.

Prerequisites

  • A cStar workspace
  • Comfort with creating API keys from Settings → API Keys
1

Understand the four key prefixes

cStar follows the Stripe-style key naming convention. Two key types × two environments = four prefixes: - `sk_live_*` — Secret key, live mode. Server-side only. Full read/write on production data. - `sk_test_*` — Secret key, test mode. Server-side only. Full read/write on test data. - `pk_live_*` — Publishable key, live mode. Safe in browsers. Read-only public surfaces (Library / Community / QuickHelp). - `pk_test_*` — Publishable key, test mode. Safe in browsers. Read-only on test data. No key at all is also valid for anonymous public surfaces — the client defaults to live mode for backwards compatibility with widgets that have shipped for years.

2

Mint a publishable test key

In the dashboard: Settings → API Keys → New Key. Pick **Publishable** and **Test**. The key starts with `pk_test_`. Copy it once — the dashboard masks it after creation.

# Or via the CLI
cstar keys create --name "Dev preview" --type publishable --environment test
3

Wire it through your client

Pass the publishable key as the `publishableKey` option to any anonymous client. The SDK derives the mode from the prefix and sends the key as `Authorization: Bearer pk_test_...`. The server validates the key against your team and scopes the response data accordingly.

preview-mode.js
import { LibraryClient } from '@cstar.help/js/library';

const lib = new LibraryClient({
  teamSlug: 'acme',
  publishableKey: 'pk_test_...'
});

console.log(lib.mode); // 'test'

const articles = await lib.articles({ limit: 50 });
// Returns articles where is_test_data = true.
// Live articles are NOT returned even though they exist in the same DB.
production.js
// Anonymous live (default — backwards-compatible with existing widgets):
const live = new LibraryClient({ teamSlug: 'acme' });
console.log(live.mode); // 'live'

// Or explicit live key (slightly higher per-key rate limits):
const liveExplicit = new LibraryClient({
  teamSlug: 'acme',
  publishableKey: 'pk_live_...'
});
4

Seed test data with the matching secret key

On the server, use a `sk_test_*` key with `CStarClient`. Anything you create — articles, categories, tickets, customers — is automatically tagged `is_test_data: true` and only visible to clients sending the matching test publishable key.

server.js
import { CStarClient } from '@cstar.help/js';

const cstar = new CStarClient({
  apiKey: process.env.CSTAR_TEST_KEY, // sk_test_...
  teamId: process.env.CSTAR_TEAM_ID
});

const article = await cstar.articles.create({
  title: 'How to reset your password',
  content: '...',
  status: 'published' // immediately visible in the public library
});
5

Verify which mode you got

Every public-route response now includes a `mode: "live" | "test"` field so you can confirm the scope server-side. The client also exposes a `mode` getter derived from the publishable-key prefix.

// Client-side: assert mode in CI smoke tests
import assert from 'node:assert';
assert.strictEqual(lib.mode, 'test');

// Server response also includes the mode:
const response = await fetch(
  'https://www.cstar.help/api/library/acme/categories',
  { headers: { Authorization: 'Bearer pk_test_...' } }
);
const json = await response.json();
console.log(json.mode); // 'test'
6

Common pitfalls

Three things developers hit on day one: 1. **Wrong prefix in the browser.** `sk_*` keys must never ship to the browser — the SDK throws on construction. Use `pk_*` for client-side code. 2. **Mixing modes accidentally.** A test publishable key returns disjoint data from a live publishable key against the same team — they will NEVER show the same articles, categories, or community posts. This is intentional. 3. **`articles.create({status: "published"})` and the article does not appear in the live library.** Per [release notes](/developers/changelog), as of `@cstar.help/js@0.13.0`, `is_public` defaults to `true` when `status === "published"`. Earlier versions required passing `isPublic: true` explicitly.

What's Next?