Skip to content

Authentication

Public keys, private keys and dashboard JWTs — which credential reaches which service, and what each error code means.

Updated View as Markdown

Pug has three auth boundaries. Each maps to a set of RPC services — using the wrong credential returns Unauthenticated or PermissionDenied.

The three credentials

Credential Format Used by Header Reaches
Public key pub_… Browser / app SDK x-api-key sdk.events.v1.EventsService, sdk.profiles.v1.ProfilesSDKService
Private key prv_… Your server x-api-key shared.* — insights, profiles, activity
JWT session token Dashboard Authorization: Bearer dashboard.* (and shared.*)

The project is resolved from the API key, so SDK and server requests don’t send a project ID. Dashboard JWT requests optionally include an x-project-id header to scope to a project.

Where keys live

Keys live under Settings → API Keys. A project holds any number of them, so you can issue one per app or service and revoke it on its own. Your project Overview also shows the public key next to the SDK setup snippets.

  • Public key is displayed in plain text — it’s safe in client code. It can write events and identify users, but cannot read analytics.
  • Private key is shown once, when you create it — copy it then. Pug stores only a digest of the key, so it cannot be revealed afterwards; the list shows a mask (prv_…3f9c) to tell your private keys apart. Treat it like a password. It can read insights, profiles, and activity.

There is no rotate button. To replace a key, create its replacement, update your SDK apiKey / server env var, deploy, then revoke the old one.

Public key — client SDKs

The Web and Flutter SDKs take the public key as apiKey and send it as x-api-key on every request:

import { init } from '@pug-sh/browser'

init('YOUR_PROJECT_ID', {
  apiKey: 'pub_YOUR_PUBLIC_KEY'
})

The public key is write-scoped: BatchCreate (events) and Identify (profiles). It cannot reach the shared.* read APIs, so it’s safe to ship in a browser bundle or mobile binary.

Private key — your servers

Use the private key from trusted server-side code to read analytics. The Node SDK wraps these reads — construct a client with your private key and call pug.insights, pug.profiles, or pug.activity:

import { Pug } from '@pug-sh/node'

const pug = new Pug({ apiKey: process.env.PUG_PRIVATE_KEY! })

const result = await pug.insights.query({ /* spec, timeRange, granularity */ })

From any other language, send the private key as x-api-key against the shared.* services (which accept a private key or a dashboard JWT):

curl -X POST https://api.pugs.dev/shared.insights.v1.InsightsService/Query \
  -H "x-api-key: prv_YOUR_PRIVATE_KEY" \
  -H "Connect-Protocol-Version: 1" \
  -H "Content-Type: application/json" \
  -d '{ /* InsightQuerySpec */ }'

Replace the body with a real InsightQuerySpec — see Insights API for the field-by-field shape and a complete example.

Never ship a private key to a browser, mobile app, or public repo.

JWT — the dashboard

The dashboard obtains a short-lived JWT when a user signs in, and sends it as Authorization: Bearer <jwt> (plus x-project-id to scope to a project). JWTs reach the dashboard.* management services and the shared.* read services. They’re refreshed automatically by the dashboard client — don’t hard-code them in scripts; use a private key for automation instead.

Connect RPC

All APIs speak Connect RPC over HTTP/2 and accept the Connect, gRPC, and gRPC-Web protocols. The request path is /<package>.<Service>/<Method>, e.g. /sdk.events.v1.EventsService/BatchCreate. For JSON requests, include Connect-Protocol-Version: 1.

To send events without an SDK, see Integrate over HTTP. To generate a typed client from the protobuf definitions, see the API overview.

Error responses

Connect returns structured errors:

Code Meaning Typical cause
Unauthenticated Missing or invalid credential Wrong key, missing x-api-key
PermissionDenied Valid credential, wrong scope Public key calling a shared.* read API
InvalidArgument Payload failed validation A rejected field — see below
NotFound Resource doesn’t exist Wrong project or profile ID

Common InvalidArgument causes:

  • A required field is missing.
  • A custom property key is prefixed with $ (reserved for auto-properties).
  • kind (the event name) starts with pug. (a reserved prefix).

Responses include field-level details from server-side validation — fix the named field and retry.

Advanced — proxying

If you need server-side logic before events reach Pug (enrichment, filtering, auth of your own), run your own endpoint that forwards requests to Pug with the appropriate key. This keeps the credential on your backend instead of in the client.

Further reading

Navigation

Type to search…

↑↓ navigate↵ selectEsc close