Community structure
Community structure endpoints of the Zanfia Workspace API, with schemas and code samples.
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
/communities/{communityId}/channelsAuthorization
bearerAuth API key from Dashboard → Integrations → API, MCP and CLI.
In: header
Path Parameters
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
/communities/{communityId}/groupsAuthorization
bearerAuth API key from Dashboard → Integrations → API, MCP and CLI.
In: header
Path Parameters
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
/communities/{communityId}/channels/{channelId}Authorization
bearerAuth API key from Dashboard → Integrations → API, MCP and CLI.
In: header
Path Parameters
Community id
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
/communities/{communityId}/channels/{channelId}Authorization
bearerAuth API key from Dashboard → Integrations → API, MCP and CLI.
In: header
Path Parameters
Community id
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
/communities/{communityId}/groups/{groupId}Authorization
bearerAuth API key from Dashboard → Integrations → API, MCP and CLI.
In: header
Path Parameters
Community id
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
/communities/{communityId}/groups/{groupId}Authorization
bearerAuth API key from Dashboard → Integrations → API, MCP and CLI.
In: header
Path Parameters
Community id
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
/communities/{communityId}/move-nodeAuthorization
bearerAuth API key from Dashboard → Integrations → API, MCP and CLI.
In: header
Path Parameters
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)
| Method | Route | Body / 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)
| Method | Route | Body |
|---|---|---|
| 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)
| Method | Route | Body |
|---|---|---|
| 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)
| Method | Route | Body / 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)
| Method | Route | Body |
|---|---|---|
| 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?

