Server Management
Manage channels, roles, members, pinned messages and scheduled events in the bot's Discord server.
The bot administers the server it is installed in: channels, roles, members, pinned messages and scheduled events.
Every call takes accountId as a query parameter and the server's guildId or channelId in the path.
Channels
List the channels in the connected server with GET /v1/accounts/{accountId}/discord-channels (List Discord channels):
const { data: channels } = await zernio.discord.getDiscordChannels({
path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' }
});
console.log(channels.channels);Response (200):
{
"channels": [
{ "id": "1234567890123456789", "name": "announcements", "type": 5 },
{ "id": "1122334455667788990", "name": "community", "type": 15 }
]
}To move the account to another channel in the same server, send channelId to the same PATCH /v1/accounts/{accountId}/discord-settings call as Webhook identity.
The response carries the account with its new channelId and channelName.
Only text (0), announcement (5) and forum (15) channels are accepted. Zernio creates a new webhook in the target channel.
Roles and members
Nine endpoints cover roles and members. The paths mirror Discord's own API.
- Roles: list, create, edit and delete.
- Members: list, search and get.
- Member roles: assign and remove a role per member.
List every role in a server:
curl "https://zernio.com/api/v1/discord/guilds/1098765432109876543/roles?accountId=66b2e19d8c3f5a7e9d0b1c2d" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Response (200):
{
"data": [
{
"id": "3344556677889900112",
"name": "Members",
"color": 3447003,
"position": 3,
"permissions": "1071698660929",
"managed": false,
"mentionable": true,
"hoist": false
}
]
}coloris a decimal value (0means no color).permissionsis a bitfield as a string.positionis the place in the hierarchy, where higher means more authority.managedistruefor roles an integration owns, which no one can assign.
List members, cursor-paginated, 100 per page by default and at most 1,000:
curl "https://zernio.com/api/v1/discord/guilds/1098765432109876543/members?accountId=66b2e19d8c3f5a7e9d0b1c2d&limit=100" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Response (200):
{
"data": [
{
"user": { "id": "2233445566778899001", "username": "sam", "global_name": "Sam" },
"nick": null,
"roles": ["3344556677889900112"],
"joined_at": "2026-01-15T10:30:00.000Z"
}
],
"pagination": { "nextCursor": "2233445566778899001", "hasMore": true }
}Pass nextCursor as after on the next call until hasMore is false.
This endpoint needs the Server Members Intent on Zernio's Discord app, which is on for new bot installs. An empty array with no error means the intent is missing.
Assign a role to a member:
curl -X PUT "https://zernio.com/api/v1/discord/guilds/1098765432109876543/members/2233445566778899001/roles/3344556677889900112?accountId=66b2e19d8c3f5a7e9d0b1c2d" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Response (200):
{
"success": true,
"operation": "role_assigned",
"guildId": "1098765432109876543",
"userId": "2233445566778899001",
"roleId": "3344556677889900112"
}DELETE on the same path removes the role and answers with operation: "role_removed".
Both are idempotent, so a role that is already assigned or already absent still returns 200.
Role hierarchy. The bot's highest role must sit above the role it assigns. A bot demoted in the server's role hierarchy loses the ability to assign higher roles even with Manage Roles.
The @everyone role, whose roleId equals the guildId, cannot be assigned or removed.
Create a role with POST /v1/discord/guilds/{guildId}/roles (Create role). name is the only required field.
coloris decimal (16711680is red).permissionsis a bitfield as a string.hoistandmentionableare booleans.
curl -X POST "https://zernio.com/api/v1/discord/guilds/1098765432109876543/roles?accountId=66b2e19d8c3f5a7e9d0b1c2d" \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Beta testers", "color": 3447003, "mentionable": true}'Response (201):
{
"data": {
"id": "3344556677889900112",
"name": "Beta testers",
"color": 3447003,
"position": 3,
"permissions": "1071698660929",
"managed": false,
"mentionable": true,
"hoist": false
}
}PATCH /v1/discord/guilds/{guildId}/roles/{roleId} (Edit role) changes any of those fields, at least one per call, and leaves the rest alone.
DELETE on the same path (Delete role) removes the role from the server and from every member. It answers { "success": true } and cannot be undone.
All three need Manage Roles on the bot and a target role below the bot's highest role.
A server that added the bot before role management shipped must re-invite it, because Discord fixes the permission set at invite time.
Read one member with GET /v1/discord/guilds/{guildId}/members/{userId} (Get member).
Prefix-match usernames and nicknames with GET /v1/discord/guilds/{guildId}/members/search (Search members).
Pinned messages
Pin messages for community operations: the announcement of the week, rules updates, or freshly published posts.
List the current pins, most recent first:
curl "https://zernio.com/api/v1/discord/channels/1234567890123456789/pins?accountId=66b2e19d8c3f5a7e9d0b1c2d" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Response (200), each entry a raw Discord message:
{
"data": [
{
"id": "4455667788990011223",
"channel_id": "1234567890123456789",
"content": "Read the rules before posting.",
"timestamp": "2027-01-01T12:00:00.000Z",
"attachments": [],
"embeds": []
}
]
}Pin a message with PUT on the same path plus its id:
curl -X PUT "https://zernio.com/api/v1/discord/channels/1234567890123456789/pins/4455667788990011223?accountId=66b2e19d8c3f5a7e9d0b1c2d" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Response (200):
{
"success": true,
"operation": "message_pinned",
"channelId": "1234567890123456789",
"messageId": "4455667788990011223"
}DELETE on that path unpins and answers with operation: "message_unpinned". Both are idempotent.
A channel holds at most 50 pins. Pinning a 51st returns 400, so unpin one first.
Scheduled events
Scheduled events are separate from messages. They appear in the server's Events panel, and Discord notifies interested members before the start time.
They fit AMAs, tournaments, office hours and livestreams. entity.type picks one of 3 kinds.
An external event (Zoom, in person, a livestream elsewhere) requires both location and endsAt:
curl -X POST https://zernio.com/api/v1/discord/guilds/1098765432109876543/events \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "66b2e19d8c3f5a7e9d0b1c2d",
"name": "Weekly AMA",
"description": "Bring your questions about the roadmap.",
"startsAt": "2027-01-01T18:00:00Z",
"entity": {
"type": "external",
"location": "https://zoom.us/j/123456789",
"endsAt": "2027-01-01T19:00:00Z"
}
}'Response (200):
{
"data": {
"id": "5566778899001122334",
"guild_id": "1098765432109876543",
"name": "Weekly AMA",
"scheduled_start_time": "2027-01-01T18:00:00.000Z",
"scheduled_end_time": "2027-01-01T19:00:00.000Z",
"status": 1,
"entity_type": 3,
"entity_metadata": { "location": "https://zoom.us/j/123456789" }
}
}A voice channel event takes channelId instead, and endsAt is optional.
A stage channel event has the same shape with "type": "stage" and a stage channel id:
{
"accountId": "66b2e19d8c3f5a7e9d0b1c2d",
"name": "Game night",
"startsAt": "2027-01-08T20:00:00Z",
"entity": { "type": "voice", "channelId": "6677889900112233445" }
}Four more calls read and change an event. Each takes accountId, as a query parameter on the reads and the delete and in the body on the PATCH.
The reads and the PATCH return the same data object as the create call:
| Call | What it does |
|---|---|
GET /v1/discord/guilds/{guildId}/events | Every event in the server. Add withUserCount=true for the RSVP count in user_count. |
GET /v1/discord/guilds/{guildId}/events/{eventId} | One event. |
PATCH /v1/discord/guilds/{guildId}/events/{eventId} | Update any subset of the fields, for example { "name": "Updated title" }. |
DELETE /v1/discord/guilds/{guildId}/events/{eventId} | Delete it. Answers { "success": true, "deleted": "5566778899001122334" } and keeps no record. |
Cancelling. Discord has no cancel endpoint, so cancelling is the PATCH with status: "cancelled". It keeps the event in the server's history as status: 4.
Delete only when no record should remain.
| Event property | Value |
|---|---|
| Name | Max 100 characters |
| Description | Max 1,000 characters |
| Privacy level | Always GUILD_ONLY (Discord deprecated public events) |
| External events | Require location (max 100 characters) and endsAt |
| Voice and stage events | Require channelId; endsAt optional |
| Cover image | Optional base64 data URI (PNG, JPEG, GIF) |
| Start time | Must be in the future |