···11# HappyView
2233-HappyView is the best way to build an [AppView](https://atproto.com/guides/glossary#app-view) for the [AT Protocol](https://atproto.com). Upload your [lexicon](reference/glossary.md#atproto-terms) schemas and get a fully functional AppView, complete with [XRPC](reference/glossary.md#atproto-terms) endpoints, OAuth, real-time network sync, and historical [backfill](guides/backfill.md), without writing a single line of server code.
33+HappyView is the best way to build an [AppView](https://atproto.com/guides/glossary#app-view) for the [AT Protocol](https://atproto.com). Upload your [lexicon](reference/glossary.md#atproto-terms) schemas and get a fully functional AppView, complete with [XRPC](reference/glossary.md#atproto-terms) endpoints, OAuth, real-time network sync, and historical [backfill](guides/indexing/backfill.md), without writing a single line of server code.
4455Building an AppView from scratch means wiring up real-time event streams, record storage, XRPC routing, OAuth flows, and PDS write proxying before you can even think about your application. HappyView handles all of that. Define your data model with lexicons, add custom logic with Lua scripts when you need it, and ship your app.
6677## Features
8899-- **Schema-driven endpoints:** Upload a [lexicon](guides/lexicons.md) and HappyView generates XRPC query and procedure routes, storage, and indexing from it — updatable at runtime with no restart.
99+- **Schema-driven endpoints:** Upload a [lexicon](guides/indexing/lexicons.md) and HappyView generates XRPC query and procedure routes, storage, and indexing from it — updatable at runtime with no restart.
10101111-- **Network sync built in:** Real-time record streaming via [Jetstream](https://github.com/bluesky-social/jetstream), historical [backfill](guides/backfill.md) from each user's PDS, and atproto OAuth with DPoP-bound proxy writes back to the PDS.
1111+- **Network sync built in:** Real-time record streaming via [Jetstream](https://github.com/bluesky-social/jetstream), historical [backfill](guides/indexing/backfill.md) from each user's PDS, and atproto OAuth with DPoP-bound proxy writes back to the PDS.
12121313-- **Customize with Lua, hooks, and plugins:** [Lua scripts](guides/scripting.md) for query and procedure logic, [index hooks](guides/index-hooks.md) that fire on every record change, WASM [plugins](guides/plugins.md) for external platform integration, and [labeler](guides/labelers.md) subscriptions for content moderation.
1313+- **Customize with Lua, hooks, and plugins:** [Lua scripts](guides/scripting.md) for query and procedure logic, [index hooks](guides/indexing/index-hooks.md) that fire on every record change, WASM [plugins](guides/features/plugins.md) for external platform integration, and [labeler](guides/features/labelers.md) subscriptions for content moderation.
14141515-- **Protocol-native:** Works with any PDS, resolves DIDs through the directory, and fetches [network lexicons](guides/lexicons.md#network-lexicons) via DNS authority resolution.
1515+- **Protocol-native:** Works with any PDS, resolves DIDs through the directory, and fetches [network lexicons](guides/indexing/lexicons.md#network-lexicons) via DNS authority resolution.
16161717-- **Full admin surface:** Built-in [dashboard](getting-started/dashboard.md) and [admin API](reference/admin-api.md) for managing lexicons, users, API keys, API clients, backfill jobs, and plugins.
1717+- **Full admin surface:** Built-in [dashboard](getting-started/dashboard.md) and [admin API](reference/admin/admin-api.md) for managing lexicons, users, API keys, API clients, backfill jobs, and plugins.
18181919## Design Principles
2020···2929## Next Steps
30303131- [Quickstart](getting-started/deployment/railway.md): Deploy HappyView on Railway or run it locally
3232-- [Lexicons](guides/lexicons.md): Upload lexicon schemas and start indexing records
3232+- [Lexicons](guides/indexing/lexicons.md): Upload lexicon schemas and start indexing records
3333- [Lua Scripting](guides/scripting.md): Write custom query and procedure logic
3434-- [Index Hooks](guides/index-hooks.md): React to record changes in real time
3535-- [Labelers](guides/labelers.md): Subscribe to external labelers and manage content labels
3636-- [Plugins](guides/plugins.md): Integrate with external platforms using WASM plugins
3737-- [Event Logs](guides/event-logs.md): Monitor system activity, debug script errors, and audit admin actions
3434+- [Index Hooks](guides/indexing/index-hooks.md): React to record changes in real time
3535+- [Labelers](guides/features/labelers.md): Subscribe to external labelers and manage content labels
3636+- [Plugins](guides/features/plugins.md): Integrate with external platforms using WASM plugins
3737+- [Event Logs](guides/admin/event-logs.md): Monitor system activity, debug script errors, and audit admin actions
···33HappyView has two distinct authentication surfaces:
4455- **XRPC** (`/xrpc/*`) — client-level identification via an **API client key** on every request, plus optional user-level atproto OAuth for endpoints that need a specific user's identity (e.g. procedures that write to a PDS).
66-- **Admin API** (`/admin/*`) — user-level authentication via admin API keys or service auth JWTs, gated by [permissions](../guides/permissions.md).
66+- **Admin API** (`/admin/*`) — user-level authentication via admin API keys or service auth JWTs, gated by [permissions](../guides/admin/permissions.md).
7788## Which endpoints require what?
99···1111| ---------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------- |
1212| Queries (`GET /xrpc/{method}`) | `X-Client-Key` required | Optional — DPoP auth if the query needs to know who the user is |
1313| Procedures (`POST /xrpc/{method}`) | `X-Client-Key` required | Required — DPoP auth so HappyView can proxy writes to the user's PDS |
1414-| Admin API (`/admin/*`) | — | Required — admin API key or service auth JWT with the right [permissions](../guides/permissions.md) |
1414+| Admin API (`/admin/*`) | — | Required — admin API key or service auth JWT with the right [permissions](../guides/admin/permissions.md) |
1515| Health check (`GET /health`) | — | — |
16161717## XRPC: API client identification
···91919292### Admin API key
93939494-For automation — CI/CD, monitoring, cron jobs — create an [admin API key](../guides/api-keys.md) at **Settings > API Keys** or via `POST /admin/api-keys` and pass it as a bearer token:
9494+For automation — CI/CD, monitoring, cron jobs — create an [admin API key](../guides/admin/api-keys.md) at **Settings > API Keys** or via `POST /admin/api-keys` and pass it as a bearer token:
95959696```sh
9797export TOKEN="hv_your-api-key-here"
···315315## Next steps
316316317317- [JavaScript SDK](../sdk/overview.md) — authenticate and make XRPC calls from JavaScript
318318-- [Permissions](../guides/permissions.md) — full list of permissions and what each one grants
319319-- [API Keys](../guides/api-keys.md) — create scoped admin API keys for automation
318318+- [Permissions](../guides/admin/permissions.md) — full list of permissions and what each one grants
319319+- [API Keys](../guides/admin/api-keys.md) — create scoped admin API keys for automation
320320- [Admin API — API Clients](../reference/admin/api-clients.md) — register API clients and configure rate limits
···11# Configuration
2233-HappyView is configured via environment variables. A `.env` file in the project root is loaded automatically on startup. See [Deployment](deployment/docker.md) for local setup or [Production Deployment](../reference/production-deployment.md) for production setup.
33+HappyView is configured via environment variables. A `.env` file in the project root is loaded automatically on startup. See [Deployment](deployment/docker.md) for local setup or [Production Deployment](production-deployment.md) for production setup.
4455## Environment variables
66···1313| `HOST` | no | `0.0.0.0` | Bind host |
1414| `PORT` | no | `3000` | Bind port |
1515| `JETSTREAM_URL` | no | `wss://jetstream1.us-east.bsky.network` | Jetstream WebSocket URL for real-time record streaming |
1616-| `RELAY_URL` | no | `https://bsky.network` | Relay URL for [backfill](../guides/backfill.md) repo discovery |
1616+| `RELAY_URL` | no | `https://bsky.network` | Relay URL for [backfill](../guides/indexing/backfill.md) repo discovery |
1717| `PLC_URL` | no | `https://plc.directory` | [PLC directory](https://github.com/did-method-plc/did-method-plc) URL for DID resolution |
1818| `STATIC_DIR` | no | `./web/out` | Directory containing the built dashboard static assets |
1919| `EVENT_LOG_RETENTION_DAYS` | no | `30` | Number of days to keep event logs before automatic cleanup. Set to `0` to disable cleanup |
···59596060- [Authentication](authentication.md) — set up OAuth and admin users
6161- [Dashboard](dashboard.md) — explore the admin dashboard
6262-- [Production deployment](../reference/production-deployment.md) — deploy HappyView to production
6262+- [Production deployment](production-deployment.md) — deploy HappyView to production
+9-9
packages/docs/docs/getting-started/dashboard.md
···11# Dashboard
2233-HappyView ships with a web dashboard that provides a visual interface for everything the [admin API](../reference/admin-api.md) offers. It runs as a separate Next.js application alongside the Rust backend and authenticates via atproto OAuth.
33+HappyView ships with a web dashboard that provides a visual interface for everything the [admin API](../reference/admin/admin-api.md) offers. It runs as a separate Next.js application alongside the Rust backend and authenticates via atproto OAuth.
4455On a fresh deployment with no users in the database, the first person to log in to the dashboard is automatically bootstrapped as the super user with all permissions — so log in with the handle you want to own the instance first.
66···23232424Toggle **Enable backfill** to index historical records when uploading a record-type lexicon.
25252626-**Network** lexicons are fetched from the atproto network. Enter an NSID (e.g. `xyz.statusphere.status`) and HappyView resolves the schema automatically. If found, the lexicon JSON is displayed in a read-only editor. Click **Add** to track it. Network lexicons are kept up to date via the Jetstream subscription. See [Lexicons - Network lexicons](../guides/lexicons.md#network-lexicons) for how resolution works.
2626+**Network** lexicons are fetched from the atproto network. Enter an NSID (e.g. `xyz.statusphere.status`) and HappyView resolves the schema automatically. If found, the lexicon JSON is displayed in a read-only editor. Click **Add** to track it. Network lexicons are kept up to date via the Jetstream subscription. See [Lexicons - Network lexicons](../guides/indexing/lexicons.md#network-lexicons) for how resolution works.
27272828### JSON editor
2929···45454646## Backfill
47474848-Navigate to **Backfill** to view and manage backfill jobs. You can start a new backfill for any record-type lexicon to import historical records from the network. The page shows job status, progress (repos processed / total), and record counts. See [Backfill](../guides/backfill.md) for how the process works.
4848+Navigate to **Backfill** to view and manage backfill jobs. You can start a new backfill for any record-type lexicon to import historical records from the network. The page shows job status, progress (repos processed / total), and record counts. See [Backfill](../guides/indexing/backfill.md) for how the process works.
49495050## Users
51515252-Navigate to **Users** to manage who can access the admin API and dashboard. You can add users by DID, assign permissions individually or via a template (`viewer`, `operator`, `manager`, `full_access`), and remove users. The super user is highlighted and has all permissions by default. See [Permissions](../guides/permissions.md) for what each permission grants.
5252+Navigate to **Users** to manage who can access the admin API and dashboard. You can add users by DID, assign permissions individually or via a template (`viewer`, `operator`, `manager`, `full_access`), and remove users. The super user is highlighted and has all permissions by default. See [Permissions](../guides/admin/permissions.md) for what each permission grants.
53535454## Events
5555···69697070### API Keys
71717272-Create and revoke admin API keys for automation. Each key is scoped to specific permissions and tied to the creating user. See [API Keys](../guides/api-keys.md) for details.
7272+Create and revoke admin API keys for automation. Each key is scoped to specific permissions and tied to the creating user. See [API Keys](../guides/admin/api-keys.md) for details.
73737474### Users
7575···77777878### Plugins
79798080-Manage installed plugins and configure plugin secrets. Plugins extend HappyView with additional functionality. Plugin secrets are encrypted at rest when `TOKEN_ENCRYPTION_KEY` is configured. See [Plugins](../guides/plugins.md) for details.
8080+Manage installed plugins and configure plugin secrets. Plugins extend HappyView with additional functionality. Plugin secrets are encrypted at rest when `TOKEN_ENCRYPTION_KEY` is configured. See [Plugins](../guides/features/plugins.md) for details.
81818282### Labelers
83838484-Configure labeler subscriptions for content labeling. See [Labelers](../guides/labelers.md) for details.
8484+Configure labeler subscriptions for content labeling. See [Labelers](../guides/features/labelers.md) for details.
85858686### Environment Variables
8787···93939494## Next steps
95959696-- [Lexicons](../guides/lexicons.md) — how lexicons drive HappyView's indexing and routing
9696+- [Lexicons](../guides/indexing/lexicons.md) — how lexicons drive HappyView's indexing and routing
9797- [Lua Scripting](../guides/scripting.md) — write custom query and procedure logic
9898-- [Permissions](../guides/permissions.md) — manage user access to admin features
9898+- [Permissions](../guides/admin/permissions.md) — manage user access to admin features
9999- [Configuration](configuration.md) — full list of environment variables
···3434The `happyview` container serves its own bundled dashboard at `http://localhost:3000`, but that copy is baked in at container build time and only updates when you rebuild the image. For day-to-day development, use the dev dashboard at `http://localhost:3001` — it hot-reloads on changes to the `web/` source.
35353636:::tip
3737-SQLite is the default and requires no extra services. To use Postgres instead, uncomment the `postgres` service in `docker-compose.yml` and update `DATABASE_URL` in `.env`. See the [database setup guide](../../guides/database-setup.md).
3737+SQLite is the default and requires no extra services. To use Postgres instead, uncomment the `postgres` service in `docker-compose.yml` and update `DATABASE_URL` in `.env`. See the [database setup guide](../../guides/database/database-setup.md).
3838:::
39394040## Next steps
···3030DATABASE_URL=postgres://happyview:happyview@localhost/happyview
3131```
32323333-See [Configuration](../configuration.md) for all available variables and the [database setup guide](../../guides/database-setup.md) for details on both backends.
3333+See [Configuration](../configuration.md) for all available variables and the [database setup guide](../../guides/database/database-setup.md) for details on both backends.
34343535## 2. Create the database (Postgres only)
3636
···24242525- [Configuration](../configuration.md) — full list of environment variables
2626- [Dashboard](../dashboard.md) — manage lexicons, users, and plugins via the web UI
2727-- [Production deployment](../../reference/production-deployment.md) — hardening checklist for production instances
2727+- [Production deployment](../production-deployment.md) — hardening checklist for production instances
···14141515## Token encryption key
16161717-If you use [plugins](../guides/plugins.md) that require secrets (API keys, OAuth credentials), set `TOKEN_ENCRYPTION_KEY` to a base64-encoded 32-byte key. This encrypts plugin secrets at rest using AES-256-GCM:
1717+If you use [plugins](../guides/features/plugins.md) that require secrets (API keys, OAuth credentials), set `TOKEN_ENCRYPTION_KEY` to a base64-encoded 32-byte key. This encrypts plugin secrets at rest using AES-256-GCM:
18181919```sh
2020openssl rand -base64 32
···4040- Larger-than-memory working sets
4141- External tools that need direct read access to the records table
42424343-See the [database setup guide](../guides/database-setup.md) for configuration details and [Postgres → SQLite migration](../guides/postgres-to-sqlite-migration.md) if you're moving the other direction. Migrations run automatically on startup regardless of backend.
4343+See the [database setup guide](../guides/database/database-setup.md) for configuration details and [Postgres → SQLite migration](../guides/database/postgres-to-sqlite-migration.md) if you're moving the other direction. Migrations run automatically on startup regardless of backend.
44444545## Rate limits
46464747-HappyView has a per-client token-bucket rate limiter for XRPC endpoints. The defaults (set via `DEFAULT_RATE_LIMIT_CAPACITY` and `DEFAULT_RATE_LIMIT_REFILL_RATE`) apply to any [API client](../guides/api-keys.md) that doesn't have per-client overrides. Raise the defaults cautiously — they exist so one misbehaving integrator can't saturate the server.
4747+HappyView has a per-client token-bucket rate limiter for XRPC endpoints. The defaults (set via `DEFAULT_RATE_LIMIT_CAPACITY` and `DEFAULT_RATE_LIMIT_REFILL_RATE`) apply to any [API client](../guides/admin/api-keys.md) that doesn't have per-client overrides. Raise the defaults cautiously — they exist so one misbehaving integrator can't saturate the server.
48484949-Per-client overrides are set at client creation or via `PUT /admin/api-clients/{id}` (see [Admin API — API Clients](admin/api-clients.md)).
4949+Per-client overrides are set at client creation or via `PUT /admin/api-clients/{id}` (see [Admin API — API Clients](../reference/admin/api-clients.md)).
50505151## Logging
5252···60606161## Event log retention
62626363-The admin [event log](../guides/event-logs.md) is stored in the same database as records. `EVENT_LOG_RETENTION_DAYS` (default `30`) controls automatic cleanup. Set to `0` to keep events indefinitely — useful for compliance-sensitive deployments, but plan for database growth.
6363+The admin [event log](../guides/admin/event-logs.md) is stored in the same database as records. `EVENT_LOG_RETENTION_DAYS` (default `30`) controls automatic cleanup. Set to `0` to keep events indefinitely — useful for compliance-sensitive deployments, but plan for database growth.
64646565## Health checks
6666···7373- **SQLite**: back up the database file (e.g. `data/happyview.db`) plus its `-wal` and `-shm` sidecar files. Use `sqlite3 happyview.db ".backup '/path/backup.db'"` for a consistent snapshot while HappyView is running.
7474- **Postgres**: standard `pg_dump` / managed-Postgres snapshots.
75757676-Most of what HappyView stores is derivable from the network — lost records can be re-indexed via [backfill](../guides/backfill.md). You can't recover from the network: user accounts and permissions, API keys, API clients, plugin secrets, and the Jetstream cursor. Prioritize those in your backup plan.
7676+Most of what HappyView stores is derivable from the network — lost records can be re-indexed via [backfill](../guides/indexing/backfill.md). You can't recover from the network: user accounts and permissions, API keys, API clients, plugin secrets, and the Jetstream cursor. Prioritize those in your backup plan.
77777878## Next steps
79798080- [Configuration](../getting-started/configuration.md) — full environment variable reference
8181-- [Permissions](../guides/permissions.md) — lock down admin access before exposing the dashboard publicly
8282-- [Troubleshooting](troubleshooting.md) — diagnose issues with a running instance
8181+- [Permissions](../guides/admin/permissions.md) — lock down admin access before exposing the dashboard publicly
8282+- [Troubleshooting](../reference/troubleshooting.md) — diagnose issues with a running instance
+2-2
packages/docs/docs/getting-started/quickstart.md
···29293030HappyView starts indexing records for that collection. A backfill job fetches historical records, and new records stream in via Jetstream.
31313232-You can also upload lexicons manually via the dashboard or the [admin API](../reference/admin-api.md). See [Lexicons](../guides/lexicons.md) for the full details.
3232+You can also upload lexicons manually via the dashboard or the [admin API](../reference/admin/admin-api.md). See [Lexicons](../guides/indexing/lexicons.md) for the full details.
33333434## 4. Verify records are being indexed
3535···5050## Next steps
51515252- [**Statusphere tutorial**](../tutorials/statusphere.md): full walkthrough building a complete AppView with record, query, and procedure lexicons
5353-- [**Lexicons guide**](../guides/lexicons.md): target collections, backfill flag, network lexicons
5353+- [**Lexicons guide**](../guides/indexing/lexicons.md): target collections, backfill flag, network lexicons
5454- [**Lua Scripting**](../guides/scripting.md): custom query and procedure logic
5555- [**Configuration**](configuration.md): environment variables and tuning
5656- [**Authentication**](authentication.md): how OAuth works and how to get API tokens
+4-4
packages/docs/docs/guides/admin/api-keys.md
···3131 -H "Authorization: Bearer hv_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
3232```
33333434-This works for all [admin API](../reference/admin-api.md) endpoints that the key has permissions for. Unlike OAuth tokens which carry the user's full permissions, API keys are limited to the specific permissions assigned at creation time.
3434+This works for all [admin API](../../reference/admin/admin-api.md) endpoints that the key has permissions for. Unlike OAuth tokens which carry the user's full permissions, API keys are limited to the specific permissions assigned at creation time.
35353636## Revoking a key
3737···55555656## Next steps
57575858-- [Admin API reference](../reference/admin-api.md) — full endpoint documentation
5959-- [Scripting](scripting.md) — automate record processing with Lua scripts
6060-- [Index hooks](index-hooks.md) — push records to external services on write
5858+- [Admin API reference](../../reference/admin/admin-api.md) — full endpoint documentation
5959+- [Scripting](../scripting.md) — automate record processing with Lua scripts
6060+- [Index hooks](../indexing/index-hooks.md) — push records to external services on write
+9-9
packages/docs/docs/guides/admin/event-logs.md
···11# Event Logs
2233-HappyView maintains an internal event log that records system activity — lexicon changes, record operations, Lua script executions and errors, user actions, API key events, backfill jobs, and Jetstream connectivity. Events are stored in the database and queryable via the [admin API](../reference/admin/events.md).
33+HappyView maintains an internal event log that records system activity — lexicon changes, record operations, Lua script executions and errors, user actions, API key events, backfill jobs, and Jetstream connectivity. Events are stored in the database and queryable via the [admin API](../../reference/admin/events.md).
4455## Event types
66···1414| `lexicon.updated` | info | Lexicon NSID | `revision`, `has_script`, `source` |
1515| `lexicon.deleted` | info | Lexicon NSID | — |
16161717-Logged when lexicons are uploaded, updated, or deleted via the [admin API](../reference/admin/lexicons.md). The `actor_did` is the user who performed the action.
1717+Logged when lexicons are uploaded, updated, or deleted via the [admin API](../../reference/admin/lexicons.md). The `actor_did` is the user who performed the action.
18181919### Record events
2020···4848| `user.permissions_updated` | info | User ID | `granted`, `revoked` |
4949| `user.super_transferred` | warn | New super user ID | `from_user_id` |
50505151-The `user.bootstrapped` event is logged when the first user is auto-promoted to super user (see [Auth - Auto-bootstrap](../reference/admin-api.md#auth)).
5151+The `user.bootstrapped` event is logged when the first user is auto-promoted to super user (see [Auth - Auto-bootstrap](../../reference/admin/admin-api.md#auth)).
52525353### Auth events
5454···7979| `hook.executed` | info | Record AT URI | `lexicon_id` |
8080| `hook.dead_lettered` | error | Record AT URI | `lexicon_id`, `error` |
81818282-Logged when [index hooks](index-hooks.md) run. Dead-lettered events indicate a hook failed all retry attempts. You can manage dead letters from the **Data > Dead Letters** page in the dashboard — see [Dead Letters](#dead-letters) below.
8282+Logged when [index hooks](../indexing/index-hooks.md) run. Dead-lettered events indicate a hook failed all retry attempts. You can manage dead letters from the **Data > Dead Letters** page in the dashboard — see [Dead Letters](#dead-letters) below.
83838484### Backfill events
8585···8989| `backfill.completed` | info | Collection NSID | `job_id`, `total_repos` |
9090| `backfill.failed` | error | Collection NSID | `job_id`, `error` |
91919292-See [Backfill](backfill.md) for background on backfill jobs.
9292+See [Backfill](../indexing/backfill.md) for background on backfill jobs.
93939494### Jetstream events
9595···118118curl "http://localhost:3000/admin/events?limit=20&cursor=2026-03-01T11:59:00Z" -H "$AUTH"
119119```
120120121121-See the [Admin API reference](../reference/admin/events.md#list-event-logs) for full parameter documentation.
121121+See the [Admin API reference](../../reference/admin/events.md#list-event-logs) for full parameter documentation.
122122123123## Retention
124124···126126127127Set `EVENT_LOG_RETENTION_DAYS=0` to disable automatic cleanup and keep logs indefinitely.
128128129129-See [Configuration](../getting-started/configuration.md) for all environment variables.
129129+See [Configuration](../../getting-started/configuration.md) for all environment variables.
130130131131## Dead Letters
132132···142142143143## Next steps
144144145145-- [Admin API — Event Logs](../reference/admin/events.md) — full query parameters and response format
145145+- [Admin API — Event Logs](../../reference/admin/events.md) — full query parameters and response format
146146- [Permissions](permissions.md) — control which users can read event logs
147147-- [Troubleshooting](../reference/troubleshooting.md) — using event logs to diagnose issues
147147+- [Troubleshooting](../../reference/troubleshooting.md) — using event logs to diagnose issues
+2-2
packages/docs/docs/guides/admin/permissions.md
···132132- `PATCH /admin/users/{id}/permissions` — grant or revoke individual permissions
133133- `POST /admin/users/transfer-super` — transfer super user status (super user only)
134134135135-See the [Admin API — Users](../reference/admin/users.md) for full details.
135135+See the [Admin API — Users](../../reference/admin/users.md) for full details.
136136137137## Next steps
138138139139-- [Admin API reference](../reference/admin-api.md) — endpoint documentation with required permissions
139139+- [Admin API reference](../../reference/admin/admin-api.md) — endpoint documentation with required permissions
140140- [API Keys](api-keys.md) — creating scoped API keys
141141- [Event Logs](event-logs.md) — permission-denied events are logged for auditing
···74747575- [SQLite → Postgres migration](sqlite-to-postgres-migration.md) — switch an existing instance from SQLite to Postgres
7676- [Postgres → SQLite migration](postgres-to-sqlite-migration.md) — switch an existing instance from Postgres to SQLite
7777-- [Lua scripting](scripting.md) — write queries that target either backend
7878-- [Configuration](../getting-started/configuration.md) — `DATABASE_URL` and related variables
7777+- [Lua scripting](../scripting.md) — write queries that target either backend
7878+- [Configuration](../../getting-started/configuration.md) — `DATABASE_URL` and related variables
···87878888- [SQLite → Postgres migration](sqlite-to-postgres-migration.md) — migrate in the opposite direction
8989- [Database setup](database-setup.md) — choose between SQLite and Postgres for new instances
9090-- [Backfill](backfill.md) — re-index records from the network after switching backends
9191-- [Lua scripting](scripting.md) — write SQL that works against either backend
9090+- [Backfill](../indexing/backfill.md) — re-index records from the network after switching backends
9191+- [Lua scripting](../scripting.md) — write SQL that works against either backend
···84848585- [Postgres → SQLite migration](postgres-to-sqlite-migration.md) — migrate in the opposite direction
8686- [Database setup](database-setup.md) — choose between SQLite and Postgres for new instances
8787-- [Backfill](backfill.md) — re-index records from the network after switching backends
8888-- [Lua scripting](scripting.md) — write SQL that works against either backend
8787+- [Backfill](../indexing/backfill.md) — re-index records from the network after switching backends
8888+- [Lua scripting](../scripting.md) — write SQL that works against either backend
+9-9
packages/docs/docs/guides/features/api-clients.md
···2233API clients identify your application to a HappyView instance. Every XRPC request — even unauthenticated queries — must include a client key. This guide walks through creating a client, choosing between public and confidential types, and authenticating users.
4455-For the admin CRUD endpoints, see the [API reference](../reference/admin/api-clients.md). For the JavaScript SDK, see the [SDK docs](../sdk/overview.md).
55+For the admin CRUD endpoints, see the [API reference](../../reference/admin/api-clients.md). For the JavaScript SDK, see the [SDK docs](../../sdk/overview.md).
6677## Concepts
88···6363 }'
6464```
65656666-See the [API reference](../reference/admin/api-clients.md#create-an-api-client) for all fields.
6666+See the [API reference](../../reference/admin/api-clients.md#create-an-api-client) for all fields.
67676868## Using your client key
6969···91919292### Authenticated requests (user identity)
93939494-Procedures — and queries whose scripts need to know who the caller is — require a user's OAuth session. This uses [DPoP authentication](../getting-started/authentication.md#dpop-key-provisioning-for-third-party-apps), where each request includes a cryptographic proof that the caller holds the right key.
9494+Procedures — and queries whose scripts need to know who the caller is — require a user's OAuth session. This uses [DPoP authentication](../../getting-started/authentication.md#dpop-key-provisioning-for-third-party-apps), where each request includes a cryptographic proof that the caller holds the right key.
95959696```sh
9797curl -X POST 'https://happyview.example.com/xrpc/com.example.createPost' \
···141141}
142142```
143143144144-For server-side Node.js apps, use the core [`@happyview/oauth-client`](../sdk/oauth-client.md) package with a confidential client. For type-safe XRPC calls, pair either client with [`@happyview/lex-agent`](../sdk/lex-agent.md).
144144+For server-side Node.js apps, use the core [`@happyview/oauth-client`](../../sdk/oauth-client.md) package with a confidential client. For type-safe XRPC calls, pair either client with [`@happyview/lex-agent`](../../sdk/lex-agent.md).
145145146146### Manual DPoP flow
147147···348348| `RateLimit-Reset` | Unix timestamp when the bucket will be full |
349349| `Retry-After` | Seconds to wait (only on `429` responses) |
350350351351-Adjust per-client rate limits via the dashboard or the [admin API](../reference/admin/api-clients.md#update-an-api-client).
351351+Adjust per-client rate limits via the dashboard or the [admin API](../../reference/admin/api-clients.md#update-an-api-client).
352352353353## Security notes
354354···359359360360## Next steps
361361362362-- [Authentication](../getting-started/authentication.md) — full protocol details and security model
363363-- [JavaScript SDK](../sdk/overview.md) — get started with the SDK
364364-- [Admin API — API Clients](../reference/admin/api-clients.md) — CRUD endpoints
365365-- [Permissions](permissions.md) — control who can manage API clients
362362+- [Authentication](../../getting-started/authentication.md) — full protocol details and security model
363363+- [JavaScript SDK](../../sdk/overview.md) — get started with the SDK
364364+- [Admin API — API Clients](../../reference/admin/api-clients.md) — CRUD endpoints
365365+- [Permissions](../admin/permissions.md) — control who can manage API clients
···101101102102- [Official plugins repository](https://github.com/gamesgamesgamesgamesgames/happyview-plugins) — ready-to-use plugins and the plugin SDK
103103- [Plugins guide](plugins.md) — install and configure plugins
104104-- [API Keys](api-keys.md) — authenticate programmatic access to admin endpoints
105105-- [Permissions](permissions.md) — configure user access to plugin management
104104+- [API Keys](../admin/api-keys.md) — authenticate programmatic access to admin endpoints
105105+- [Permissions](../admin/permissions.md) — configure user access to plugin management
+4-4
packages/docs/docs/guides/features/labelers.md
···65656666Self-labels (applied by the record author) use an outline badge style to distinguish them from external labels. Hover over a badge to see the source labeler's DID.
67676868-Labels are also available in the records API response and in Lua scripts via the [`atproto.get_labels` and `atproto.get_labels_batch`](../reference/lua/atproto-api.md#atprotoget_labels) functions.
6868+Labels are also available in the records API response and in Lua scripts via the [`atproto.get_labels` and `atproto.get_labels_batch`](../../reference/lua/atproto-api.md#atprotoget_labels) functions.
69697070## Using labels in your AppView
7171···85858686## Next steps
87878888-- [Admin API — Labelers](../reference/admin/labelers.md) — full endpoint documentation
8989-- [atproto API](../reference/lua/atproto-api.md) — access labels in Lua scripts with `get_labels` and `get_labels_batch`
9090-- [Permissions](permissions.md) — manage user access to labeler operations
8888+- [Admin API — Labelers](../../reference/admin/labelers.md) — full endpoint documentation
8989+- [atproto API](../../reference/lua/atproto-api.md) — access labels in Lua scripts with `get_labels` and `get_labels_batch`
9090+- [Permissions](../admin/permissions.md) — manage user access to labeler operations
+2-2
packages/docs/docs/guides/features/plugins.md
···75757676- [Developing Plugins](developing-plugins.md) — create your own plugins with the WASM plugin API
7777- [Official plugins repository](https://github.com/gamesgamesgamesgamesgames/happyview-plugins) — ready-to-use plugins for Steam, Xbox, itch.io, and more
7878-- [API Keys](api-keys.md) — authenticate programmatic access to admin endpoints
7979-- [Permissions](permissions.md) — configure user access to plugin management
7878+- [API Keys](../admin/api-keys.md) — authenticate programmatic access to admin endpoints
7979+- [Permissions](../admin/permissions.md) — configure user access to plugin management
+3-3
packages/docs/docs/guides/indexing/backfill.md
···55## When backfill runs
6677- **Automatically** when a record-type lexicon is uploaded with `backfill: true` (the default). See [Lexicons - Backfill flag](lexicons.md#backfill-flag).
88-- **Manually** via `POST /admin/backfill` or the [dashboard](../getting-started/dashboard.md). You can scope a manual backfill to a specific collection, a specific DID, or both.
88+- **Manually** via `POST /admin/backfill` or the [dashboard](../../getting-started/dashboard.md). You can scope a manual backfill to a specific collection, a specific DID, or both.
991010-See the [admin API](../reference/admin/backfill.md) for endpoint details.
1010+See the [admin API](../../reference/admin/backfill.md) for endpoint details.
11111212## How it works
1313···3434## Next steps
35353636- [Lexicons](lexicons.md#backfill-flag): Control whether lexicons trigger backfill on upload
3737-- [Admin API — Backfill](../reference/admin/backfill.md): Full reference for backfill endpoints
3737+- [Admin API — Backfill](../../reference/admin/backfill.md): Full reference for backfill endpoints
+11-11
packages/docs/docs/guides/indexing/index-hooks.md
···2233Index hooks are Lua scripts that run whenever a record in a collection is created, updated, or deleted. They run **before** the record is indexed, giving you the ability to filter out unwanted records, transform record data before storage, or trigger side effects like syncing with external services.
4455-Index hooks fire on **all** record events for the collection — including records created by HappyView procedure endpoints, not just events from the network. Unlike [query and procedure scripts](scripting.md) that run in response to XRPC requests, index hooks are triggered by incoming Jetstream events (which include events caused by HappyView's own PDS writes).
55+Index hooks fire on **all** record events for the collection — including records created by HappyView procedure endpoints, not just events from the network. Unlike [query and procedure scripts](../scripting.md) that run in response to XRPC requests, index hooks are triggered by incoming Jetstream events (which include events caused by HappyView's own PDS writes).
6677## Attaching a hook
8899-Each record-type lexicon can have one index hook. You can add it through the [dashboard](../getting-started/dashboard.md) (click "Add Index Hook" on any record lexicon's detail page) or via the [admin API](../reference/admin/lexicons.md#upload--upsert-a-lexicon) by including the `index_hook` field when uploading a lexicon.
99+Each record-type lexicon can have one index hook. You can add it through the [dashboard](../../getting-started/dashboard.md) (click "Add Index Hook" on any record lexicon's detail page) or via the [admin API](../../reference/admin/lexicons.md#upload--upsert-a-lexicon) by including the `index_hook` field when uploading a lexicon.
10101111## Script structure
1212···57575858Index hooks have access to:
59596060-- **[Database API](../reference/lua/database-api.md)** — `db.query`, `db.get`, `db.search`, `db.backlinks`, `db.count`, `db.raw`
6161-- **[HTTP API](../reference/lua/http-api.md)** — `http.get`, `http.post`, `http.put`, `http.patch`, `http.delete`, `http.head`
6262-- **[JSON API](../reference/lua/json-api.md)** — `json.encode`, `json.decode`
6363-- **[Utility globals](scripting.md#utility-globals)** — `log()`, `now()`, `TID()`, `toarray()`
6060+- **[Database API](../../reference/lua/database-api.md)** — `db.query`, `db.get`, `db.search`, `db.backlinks`, `db.count`, `db.raw`
6161+- **[HTTP API](../../reference/lua/http-api.md)** — `http.get`, `http.post`, `http.put`, `http.patch`, `http.delete`, `http.head`
6262+- **[JSON API](../../reference/lua/json-api.md)** — `json.encode`, `json.decode`
6363+- **[Utility globals](../scripting.md#utility-globals)** — `log()`, `now()`, `TID()`, `toarray()`
64646565## Error handling and retries
6666···70702. If all retries are exhausted, the failed event is inserted into the `dead_letter_hooks` table for later inspection.
71713. On failure the system **fails open** — the original record is stored as-is so indexing is not permanently blocked.
72727373-Failed hooks are logged as errors. Check the [event logs](event-logs.md) or query the `dead_letter_hooks` table directly to find and replay failures.
7373+Failed hooks are logged as errors. Check the [event logs](../admin/event-logs.md) or query the `dead_letter_hooks` table directly to find and replay failures.
74747575### Performance considerations
7676···178178end
179179```
180180181181-See the full [Algolia sync reference](../reference/scripts/algolia-sync.md) for more detail.
181181+See the full [Algolia sync reference](../scripting/algolia-sync.md) for more detail.
182182183183### Sync to Meilisearch
184184···213213end
214214```
215215216216-See the full [Meilisearch sync reference](../reference/scripts/meilisearch-sync.md) for more detail.
216216+See the full [Meilisearch sync reference](../scripting/meilisearch-sync.md) for more detail.
217217218218## Next steps
219219220220-- [Lua Scripting](scripting.md): Full reference for the sandbox, APIs, and debugging
220220+- [Lua Scripting](../scripting.md): Full reference for the sandbox, APIs, and debugging
221221- [Lexicons](lexicons.md): Understand how record, query, and procedure lexicons work together
222222-- [Admin API — Lexicons](../reference/admin/lexicons.md#upload--upsert-a-lexicon): Upload lexicons with index hooks via the API
222222+- [Admin API — Lexicons](../../reference/admin/lexicons.md#upload--upsert-a-lexicon): Upload lexicons with index hooks via the API
+7-7
packages/docs/docs/guides/indexing/lexicons.md
···2233Lexicons are the core building block of HappyView. They're [atproto schema definitions](https://atproto.com/specs/lexicon) that describe your data model, and HappyView uses them to decide which records to index from the network and what XRPC endpoints to serve.
4455-You don't write route handlers or database queries; you upload a lexicon and HappyView generates the infrastructure from it. There are two ways to add lexicons: uploading them via the [admin API](../reference/admin/lexicons.md) or [dashboard](../getting-started/dashboard.md), or fetching them directly from the atproto network via [DNS authority resolution](#network-lexicons).
55+You don't write route handlers or database queries; you upload a lexicon and HappyView generates the infrastructure from it. There are two ways to add lexicons: uploading them via the [admin API](../../reference/admin/lexicons.md) or [dashboard](../../getting-started/dashboard.md), or fetching them directly from the atproto network via [DNS authority resolution](#network-lexicons).
6677## Supported lexicon types
88···1313| `procedure` | Registers a `POST /xrpc/{nsid}` endpoint that proxies writes to the user's PDS |
1414| `definitions` | Stored but does not generate routes or subscriptions |
15151616-A typical setup has three lexicons working together: a **record** lexicon that defines the data and triggers indexing, a **query** lexicon that exposes a read endpoint, and a **procedure** lexicon that exposes a write endpoint. The [Statusphere tutorial](../tutorials/statusphere.md) walks through this pattern end-to-end.
1616+A typical setup has three lexicons working together: a **record** lexicon that defines the data and triggers indexing, a **query** lexicon that exposes a read endpoint, and a **procedure** lexicon that exposes a write endpoint. The [Statusphere tutorial](../../tutorials/statusphere.md) walks through this pattern end-to-end.
17171818## Target collection
1919···21212222For example, a query lexicon `xyz.statusphere.listStatuses` would set `target_collection` to `xyz.statusphere.status` to read from that record collection.
23232424-See the [admin API](../reference/admin/lexicons.md#upload--upsert-a-lexicon) for how to set `target_collection` when uploading.
2424+See the [admin API](../../reference/admin/lexicons.md#upload--upsert-a-lexicon) for how to set `target_collection` when uploading.
25252626:::note
2727The `target_collection` is available in Lua scripts as the `collection` global, but it is not required if your endpoint uses a Lua script.
···8989A few things to note:
90909191- HappyView does **not** proxy to the reversed hostname directly. `foo.example.com` is only the DNS host for the TXT record — the actual XRPC request goes to whatever PDS endpoint the authority DID resolves to.
9292-- Proxying applies equally to queries and procedures. For procedures, HappyView uses the caller's OAuth session to attach a DPoP-bound access token (see [Authentication](../getting-started/authentication.md#proxying-procedures-to-the-users-pds)).
9292+- Proxying applies equally to queries and procedures. For procedures, HappyView uses the caller's OAuth session to attach a DPoP-bound access token (see [Authentication](../../getting-started/authentication.md#proxying-procedures-to-the-users-pds)).
9393- If authority resolution fails — no TXT record, unresolvable DID, or the target PDS doesn't support the method — the client gets an error back. HappyView does not fall back to any other routing strategy.
9494- Tracking a network lexicon does **not** make HappyView handle requests for that NSID locally. Network lexicons are only about indexing record collections and keeping the schema up to date. If a client calls a query NSID that you've tracked as a network lexicon but haven't uploaded a local query lexicon for, HappyView still proxies the request out — it won't query your local record table. To serve a method locally, upload a local query or procedure lexicon with a matching `target_collection`.
9595···97979898## Next steps
9999100100-- [Lua Scripting](scripting.md): Add custom query and procedure logic to your endpoints
100100+- [Lua Scripting](../scripting.md): Add custom query and procedure logic to your endpoints
101101- [Index Hooks](index-hooks.md): Run Lua scripts when records are indexed from the network
102102-- [XRPC API](../reference/xrpc-api.md): Understand how the generated endpoints behave
102102+- [XRPC API](../../reference/xrpc-api.md): Understand how the generated endpoints behave
103103- [Backfill](backfill.md): Learn how historical records are indexed
104104-- [Admin API](../reference/admin-api.md): Full reference for lexicon management endpoints
104104+- [Admin API](../../reference/admin/admin-api.md): Full reference for lexicon management endpoints
+15-15
packages/docs/docs/guides/scripting.md
···10101111Scripts are attached to query and procedure lexicons and run in a sandboxed Lua VM with access to the [Record API](#record-api), a [database API](#database-api), an [HTTP client API](#http-api), a [JSON API](#json-api), and a set of [context globals](#context-globals).
12121313-For scripts that react to record changes from the network (rather than XRPC requests), see [Index Hooks](index-hooks.md).
1313+For scripts that react to record changes from the network (rather than XRPC requests), see [Index Hooks](indexing/index-hooks.md).
14141515## Script structure
1616···177177See the example script references for complete, ready-to-use scripts:
178178179179**Queries:**
180180-- [Get a record](../reference/scripts/get-record.md) — fetch a single record by AT URI
181181-- [Paginated list](../reference/scripts/paginated-list.md) — list records with cursor-based pagination and DID filtering
182182-- [List or fetch](../reference/scripts/list-or-fetch.md) — combined single-record lookup and paginated listing
183183-- [Expanded query](../reference/scripts/expanded-query.md) — list statuses with user profiles in a single response
180180+- [Get a record](scripting/get-record.md) — fetch a single record by AT URI
181181+- [Paginated list](scripting/paginated-list.md) — list records with cursor-based pagination and DID filtering
182182+- [List or fetch](scripting/list-or-fetch.md) — combined single-record lookup and paginated listing
183183+- [Expanded query](scripting/expanded-query.md) — list statuses with user profiles in a single response
184184185185**Procedures:**
186186-- [Create a record](../reference/scripts/create-record.md) — simple write that saves input as a record
187187-- [Upsert a record](../reference/scripts/upsert-record.md) — create or update using a deterministic rkey
188188-- [Update or delete](../reference/scripts/update-or-delete.md) — single endpoint handling create, update, and delete
189189-- [Batch save](../reference/scripts/batch-save.md) — create multiple records in parallel with `Record.save_all()`
190190-- [Sidecar records](../reference/scripts/sidecar-records.md) — create linked records across collections with a shared rkey
191191-- [Cascading delete](../reference/scripts/cascading-delete.md) — delete a record and all related records
192192-- [Complex mutations](../reference/scripts/complex-mutations.md) — load, transform, and save a record with multiple field changes
186186+- [Create a record](scripting/create-record.md) — simple write that saves input as a record
187187+- [Upsert a record](scripting/upsert-record.md) — create or update using a deterministic rkey
188188+- [Update or delete](scripting/update-or-delete.md) — single endpoint handling create, update, and delete
189189+- [Batch save](scripting/batch-save.md) — create multiple records in parallel with `Record.save_all()`
190190+- [Sidecar records](scripting/sidecar-records.md) — create linked records across collections with a shared rkey
191191+- [Cascading delete](scripting/cascading-delete.md) — delete a record and all related records
192192+- [Complex mutations](scripting/complex-mutations.md) — load, transform, and save a record with multiple field changes
193193194194**Index Hooks:**
195195-- [Algolia sync](../reference/scripts/algolia-sync.md) — push records to an Algolia search index on create/update/delete
195195+- [Algolia sync](scripting/algolia-sync.md) — push records to an Algolia search index on create/update/delete
196196197197## Next steps
198198199199-- [Index Hooks](index-hooks.md): React to record changes from the network in real time
200200-- [Lexicons](lexicons.md): Understand how record, query, and procedure lexicons work together
199199+- [Index Hooks](indexing/index-hooks.md): React to record changes from the network in real time
200200+- [Lexicons](indexing/lexicons.md): Understand how record, query, and procedure lexicons work together
201201- [XRPC API](../reference/xrpc-api.md): See how endpoints behave with and without Lua scripts
202202- [Dashboard](../getting-started/dashboard.md#lua-editor): Use the web editor with context-aware completions
···37371. On **create** or **update**: sends a `PUT` request to Algolia's index API with the record data, using the AT URI as the `objectID`. Algolia upserts the object — if it already exists, it's replaced.
38382. On **delete**: sends a `DELETE` request to remove the object from the index by its AT URI.
39394040-The `json.encode()` function converts the Lua table into a JSON string for the request body. See [JSON API](../../guides/index-hooks.md#json-api).
4040+The `json.encode()` function converts the Lua table into a JSON string for the request body. See [JSON API](../../reference/lua/json-api.md).
41414242## Configuration
4343
+2-2
packages/docs/docs/guides/scripting/batch-save.md
···23232424## How it works
25252626-1. Iterate over `input.items` and create a [`Record`](../lua/record-api.md) instance for each item.
2727-2. Call [`Record.save_all()`](../lua/record-api.md#static-methods) to save all records in parallel, rather than one at a time.
2626+1. Iterate over `input.items` and create a [`Record`](../../reference/lua/record-api.md) instance for each item.
2727+2. Call [`Record.save_all()`](../../reference/lua/record-api.md#static-methods) to save all records in parallel, rather than one at a time.
28283. Collect the resulting AT URIs and return them.
29293030## Usage
···494950501. Load the primary record by URI. Return early if it doesn't exist.
51512. Query for related records, in this example comments by the same user that reference the primary record's URI.
5252-3. Load each related record with [`Record.load`](../lua/record-api.md#static-methods) to get a deletable `Record` instance.
5252+3. Load each related record with [`Record.load`](../../reference/lua/record-api.md#static-methods) to get a deletable `Record` instance.
53534. Delete everything. Each `r:delete()` removes the record from the user's PDS and the local index.
54545555## Usage
···56565757## How it works
58585959-1. Load the existing record with [`Record.load`](../lua/record-api.md#static-methods). This gives you a mutable `Record` instance with all the current field values.
5959+1. Load the existing record with [`Record.load`](../../reference/lua/record-api.md#static-methods). This gives you a mutable `Record` instance with all the current field values.
60602. Apply transformations directly on the record's fields:
6161 - **Increment a counter**: use `or 0` to handle the field being `nil` on first access.
6262 - **Merge tags**: iterate over `input.tags`, skip duplicates already in `r.tags`, append new ones, then trim the list to 10.
···14141515## How it works
16161717-1. Create a new [`Record`](../lua/record-api.md) instance from the target collection, populated with the fields from the request body.
1717+1. Create a new [`Record`](../../reference/lua/record-api.md) instance from the target collection, populated with the fields from the request body.
18182. Call `r:save()`, which creates the record on the caller's PDS and indexes it locally.
19193. Return the AT URI and CID of the newly created record.
2020
···51511. Query statuses from the target collection with pagination, same as a normal list query.
52522. Extract the unique DIDs from the returned status URIs using `string.match`.
53533. Build an AT URI for each DID's `app.bsky.actor.profile/self` record (this is where Bluesky profiles live).
5454-4. Load all profiles in parallel with [`Record.load_all`](../lua/record-api.md#static-methods). Profiles that aren't indexed locally return `nil` and are skipped.
5454+4. Load all profiles in parallel with [`Record.load_all`](../../reference/lua/record-api.md#static-methods). Profiles that aren't indexed locally return `nil` and are skipped.
55555. Return statuses and profiles as separate keys, with the cursor from the status query.
56565757## Usage
+1-1
packages/docs/docs/guides/scripting/get-record.md
···2222## How it works
232324241. Check that the `uri` query parameter is present. Return a structured error if missing.
2525-2. Look up the record with [`db.get`](../lua/database-api.md#dbget), which returns the record table or `nil`.
2525+2. Look up the record with [`db.get`](../../reference/lua/database-api.md#dbget), which returns the record table or `nil`.
26263. Return the record wrapped in an object.
27272828## Usage
···25252626## How it works
27272828-1. If a `uri` query parameter is provided, fetch that single record with [`db.get`](../lua/database-api.md#dbget) and return it. If it doesn't exist, return a structured error (using `error()` would trigger a 500 response).
2929-2. Otherwise, list records from the target collection using [`db.query`](../lua/database-api.md#dbquery), with optional filtering by `did` and cursor-based pagination. The `cursor` is an opaque string from a previous response — pass it through directly. Since `limit` arrives as a string, `tonumber()` converts it to a number.
2828+1. If a `uri` query parameter is provided, fetch that single record with [`db.get`](../../reference/lua/database-api.md#dbget) and return it. If it doesn't exist, return a structured error (using `error()` would trigger a 500 response).
2929+2. Otherwise, list records from the target collection using [`db.query`](../../reference/lua/database-api.md#dbquery), with optional filtering by `did` and cursor-based pagination. The `cursor` is an opaque string from a previous response — pass it through directly. Since `limit` arrives as a string, `tonumber()` converts it to a number.
30303131## Usage
3232
···38381. On **create** or **update**: sends a `POST` request to Meilisearch's document API with the record data wrapped in an array. Meilisearch upserts by `id` — if a document with the same AT URI already exists, it's replaced.
39392. On **delete**: sends a `DELETE` request to remove the document from the index by its AT URI.
40404141-The `toarray()` function ensures the table is encoded as a JSON array (Meilisearch expects an array of documents). See [JSON API](../../guides/index-hooks.md#json-api).
4141+The `toarray()` function ensures the table is encoded as a JSON array (Meilisearch expects an array of documents). See [JSON API](../../reference/lua/json-api.md).
42424343## Configuration
44444545-This script uses [script variables](../../guides/scripting.md) instead of hardcoded values. Set these via the [admin API](../../reference/admin-api.md) or dashboard:
4545+This script uses [script variables](../../guides/scripting.md) instead of hardcoded values. Set these via the [admin API](../../reference/admin/admin-api.md) or dashboard:
46464747| Variable | Value |
4848| --------------------- | ------------------------------------------------------------------------------ |
···2323## How it works
242425251. Parse `limit` from the query string, defaulting to 20 and capping at 100.
2626-2. Call [`db.query`](../lua/database-api.md#dbquery) with the target collection, optional DID filter, and cursor for pagination.
2626+2. Call [`db.query`](../../reference/lua/database-api.md#dbquery) with the target collection, optional DID filter, and cursor for pagination.
27273. Return the result directly. `db.query` returns `{ records = [...], cursor = "..." }` where `cursor` is an opaque string present when more records exist.
28282929## Usage
···343435351. Generate a single [`TID()`](../../guides/scripting.md#utility-globals) to use as the rkey for both records.
36362. Create a `Record` for each collection and call `r:set_rkey()` with the shared rkey.
3737-3. Save both records in parallel with [`Record.save_all()`](../lua/record-api.md#static-methods).
3737+3. Save both records in parallel with [`Record.save_all()`](../../reference/lua/record-api.md#static-methods).
38384. Return both URIs so the client knows the identity of each record.
39394040## Usage
···30303131## How it works
32323333-1. If `input.delete` is truthy and `input.uri` is provided, load the record with [`Record.load`](../lua/record-api.md#static-methods) and delete it.
3434-2. If only `input.uri` is provided, load the existing record with [`Record.load`](../lua/record-api.md#static-methods), update its fields, and save it back. Since `_uri` is already set, `r:save()` calls `putRecord` instead of `createRecord`.
3333+1. If `input.delete` is truthy and `input.uri` is provided, load the record with [`Record.load`](../../reference/lua/record-api.md#static-methods) and delete it.
3434+2. If only `input.uri` is provided, load the existing record with [`Record.load`](../../reference/lua/record-api.md#static-methods), update its fields, and save it back. Since `_uri` is already set, `r:save()` calls `putRecord` instead of `createRecord`.
35353. If neither condition matches, create a new record from the input.
36363737## Usage
···3333## How it works
343435351. Use the client-provided `input.rkey` if present, otherwise generate a new [`TID()`](../../guides/scripting.md#utility-globals). This means omitting `rkey` always creates, while providing one enables updates.
3636-2. Build the AT URI from the caller's DID, the target collection, and the rkey, then try to load it with [`Record.load`](../lua/record-api.md#static-methods).
3636+2. Build the AT URI from the caller's DID, the target collection, and the rkey, then try to load it with [`Record.load`](../../reference/lua/record-api.md#static-methods).
37373. If the record exists, update its fields and save. Since `_uri` is already set, `r:save()` calls `putRecord`.
38384. If it doesn't exist, create a new record, set the rkey explicitly with `r:set_rkey()`, and save. This calls `createRecord` with the specified rkey.
3939
+15-15
packages/docs/docs/reference/admin/admin-api.md
···11# Admin API
2233-The admin API lets you manage lexicons, monitor records, run backfill jobs, and control user access. All endpoints live under `/admin` and require authentication from a DID that exists in the `users` table, with the appropriate [permissions](../guides/permissions.md) for the endpoint being called. You can also manage all of this through the [web dashboard](../getting-started/dashboard.md).
33+The admin API lets you manage lexicons, monitor records, run backfill jobs, and control user access. All endpoints live under `/admin` and require authentication from a DID that exists in the `users` table, with the appropriate [permissions](../../guides/admin/permissions.md) for the endpoint being called. You can also manage all of this through the [web dashboard](../../getting-started/dashboard.md).
4455## Auth
6677The admin API supports two authentication methods:
8899-1. **API keys** — read/write tokens starting with `hv_`, passed as `Authorization: Bearer hv_...`. See the [API Keys guide](../guides/api-keys.md) for details.
99+1. **API keys** — read/write tokens starting with `hv_`, passed as `Authorization: Bearer hv_...`. See the [API Keys guide](../../guides/admin/api-keys.md) for details.
10102. **Service auth JWT** — atproto inter-service authentication via signed JWTs.
11111212In all cases the resolved DID is checked against the `users` table, and the user's permissions are loaded to authorize the request.
···41414242| Group | Description |
4343| ----- | ----------- |
4444-| [Lexicons](admin/lexicons.md) | Upload, list, get, and delete lexicons and network lexicons |
4545-| [Stats](admin/stats.md) | Record counts by collection |
4646-| [Backfill](admin/backfill.md) | Create and monitor historical backfill jobs |
4747-| [Event Logs](admin/events.md) | Query the audit trail of system events |
4848-| [API Keys](admin/api-keys.md) | Create, list, and revoke API keys |
4949-| [Users](admin/users.md) | Create, list, update, and delete admin users |
5050-| [Labelers](admin/labelers.md) | Manage external labeler subscriptions |
5151-| [Instance Settings](admin/settings.md) | Configure app name, logo, and policy URLs |
5252-| [Domains](admin/domains.md) | Manage domains and their OAuth client identities |
5353-| [Script Variables](admin/script-variables.md) | Encrypted key/value pairs for Lua scripts |
5454-| [API Clients](admin/api-clients.md) | Register and manage third-party XRPC clients |
5555-| [Plugins](admin/plugins.md) | Install, configure, and manage WASM plugins |
4444+| [Lexicons](lexicons.md) | Upload, list, get, and delete lexicons and network lexicons |
4545+| [Stats](stats.md) | Record counts by collection |
4646+| [Backfill](backfill.md) | Create and monitor historical backfill jobs |
4747+| [Event Logs](events.md) | Query the audit trail of system events |
4848+| [API Keys](api-keys.md) | Create, list, and revoke API keys |
4949+| [Users](users.md) | Create, list, update, and delete admin users |
5050+| [Labelers](labelers.md) | Manage external labeler subscriptions |
5151+| [Instance Settings](settings.md) | Configure app name, logo, and policy URLs |
5252+| [Domains](domains.md) | Manage domains and their OAuth client identities |
5353+| [Script Variables](script-variables.md) | Encrypted key/value pairs for Lua scripts |
5454+| [API Clients](api-clients.md) | Register and manage third-party XRPC clients |
5555+| [Plugins](plugins.md) | Install, configure, and manage WASM plugins |
56565757## Permissions
58585959-Each admin API endpoint requires a specific permission. See the [Permissions guide](../guides/permissions.md) for the full list of permissions and templates.
5959+Each admin API endpoint requires a specific permission. See the [Permissions guide](../../guides/admin/permissions.md) for the full list of permissions and templates.
60606161| Endpoint | Required Permission |
6262| ---------------------------------------- | -------------------------- |
+1-1
packages/docs/docs/reference/admin/api-clients.md
···4455A single API client represents your application, not individual users. Create one client for your app and use the same client key across all instances. Users authenticate separately via OAuth — the client key identifies _your app_, not _who is using it_.
6677-Each client has an `hvc_`-prefixed client key and an `hvs_`-prefixed client secret. The secret is only returned at creation and is sha256-hashed in the database. Server-to-server callers pass the secret as `X-Client-Secret`. Browser callers use the `Origin` header, which is matched against the client's `client_uri`. Mismatches currently log warnings rather than rejecting the request, but rate limiting applies either way. See [Authentication — XRPC](../../getting-started/authentication.md#xrpc-api-client-identification) for the client-side view, and the [API Keys guide](../../guides/api-keys.md) for how admin API keys differ from API clients.
77+Each client has an `hvc_`-prefixed client key and an `hvs_`-prefixed client secret. The secret is only returned at creation and is sha256-hashed in the database. Server-to-server callers pass the secret as `X-Client-Secret`. Browser callers use the `Origin` header, which is matched against the client's `client_uri`. Mismatches currently log warnings rather than rejecting the request, but rate limiting applies either way. See [Authentication — XRPC](../../getting-started/authentication.md#xrpc-api-client-identification) for the client-side view, and the [API Keys guide](../../guides/admin/api-keys.md) for how admin API keys differ from API clients.
8899```sh
1010# All examples assume $TOKEN is an API key (hv_...)
+1-1
packages/docs/docs/reference/admin/api-keys.md
···11# Admin API: API Keys
2233-Manage API keys for programmatic access. See the [API Keys guide](../../guides/api-keys.md) for usage details.
33+Manage API keys for programmatic access. See the [API Keys guide](../../guides/admin/api-keys.md) for usage details.
4455```sh
66# All examples assume $TOKEN is an API key (hv_...)
+1-1
packages/docs/docs/reference/admin/backfill.md
···11# Admin API: Backfill
2233-Create and monitor historical backfill jobs. See the [Backfill guide](../../guides/backfill.md) for background.
33+Create and monitor historical backfill jobs. See the [Backfill guide](../../guides/indexing/backfill.md) for background.
4455```sh
66# All examples assume $TOKEN is an API key (hv_...)
+1-1
packages/docs/docs/reference/admin/events.md
···11# Admin API: Event Logs
2233-HappyView logs system events — lexicon changes, record operations, script errors, user actions, and more. See the [Event Logs guide](../../guides/event-logs.md) for details on event types and retention.
33+HappyView logs system events — lexicon changes, record operations, script errors, user actions, and more. See the [Event Logs guide](../../guides/admin/event-logs.md) for details on event types and retention.
4455```sh
66# All examples assume $TOKEN is an API key (hv_...)
+1-1
packages/docs/docs/reference/admin/labelers.md
···11# Admin API: Labelers
2233-Manage external labeler subscriptions. See the [Labelers guide](../../guides/labelers.md) for background.
33+Manage external labeler subscriptions. See the [Labelers guide](../../guides/features/labelers.md) for background.
4455```sh
66# All examples assume $TOKEN is an API key (hv_...)
+3-3
packages/docs/docs/reference/admin/lexicons.md
···11# Admin API: Lexicons
2233-Manage lexicons and network lexicons. See the [Lexicons guide](../../guides/lexicons.md) for background on how lexicons drive indexing and XRPC routing.
33+Manage lexicons and network lexicons. See the [Lexicons guide](../../guides/indexing/lexicons.md) for background on how lexicons drive indexing and XRPC routing.
4455```sh
66# All examples assume $TOKEN is an API key (hv_...)
···3030| `backfill` | boolean | no | Whether uploading triggers historical backfill (default `true`) |
3131| `target_collection` | string | no | For query/procedure lexicons, the record collection they operate on |
3232| `script` | string | no | Lua script for query/procedure endpoints |
3333-| `index_hook` | string | no | [Index hook](../../guides/index-hooks.md) Lua script for record lexicons |
3333+| `index_hook` | string | no | [Index hook](../../guides/indexing/index-hooks.md) Lua script for record lexicons |
34343535**Response**: `201 Created` (new) or `200 OK` (upsert)
3636···92929393## Network Lexicons
94949595-Network lexicons are fetched from the atproto network via DNS TXT resolution and kept updated via the Jetstream subscription. See [Lexicons - Network lexicons](../../guides/lexicons.md#network-lexicons) for background.
9595+Network lexicons are fetched from the atproto network via DNS TXT resolution and kept updated via the Jetstream subscription. See [Lexicons - Network lexicons](../../guides/indexing/lexicons.md#network-lexicons) for background.
96969797### Add a network lexicon
9898
+1-1
packages/docs/docs/reference/admin/plugins.md
···11# Admin API: Plugins
2233-Plugins extend HappyView with WebAssembly modules sourced from the [official plugin registry](../../guides/plugins.md) or any URL serving a `manifest.json`. Most endpoints take a plugin manifest URL and load (or reload) the plugin in place — no restart needed. Encrypted plugin secrets require `TOKEN_ENCRYPTION_KEY` to be configured.
33+Plugins extend HappyView with WebAssembly modules sourced from the [official plugin registry](../../guides/features/plugins.md) or any URL serving a `manifest.json`. Most endpoints take a plugin manifest URL and load (or reload) the plugin in place — no restart needed. Encrypted plugin secrets require `TOKEN_ENCRYPTION_KEY` to be configured.
4455```sh
66# All examples assume $TOKEN is an API key (hv_...)
+1-1
packages/docs/docs/reference/admin/users.md
···11# Admin API: Users
2233-Manage admin users and their permissions. See the [Permissions guide](../../guides/permissions.md) for available permissions and templates.
33+Manage admin users and their permissions. See the [Permissions guide](../../guides/admin/permissions.md) for available permissions and templates.
4455```sh
66# All examples assume $TOKEN is an API key (hv_...)
+1-1
packages/docs/docs/reference/architecture.md
···3131 Labeler["Labeler<br/><small>WebSocket (out-of-band)</small>"] -->|label events| DB
3232```
33333434-Queries go through the query handler to the database (SQLite by default, or Postgres). Writes go through the procedure handler to the user's PDS, then HappyView indexes the record locally. Real-time record events stream in via [Jetstream](https://github.com/bluesky-social/jetstream); historical records are backfilled in-process by discovering repos via the relay's `listReposByCollection` and fetching records directly from each PDS. [Labelers](../guides/labelers.md) are external services that emit content labels over a direct WebSocket connection — they operate out-of-band, outside the relay/repo system.
3434+Queries go through the query handler to the database (SQLite by default, or Postgres). Writes go through the procedure handler to the user's PDS, then HappyView indexes the record locally. Real-time record events stream in via [Jetstream](https://github.com/bluesky-social/jetstream); historical records are backfilled in-process by discovering repos via the relay's `listReposByCollection` and fetching records directly from each PDS. [Labelers](../guides/features/labelers.md) are external services that emit content labels over a direct WebSocket connection — they operate out-of-band, outside the relay/repo system.
35353636## Request flow
3737
+5-5
packages/docs/docs/reference/glossary.md
···12121313**Handle** — A human-readable name for an account (e.g. `user.bsky.social`). Handles resolve to a DID via a DNS TXT record or an HTTP `.well-known/atproto-did` lookup.
14141515-**Lexicon** — A schema definition for atproto data types and API methods. Lexicons define what records look like, what endpoints exist, and what parameters they accept. See [Lexicons](../guides/lexicons.md).
1515+**Lexicon** — A schema definition for atproto data types and API methods. Lexicons define what records look like, what endpoints exist, and what parameters they accept. See [Lexicons](../guides/indexing/lexicons.md).
16161717**NSID** (Namespaced Identifier) — A reverse-DNS identifier for a lexicon (e.g. `xyz.statusphere.status`). The authority is everything except the last segment.
1818···22222323**Record** — A single piece of data in an atproto repository, identified by an AT URI (e.g. `at://did:plc:abc/xyz.statusphere.status/abc123`).
24242525-**Relay** — A network service that aggregates repository data from many PDSes. HappyView queries the relay during [backfill](../guides/backfill.md) to discover which repos contain records for a given collection, then fetches each repo's records directly from its PDS.
2525+**Relay** — A network service that aggregates repository data from many PDSes. HappyView queries the relay during [backfill](../guides/indexing/backfill.md) to discover which repos contain records for a given collection, then fetches each repo's records directly from its PDS.
26262727**rkey** (Record Key) — The unique key for a record within a collection and repo. These are most commonly TIDs (timestamp-based) or NSIDs.
2828···34343535## HappyView-specific terms
36363737-**Backfill** — The process of bulk-indexing existing records from the network. HappyView discovers repos via the relay and fetches each repo's records directly from its PDS. Runs when a new record-type lexicon is uploaded or triggered manually. See [Backfill](../guides/backfill.md).
3737+**Backfill** — The process of bulk-indexing existing records from the network. HappyView discovers repos via the relay and fetches each repo's records directly from its PDS. Runs when a new record-type lexicon is uploaded or triggered manually. See [Backfill](../guides/indexing/backfill.md).
38383939-**Network lexicon** — A lexicon fetched directly from the atproto network via DNS authority resolution, rather than uploaded manually. See [Lexicons - Network lexicons](../guides/lexicons.md#network-lexicons).
3939+**Network lexicon** — A lexicon fetched directly from the atproto network via DNS authority resolution, rather than uploaded manually. See [Lexicons - Network lexicons](../guides/indexing/lexicons.md#network-lexicons).
40404141-**Permission** — A granular access control right that authorizes a specific action in the admin API. HappyView defines 20 permissions organized by category (e.g. `lexicons:create`, `users:read`). See [Permissions](../guides/permissions.md).
4141+**Permission** — A granular access control right that authorizes a specific action in the admin API. HappyView defines 20 permissions organized by category (e.g. `lexicons:create`, `users:read`). See [Permissions](../guides/admin/permissions.md).
42424343**Permission template** — A predefined set of permissions that can be applied when creating a user. Templates are: **Viewer** (read-only access), **Operator** (viewer + backfill and API key management), **Manager** (operator + lexicon and record management), and **Full Access** (all 20 permissions).
4444
+1-1
packages/docs/docs/reference/lua/atproto-api.md
···11# atproto API
2233-The `atproto` table provides atproto utility functions. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md).
33+The `atproto` table provides atproto utility functions. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md).
4455## atproto.resolve_service_endpoint
66
+2-2
packages/docs/docs/reference/lua/database-api.md
···11# Database API
2233-The `db` table provides access to the database. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md).
33+The `db` table provides access to the database. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md).
4455## db.query
66···95959696### SQL dialect
97979898-Write SQL in **SQLite syntax** — HappyView translates it to Postgres at runtime if you're using Postgres. See [Database Setup](../../guides/database-setup.md) for details on what gets translated. If you need database-specific SQL that can't be translated, check `db.is_postgres()` at runtime.
9898+Write SQL in **SQLite syntax** — HappyView translates it to Postgres at runtime if you're using Postgres. See [Database Setup](../../guides/database/database-setup.md) for details on what gets translated. If you need database-specific SQL that can't be translated, check `db.is_postgres()` at runtime.
9999100100### Column type mapping
101101
+1-1
packages/docs/docs/reference/lua/http-api.md
···11# HTTP API
2233-The `http` table provides async HTTP client functions. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md).
33+The `http` table provides async HTTP client functions. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md).
4455## Methods
66
+1-1
packages/docs/docs/reference/lua/json-api.md
···11# JSON API
2233-The `json` global provides JSON serialization and deserialization. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md).
33+The `json` global provides JSON serialization and deserialization. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md).
4455## json.encode
66
···11# Utility Globals
2233-Global functions available in queries, procedures, and [index hooks](../../guides/index-hooks.md). These don't belong to a specific API table — they're available at the top level of any Lua script.
33+Global functions available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md). These don't belong to a specific API table — they're available at the top level of any Lua script.
4455## now
66
+6-6
packages/docs/docs/reference/troubleshooting.md
···18181919**Causes**:
20202121-- The query lexicon is missing a `target_collection`. Without it, the query doesn't know which records to read. See [Lexicons - target_collection](../guides/lexicons.md#target-collection).
2121+- The query lexicon is missing a `target_collection`. Without it, the query doesn't know which records to read. See [Lexicons - target_collection](../guides/indexing/lexicons.md#target-collection).
2222- The record-type lexicon hasn't finished backfilling. Check backfill status with `GET /admin/backfill/status` or the dashboard.
2323-- Records exist on the network but HappyView hasn't indexed them yet. Jetstream only delivers events from after the collection was added to the filter. Use [backfill](../guides/backfill.md) to import historical records.
2323+- Records exist on the network but HappyView hasn't indexed them yet. Jetstream only delivers events from after the collection was added to the filter. Use [backfill](../guides/indexing/backfill.md) to import historical records.
24242525## Procedure returns 401 Unauthorized
2626···48484949**Causes**:
50505151-- Your user account doesn't have the specific permission required by the endpoint. Each endpoint requires a specific permission — see the [permissions table](admin-api.md#permissions).
5151+- Your user account doesn't have the specific permission required by the endpoint. Each endpoint requires a specific permission — see the [permissions table](admin/admin-api.md#permissions).
5252- If using an API key, the key's effective permissions are the intersection of the key's permissions and your user permissions. A key can never have more access than the user who created it.
5353- Only the super user can call `POST /admin/users/transfer-super`. This endpoint cannot be accessed with any permission — it requires super user status.
5454···7474- The relay (`RELAY_URL`) may be unreachable or slow to respond. Check connectivity.
7575- Individual PDS fetches can fail silently. The worker logs warnings and continues. Check server logs for details.
76767777-See [Backfill](../guides/backfill.md) for how the process works.
7777+See [Backfill](../guides/indexing/backfill.md) for how the process works.
78787979## Records not appearing in real time
8080···102102103103**Causes**:
104104105105-- `TOKEN_ENCRYPTION_KEY` is not set. Plugin secrets are encrypted at rest and cannot be read without this key. See [Plugins - Configuration](../guides/plugins.md#plugin-configuration).
105105+- `TOKEN_ENCRYPTION_KEY` is not set. Plugin secrets are encrypted at rest and cannot be read without this key. See [Plugins - Configuration](../guides/features/plugins.md#plugin-configuration).
106106- If `TOKEN_ENCRYPTION_KEY` changed since the secrets were saved, the existing encrypted values are unreadable. Re-enter the secrets via the dashboard or `PUT /admin/plugins/{id}/secrets`.
107107- Environment variable secrets (`PLUGIN_<ID>_<KEY>`) are overridden by dashboard-configured secrets. If you've set both, the dashboard values take precedence.
108108···143143144144**Explanation**: Each database is independent. Switching `DATABASE_URL` points HappyView at a fresh database. Your old data is still in the previous database file or Postgres instance.
145145146146-**Recovery**: Re-upload your lexicons and run backfills to re-index records from the network. Admin settings, users, and API keys need to be re-created manually. See the [SQLite → Postgres](../guides/sqlite-to-postgres-migration.md) or [Postgres → SQLite](../guides/postgres-to-sqlite-migration.md) migration guides.
146146+**Recovery**: Re-upload your lexicons and run backfills to re-index records from the network. Admin settings, users, and API keys need to be re-created manually. See the [SQLite → Postgres](../guides/database/sqlite-to-postgres-migration.md) or [Postgres → SQLite](../guides/database/postgres-to-sqlite-migration.md) migration guides.
147147148148## Jetstream disconnects frequently
149149
+3-3
packages/docs/docs/reference/xrpc-api.md
···11# XRPC API
2233-[XRPC](https://atproto.com/specs/xrpc) is the HTTP-based RPC protocol used by the atproto. HappyView dynamically registers XRPC endpoints based on your uploaded [lexicons](../guides/lexicons.md): query lexicons become `GET /xrpc/{nsid}` routes, procedure lexicons become `POST /xrpc/{nsid}` routes.
33+[XRPC](https://atproto.com/specs/xrpc) is the HTTP-based RPC protocol used by the atproto. HappyView dynamically registers XRPC endpoints based on your uploaded [lexicons](../guides/indexing/lexicons.md): query lexicons become `GET /xrpc/{nsid}` routes, procedure lexicons become `POST /xrpc/{nsid}` routes.
4455If a query or procedure lexicon has a [Lua script](../guides/scripting.md) attached, the script handles the request. Otherwise, HappyView uses built-in default behavior (described below).
66···210210## Next steps
211211212212- [Lua Scripting](../guides/scripting.md): Override the default query and procedure behavior with custom logic
213213-- [Lexicons](../guides/lexicons.md): Understand how lexicons generate these endpoints
214214-- [Admin API](admin-api.md): Manage lexicons and monitor your instance
213213+- [Lexicons](../guides/indexing/lexicons.md): Understand how lexicons generate these endpoints
214214+- [Admin API](admin/admin-api.md): Manage lexicons and monitor your instance
+4-4
packages/docs/docs/tutorials/statusphere.md
···21212222## Step 1: Add the record lexicon
23232424-First, tell HappyView to start indexing Statusphere records. Since `xyz.statusphere.status` is [published on the atproto network](../guides/lexicons.md#network-lexicons), you can add it directly from the dashboard:
2424+First, tell HappyView to start indexing Statusphere records. Since `xyz.statusphere.status` is [published on the atproto network](../guides/indexing/lexicons.md#network-lexicons), you can add it directly from the dashboard:
252526261. Go to **Lexicons > Add Lexicon > Network**
27272. Enter `xyz.statusphere.status`
···207207- **A query endpoint** (`xyz.statusphere.listStatuses`) with filtering, pagination, and single-record lookups
208208- **A write endpoint** (`xyz.statusphere.setStatus`) that creates records on the user's PDS and indexes them locally
209209210210-Everything was done through the dashboard — no server restarts, no config files, no deploys. For automation and CI/CD, the same operations are available via the [admin API](../reference/admin-api.md).
210210+Everything was done through the dashboard — no server restarts, no config files, no deploys. For automation and CI/CD, the same operations are available via the [admin API](../reference/admin/admin-api.md).
211211212212## Next steps
213213214214- [Lua Scripting](../guides/scripting.md): Explore the full Record and database APIs to build more complex queries
215215-- [Lexicons](../guides/lexicons.md): Learn about network lexicons, the backfill flag, and target collections
215215+- [Lexicons](../guides/indexing/lexicons.md): Learn about network lexicons, the backfill flag, and target collections
216216- [XRPC API](../reference/xrpc-api.md): Understand how the generated endpoints behave
217217-- [Admin API](../reference/admin-api.md): Automate lexicon management via the API
217217+- [Admin API](../reference/admin/admin-api.md): Automate lexicon management via the API
218218- [Statusphere example app](https://github.com/bluesky-social/statusphere-example-app): See the full Statusphere frontend
219219- [ATProto Statusphere guide](https://atproto.com/guides/applications): How the app works at the protocol level