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:mio into /usr/local/bin.
Install somewhere you own with PREFIX:
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:
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 drivemio 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:
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: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 return403 for a key. A key gains that power only when it is explicitly granted the can_moderate flag:
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 athttps://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 bare200 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
contactsverbs — pluscontact-attributesandtags— 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 arehub-memberships(add / set-role / ban / unban / warn),activity contact, community members (ban / unban / warn / soft-ban),email enrollments(create, list-by-contact), andaccess-rulesoverrides create.
.id into a {contact_id} route 404s. Read the global id from the flattened output first:
Recently fixed
These commands regressed or were incomplete and now work as documented:media files update(previously returned400).tags assignby tag id (--tag-id) and by name/slug (--tag).contact-attributes values set/get, andcontact-attributes hub-config create. Hub-config gained per-hub flags--in-profile,--in-onboarding,--required,--read-only, and--searchable(replacing the old--visible).values setnow routes each value into the attribute’s typed field (number/boolean/date/text) instead of always sendingvalue_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).