Workspace API

Community structure

Community structure endpoints of the Zanfia Workspace API, with schemas and code samples.

4 min readLast updated Sep 10, 2026

Channel and group management. Community content — posts, comments, chat — lives on the Community API. All routes require a Authorization: Bearer header — see Authentication.

Create a channel

POST/communities/{communityId}/channels

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

communityId*string

Community id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

curl -X POST "https://example.com/communities/string/channels" \  -H "Content-Type: application/json" \  -d '{    "name": "string",    "type": "feed"  }'
{  "channel": {    "id": "string",    "name": "string",    "type": "feed",    "groupId": "string",    "description": "string",    "readonly": true,    "access": "inherit"  }}
{  "error": "string",  "message": "string"}

Create a channel group

POST/communities/{communityId}/groups

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

communityId*string

Community id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

curl -X POST "https://example.com/communities/string/groups" \  -H "Content-Type: application/json" \  -d '{    "name": "string"  }'
{  "group": {    "id": "string",    "name": "string",    "description": "string"  }}
{  "error": "string",  "message": "string"}

Update a channel

PATCH/communities/{communityId}/channels/{channelId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

communityId*string

Community id

channelId*string

Channel id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

curl -X PATCH "https://example.com/communities/string/channels/string" \  -H "Content-Type: application/json" \  -d '{}'
{  "channel": {    "id": "string",    "name": "string",    "type": "feed",    "groupId": "string",    "description": "string",    "readonly": true,    "access": "inherit"  }}
{  "error": "string",  "message": "string"}

Delete a channel

DELETE/communities/{communityId}/channels/{channelId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

communityId*string

Community id

channelId*string

Channel id

Response Body

application/json

application/json

curl -X DELETE "https://example.com/communities/string/channels/string"
{  "channelId": "string"}
{  "error": "string",  "message": "string"}

Update a group

PATCH/communities/{communityId}/groups/{groupId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

communityId*string

Community id

groupId*string

Channel group id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

curl -X PATCH "https://example.com/communities/string/groups/string" \  -H "Content-Type: application/json" \  -d '{}'
{  "group": {    "id": "string",    "name": "string",    "description": "string"  }}
{  "error": "string",  "message": "string"}

Delete a group

DELETE/communities/{communityId}/groups/{groupId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

communityId*string

Community id

groupId*string

Channel group id

Response Body

application/json

application/json

curl -X DELETE "https://example.com/communities/string/groups/string"
{  "groupId": "string"}
{  "error": "string",  "message": "string"}

Reorder or re-parent a channel/group

POST/communities/{communityId}/move-node

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

communityId*string

Community id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Flat mirror of the MoveContentNode intent (one node + one target per call).

Response Body

application/json

application/json

curl -X POST "https://example.com/communities/string/move-node" \  -H "Content-Type: application/json" \  -d '{    "nodeType": "channel",    "nodeId": "string",    "positionMode": "into-group"  }'
{  "communityId": "string"}
{  "error": "string",  "message": "string"}

Community API (management routes)

Members, roles, settings, calendar events and the moderation queue live on the Community API — base URL https://api.zanfia.com/community, routes below are relative to it. They are not part of the Workspace API spec above. Workspace keys need the communities:read / communities:write scopes and pass as staff; member keys are admitted by the guard, but every group requires the community permission listed next to it, so a plain member key gets 403 insufficient-permissions. Ids in the path: communityId from GET /v1/communities; memberId is the member doc id from the member list.

Members (manageMembers; mute needs muteMembers)

MethodRouteBody / query
GET/v1/communities/{communityId}/members?roleId / ?disabled=true|false / ?emailPrefix (mutually exclusive — 400 filter-conflict), ?cursor, ?limit ≤ 100
POST/v1/communities/{communityId}/members/{memberId}/roles{ "roleIds": [] } — the member's full custom-role set (the built-in Owner role is immutable)
POST/v1/communities/{communityId}/members/{memberId}/status{ "disabled": true } disables (soft-bans), false re-enables; hierarchy-checked (403 hierarchy-violation)
POST/v1/communities/{communityId}/members/{memberId}/mute{ "durationMinutes": 60 } times the member out; null unmutes (max 28 days)

Roles (manageRoles)

MethodRouteBody
GET/v1/communities/{communityId}/roles
POST/v1/communities/{communityId}/roles{ "name", "color", "icon"?, "position"?, "systemPermissions"?, "contentAccess"? }
POST/v1/communities/{communityId}/roles/reorder{ "orderedRoleIds": [] } — the full list
PATCH/v1/communities/{communityId}/roles/{roleId}any subset of the create fields
DELETE/v1/communities/{communityId}/roles/{roleId}built-in roles can't be deleted; members holding the role fall back to Member

Settings (manageCommunity)

MethodRouteBody
GET/v1/communities/{communityId}/settings— (settings is null when never written)
PATCH/v1/communities/{communityId}/settings{ "patch": { … } } — typed whitelist, 400 empty-patch when nothing survives

Accepted patch keys: uploadVideo, membersDisplay, viewStyle, displayName, icon, hiddenWhenUnavailable, voiceMessages, enableMemberApiAccess, publicAccess, publicAccessTermsUrl, contentVisibleFromJoinDate, eventsEnabled, directMessagesEnabled, gamification, moderation, assistant, digest. Nested blocks replace their whole sub-object:

{
  "patch": {
    "eventsEnabled": true,
    "directMessagesEnabled": true,
    "contentVisibleFromJoinDate": false,
    "gamification": { "enabled": true },
    "moderation": { "autoHideAfterReports": 3 }
  }
}

Calendar events (manageCommunity)

MethodRouteBody / query
GET/v1/communities/{communityId}/events?from / ?to (ISO; inclusive range on startAt, default now ± 365 days), ?excludeCancelled=true, ?limit ≤ 500 (default 100). Soonest first.
POST/v1/communities/{communityId}/events{ "title", "startAt", "endAt", "timezone", "location": { "type": "online" | "inPerson", "url"?, "address"? }, "description"?, "type"?, "allDay"?, "coverImage"? }201 { event }
PATCH/v1/communities/{communityId}/events/{eventId}any subset of the create fields plus "status": "scheduled" | "cancelled"
DELETE/v1/communities/{communityId}/events/{eventId}hard delete → 200 {}

startAt / endAt are ISO 8601 instants; timezone is the IANA zone the event was authored in (Europe/Warsaw). type is one of liveCall, workshop, meetup, conference, retreat, other (default). End must be after start and an event may span at most 31 days — 400 invalid-time-range. Unknown event → 404 event-not-found. Events only show to members while eventsEnabled is on in the settings.

Moderation queue (moderatePosts or moderateComments)

MethodRouteBody
GET/v1/communities/{communityId}/moderation/reports— → { "pending": [], "resolved": [] } (≤ 100 pending, the 25 most recent resolutions)
POST/v1/communities/{communityId}/moderation/reports/{reportId}/resolve{ "action": "remove" | "dismiss", "note"? }200 {}

One report per reported target (id is {targetType}_{targetId}); reporters lists who flagged it and why, autoHidden tells you the report threshold already hid the content. remove soft-deletes the post/comment and notifies its author (the note is included); dismiss keeps it and restores an auto-hidden target. Resolving a post report needs moderatePosts, a comment report moderateComments. 404 report-not-found, 409 report-already-resolved on a second verdict.

Was this article helpful?

Related articles

Spotted something off? Tell us at support@zanfia.com.