Features Developers About Support
Register
Developer Documentation

Build on Echoed

Full REST API with 40+ endpoints, real-time WebSocket events, and OAuth2 integration. Build bots that manage servers, automate tasks, send messages, and more.

Introduction

The Echoed Bot API lets you build bots that interact with servers, channels, members, messages, tasks, and calendar events. Bots authenticate with a simple token and can access any server they've been invited to.

Quick Start

  1. Create a bot account in your Settings → Bot Profile
  2. Copy your Bot API Key (starts with zbot_)
  3. Invite your bot to a server via the Bot Discovery page or directly
  4. Start making API requests with the X-Bot-Token header

Authentication

All Bot API requests require the X-Bot-Token header with your bot's API key.

HTTP Headers
X-Bot-Token: zbot_your_api_key_here
Content-Type: application/json
Keep your bot token secret. Never commit it to version control or share it publicly. If compromised, regenerate it immediately from your bot settings.

Base URL

All Bot API endpoints are prefixed with:

Base URL
https://go.echoed.gg/v1/bots

For example, to validate your token: GET https://go.echoed.gg/v1/bots/validate

Error Handling

The API returns standard HTTP status codes. Error responses include a JSON body with a message field.

Status Meaning
200Success
201Created (new resource)
400Bad Request — invalid parameters
401Unauthorized — invalid or missing token
403Forbidden — insufficient permissions
404Not Found — resource doesn't exist
500Internal Server Error
Error Response
{
  "message": "Bot is not a member of this server",
  "success": false
}

Bot Profile

GET /v1/bots/validate
Validate your bot token and confirm it's active.
Response
{
  "valid": true,
  "bot_id": "67e537d200011f5275d2",
  "message": "Bot API key is valid."
}
GET /v1/bots/profile
Get your bot's profile information.
Response
{
  "id": "67e537d200011f5275d2",
  "username": "my-awesome-bot",
  "isBot": true,
  "botDescription": "A helpful server bot",
  "botCategory": "utility",
  "isPublic": true,
  "avatarUrl": "https://s3.echoed.gg/..."
}
PUT /v1/bots/profile
Update your bot's profile description, category, and visibility.
Request Body
{
  "botDescription": "Updated bot description",
  "botCategory": "utility",
  "isPublic": true
}

Servers

GET /v1/bots/servers
List all servers your bot has been invited to.
Response
{
  "count": 2,
  "servers": [
    {
      "serverId": "6894d3d90011f842607c",
      "serverName": "My Server",
      "serverIcon": "",
      "invitedAt": "2026-01-06T13:41:44Z",
      "invitedBy": "user-id"
    }
  ]
}
GET /v1/bots/{server_id}/info
Get detailed information about a specific server.
Response
{
  "id": "6894d3d90011f842607c",
  "name": "My Server",
  "description": "A cool server",
  "ownerId": "user-id",
  "memberCount": 42,
  "channelCount": 8,
  "iconUrl": "",
  "createdAt": "2025-08-07T16:27:05Z"
}

Channels

GET /v1/bots/{server_id}/channels
List all channels in a server.
Response
{
  "channels": [
    {
      "id": "647a23fede03765e0348",
      "name": "general",
      "type": "text",
      "description": "",
      "createdAt": "2026-01-05T11:20:43Z"
    }
  ],
  "total": 8
}
POST /v1/bots/{server_id}/channels
Create a new channel. Types: text, video, announcement.
Request Body
{
  "name": "bot-updates",
  "type": "text",
  "topic": "Automated bot announcements"
}
PUT /v1/bots/{server_id}/channels/{channel_id}
Edit a channel's name, description, or topic.
Request Body
{
  "name": "renamed-channel",
  "description": "Updated description"
}
DELETE /v1/bots/{server_id}/channels/{channel_id}
Delete a channel permanently.
PUT /v1/bots/{server_id}/channels/reorder
Move a channel to a different position or category.
Request Body
{
  "channelId": "channel-id",
  "categoryId": "category-id",
  "position": 0
}

Categories

POST /v1/bots/{server_id}/categories
Create a new channel category.
Request Body
{
  "name": "Bot Channels",
  "position": 0
}
PUT /v1/bots/{server_id}/categories/{category_id}
Edit a category's name or position.
DELETE /v1/bots/{server_id}/categories/{category_id}
Delete a category. Channels within it become uncategorized.
PUT /v1/bots/{server_id}/categories/reorder
Reorder multiple categories at once.
Request Body
{
  "categories": [
    { "categoryId": "id1", "position": 0 },
    { "categoryId": "id2", "position": 1 }
  ]
}

Members

GET /v1/bots/{server_id}/members
List server members with pagination.
ParameterTypeDescription
limitintegerMax results (default 10)
offsetintegerPagination offset
GET /v1/bots/{server_id}/members/search?q={query}
Search members by username. Query must be at least 2 characters.
GET /v1/bots/{server_id}/members/{user_id}/permissions
Get a member's resolved permissions as a list of permission names.
Response
{
  "userId": "user-id",
  "serverId": "server-id",
  "permissions": [
    "VIEW_CHANNELS", "SEND_MESSAGES",
    "READ_MESSAGE_HISTORY", "CONNECT", "SPEAK"
  ]
}
POST /v1/bots/{server_id}/members/{user_id}/kick
Kick a member from the server. Requires KICK_MEMBERS permission.
Request Body
{
  "reason": "Optional reason"
}
POST /v1/bots/{server_id}/members/{user_id}/ban
Ban a member from the server. Requires BAN_MEMBERS permission.
Request Body
{
  "reason": "Reason for ban",
  "deleteMessageDays": 1
}
POST /v1/bots/{server_id}/members/{user_id}/unban
Unban a previously banned member.

Roles

GET /v1/bots/{server_id}/roles
List all roles in a server.
POST /v1/bots/{server_id}/roles
Create a new role.
Request Body
{
  "name": "Moderator",
  "color": "#FF5733",
  "permissions": ["SEND_MESSAGES", "MANAGE_MESSAGES", "KICK_MEMBERS"]
}
GET /v1/bots/{server_id}/members/{user_id}/roles
Get all roles assigned to a specific member.
PUT /v1/bots/{server_id}/members/{user_id}/roles
Set all roles for a member (replaces existing).
Request Body
{
  "roles": ["role-id-1", "role-id-2"]
}
POST /v1/bots/{server_id}/members/{user_id}/roles/{role_id}
Add a single role to a member.
DELETE /v1/bots/{server_id}/members/{user_id}/roles/{role_id}
Remove a single role from a member.

Messages

GET /v1/bots/{server_id}/messages?channel_id={id}
Get messages from a channel. Supports limit and offset query parameters.
POST /v1/bots/{server_id}/messages/send
Send a message to a channel.
Request Body
{
  "channelId": "channel-id",
  "content": "Hello from my bot! 🤖",
  "attachmentIds": [],
  "mentions": [],
  "replyToId": ""
}

Sending Messages with Attachments

To send a message with file attachments, upload the file first, then reference the returned fileId in your message.

POST /v1/bots/{server_id}/upload/{channel_id}
Upload a file attachment. Send as multipart/form-data with a file field. Max file size: 50 MB. Supported types: images, videos, audio, documents, archives, and code files.
Response
{
  "success": true,
  "fileId": "abc123",
  "path": "channels/server-id/channel-id/abc123.png",
  "url": "https://s3.echoed.gg/files/channels/...",
  "filename": "screenshot.png",
  "size": 184320,
  "contentType": "image/png",
  "width": 1920,
  "height": 1080,
  "blurhash": "U.P,rXfQkCbH"
}

width, height, and blurhash are only returned for image uploads.

Message with Attachment
{
  "channelId": "channel-id",
  "content": "Check out this image!",
  "attachmentIds": ["abc123"],
  "mentions": [],
  "replyToId": ""
}
PUT /v1/bots/{server_id}/messages/{message_id}
Edit a message. Bots can edit their own messages, or others' messages if they have MANAGE_MESSAGES permission.
Request Body
{
  "content": "Edited message content"
}
DELETE /v1/bots/{server_id}/messages/{message_id}
Delete a message.

Direct Messages

POST /v1/bots/dm/send
Send a direct message to a user by username.
Request Body
{
  "username": "target-user",
  "content": "Hey! This is a DM from my bot."
}
Response
{
  "message": "DM sent successfully.",
  "messageId": "message-id",
  "receiverId": "user-id",
  "content": "Hey! This is a DM from my bot."
}

Tasks

Manage tasks in Task-type channels. Your bot can create, update, and delete tasks for project management and automation.

GET /v1/bots/{server_id}/tasks?channelId={id}
Get all tasks from a task channel.
POST /v1/bots/{server_id}/tasks
Create a new task.
Request Body
{
  "channelId": "task-channel-id",
  "title": "Fix login bug",
  "description": "Users can't login with OAuth",
  "assigneeId": "user-id",
  "dueDate": "2026-04-01T00:00:00Z",
  "priority": "high"
}
FieldValues
prioritylow, medium, high, urgent
statusbacklog, todo, in_progress, review, done
PUT /v1/bots/{server_id}/tasks/{task_id}
Update a task's status, description, priority, or other fields.
DELETE /v1/bots/{server_id}/tasks/{task_id}
Delete a task.

Calendar Events

Manage events in Calendar-type channels. Create, update, and manage event participants.

GET /v1/bots/{server_id}/calendar?channelId={id}
Get all events from a calendar channel.
POST /v1/bots/{server_id}/calendar
Create a new calendar event.
Request Body
{
  "channelId": "calendar-channel-id",
  "title": "Game Night",
  "description": "Weekly gaming session",
  "startTime": "2026-04-01T20:00:00Z",
  "endTime": "2026-04-01T23:00:00Z",
  "location": "Voice Channel #gaming",
  "isAllDay": false
}
PUT /v1/bots/{server_id}/calendar/{event_id}
Update an event's title, description, time, or location.
DELETE /v1/bots/{server_id}/calendar/{event_id}
Delete a calendar event.

Event Participants

GET /v1/bots/{server_id}/calendar/{event_id}/participants
List all participants for an event.
POST /v1/bots/{server_id}/calendar/{event_id}/participants/add
Add a participant to an event.
Request Body
{ "userId": "user-id" }
POST /v1/bots/{server_id}/calendar/{event_id}/participants/remove
Remove a participant from an event.
Request Body
{ "userId": "user-id" }

WebSocket Events

Connect via Socket.IO to receive real-time events. Authenticate with your bot token after connecting.

Connecting

Python (socket.io)
# pip install python-socketio
import socketio

sio = socketio.Client()
sio.connect("https://socket.echoed.gg")

# Authenticate after connecting
sio.emit("authenticate", {
    "botToken": "zbot_your_token_here"
})

Subscribing to Events

After authentication, subscribe to specific servers or channels:

Subscribe
sio.emit("subscribe", {
    "botToken": "zbot_your_token",
    "type": "server",    # or "channel"
    "id": "server-id"
})

Event Types

All events follow the pattern { type: "resource:action", data: { ... } }

Socket Event Types Description
messageEvent message:created, message:updated, message:deleted Message send, edit, delete in channels
channelEvent channel:created, channel:updated, channel:deleted Channel CRUD operations
categoryEvent category:created, category:updated, category:deleted Category changes
serverEvent server:updated, server:member:joined, server:member:left Server and membership events
taskEvent task:created, task:updated, task:deleted Task changes in task channels
eventEvent event:created, event:updated, event:deleted Calendar event changes
reactionEvent reaction:created, reaction:deleted Message reaction add/remove
friendEvent friend:request_sent, friend:request_accepted Friend request events
countEvent Unread count updates

Message Event Payload

messageEvent Example
{
  "type": "message:created",
  "data": {
    "id": "message-id",
    "channelId": "channel-id",
    "serverId": "server-id",
    "senderId": "user-id",
    "content": "Hello everyone!",
    "messageType": "user",
    "createdAt": "2026-03-14T12:00:00Z",
    "author": {
      "id": "user-id",
      "name": "Username",
      "avatarUrl": "https://..."
    }
  }
}

OAuth2

Integrate "Login with Echoed" into your application, access user data, and invite bots to servers using standard OAuth2 authorization code flow.

Authorization Flow

  1. Register an OAuth2 client in Settings → OAuth2 Apps
  2. Redirect users to the authorization URL with your client_id and requested scopes
  3. User approves — Echoed redirects back with an authorization code
  4. Exchange the code for access + refresh tokens
  5. Use the access token to call OAuth2 API endpoints
Step 1 — Authorization URL
https://go.echoed.gg/oauth2/authorize?
  response_type=code&
  client_id=your_client_id&
  redirect_uri=https://yourapp.com/callback&
  scope=openid profile email servers&
  state=random_csrf_token
Step 2 — Exchange Code for Tokens
POST https://go.echoed.gg/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=authorization_code_from_callback&
redirect_uri=https://yourapp.com/callback&
client_id=your_client_id&
client_secret=your_client_secret
Token Response
{
  "access_token": "MKquTIm08LMU...",
  "refresh_token": "dR4nG7kP...",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Scopes

Scope Access
openidBasic identity (user ID, issuer)
profileName, username, avatar, created date
emailEmail address
serversUser's servers, invite bots to servers
friendsUser's friends list
offline_accessReceive a refresh token for long-lived access

Token Lifetimes

Token Lifetime
Authorization Code10 minutes
Access Token1 hour
Refresh Token30 days

OAuth2 Endpoints

GET /oauth2/userinfo
Get the authenticated user's profile. Requires Bearer token in Authorization header.
Response (with profile + servers scopes)
{
  "sub": "user-id",
  "name": "Display Name",
  "username": "username",
  "avatar_url": "https://s3.echoed.gg/...",
  "email": "[email protected]",
  "owned_servers": [
    { "id": "server-id", "name": "My Server", "type": "public" }
  ],
  "servers_count": 5
}
GET /oauth2/api/user/servers
Get user's servers where they can invite bots. Requires servers scope.
GET /oauth2/api/user/friends
Get the user's friends list. Requires friends scope.
POST /oauth2/api/servers/{server_id}/invite-bot
Invite a bot to a server. Requires servers scope and bot management permission.
Request Body
{ "bot_id": "bot-user-id" }
POST /oauth2/token
Exchange authorization code for tokens, or refresh an existing token. Uses application/x-www-form-urlencoded.
POST /oauth2/revoke
Revoke an access or refresh token.
Request Body (form-encoded)
token=access_token_value&
token_type_hint=access_token&
client_id=your_client_id&
client_secret=your_client_secret
Tip: Request only the scopes you need. Users are more likely to approve fewer permissions. Use offline_access only if your app needs long-lived access.