diff --git a/funkwhale_cli/cli/__init__.py b/funkwhale_cli/cli/__init__.py
index 2ae0ba71eac37e0f60f0a24546a516c532ce0be8..ce007b70aa04b714fe388b7fd1c6c7e02f4fd130 100644
--- a/funkwhale_cli/cli/__init__.py
+++ b/funkwhale_cli/cli/__init__.py
@@ -1,6 +1,7 @@
 from . import auth
 from . import albums
 from . import artists
+from . import channels
 from . import favorites
 from . import libraries
 from . import playlists
@@ -14,6 +15,7 @@ __all__ = [
     "auth",
     "albums",
     "artists",
+    "channels",
     "favorites",
     "libraries",
     "playlists",
diff --git a/funkwhale_cli/cli/base.py b/funkwhale_cli/cli/base.py
index e4f86767a51babdc99bdd7000c7a7eefffadd5a0..d2ad3653bad150f10564807850296bd4275f40db 100644
--- a/funkwhale_cli/cli/base.py
+++ b/funkwhale_cli/cli/base.py
@@ -135,6 +135,14 @@ def async_command(f):
     return functools.update_wrapper(wrapper, f)
 
 
+async def check_status(response):
+    text = await response.text()
+    try:
+        response.raise_for_status()
+    except aiohttp.client_exceptions.ClientError as e:
+        raise click.ClickException(str(e) + ": {}".format(text))
+
+
 SERVER_DECORATOR = click.option(
     "-H",
     "--url",
@@ -240,6 +248,7 @@ def get_ls_command(
     pagination=True,
     filter=True,
     ordering=True,
+    scope=False,
     with_id=False,
     owned_conf=None,
     name="ls",
@@ -271,6 +280,8 @@ def get_ls_command(
     ordering_decorator = (
         click.option("--ordering", "-o", default=None) if ordering else noop_decorator
     )
+
+    scope_decorator = click.option("--scope", default=None) if scope else noop_decorator
     filter_decorator = (
         click.option("--filter", "-f", multiple=True) if filter else noop_decorator
     )
@@ -291,6 +302,7 @@ def get_ls_command(
     @page_decorator
     @page_size_decorator
     @ordering_decorator
+    @scope_decorator
     @filter_decorator
     @limit_decorator
     @owned_decorator
@@ -310,6 +322,7 @@ def get_ls_command(
         page = kwargs.get("page")
         page_size = kwargs.get("page_size")
         ordering = kwargs.get("ordering")
+        scope = kwargs.get("scope")
         filter = kwargs.get("filter")
         query = kwargs.get("query")
         owned = kwargs.get("owned")
@@ -335,6 +348,8 @@ def get_ls_command(
                         params["page_size"] = page_size
                     if ordering:
                         params["ordering"] = ordering
+                    if scope:
+                        params["scope"] = scope
                     if query:
                         params["q"] = " ".join(query)
                     if filter:
diff --git a/funkwhale_cli/cli/channels.py b/funkwhale_cli/cli/channels.py
new file mode 100644
index 0000000000000000000000000000000000000000..eefb6147187e5dfdfef140d3d78d493f5d50c043
--- /dev/null
+++ b/funkwhale_cli/cli/channels.py
@@ -0,0 +1,118 @@
+import click
+import json
+
+from . import base
+from .. import output
+
+
+@base.cli.group()
+@click.pass_context
+def channels(ctx):
+    """
+    Manage channels
+    """
+
+
+channels_ls = base.get_ls_command(
+    channels,
+    "api/v1/channels/",
+    scope=True,
+    output_conf={
+        "labels": ["UUID", "Name", "Category", "Username", "Tags"],
+        "type": "CHANNEL",
+    },
+)
+channels_rm = base.get_delete_command(channels, "api/v1/channels/{}/")
+
+
+@channels.command("create")
+@click.option("--name", prompt=True)
+@click.option("--username", prompt=True)
+@click.option("--cover")
+@click.option("--description")
+@click.option("--tags", default="")
+@click.option("--itunes-category", default=None)
+@click.option("--language", default=None)
+@click.option(
+    "--content-category",
+    type=click.Choice(["music", "podcast", "other"]),
+    default="podcast",
+    prompt=True,
+)
+@click.option("--raw", is_flag=True)
+@click.pass_context
+@base.async_command
+async def channels_create(
+    ctx,
+    raw,
+    name,
+    username,
+    cover,
+    description,
+    tags,
+    language,
+    itunes_category,
+    content_category,
+):
+    data = {
+        "name": name,
+        "username": username,
+        "content_category": content_category,
+    }
+    if description:
+        data["description"] = {
+            "text": description,
+            "content_type": "text/markdown",
+        }
+    else:
+        data["description"] = None
+
+    if cover:
+        data["cover"] = cover
+
+    missing_fields = []
+    if not itunes_category:
+        missing_fields.append(("--itunes-category", "itunes_category"))
+    if not language:
+        missing_fields.append(("--language", "language"))
+
+    if content_category == "podcast" and missing_fields:
+        click.secho(
+            "You must provide adequate values for these fields: {}. Allowed values are listed below".format(
+                ", ".join([f for f, _ in missing_fields])
+            ),
+            fg="red",
+        )
+
+        async with ctx.obj["remote"]:
+            result = await ctx.obj["remote"].request(
+                "get", "api/v1/channels/metadata-choices"
+            )
+            choices = await result.json()
+
+        for flag, field in missing_fields:
+            click.echo(
+                "{}: {}".format(flag, ", ".join([c["value"] for c in choices[field]]))
+            )
+
+        raise click.ClickException("Missing params")
+    data["tags"] = [t.strip() for t in tags.replace(" ", ",").split(",") if t]
+    if content_category == "podcast":
+        data["metadata"] = {
+            "itunes_category": itunes_category,
+            "language": language,
+        }
+
+    async with ctx.obj["remote"]:
+        result = await ctx.obj["remote"].request("post", "api/v1/channels/", json=data)
+        await base.check_status(result)
+        payload = await result.json()
+    if raw:
+        click.echo(json.dumps(payload, sort_keys=True, indent=4))
+    else:
+        click.echo("Channel created:")
+        click.echo(
+            output.table(
+                [payload], ["UUID", "Name", "Category", "Username"], type="CHANNEL"
+            )
+        )
diff --git a/funkwhale_cli/output.py b/funkwhale_cli/output.py
index 0616153f77ca5f18c3185bd84abdbf7d5e0d982d..958e779e53ca60d3b910015f6995656ed80a8eef 100644
--- a/funkwhale_cli/output.py
+++ b/funkwhale_cli/output.py
@@ -1,6 +1,10 @@
 import tabulate
 
 
+def comma_separated(it):
+    return ", ".join(it)
+
+
 FIELDS = {
     "ARTIST": {
         "ID": {"field": "id", "truncate": 0},
@@ -19,6 +23,16 @@ FIELDS = {
         "Tracks": {"field": "tracks", "handler": lambda v: len(v), "truncate": 0},
         "Artist": {"field": "artist.name"},
     },
+    "CHANNEL": {
+        "Category": {"field": "artist.content_category", "truncate": 0},
+        "Username": {"field": "actor.full_username", "truncate": 0},
+        "Name": {"field": "artist.name"},
+        "Descsription": {"field": "artist.description.text"},
+        "RSS URL": {"field": "rss_url"},
+        "Artist": {"field": "artist.id"},
+        "Metadata": {"field": "metadata"},
+        "Tags": {"field": "artist.tags", "handler": ", ".join,},
+    },
     "TRACK": {
         "ID": {"field": "id", "truncate": 0},
         "Title": {"field": "title"},
@@ -75,6 +89,7 @@ FIELDS = {
         "Modified": {"field": "modification_date"},
         "UUID": {"field": "uuid", "truncate": 0},
         "ID": {"field": "id", "truncate": 0},
+        "Tags": {"field": "tags", "handler": ", ".join,},
     },
 }
 
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 5d66cc7c2cbde3c339f9271c62f0723eb4a3ec7f..4b20b557e09affb1a407f186eb1b05c634be0125 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -467,3 +467,105 @@ def test_playlists_tracks(cli_ctx, session, responses, get_requests):
 
     requests = get_requests("get", url)
     assert len(requests) == 1
+
+
+def test_channel_create_music(cli_ctx, session, responses, get_requests):
+    command = cli.channels.channels_create
+    url = "https://test.funkwhale/api/v1/channels/"
+    responses.post(url)
+
+    command.callback(
+        name="test",
+        cover="uuid",
+        username="hello",
+        content_category="music",
+        description="description text",
+        tags='punk,rock, ska',
+        language=None,
+        itunes_category=None,
+        raw=False,
+        _async_reraise=True
+    )
+
+    requests = get_requests("post", url)
+    assert len(requests) == 1
+    assert requests[0].kwargs["json"] == {
+        "name": "test",
+        "cover": "uuid",
+        "username": "hello",
+        "content_category": "music",
+        "description": {"text": "description text", "content_type": "text/markdown"},
+        "tags": ["punk", "rock", "ska"],
+    }
+
+
+def test_channel_create_podcast(cli_ctx, session, responses, get_requests):
+    command = cli.channels.channels_create
+    url = "https://test.funkwhale/api/v1/channels/"
+    responses.post(url)
+
+    command.callback(
+        name="test",
+        cover="uuid",
+        username="hello",
+        content_category="podcast",
+        description="description text",
+        tags='punk,rock, ska',
+        language="en",
+        itunes_category="Leisure",
+        raw=False,
+        _async_reraise=True
+    )
+
+    requests = get_requests("post", url)
+    assert len(requests) == 1
+    assert requests[0].kwargs["json"] == {
+        "name": "test",
+        "cover": "uuid",
+        "username": "hello",
+        "content_category": "podcast",
+        "description": {"text": "description text", "content_type": "text/markdown"},
+        "tags": ["punk", "rock", "ska"],
+        "metadata": {
+            "itunes_category": "Leisure",
+            "language": "en",
+        }
+    }
+
+
+def test_channels_ls(cli_ctx, session, responses, get_requests):
+    command = cli.channels.channels_ls
+    url = "https://test.funkwhale/api/v1/channels/?ordering=-creation_date&page=1&page_size=5&q=hello&scope=me"
+    responses.get(
+        url, payload={"results": [], "next": None, "previous": None, "count": 0}
+    )
+
+    command.callback(
+        raw=False,
+        page=1,
+        page_size=5,
+        ordering="-creation_date",
+        filter="subscribed=true",
+        scope="me",
+        query=["hello"],
+        column=None,
+        format=None,
+        no_headers=False,
+        ids=False,
+        limit=1,
+    )
+
+    requests = get_requests("get", url)
+    assert len(requests) == 1
+
+
+def test_channels_rm(cli_ctx, session, responses, get_requests):
+    command = cli.channels.channels_rm
+    url = "https://test.funkwhale/api/v1/channels/"
+    responses.delete(url + "uuid1/")
+    responses.delete(url + "uuid2/")
+
+    command.callback(id=["uuid1", "uuid2"], raw=False, no_input=True, _async_reraise=True)
+
+    assert len(get_requests("delete", url + "uuid1/")) == 1
+    assert len(get_requests("delete", url + "uuid2/")) == 1