Skip to main content
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:
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:
Pin a release with VERSION:

Update

Release installs can update themselves from the CLI:
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:
If you installed from source with go install, update through Go instead:
mio update also keeps 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:
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:
For scripts, CI, and agents, set a team API key:
The CLI reads credentials in this order:

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:
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 for the ordered recipe, and 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:
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).