Skip to content

Keys (publishable vs secret)

Every UserVane project has two keys. Mixing them up is the fastest way to either break production or leak a credential.

Safe inBrowser, mobile client, any public bundle
Used forUserVane.init / UserVaneProvider apiKey, bootstrap, submit, dismissals
RoutesPOST /v1/sdk/bootstrap, POST /v1/sdk/responses, POST /v1/dismissals

The publishable key is world-readable by design (same model as analytics write keys). It is not a proof of respondent identity. Integrity comes from the show-token, not from hiding the pk.

Origin allowlisting is defense-in-depth. Empty allowlists fail closed for browser calls.

Safe inYour server, cron, edge function env only
Never inNEXT_PUBLIC_*, client components, mobile apps, git
Used forSession-bound token mint, pending-scores queue, privacy export/erase
RoutesPOST /v1/sdk/tokens, GET /v1/sdk/pending-scores, POST /v1/sdk/pending-scores/ack, GET /v1/privacy/export, DELETE /v1/privacy/erase

Auth header forms:

Authorization: Bearer uv_sk_...

or

X-UserVane-Key: uv_sk_...

A publishable key sent as Bearer on a secret route returns 401. That failure mode is intentional: session binding is only non-forgeable because mint requires the secret.

Package / APIKey
@uservane/browser UserVane.init({ key })Publishable
@uservane/react UserVaneProvider apiKeyPublishable
@uservane/vercel-ai UserVaneProvider apiKeyPublishable
@uservane/vercel-ai/server mintFeedbackToken({ secretKey })Secret
@uservane/langfuse-push pushPendingScores({ uservane: { secretKey } })Secret
  1. Mint bound tokens on the server that already knows sessionId and the respondent id.
  2. Thread only showToken + sessionId (+ survey id) to the client, never the secret.
  3. Rotate the secret if it ever lands in a client bundle or ticket.
  4. Treat the publishable key as public; still do not put secrets in targeting traits (targeting is readable to anyone with the pk).