mio CLI, or with the raw API. The admin is the quickest way to create, open, rename and delete a hub. Its Pages list can create and delete pages and link to supported page views on the member-facing hub. Hub owners can edit supported pages in the mio-hub inline editor; availability depends on the hub environment’s release. The CLI and API remain options for authoring page trees. Start with mio hubs scaffold, which builds an entire hub in one command; drop to the step-by-step recipe when you need something the template does not cover. It also spells out the render-contract gotchas that cause silent failures: the API returns 200, but the hub renderer drops malformed input, so a card or section never appears and nothing tells you why. These silent drops are the number-one pain point in real CLI-only builds.
Every
mio command in this guide is verified against the CLI. Prefer the CLI for hub authoring — it validates blob keys, typed menu items, and hub-scoped hrefs before firing a request. The raw API is documented at the end for direct integrators.In the admin
The admin at app.member.dev is where you manage your account: your hubs, the media library, API keys and account settings. If you are new to it, Getting started in the admin walks you from sign-up to your first open hub. This section covers the hub tasks the admin handles and names the API request that does the same thing. Only the team owner can create, change or delete a hub, in the admin and through the API alike.Create a hub
- Open Hubs in the sidebar. The admin opens on this page when you sign in.
- Click Create hub at the top of the page. When you have no hubs yet, the empty state has the same button, and so does the Create a hub card on the Getting started page.
- Fill in the Create hub dialog and click Create hub.
The new hub appears in the list and the admin confirms with
Created 'Member Academy'.
Once the MIO-4519 Admin release is deployed and that environment enables the scaffold API, Admin creates hubs from the active starter template. The template gives the hub neutral branding, a Home, Content, Discussions, Events, and Members menu, public Announcements and General discussion spaces, a Getting Started playlist with placeholder lessons, a welcome post, and a starter homepage. The homepage is public, but a hub created with Private hub on remains private until you make it public. You can edit or replace the starter content after creation.
Scaffolding is enabled per backend environment and is off by default. While it is unavailable, Admin creates a bare hub through the regular create endpoint; that fallback does not add the starter menu, spaces, playlist, welcome post, or homepage. Availability in one environment does not mean it has rolled out to every environment.
Slugs are unique across all of Membership.io, not only within your team. If another hub already uses the slug you chose, your hub is still created, with a random six-character suffix added, and the confirmation says so: Created 'Member Academy' with slug 'member-academy-a1b2c3'; your requested slug was unavailable. You can choose a different slug afterwards in General settings.
API equivalent for the MIO-4519 Admin release where scaffolding is enabled: POST /api/v1/teams/{team_id}/hubs/from-template. The fallback uses POST /api/v1/teams/{team_id}/hubs/. See Create a hub with the API.
Open the live hub
Each hub card shows the hub’s name, its slug with a button to copy it, when it was last updated, and a Private or Public badge. The card actions are:- Manage opens the hub’s Pages list in the admin (see Manage a hub).
- Open hub opens the live hub in a new tab when the admin has a hub origin configured. The destination uses that environment’s hub origin and the hub slug, even if the hub has a custom domain. A private hub is visible only to its members; to anyone else the address answers as if the hub did not exist. The action is not shown when the origin is unavailable.
data.attributes.slug in the same way. To read a hub by its slug, see Look a hub up by slug.
Manage a hub
Manage opens the hub’s Pages list (/hubs/{hub_id}/pages). Its sidebar lists Pages, Payments, and Settings. On Pages, select Create page to add a page. The dialog asks for Title, Slug, and Privacy (Members by default). The list shows each page’s access level. Select a page title or choose View page from More options to open a supported page on the member-facing hub in a new tab; some rows do not have a page view. A new, unpublished page is not visible to public visitors or members; its signed-in hub owner can open the draft in the inline editor, while other visitors may see not found. Use More options to delete a page when allowed. In the workspace sidebar, View hub opens the member-facing hub in a new tab. These links are available only when the current admin environment has a hub origin configured.
Viewing a page does not sign you in as a hub contact. Public pages can be viewed without a hub session; a private hub or member-only page may require you to sign in through the hub’s existing sign-in flow. To edit a supported homepage, custom page, or sales page, sign in to the hub as its owner and select the floating Edit button. The editor is part of the mio-hub release, so its availability depends on the hub environment. The CLI and API also support page-tree authoring; see the Pages and page builder guide.
Choose Settings to open General settings (/hubs/{hub_id}/settings/general). There you can change:
- Hub name and Description (optional).
- Slug. This changes the hub’s address, and links that use the old slug stop working. If another hub already uses the new slug, the field shows That slug is already taken. Unlike when you create a hub, no suffix is added for you.
- Private hub. Making a private hub public does not change its registration setting. A hub created from
starteralready has registration enabled; a bare hub created private needs registration enabled separately (see the warning under Create a hub).
PATCH /api/v1/teams/{team_id}/hubs/{hub_id}. See Update a hub with the API.
Delete a hub
Delete a hub from Settings → Danger zone (Delete hub) or with Delete hub in the hub card’s ⋯ menu. In the Delete hub? dialog, type the hub’s exact saved name into Type to confirm. The destructive Delete hub button stays disabled until the name matches. A successful delete returns you to the Hubs list. Deleting is a soft delete that cascades: the hub, its pages and their sections, and its content items are all marked deleted, and every membership in the hub ends, so members lose access straight away. Neither the admin nor the API can restore a deleted hub. Its slug becomes free for a new hub. A hub with a custom domain cannot be deleted until the domain is removed. API equivalent:DELETE /api/v1/teams/{team_id}/hubs/{hub_id}. See Delete a hub with the API.
What stays out of the admin
The admin handles account-level work and lists a hub’s pages. Managing a hub’s content and members happens in the hub itself, and the CLI and the API cover the same ground. Admin applies thestarter template during hub creation where the scaffold rollout is enabled, but it does not offer a template picker or controls for branding and navigation. Use the CLI recipe in the rest of this guide when you need to choose or customize a template.
Before you start
Set your team (and, once created, your hub) context so you can drop the repeated flags:hub_abc123, page_home123); substitute the real UUIDs your own commands return.
The CLI flattens JSON:API resources by default, so mio hubs create ... --jq .id gives you the new id for the next step.
Start here: scaffold the whole hub
mio hubs scaffold collapses the entire sequence below into a single idempotent command, and it is the recommended way to reach a working hub. It creates the hub and applies a full-experience template — brand colours, navigation, registration, discussion spaces, onboarding schema, policies, playlists, and a homepage. Either the backend builds the whole hub in one request, or the CLI builds it step by step through its own request-builders and client layer (never raw REST), where every template value passes the same validation as the individual commands below; see where the hub gets built.
The shipped
starter template applies no imagery — no logo, favicon, social image, or page images. Set your own with --logo-url / --favicon-url (below) and by filling the homepage’s image nodes with durable URLs.A hub with no logo_url renders its title as text rather than a broken image, so a freshly scaffolded hub looks intentional — just unbranded.starter is the one hub template the current catalog ships. A template id the backend does not serve exits 2 before anything is written, naming the ones it does: hub template "community" is not in the catalog — available: starter.
Templates are read from the page-builder catalog served by the backend the CLI is pointed at, so mio hubs templates needs credentials and lists exactly what a scaffold against that backend would apply.
It is safe to re-run: existing spaces, onboarding attributes, and playlists are skipped rather than duplicated. The hub is left private by default — add --publish to make it public. Self-registration comes from the template (the starter template sets settings.registration.enabled: true), not from publishing; see what publishing does and does not turn on. When the template declares a welcome discussion post, the scaffold creates it once (a re-run finds it by title and does not post a second one), and the backend makes the team owner the hub’s owner-admin as part of creating the hub.
A
--hub run fills gaps; it does not overwrite. It adds what the hub is missing and keeps everything the hub already has:- branding and settings keys, including a closed self-registration;
- navigation menus, bucket by bucket. An empty bucket is filled; a bucket the template does not have, such as the mobile menu, is never removed;
- policy text and the policy enforcement gate;
- onboarding attribute settings.
--primary-color, --logo-url, --registration-enabled, and the rest) still win over the hub. Each value it keeps is reported on stderr and in --dry-run.To let the template win instead, add --reapply-template. Off a TTY it needs --yes, and without --hub it is a usage error. It changes the hub like this:- every branding and settings key the template sets replaces the hub’s value, except the policy settings (enforcement is covered below). Keys the template does not set are left alone;
- the template’s menu replaces the hub’s whole menu, so a bucket the template does not have, such as the mobile menu, is removed;
- hand-written legal text is replaced with the template’s text, or reset to the platform default when the template has none;
- policy enforcement is turned on when the template enables it. It is never turned off;
- the onboarding settings of the template’s attributes are overwritten.
--reapply-template. Every --hub run checks the template’s pages first. It stops with exit code 2 before anything is written, --dry-run included, if it finds any of these:- a page you edited;
- someone else’s page at a template slug;
- an existing homepage that the template’s homepage would replace.
--reapply-template run asks members to accept the terms of service again only when it changes the terms’ version. Only a template whose terms require acceptance changes it, and the starter template’s do:- A reset to the platform default sets the version to
default-v1. Ifmio hubs policies getalready reportsdefault-v1for the terms, the version stays the same and nobody who accepted the terms is asked again, although the text is still reset. Any other version was set when custom text was saved with acceptance required (--require-acceptance), and later saves or resets without it keep that version. In that case the reset asks everyone who accepted it. - Template text that differs from the stored text gets a new version, so everyone is asked. Identical text keeps its version.
- A template whose terms do not require acceptance never changes the version, so the text is replaced without asking anyone who accepted the terms to accept them again.
mio release after v0.23.0. On v0.23.0 and earlier, every --hub run overwrites the hub’s palette, menu, registration and policy text with the template’s, and --reapply-template is an unknown flag.A scaffolded page already has a draft. Its
draft_version is 1, not 0, so the first tree write you make against it needs --if-match 1 — see step 6. Never guess the token; read it back with mio pages tree get <page_id> --jq .draft_version.Where the hub gets built
A create (--name and --slug) first asks the backend to build the whole hub in one transaction, with POST /api/v1/teams/{team_id}/hubs/from-template. Each deployment switches that operation on or off, and it is off by default. When the backend does not offer it, the CLI builds the hub itself, step by step, and says so on stderr (hub scaffold op not available on this backend — applying client-side). The CLI also builds client-side, naming the reason on stderr, for --hub, for a create missing --name or --slug, for --catalog, and for any branding override (--branding-json, or a palette flag such as --primary-color). --dry-run never calls the operation. -o json reports the same keys either way.
Every playlist the scaffold creates also gets content items: a container for the playlist and a lesson for each of its items. Content items are what give a playlist’s files progress tracking, “My List” saves and comments, and they are what a page’s file binding points at (see which id a dataSource takes). -o json lists them under content_nodes. On a --hub run the CLI reconciles the playlists it created or can recover, and names any others on stderr with the command to run.
Only the server-side build records which template built the hub, so a bare mio content reconcile --hub <hub_id>, which reconciles the playlists the hub was scaffolded with, works only on a hub the backend built. On a hub the CLI built, or one built by hand, it answers 422 (exit 2). Pass the playlists instead:
-o json result (.playlists[].playlist_id), or list the hub’s own playlists with mio media playlists list --limit 100 -o json --jq '.[] | select(.hub_id == "hub_abc123") | {id, title}'. content reconcile is safe to re-run: content items that already exist are adopted, not duplicated.
Content items on the client-side path, and the server-side build for
starter, arrive in mio v0.24.0 (the release after v0.23.0). On v0.23.0 and earlier, a template that declares space icons, playlist documents or a playlist dataSource key (starter declares all three) is always built client-side, and the client-side build creates no content items: run mio content reconcile with --playlist-id for each of the hub’s playlists.1. Create the hub
--name maps to the hub title; --slug is the unique lookup key public routes use. A new hub is private and unpublished — not reachable by members yet. The CLI says so and tells you how to publish (this hint prints to stderr, so JSON stdout stays clean):
https://hub.member.dev/{slug}. For other environments, use that environment’s hub origin. The admin builds its Open hub link from its configured hub origin and the slug.
2. Branding, favicon, registration, and menu
Branding, settings, and meta are JSONB blobs. The--branding-json / --settings-json / --meta-json flags merge (read-modify-write, so a partial edit never clobbers sibling keys). The API stores branding and meta keys as sent, so the CLI validates those keys itself: an unknown key warns on stderr and is still sent, and --strict-keys upgrades that to a hard error with no request fired.
The API validates settings keys. An unknown top-level key, or an unknown sub-key of policies, registration, email or auth, is rejected with a 422 naming the key (exit 2), and nothing is written. The API also checks the value of language (en or de) and the shapes of playlistHero, discussionsBanner and buttonColors. It stores the sub-keys of every other section, such as appearance or header, as sent. From v0.24.0 (the release after v0.23.0) the CLI leaves settings keys to the API, with or without --strict-keys. It still warns about an unknown sub-key of settings.achievements, because the API stores those as sent and reads only enabled. On v0.23.0 and earlier the CLI checks settings keys against an older list of its own: it warns that an unknown key “is stored verbatim” and the API then rejects it anyway, and --strict-keys blocks, with no request, keys the API accepts, such as language, moderation, playlistHero and email.from_localpart.
--favicon-urlsetsbranding.favicon_url;--logo-urlsetsbranding.logo_url. Both are read-modify-write, so sibling branding keys are preserved.--registration-enabledsetssettings.registration.enabled. It is tri-state: pass--registration-enabledto enable,--registration-enabled=falseto disable explicitly, or omit it to leave it untouched.mio hubs retrievesurfaces a derivedregistration_enabledfield so you can confirm it.- On
hubs update,--navigation-jsonreplaces the whole navigation blob. For item-by-item edits usemio hubs navigation list|add|remove|reorder.
What each branding key paints
Most of the branding blob is freeform, but the keys the hub renderer reads are fixed. Gettingsecondary wrong is the single most common branding mistake.
*_url branding keys are the exception to “stored verbatim”. The rest of the blob has no server schema, but every branding key whose name ends in _url — logo_url, favicon_url, social_image_url, custom_font_url, and any future one — is validated on write: it must be an absolute https:// URL with a real hostname, and must carry no embedded credentials, no percent-encoding in the host, and no control characters. A data: URI, a javascript: URI, a bare http:// URL, or a relative path is rejected with a 422, naming the offending key. So a data: SVG logo fails — upload the asset and use a durable URL instead.
Who controls light and dark mode
The hub’s own mode lives atsettings.background.type (light, dark, or custom; default light), and it is consulted for exactly one purpose: custom is the only value that overrides the visitor, and the only mode in which branding.text and branding.background are read at all.
background is a settings key the API accepts, so this passes without a warning on every CLI version.
branding.dark_mode does not choose the mode, but it is not inert either: it selects the defaults for text and background — exactly the two keys a custom hub relies on. With dark_mode: true and neither key set, background defaults to secondary and text to white; with it false or absent, background defaults to white and text to secondary. So on a custom hub, {"secondary":"#0B1F1C","dark_mode":true} renders a dark page with white text, and dropping dark_mode flips the same hub to a white page with dark text. Set it in step with the mode you chose — the renderer logs a warning when the two disagree.
Menu items and hrefs
- Menu items must be typed. The hub frontend’s parser silently drops any
header/footeritem that lacks a non-emptytype(url,page,playlist, ordiscussions), so a menu shipped without types renders empty. The CLI rejects untyped items up front.header/footermust each be an array of objects; themobilebucket uses a different{id,label,route,icon}shape. - Every menu
hrefmust be a root-relative path, and hub-scoped. The API validates this on write: anhrefmust start with exactly one/, carry no URL scheme, no protocol-relative//prefix, no backslashes, no whitespace, and no control characters — the raw value is checked for whitespace and control characters, and its percent-decoded form for control characters only, so an encoded space (/content/My%20Playlist) is deliberately fine. Maximum length is 2048. An absolutehttps://…menu href is rejected with a422— the CLI’s own check still lets it through, so this is one of the few places the CLI is more permissive than the API. Keep the path inside your hub (/member-academy/about), or it escapes the hub and 404s. - A
type:"url"item needslabel,href, andposition. Omittingpositionis a422, not a default.labelis 1–120 characters, and each bucket holds at most 50 items.
Navigation icons — two different vocabularies
The API checks that anicon is a string of at most 100 characters, but not that the name exists — so a valid-looking wrong name is a silent drop rather than a 422.
headeraccepts any id in the hub frontend’s icon sprite (~205 glyphs), and is the only bucket that actually renders one.iconis optional here: an unknown name drops the icon but keeps the menu item. Verified-present picks:home,content,chat,users,search,star,link,earth,calendar,bell,play,video,settings,email,folder,heart,lock,tag,podcast,download.infoandglobeare not in the sprite — they render blank with no error. Useinformation/information-circleandearth/globalinstead.footeraccepts and storesicon, but the footer never renders it — only the header does. A footer icon is not an error, just inert.mobiletabs accept an eight-value whitelist only, andiconis mandatory — an item with a missing or off-list icon is dropped entirely. These are frontend component names, not sprite ids, and the casing matters:Home,Bell,User,Users,MessageSquare,MessageCircle,Search, andcontent(that last one is deliberately lowercase). Copying a header value such ashomeloses the tab. Two more mobile-only rules: fewer than three valid tabs and the whole list is replaced by the frontend defaults; more than five and it is truncated to five.
-json flags are merge-only and cannot remove one — a null persists as literal null), use --unset with a dotted path whose first segment picks the blob:
--unset is repeatable/comma-separated and is applied after the -json merges and scalar flags.
3. Discussion spaces
--access-level is public or restricted; --posting-permission is any_member, admins_only, or segment (with --segment-id).
4. Playlists → items → publish to the hub
Build a playlist, curate its items, give it a cover, then publish it to the hub. Publishing writes thehub_media row that surfaces the playlist on the hub’s /content browse grid and homepage content-grid.
playlists items also supports list, remove <item_id>, and reorder <item_id> --position N. Note that remove and reorder take the item id (the id from items list), not the file id.
5. Images inside page trees — use durable URLs
Page-tree image nodes must reference a URL that does not expire. Thevariants map returned by an upload is imgproxy-signed and expires in ~24–48h — inline one of those and the image silently 404s a day later. Use media files durable-url instead:
- The durable URL is the file’s
durable_variantsentry joined with the required?hub_id=param (the command adds it) so it resolves for--hub. - The URL 404s until the file is published public to that hub.
--publishdoes that inline (visibility public, published now); otherwise runmio media hub-media publish --hub hub_abc123 --file-id file_hero --visibility publicfirst. --presetemits one variant (e.g.thumbnail-160,medium-720,large-1440,webp-medium); omit it to print every preset. Durable URLs are image-only — a non-image file has no durable variants.
6. Build the homepage
Create the page, scaffold a node-tree from the page-builder catalog, fill in real values, then set the draft and publish it.pages tree set --if-matchis optional and defaults to0. Omit it only for the first tree on a page that has never had a draft:pages tree get404s until a draft exists, so there is no version to read back. The0default does not bypass the concurrency guard — sending0(or any stale value) against a page that already has a draft is rejected as a conflict, so you can never silently clobber an existing draft. For every later write, pass thedraft_versionfrom a priorpages tree get. A page created bymio hubs scaffoldalready has a draft at version1, so your first write against it needs--if-match 1.- Scaffolding is offline-capable and prints an informational
catalog: …line to stderr, so> tree.jsoncaptures only the JSON. Apage-*template emits a complete{"root": …}tree ready fortree set; a section template emits a bare subtree to drop into a root’schildren.
Scaffold the page, then fill it in
Page templates come in three kinds, and each is filled in differently:mio pages catalog templates lists the templates the backend you point at serves. To classify any of them, run the command below. It prints "outline", "complete" or "system", quotes included, because scaffold output is always JSON:
page-homepage’s hero child arrives as {"kind":"row","template":"hero","settings":{}} — no surface, no values. Scaffolding each section on its own gives you the real, design-conformed recipe (correct kind, settings.surface, column widths): the same hero scaffolded from the hero template is a container carrying {"maxWidth":"content","padding":0,"surface":{"padding":"section","background":{"type":"tint"}}}.
So scaffold the page for the skeleton, scaffold each section separately, and splice:
row is the unified 1–4 column section — pick the layout with --variant (1col, 2eq, 2left, 2right, 3eq, 4eq, bound-cards, cta-band, faq). cta-band, faq, and bound-cards arrive already filled with placeholder values, which is the fastest way to see the node contract in practice. Discover the rest with mio pages catalog templates and mio pages catalog section-types --writable-only; there is no pages catalog list.
A sales page
page-sales is a complete page, so scaffold it and edit its values in place:
--type sales makes the hub render the page without its header and mobile navigation, because a sales page owns its full-bleed layout; the footer and its menu still render. Every other page type keeps the hub’s chrome, and a page created without --type is generic. Pass --privacy public here too: a page of any type other than login, register or payments is created members-only without it.
pages tree get and pages tree set do not use the same shape. tree get returns the resolved author draft with tree set to the bare root node — already unwrapped from {"root": …} — alongside draft_version. tree set requires the wrapper. Re-wrap on the way back in:tree get response straight back into tree set fails: a tree with no top-level "root" key is a 422 invalid_tree. pages catalog scaffold already emits the set shape, which is why the scaffold → set pipe needs no transform. Note that tree get returns the resolved draft (actions mapped, dataSource expanded), so it is a read-back, not a byte-exact copy of what you wrote.7. Publish the hub
--registration-enabled is not redundant here. Because this recipe created the hub private in step 1, the self-registration default was never injected, and publishing does not add it — so without that flag the hub goes live with no way for members to sign themselves up. See what publishing does not turn on.
Render-contract gotchas
The API validates structure, not renderability. Every mistake below returns200 and then goes wrong at render time with no error anywhere — usually by silently dropping the node, section, or card, sometimes by quietly rendering something other than what you asked for. Verify the outcome, don’t trust the write:
- A node’s content goes in a top-level
value, notsettings.value. This is the single biggest silent-drop trap — see the node envelope. weightmust be numeric. A nodeweightis a number like700, never a CSS keyword like"bold"— an unrecognized weight is discarded and the node renders at whatever weight it would have had with noweightset at all. (pages tree setcatches any non-numeric weight client-side, before any HTTP.)- A section must carry its
template. The catalog scaffold setstemplateon every section node ("hero","carousel","row", …). If you hand-build a tree, a section without itstemplatewill not render. A blank or non-stringtemplateis rejected client-side. - Button nodes need the correct
actionshape. A malformed or missingactionon a button node leaves a button that renders but navigates nowhere. - An off-enum
surface.background.typerenders a transparent band with no error at all — see surfaces and backgrounds. section_countis your “did it apply” signal.mio pages publishreturns apage_publishesresource withsection_countandgate_count. Ifsection_countis lower than the number of sections you authored, the renderer rejected some — inspect the tree, don’t infer success from the200.- Homepage content-grids need STATIC cards, not a data-source binding. The homepage route prefetches only
type:"playlist"sources and runshub_playlistswithenabled:false, so a content-grid bound todataSource:{type:"hub_playlists"}renders empty on the homepage. (hub_playlistsfeeds the/contentbrowse page, not the homepage.)
What publishing does not turn on
Registration and Moderation defaults are injected only when the hub is created, never on update — so which flow you use decides what you end up with:Raw API reference
Direct integrators can manage hubs without the CLI. The create, update and delete requests below are the ones the admin sends for the same actions.$TOKEN is a platform access token (see Authentication) or a team API key. Creating, changing and deleting hubs is limited to the team owner; anyone else gets 403.
The generated endpoint pages have the full request and response schemas: create, list, get, update and delete.
Create a hub with the API
The body is a JSON:API envelope,{"data": {"type": "hubs", "attributes": {…}}}. The envelope, the resource object and the attributes all reject keys the schema does not list (additionalProperties: false), so a misspelled attribute, a name instead of title, or an id in the body returns 422 rather than being ignored. The CLI --name flag maps to the title attribute. The collection path ends with a slash.
The other optional attributes (
branding, navigation, settings, meta, discussions_default_title and discussions_default_description) are covered by the recipe above and listed on the generated endpoint page.
A successful create returns 201 with the new hub. Read data.id for later requests, and read the slug back from data.attributes.slug instead of assuming the one you sent: if that slug is taken, the hub is created with a random six-character suffix (for example member-academy-a1b2c3). A 503 with the code email_pool_unavailable means hub creation is temporarily unavailable; try again later. See Error handling for the error format.
To create the whole starter hub through the API, use POST /api/v1/teams/{team_id}/hubs/from-template. This route is also controlled by a per-environment flag and returns 405 when it is off. It requires an Idempotency-Key header and the current catalog digest; omitting description is supported. Repeating the same key and request returns 201 with the same hub id and summary, replayed: true, and an empty created_resource_ids map. Reusing the key with a different request returns 409.
meta.digest from GET /api/v1/page-builder/catalog for CATALOG_DIGEST. Set publish to true to create a public hub. See the generated scaffold endpoint reference for the full contract.
Update a hub with the API
Send only the attributes you want to change. Name the hub in the path by its id or its current slug. The body has noid, and adding one returns 422.
409 and the code hub_slug_conflict, which the admin shows as That slug is already taken. GET and PATCH responses carry an ETag header. Send it back as If-Match on your next update, and if someone else changed the hub in between, the API answers 409 with the code stale_hub instead of overwriting their change. Setting is_private to false does not turn on self-registration; see what publishing does not turn on.
Delete a hub with the API
204 with no body. It is a soft delete that cascades to the hub’s pages, their sections and its content items, and it ends every membership in the hub. There is no restore endpoint. A hub with a custom domain returns 409 with the code hub_has_custom_domain until you remove the domain.
Look a hub up by slug
Read a hub by its slug on the public route:Attach legal policies
Set the hub’s terms and privacy policy text, or usemio hubs policies update: