SegOps AIDocs

Public API Keys & the Client Handshake

SegOps has three key types. Picking the right one is a security decision, not a preference.

KeyPrefixWhere it runsCan it...
Secretsk_Your server onlyIngest + read tenant resources. Full power.
Publicpk_Browser / mobile (embeddable)Mint a session, then track/identify/page for one user. Cannot read tenant data.
MCPmk_MCP connectorsReserved 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:

Browser (pk_live_…)                         SegOps API
   │  POST /api/auth/session/                   │
   │  Authorization: ApiKey pk_…                │
   │  Origin: https://app.tenant.com            │
   │  { user_id, [user_id_sig, user_id_ts] }    │
   │ ─────────────────────────────────────────▶ │  • origin allowlist check
   │                                            │  • optional signed-uid check
   │  { token, expires_at, uid }                │  • mint 15-min JWT (uid-locked)
   │ ◀───────────────────────────────────────── │
   │                                            │
   │  POST /api/ingestion/track/                │
   │  Authorization: Bearer <token>             │
   │ ─────────────────────────────────────────▶ │  • user_id forced to token.uid
  • Origin allowlist — a pk_ key only works from the origins you list (https://app.tenant.com, or https://*.tenant.com for one subdomain level). Requests from any other origin are rejected before they reach a view.
  • user_id locking — the session token pins one uid. Every event sent with it is rewritten to that uid server-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):

  1. Your backend signs the authenticated user's id — see Server-side signing.
  2. The browser passes user_id, user_id_sig, user_id_ts into the SDK.
  3. The session minter recomputes the HMAC and rejects any mismatch (and any signature older than 5 minutes).

Using it (TypeScript)#

ts
import { SegOpsClient } from '@segops/sdk';

const segops = new SegOpsClient({
  apiUrl: 'https://api.segops.ai',
  apiKey: 'pk_live_…',            // public key → handshake kicks in automatically
  getUser: () => ({
    userId: currentUser?.id,      // or anonymousId for logged-out visitors
    ...signedFields,              // from your backend, if signed uid is required
  }),
});

segops.track({ userId: currentUser.id, eventType: 'page_viewed' });

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 of SECRET_KEY; rotating it invalidates outstanding session tokens within 15 min).