Public API Keys & the Client Handshake
SegOps has three key types. Picking the right one is a security decision, not a preference.
| Key | Prefix | Where it runs | Can it... |
|---|---|---|---|
| Secret | sk_ | Your server only | Ingest + read tenant resources. Full power. |
| Public | pk_ | Browser / mobile (embeddable) | Mint a session, then track/identify/page for one user. Cannot read tenant data. |
| MCP | mk_ | MCP connectors | Reserved for AI client integrations. |
Never ship an sk_ key to a browser or mobile app. Anyone can read it from
the bundle and impersonate your whole tenant. The SDK prints a console warning
if it detects one client-side.
Why public keys need a handshake#
A pk_ key is, by design, visible to anyone who views your site. On its own that
would let an attacker (1) call your endpoints from an unrelated origin, or
(2) forge user_ids to poison segments. The handshake closes both holes:
- Origin allowlist — a
pk_key only works from the origins you list (https://app.tenant.com, orhttps://*.tenant.comfor one subdomain level). Requests from any other origin are rejected before they reach a view. user_idlocking — the session token pins oneuid. Every event sent with it is rewritten to thatuidserver-side, so a client cannot write events as someone else.- Short TTL — tokens live 15 minutes; the SDK refreshes them transparently.
Signed user_id (stops impersonation)#
The origin check stops other sites. To stop your own anonymous visitors from claiming to be a known user, enable Require signed user_id on the key. Then the browser must present a signature your backend produced with the key's HMAC secret (shown once at creation):
- Your backend signs the authenticated user's id — see Server-side signing.
- The browser passes
user_id,user_id_sig,user_id_tsinto the SDK. - The session minter recomputes the HMAC and rejects any mismatch (and any signature older than 5 minutes).
Using it (TypeScript)#
The SDK detects the pk_ prefix, calls /api/auth/session/ on first use, caches
the token, and refreshes it ~60 s before expiry. On a 401 it re-mints once and
retries.
Managing keys#
Settings → API keys (owners only):
- Public / Secret / MCP tabs.
- Creating a key shows the raw value once — copy it immediately. Public keys also show a one-time HMAC secret.
- Public-key fields: allowed origins, scopes, and the Require signed user_id toggle. Origins and scopes can be rotated later; the key type cannot.
- Revoking a key takes effect on the next request.
FAQ#
Can a pk_ key read my segments or users? No. It can only mint a session and
send events for the session's uid. All resource endpoints reject it.
What if my session token leaks? It expires in 15 minutes and is locked to a
single uid and origin. Revoke the parent key to invalidate future mints.
Do I still need sk_ keys? Yes — for server-side ingestion and any
programmatic access. Just never in client code.
Operational flags#
ENABLE_JWT_INGEST— accept session JWTs on ingest (off in prod for the first deploy, then flip on after a soak).SEGOPS_SESSION_JWT_SECRET— dedicated signing secret (rotate independently ofSECRET_KEY; rotating it invalidates outstanding session tokens within 15 min).