---
title: "Core concepts"
description: "Orgs, projects, events, profiles, sessions and insights — the vocabulary every other page builds on."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.pug.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Core concepts

Shared vocabulary used across the SDKs, the API, and the dashboard. Read this once — every other page builds on these ideas.

## Organization and project

Pug is multi-tenant. Your data always lives inside a **project**; projects belong to an **organization**. You only deal with these for billing and access — the things you actually instrument are **events** and **profiles**.

- `Organization` — billing & member boundary
  - `Members (roles)`
  - `Project` — data boundary
    - `API keys (public + private)`
    - `Events`
    - `Profiles`

| Concept | What it is | Scope |
|---------|-----------|-------|
| **Organization** | Team workspace with members and billing | All projects in the org |
| **Project** | Isolated analytics environment | Events, profiles, insights |
| **Public key** (`pub_…`) | Client credential for SDKs | Single project |
| **Private key** (`prv_…`) | Secret credential for your servers | Single project |

The dashboard URL is prefixed with the active project: `/p/<projectId>/…`.

## Event pipeline

When your app calls `track()`, the event travels through an async pipeline before it appears in analytics.

  <Stage title="SDK (browser / app)">{"track('purchase', { productId: 'sku_123', amount: 29.99, currency: 'USD' })"}</Stage>

**Timing:** ingestion is near-real-time. Events typically appear in **Live** within seconds and in **Insights** queries within the same minute. Profile writes are fully async, processed by profile workers.

## Events

An **event** is a named occurrence with properties and a timestamp.

```ts
track('button_clicked', { label: 'Sign up', page: '/pricing' })
```

- **Event name** (`kind`) — any string; [well-known events](/reference/well-known-events) have typed schemas. Names starting with `pug.` are reserved.
- **Custom properties** — your key/value pairs (string, number, boolean, timestamp).
- **Auto-properties** — fields prefixed with `$` that the SDK and server attach automatically (e.g. `$url`, `$browser`, `$country`). `$`-prefixed keys are reserved — see [Auto-properties](/reference/auto-properties).
- **Timestamp** — defaults to send time; override per event for offline replay.

Events are immutable once stored. Corrections mean a new event or a profile-trait update.

## Profiles

A **profile** represents a user — anonymous or identified.

### Anonymous → identified

1. A visitor arrives. The SDK creates an **anonymous ID** (`anon-…`) tied to the device.
2. Events accumulate against that anonymous ID.
3. The user signs in. You call `identify('user-123', { email: '…' })`.
4. On that **first** identify, the SDK sends the anonymous ID alongside your external ID so the backend can **merge** the prior anonymous activity into the identified profile.

```ts
// Before sign-in — anonymous
track('page_view')
track('add_to_cart', { productId: 'sku_123' })

// After sign-in — merges anonymous history into the identified profile
await identify('user-123', { email: 'user@example.com', plan: 'pro' })
track('purchase', { productId: 'sku_123', amount: 29.99, currency: 'USD' })
```

Three ID terms recur across the SDKs and API:

| ID | What it is | When used |
|----|-----------|-----------|
| **Anonymous ID** (`anon-…`) | Device ID the SDK generates | Before sign-in |
| **External ID** | Your stable user ID, passed to `identify()` | After sign-in |
| **Distinct ID** | The umbrella value stored on every event | Anonymous ID until identified, then external ID |

### Storage and timing

- **Writes** (identify, trait updates, merges) are processed asynchronously by profile workers via NATS.
- Expect a few seconds between an `identify()` call and the profile appearing in dashboard search.
- Use a **stable** external ID (your database user ID), not something that changes like an email.

## Sessions

A **session** groups events within a single visit. SDKs manage session IDs automatically:

- A new session starts on the first event after the **idle timeout** (default 30 minutes of inactivity).
- The session extends on each event inside the window, up to a **max duration** (default 24 hours).
- The Web SDK syncs the session ID across browser tabs via `localStorage`.

Force a new session without clearing identity with `rotate()`. Clear identity and start fresh with `reset()`.

## Insights

An **insight** is a query specification — the definition of an analytics question, not a saved chart:

| Field | Example |
|-------|---------|
| Events | `page_view`, `purchase` |
| Time range | Last 7 days |
| Granularity | Day |
| Aggregation | Unique users |
| Breakdown | `$country` |
| Filters | `plan = 'pro'` |

The same insight model powers the dashboard's charts and the [`shared.insights.v1.InsightsService`](/api/insights) API, which you can query from your own backend with a **private key**. Insight types include trends, funnels, retention, segmentation, user flow, and session metrics.

## Authentication modes

Pug has three auth boundaries: the **public key** (`pub_…`) authenticates client SDKs, the **private key** (`prv_…`) authenticates your server, and a **JWT** authenticates the dashboard. See [Authentication](/get-started/authentication) for the full matrix of headers, scopes, and examples.

## Further reading

- [Authentication](/get-started/authentication) — keys, headers, Connect RPC
- [Well-known events](/reference/well-known-events) — typed event schemas
- [Glossary](/reference/glossary) — quick term lookup

Source: https://docs.pug.sh/get-started/concepts/index.mdx
