> ## 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.

# Content and access

> Create content, track progress, and check whether a member can see a resource.

Content and access work together. Content defines what exists. Access rules decide who can see it.

## Admin content tree

Admin routes are scoped to a team and hub:

```text theme={null}
GET  /api/v1/teams/{team_id}/hubs/{hub_id}/content
POST /api/v1/teams/{team_id}/hubs/{hub_id}/content
GET  /api/v1/teams/{team_id}/hubs/{hub_id}/content/{node_id}
PATCH /api/v1/teams/{team_id}/hubs/{hub_id}/content/{node_id}
DELETE /api/v1/teams/{team_id}/hubs/{hub_id}/content/{node_id}
```

Use content nodes for libraries, folders, modules, lessons, articles, videos, and other structured member content.

## Member content

Members read content through hub routes:

```text theme={null}
GET  /api/v1/hub/{hub_id}/content
GET  /api/v1/hub/{hub_id}/content/{node_id}
GET  /api/v1/hub/{hub_id}/content/{node_id}/children
POST /api/v1/hub/{hub_id}/content/{node_id}/progress
```

Progress is member-specific and can feed segments, email, automations, and access decisions.

### Publishing and visibility

There is **no separate "publish" verb** for a content node — a node becomes member-visible when **both** are true:

1. **`published_at` is set at or before now.** A node with `published_at` unset (draft) or in the future (scheduled) is hidden from `GET /api/v1/hub/{hub_id}/content`. Set it with `mio content create … --published-at <RFC3339>` or `mio content update <id> --published-at <RFC3339>`.
2. **`privacy` permits the viewer** — `public`, `members`, or `paid` (`mio content create … --privacy members`).

So `mio content create --node-type lesson --privacy public --published-at "$(date -u +%Y-%m-%dT%H:%M:%SZ)"` yields an immediately member-visible node.

### Content nodes vs. media playlists

A hub has **two independent member-content surfaces**, and it's easy to conflate them:

* **Content nodes** (this guide) — `GET /api/v1/hub/{hub_id}/content`, managed with `mio content …`.
* **Media playlists** — the `hub_media` surface at `GET /api/v1/hub/{hub_id}/playlists`, published with `mio media hub-playlists publish` (see the [media workflow](/guides/media-workflow) guide), **not** `mio content`.

A hub page (homepage, `/content`, etc.) built in the page builder renders whichever **data source** its content grid is bound to. A grid bound to `dataSource: { "type": "hub_playlists" }` shows only **media playlists** — that's why `mio content create` nodes don't appear on a playlist-bound page even when published. To surface content nodes on a page, bind that page's grid to the content-node source; to fill a playlist-bound page, publish media playlists.

## Access rules

Access rules are hub-scoped:

```text theme={null}
GET  /api/v1/teams/{team_id}/hubs/{hub_id}/access-rules
POST /api/v1/teams/{team_id}/hubs/{hub_id}/access-rules
GET  /api/v1/hub/{hub_id}/access-check
```

Use access checks when a frontend needs a direct "can this member see this target?" answer.
