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

# Data model

> The tables behind Stage, grouped by what they hold. Every row carries an org id.

Every table is scoped by `org_id`. Row-level security enforces the scope for anything the admin reads or writes directly. Tables the API owns outright have no client policies at all.

## Content

| Table                  | Holds                                                                                     |
| ---------------------- | ----------------------------------------------------------------------------------------- |
| `pages`                | `slug`, `components` (the stream), SEO fields, `version`, `updated_by`, `updated_by_name` |
| `articles`             | Title, date, slug, `body` (a stream), status, `publish_at`, `version`                     |
| `article_drafts`       | The shadow of a published article's edits until the next publish                          |
| `forms`, `form_drafts` | Forms and their shadows, same shape                                                       |
| `globals`              | Site-wide content keyed by name. No version counter; last write wins                      |
| `media`                | One row per upload: hash, dimensions, alt text, kind                                      |

The public read policy on articles and forms is published-only, and for articles also respects `publish_at`.

## Organisation

| Table                  | Holds                                                                                                                                            |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `organizations`        | `settings` JSON: editing on, private mode, publish mode, site URL, feature flags, GA id, consent gating, notification addresses, last build time |
| `org_secrets`          | Server-only: deploy hook URL, revalidation secret. Never readable from a browser                                                                 |
| `users`, `memberships` | People and their per-org role, `editor` or `admin`                                                                                               |
| `access_requests`      | Requests to join a private site. API-only                                                                                                        |

## Tracking

| Table                       | Holds                                                                                                                                         |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `visits`                    | Session, path, referrer, search query, UTMs, device, country, city, `ip_key`, `ip_hash`, consent, then `scroll_depth`, `time_seconds`, clicks |
| `ai_crawls`, `ai_referrals` | AI user-agent hits by category and intent; human-triggered fetches                                                                            |
| `persons`, `person_events`  | People who identified themselves, and their event timeline (refs only, never answers)                                                         |
| `form_submissions`          | Answers, self-describing with the label as asked, linked to a person when one exists                                                          |
| `analytics_cache`           | The per-org, per-range dashboard payload, refreshed on read when stale                                                                        |

## Campaigns

| Table              | Holds                                                                                               |
| ------------------ | --------------------------------------------------------------------------------------------------- |
| `campaigns`        | Title, canvas JSON, status, and the frozen `ref` used in UTMs                                       |
| `campaign_posts`   | One row per scheduled frame: channel, account, payload, schedule, status, result, polling state     |
| `campaign_links`   | Every minted link: the code, the decorated destination, `hits`. The campaign-level link has no post |
| `campaign_metrics` | Platform metric snapshots per post over time                                                        |
| `social_accounts`  | Connected accounts with encrypted tokens                                                            |
| `social_api_usage` | Per-day, per-platform call budget                                                                   |

All campaign tables are API-only.

## Functions

The heavy lifting is in database functions under `SECURITY DEFINER`, each asserting org access first and executable only by the API: analytics aggregation, the live pulse, engagement updates, person upsert, link hit counting, the social call budget.

## Change rules

Additive by default, applied fleet-wide. See [Versioning and releases](/developers/reference/releases#expand-migrate-contract).
