Event Logs
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.
Event types
Section titled “Event types”Events follow a category.action naming convention. Each event has a severity level (info, warn, or error), an optional actor_did (the user who triggered it), an optional subject (what was affected), and a detail JSON object with event-specific data.
Lexicon events
Section titled “Lexicon events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
lexicon.created | info | Lexicon NSID | revision, has_script, source |
lexicon.updated | info | Lexicon NSID | revision, has_script, source |
lexicon.deleted | info | Lexicon NSID | — |
Logged when lexicons are uploaded, updated, or deleted via the admin API. The actor_did is the user who performed the action.
Record events
Section titled “Record events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
record.created | info | Record AT URI | collection, did, rkey |
record.deleted | info | Record AT URI | collection, did, rkey |
Logged when records are received from Jetstream and stored or removed from the local database. These are system-triggered events (actor_did is null). If a database error occurs during the operation, the same event type is logged with error severity and the error message is included in the detail.
Script events
Section titled “Script events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
script.executed | info | Method NSID | method, caller_did, duration_ms |
script.error | error | Method NSID | error, script_source, input, caller_did, method |
Logged when Lua scripts run for XRPC query or procedure endpoints. Script errors capture the full context needed to reproduce and debug the issue: the error message, the complete Lua script source, the input that triggered it, and the caller’s DID.
User events
Section titled “User events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
user.created | info | New user DID | template (if used) |
user.deleted | info | Removed user ID | — |
user.bootstrapped | info | Bootstrapped user DID | — |
user.permissions_updated | info | User ID | granted, revoked |
user.super_transferred | warn | New super user ID | from_user_id |
The user.bootstrapped event is logged when the first user is auto-promoted to super user (see Auth - Auto-bootstrap).
Auth events
Section titled “Auth events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
auth.permission_denied | error | Endpoint path | required_permission, user_id |
Logged when a user attempts to access an endpoint they don’t have permission for.
API Key events
Section titled “API Key events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
api_key.created | info | Key ID | name, permissions |
api_key.revoked | info | Key ID | name |
Script Variable events
Section titled “Script Variable events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
script_variable.upserted | info | Variable key | — |
script_variable.deleted | info | Variable key | — |
Hook events
Section titled “Hook events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
script.executed | info | Trigger ID | trigger_id |
script.dead_lettered | error | Trigger ID | trigger_id, error |
Logged when record/label scripts run. Dead-lettered events indicate a script failed all retry attempts. You can manage dead letters from the Data > Dead Letters page in the dashboard — see Dead Letters below.
Backfill events
Section titled “Backfill events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
backfill.started | info | Collection NSID | job_id |
backfill.completed | info | Collection NSID | job_id, total_repos |
backfill.failed | error | Collection NSID | job_id, error |
See Backfill for background on backfill jobs.
Jetstream events
Section titled “Jetstream events”| Event Type | Severity | Subject | Detail |
|---|---|---|---|
jetstream.connected | info | — | url |
jetstream.disconnected | warn | — | reason |
Logged when the WebSocket connection to Jetstream is established or lost.
Querying events
Section titled “Querying events”Use the admin API to query event logs with filters:
const TOKEN = "hv_..."; // your API keyconst headers = { Authorization: `Bearer ${TOKEN}` };
interface Event { id: string; event_type: string; severity: string; actor_did?: string; subject?: string; detail: Record<string, unknown>; created_at: string;}
interface EventsResponse { events: Event[]; cursor?: string;}
// Get all errorsconst errors: EventsResponse = await fetch( "http://127.0.0.1:3000/admin/events?severity=error", { headers },).then((r) => r.json());
// Get script errors for a specific lexiconconst scriptErrors: EventsResponse = await fetch( "http://127.0.0.1:3000/admin/events?event_type=script.error&subject=com.example.feed.like", { headers },).then((r) => r.json());
// Get all lexicon-related eventsconst lexiconEvents: EventsResponse = await fetch( "http://127.0.0.1:3000/admin/events?category=lexicon", { headers },).then((r) => r.json());
// Paginate through resultsconst page: EventsResponse = await fetch( "http://127.0.0.1:3000/admin/events?limit=20&cursor=2026-03-01T11:59:00Z", { headers },).then((r) => r.json());const TOKEN = "hv_..."; // your API keyconst headers = { Authorization: `Bearer ${TOKEN}` };
// Get all errorsconst errors = await fetch( "http://127.0.0.1:3000/admin/events?severity=error", { headers },).then((r) => r.json());
// Get script errors for a specific lexiconconst scriptErrors = await fetch( "http://127.0.0.1:3000/admin/events?event_type=script.error&subject=com.example.feed.like", { headers },).then((r) => r.json());
// Get all lexicon-related eventsconst lexiconEvents = await fetch( "http://127.0.0.1:3000/admin/events?category=lexicon", { headers },).then((r) => r.json());
// Paginate through resultsconst page = await fetch( "http://127.0.0.1:3000/admin/events?limit=20&cursor=2026-03-01T11:59:00Z", { headers },).then((r) => r.json());let client = reqwest::Client::new();let token = "hv_..."; // your API key
// Get all errorslet errors: serde_json::Value = client .get("http://127.0.0.1:3000/admin/events") .query(&[("severity", "error")]) .bearer_auth(token) .send() .await? .json() .await?;
// Get script errors for a specific lexiconlet script_errors: serde_json::Value = client .get("http://127.0.0.1:3000/admin/events") .query(&[ ("event_type", "script.error"), ("subject", "com.example.feed.like"), ]) .bearer_auth(token) .send() .await? .json() .await?;
// Get all lexicon-related eventslet lexicon_events: serde_json::Value = client .get("http://127.0.0.1:3000/admin/events") .query(&[("category", "lexicon")]) .bearer_auth(token) .send() .await? .json() .await?;
// Paginate through resultslet page: serde_json::Value = client .get("http://127.0.0.1:3000/admin/events") .query(&[("limit", "20"), ("cursor", "2026-03-01T11:59:00Z")]) .bearer_auth(token) .send() .await? .json() .await?;token := "hv_..." // your API key
// Get all errorsreq, _ := http.NewRequest("GET", "http://127.0.0.1:3000/admin/events?severity=error", nil)req.Header.Set("Authorization", "Bearer "+token)errors, err := http.DefaultClient.Do(req)
// Get script errors for a specific lexiconreq, _ = http.NewRequest("GET", "http://127.0.0.1:3000/admin/events?event_type=script.error&subject=com.example.feed.like", nil)req.Header.Set("Authorization", "Bearer "+token)scriptErrors, err := http.DefaultClient.Do(req)
// Get all lexicon-related eventsreq, _ = http.NewRequest("GET", "http://127.0.0.1:3000/admin/events?category=lexicon", nil)req.Header.Set("Authorization", "Bearer "+token)lexiconEvents, err := http.DefaultClient.Do(req)
// Paginate through resultsreq, _ = http.NewRequest("GET", "http://127.0.0.1:3000/admin/events?limit=20&cursor=2026-03-01T11:59:00Z", nil)req.Header.Set("Authorization", "Bearer "+token)page, err := http.DefaultClient.Do(req)AUTH="Authorization: Bearer hv_..." # your API key
# Get all errorscurl "http://127.0.0.1:3000/admin/events?severity=error" -H "$AUTH"
# Get script errors for a specific lexiconcurl "http://127.0.0.1:3000/admin/events?event_type=script.error&subject=com.example.feed.like" -H "$AUTH"
# Get all lexicon-related eventscurl "http://127.0.0.1:3000/admin/events?category=lexicon" -H "$AUTH"
# Paginate through resultscurl "http://127.0.0.1:3000/admin/events?limit=20&cursor=2026-03-01T11:59:00Z" -H "$AUTH"See the Admin API reference for full parameter documentation.
Retention
Section titled “Retention”Event logs are automatically cleaned up based on the EVENT_LOG_RETENTION_DAYS environment variable (default: 30 days). A background task runs hourly to delete events older than the configured retention period.
Set EVENT_LOG_RETENTION_DAYS=0 to disable automatic cleanup and keep logs indefinitely.
See Configuration for all environment variables.
Dead Letters
Section titled “Dead Letters”When a record or label script fails after all retry attempts, the event is stored in the dead letters queue. You can manage dead letters from the Data > Dead Letters page in the dashboard.
From the dead letters page you can:
- Retry Script — replay the stored event through the script (use after fixing the script)
- Re-index — fetch the record fresh from the PDS and run it through the full indexing pipeline (use when the record may have changed)
- Dismiss — mark the dead letter as resolved without retrying
Bulk actions are available for selected rows or all entries matching the current filters.
Next steps
Section titled “Next steps”- Admin API — Event Logs — full query parameters and response format
- Permissions — control which users can read event logs
- Troubleshooting — using event logs to diagnose issues

