---
name: happenings-page
description: >-
  Use when operating a Happenings PAGE through the CLI as a page admin, editor,
  finance, communication or moderator — events, tickets, orders, members,
  bundles, payouts, posts, votes, media and the rest of one page's day-to-day
  running. TRIGGER when the user asks to do something for a specific page or
  organisation ("publish the friday event", "who is on the guest list", "export
  our members", "refund that order"), when connecting or driving the page CLI
  MCP (`/page/mcp`, scope `page:run`, tools `hap_run` and `hap_help`), when a
  page command or connection is refused, or when deciding whether a command
  needs a Happenings system role. This is the PAGE-level surface; for
  platform-wide operator work use `happenings-cli` and `hap-mcp` instead.
---

# hap for page admins

The Happenings CLI has three access tiers, decided per command by the CLI's own
annotations. This skill covers the middle one.

| tier | commands | who |
|---|---|---|
| `local` | 592 | anyone, no account needed |
| **`backoffice`** | **419** | **anyone with a role on at least one page** |
| `system-role` | 437 | Happenings staff with a system role |

Everything below is the 419. If a command is not in that tier, a page admin
cannot run it — not through the CLI, not through the page MCP. That is a
property of the command, not of how you ask for it.

## The two things that bound you

**1. The command must be page-tier.** Verify rather than guess:

```bash
hap commands --access backoffice --json     # the authoritative list
hap commands --access backoffice --search members
```

The catalog is generated from the CLI's own annotations, so it is never stale
relative to the binary you are running. An unauthenticated call shows only
public bootstrap commands — log in first, or the list will look empty.

**2. Your role on the page decides the rest.** All 42 page actions resolve
through one permission matrix:

| role | actions | shape |
|---|---|---|
| `admin` | 40 / 42 | everything except the two creator-only actions |
| `editor` | 27 | content and events; no finance, no settings, no roles |
| `finance` | 16 | money; no content |
| `communication` | 12 | messaging and announcements |
| `moderator` | 9 | moderation only |

Two actions are **creator-only** and no role grants them: deleting the page and
transferring ownership.

A command you are allowed to *run* can still be refused for the page you point
it at. That is the matrix doing its job, not a bug — read the error before
retrying.

## Working on a page

**The page flag is not spelled consistently.** Most commands take `--page`
(259 flags), a minority take `--page-id` (47). Events are the same story:
`--event` (79) and `--event-id` (37). Do not assume — check the command:

```bash
hap events list --help
```

Resolve the page id once:

```bash
hap pages list --json                       # pages you hold a role on
hap pages get <id> --json
```

Useful entry points, by what you are actually doing:

```bash
# events
hap events list --page <id> --json
hap events create --page <id> --title "Fredagsbar" --start 2026-09-04T20:00
hap events publish ...                      # check --help for its flag spelling
hap events attendees ... --json

# people
hap members list --page <id> --json
hap members import --page <id> --file members.csv
hap titles list --page <id> --json

# money
hap orders list --page <id> --json
hap payouts list --page <id> --json
hap bank-accounts list --json               # note: bank-accounts, not bank

# commerce
hap products list --json
hap bundles list --json
hap vouchers list --json
hap coupons list --json

# door
hap checkin scan --help                     # scan/code/undo/list/stats
hap participants list --json
```

Flag spellings above are verified for `events list/create`, `members
list/import`, `orders list`, `payouts list`. For anything else, `--help` first —
guessing a flag wastes a round trip and an agent will happily invent one.

The 63 page-tier groups, largest first: `page`, `wardrobe`, `forums`, `members`,
`events`, `assistant`, `bundles`, `titles`, `pages`, `analytics`, `media`,
`deals`, `products`, `notif`, `table`, `music`, `bank`, `votes`, `tickets`,
`checkin`, `vouchers`, `articles`, `plans`, `links`, `locations`, `school`,
`tags`, `coupons`, `orders`, `payouts`, `cohosts`, `participants`, `access`,
`year`, `dashboard`, `fees`, `feedback`, `stories`, `verification` and the rest.

## Two flags worth habit

- `--json` on every read. Output is meant to be parsed, and the text format is
  for humans only.
- `--dry-run` on writes you are not certain about. Commands marked
  prod-sensitive report what they would change instead of changing it.

## Through the page MCP

`/page/mcp` (scope `page:run`) exposes exactly this surface to an agent as
`hap_run` and `hap_help`. It is the page-level sibling of `/operator/mcp`.

| | `/mcp` | **`/page/mcp`** | `/operator/mcp` |
|---|---|---|---|
| shape | 104 curated tools | **`hap_run` + `hap_help`** | `hap_run` + `hap_help` |
| who | any page admin | **any page admin** | `super_admin` only |
| reach | per-tool, per page | **419 backoffice commands** | all 1449 |
| token life | 365 days | **30 days** | 12 hours |

### Connecting

```bash
claude mcp add --transport http happenings-page https://api.happenings.social/page/mcp
```

```bash
codex mcp add happenings-page \
  --url https://api.happenings.social/page/mcp \
  --oauth-resource https://api.happenings.social/page/mcp
codex mcp login happenings-page
```

Consent opens in Connect. **Tick the pages this connection may act on** — that
selection is the ceiling, and it is enforced by the token itself, not by the
commands you send.

### The two tools

```jsonc
// hap_run — argv as an array, no leading "hap"
{"args": ["events", "list", "--page", "<page-id>", "--json"]}
{"args": ["members", "import", "--page", "<page-id>", "--file", "m.csv"], "dry_run": true}

// hap_help — when unsure of a flag, ask before guessing
{"command": "events list"}
```

`--json` is appended automatically unless you passed `--json`, `--help` or `-h`.
`dry_run: true` appends `--dry-run`.

### Why a call gets refused

Three different gates, three different fixes. Read the message before retrying —
none of them yield to a repeated call.

**"… is not available to a page connection"** — the command needs a system role,
or does not exist. The allowlist comes from the CLI's own annotations
(`hap commands --access backoffice --json`), so this is a property of the
command. There is no flag that unlocks it. If the work genuinely needs platform
scope, it belongs on `/operator/mcp` with a `super_admin`.

**A page-level permission error** — the command is allowed, but your role on
that page is not. `admin` reaches 40 of 42 actions, `editor` 27, `finance` 16,
`communication` 12, `moderator` 9. Two actions (delete page, transfer
ownership) are creator-only and no role grants them. Fix: get the role, or ask
someone who holds it.

**"no granted page is still accessible"** — the pages are re-resolved on every
request, so a role removed after consent takes effect immediately rather than at
token expiry. Reconnect after the role is restored.

### What confines it

Worth understanding, because it explains why some things are impossible rather
than merely blocked:

- The child credential is **page-confined in the token**. A command that takes
  no page flag at all still cannot touch a page outside the grant, and the
  confinement outranks page-creator, membership and even `super_admin`.
- The allowlist is read from the CLI binary that will run the command, so it can
  never grant something the binary does not classify as page-level.
- If the allowlist cannot be read, **nothing runs**. An unreadable catalog
  cannot prove a command is page-level, so it fails closed rather than open.
- Every call is audited with the token, command path, exit code and duration.

So the reach is exactly: page-tier command ∧ page in the grant ∧ your role on
that page.

### Working habits

- Read before you write. `hap_run` with a `list`/`get` first, confirm the ids,
  then act.
- `dry_run: true` on anything destructive, and show the user the output before
  running it for real.
- Never paste a page id you have not confirmed belongs to the granted set —
  `{"args": ["pages", "list", "--json"]}` tells you.
- Prefer `hap_help` over guessing a flag. Flag spelling varies (`--page` vs
  `--page-id`), and an invented flag costs a round trip.

## Environment safety

The CLI is frequently pointed at production. Before any write:

```bash
hap env            # which environment am I on
hap whoami --json  # who am I, and what roles do I hold
```

A page connection cannot cross environments — its token is bound to one API.

## See also

- `hap-mcp` / `happenings-cli` — the operator endpoint and the full CLI,
  including the 437 system-role
  commands and `hap mcp serve`. Reach for those only when the work genuinely
  needs platform scope.
- `pac` — task tracking, not page operations.
