# App Events

> Read the account-visible activity log for releases, runtime errors, logs, jobs, and webhooks.

- Canonical page: https://docs.userland.fun/guides/app-events/
- Docs index for agents: https://docs.userland.fun/llms.txt

> **For agents:** After every publish and whenever something fails, run `userland apps events "$APP_ID" --severity error --limit 25`. Filter with `--type` using an exact event type such as `job.failed`. Write your own log lines with `ctx.log`, and never log secret values.

## Read events

```bash
userland apps events "$APP_ID" --limit 25
userland apps events "$APP_ID" --severity error
userland apps events "$APP_ID" --type webhook.rejected
userland apps events "$APP_ID" --release "$RELEASE_ID"
```

The API equivalent is `GET /v0/apps/:app_id/events` with optional `type`, `severity` (`debug`, `info`, `warn`, `error`), `release_id`, `limit` (1-100, default 100), and `cursor`. Events are newest first. When more events exist, the response includes a `cursor`; pass it back to read the next page.

Any member of the app's platform account can read events. App users inside the published app cannot.

## Event types

`--type` matches the full type exactly.

| Area | Types |
| --- | --- |
| Releases | `release.created`, `release.activated`, `release.rolled_back`, `release.retention_failed`, `resource.provisioned`, `resource.omitted` |
| Runtime | `runtime.request_error`, `runtime.deploy_failed`, `runtime.log.debug`, `runtime.log.info`, `runtime.log.warn`, `runtime.log.error` |
| Jobs | `job.enqueued`, `job.succeeded`, `job.retry`, `job.failed`, `job.blocked`, `job.enqueue_failed` |
| Webhooks | `webhook.received`, `webhook.queued`, `webhook.delivered`, `webhook.rejected`, `webhook.delivery_failed`, `webhook.quota_exceeded`, `webhook.handler_missing` |
| Secrets | `secret.set`, `secret.deleted` (the event names the secret, never its value) |
| Files and apps | `files.uploaded`, `files.deleted`, `app.unpublished` |

`release.retention_failed` is a warning that old releases could not be cleaned up; the new release is still live. `resource.omitted` means a resource from an earlier release is no longer in the new release's manifest.

`runtime.request_error` means a server request threw or returned a 5xx; the app visitor got a generic `runtime_error` response. `webhook.rejected` usually means a missing or wrong signature, a stale timestamp, or a plan that does not include webhooks.

## Write log events

Server code writes its own events with `ctx.log`:

```js
await ctx.log.info("order created", { order_id: order.id });
await ctx.log.error("payment provider failed", { status: response.status });
```

Each call becomes a `runtime.log.<level>` event with your message and metadata. Userland redacts metadata keys that look secret (`token`, `secret`, `password`, `api_key`, `authorization`), long token-like values, and values read through `ctx.secrets`. Treat that as a safety net, not permission to log secrets.

## Retention

Events are kept for the plan's event retention window: Free 1 day, Starter 7 days, Business 30 days, Business Plus 90 days.

For traffic totals, top paths, and error rates over time, use [App Analytics](/guides/app-analytics/).
