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

# MIO CLI

> Install, update, and authenticate the Membership.io command-line interface.

Use the `mio` CLI for creator-side automation, scripted admin workflows, and quick API exploration from a terminal.

## Install

On macOS or Linux, install the latest released binary with the official installer:

```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/Searchie-Inc/mio-cli/main/scripts/install.sh | sh
```

The installer detects your OS and CPU architecture, downloads the matching GitHub release asset, verifies the SHA-256 checksum when checksum tooling is available, and installs `mio` into `/usr/local/bin`.

Install somewhere you own with `PREFIX`:

```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/Searchie-Inc/mio-cli/main/scripts/install.sh | PREFIX="$HOME/.local/bin" sh
```

Pin a release with `VERSION`:

```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/Searchie-Inc/mio-cli/main/scripts/install.sh | VERSION=0.2.1 sh
```

## Update

Release installs can update themselves from the CLI:

```bash theme={null}
mio update
```

`mio update` reruns the official release installer into the directory containing the current executable. Use `--version` to pin a release, or `--prefix` to install into another directory:

```bash theme={null}
mio update --version 0.2.1
mio update --prefix "$HOME/.local/bin"
```

If you installed from source with `go install`, update through Go instead:

```bash theme={null}
go install github.com/Searchie-Inc/mio-cli@latest
```

`mio update` also keeps the [agent skill](#install-the-agent-skill) in sync: it refreshes an unmodified install to the version bundled with the new binary, and leaves a hand-edited skill untouched.

## Install the agent skill

If you drive `mio` from a coding agent (Claude Code or Codex), install the bundled agent skill so the agent knows the CLI-only hub recipe and the render-contract traps *before* it starts calling commands:

```bash theme={null}
mio skills install                 # Claude Code — ~/.claude/skills/mio/SKILL.md
mio skills install --target codex  # Codex — $CODEX_HOME/skills/mio/SKILL.md
mio skills install --project       # scope to this repo (./.claude or ./.codex)
```

The skill is embedded in the `mio` binary, so it never needs a separate download and `mio update` always ships the current version. The install is idempotent and version-stamped: re-running is a no-op, an unmodified install is refreshed in place, and a skill you hand-edited is never overwritten unless you pass `--force`. Inspect or pipe the skill body with `mio skills print`.

## Authenticate

Interactive users can log in once:

```bash theme={null}
mio login
```

For scripts, CI, and agents, set a team API key:

```bash theme={null}
export MIO_API_KEY=mio_sk_live_xxxxx
```

The CLI reads credentials in this order:

```text theme={null}
--api-key <key> -> MIO_API_KEY -> key stored by mio login
```

## Moderation with an API key

By default a team API key **cannot** run hub moderation — ban/unban/warn/soft-ban a member, resolve reports, or remove/restore content all return `403` for a key. A key gains that power only when it is explicitly granted the **`can_moderate`** flag:

```text theme={null}
# grant at mint:
POST  /api/v1/teams/{team_id}/api-keys            { "name": "…", "can_moderate": true }
# or toggle an existing key:
PATCH /api/v1/teams/{team_id}/api-keys/{key_id}   { "can_moderate": true }
```

Both routes require a **real user (JWT) session** — a key can never grant itself moderation power, and the flag defaults to **off**.

Once granted, the key can drive the moderation console for hubs under its team, and every key-driven moderation action carries the acting key's `api_key_id` so it is never attributed solely to the key's creator — on the moderation audit log, on the durable moderation events, on the banned-members list (`banned_by_api_key_id`), and on every report the action auto-dismisses. Member-management (adding members, role changes) and report-reason management already work with any team key and do **not** require `can_moderate`. Team-security routes (roles, api-keys, verified domains, providers, OAuth clients) remain web/JWT-only.

## API Base

The CLI talks to the production API at `https://api.member.dev` by default. Override it per command with `--api-base <url>` or globally with `MIO_API_BASE_URL`.

Current API requests are sent to `/api/v1` paths. The backend still accepts older `/api` paths as a compatibility alias during the alpha period, but `/api/v1` is the documented contract.

## Build a hub end to end

The CLI can author a full, render-faithful hub — hub branding, menus, discussion spaces, playlists, media, and the homepage tree — without touching the raw API. Hub authoring is worth doing through the CLI because it validates blob keys, typed menu items, and hub-scoped hrefs *before* firing a request, and it surfaces the silent render-contract traps that a bare `200` hides. See [Create a hub](/guides/creating-a-hub) for the ordered recipe, and [Media workflow](/guides/media-workflow) for the media command group.

## Contact id vs. contact\_id (the id-namespace trap)

`mio` surfaces **two** contact identifiers, and mixing them up 404s a live contact:

* The `contacts` verbs — plus `contact-attributes` and `tags` — operate on the **team-contact id**, surfaced as `.id` (route param `{team_contact_id}`).
* The **member-shaped** verbs operate on the **global contact id**, a separate field surfaced as top-level `.contact_id` (from `.attributes.contact_id`). These are `hub-memberships` (add / set-role / ban / unban / warn), `activity contact`, community members (ban / unban / warn / soft-ban), `email enrollments` (create, list-by-contact), and `access-rules` overrides create.

Piping a `.id` into a `{contact_id}` route 404s. Read the global id from the flattened output first:

```bash theme={null}
CID=$(mio contacts retrieve ctt_abc123 -o plain --jq .contact_id)
mio hub-memberships add "$CID" --hub hub_abc123
```

The CLI appends an actionable hint on the resulting exit-4 error to point you at the right field.

## Recently fixed

These commands regressed or were incomplete and now work as documented:

* `media files update` (previously returned `400`).
* `tags assign` by tag id (`--tag-id`) and by name/slug (`--tag`).
* `contact-attributes values set` / `get`, and `contact-attributes hub-config create`. Hub-config gained per-hub flags `--in-profile`, `--in-onboarding`, `--required`, `--read-only`, and `--searchable` (replacing the old `--visible`). `values set` now routes each value into the attribute's typed field (`number`/`boolean`/`date`/`text`) instead of always sending `value_text`, so number, boolean, and date attributes are settable from the CLI; an unknown slug or a value that doesn't parse for its type is rejected up front with no write.
* `content reorder`.
* `automations test` (dry-run enrollment, no side effects).
