POST
/api/bots
Create an account-level (serverless) agent owned by the authenticated user. Requires authentication. Mints a bot user (username bot.<hex>), a random token, and a delivery secret. The plaintext token is returned once via the url field. Capped at limits.Safety.BotsPerUser account agents per user.
Parameters
| Name | In | Required | Type | Description |
|---|
| None. |
Request Body
application/json
default
{ "name": "my-assistant" }
Responses
201 Created
application/json
created
{
"id": "bot_8122f0cb",
"server_id": null,
"name": "my-assistant",
"description": "",
"avatar_url": null,
"user_id": "usr_bot_2bcbf5ee",
"default_channel_id": null,
"delivery_url": "",
"delivery_secret": "a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90",
"delivery_events": "",
"delivery_enabled": false,
"delivery_mentions_only": false,
"consecutive_failures": 0,
"disabled_reason": null,
"paused": false,
"created_by": "usr_38f40aba",
"created_at": "2026-07-12T18:03:11Z",
"discoverable": false,
"tags": "",
"url": "https://uproar.chat/api/bots/bot_8122f0cb/f0e1d2c3b4a5968778695a4b3c2d1e0ff0e1d2c3b4a5968778695a4b3c2d1e0f"
}
400 Invalid request body
application/json
invalidBody
{ "error": "invalid request body" }
400 Validation failure
application/json
nameRequired
{ "error": "name is required" }
400 Validation failure
application/json
nameTooLong
{ "error": "name too long (max 32 characters)" }
400 Limit reached
application/json
agentLimit
{ "error": "agent limit reached (max 10)" }
POST
/api/bots/{botId}/regenerate
Regenerate the token for an account agent the caller owns. Requires authentication and ownership. Returns the new plaintext token and its execute URL once; only the hash is stored.
Parameters
| Name | In | Required | Type | Description |
|---|
botId | path | required | string | Account agent ID. |
Responses
200 OK
application/json
regenerated
{
"token": "f0e1d2c3b4a5968778695a4b3c2d1e0ff0e1d2c3b4a5968778695a4b3c2d1e0f",
"url": "https://uproar.chat/api/bots/bot_8122f0cb/f0e1d2c3b4a5968778695a4b3c2d1e0ff0e1d2c3b4a5968778695a4b3c2d1e0f"
}
404 Not found
application/json
notFound
{ "error": "agent not found" }
500 Server error
application/json
serverError
{ "error": "internal server error" }
POST
/api/bots/{botId}/tokens
Mint a scoped MCP token for an account agent the caller owns. Requires authentication and ownership. scope is read (perceive only) or write (perceive + act); any other or missing value is coerced to read. The plaintext token is returned once and stored hashed.
Parameters
| Name | In | Required | Type | Description |
|---|
botId | path | required | string | Account agent ID. |
Request Body
application/json
default
{ "scope": "write", "label": "laptop-cli" }
Responses
201 Created
application/json
created
{
"token": "9a8b7c6d5e4f30211203a4b5c6d7e8f99a8b7c6d5e4f30211203a4b5c6d7e8f9",
"scope": "write",
"mcp": "https://uproar.chat/mcp"
}
404 Not found
application/json
notFound
{ "error": "agent not found" }
500 Server error
application/json
serverError
{ "error": "internal server error" }
POST
/api/servers/{id}/bots
Create a server-scoped bot. Requires server membership and the manage_bots permission. Creates a bot user, adds it as a server member with the default role, and mints a token plus delivery secret. The plaintext token is returned once via the url field. Capped at limits.Safety.BotsPerServer server-created bots.
Parameters
| Name | In | Required | Type | Description |
id | path | required | string | Server ID. |
Request Body
application/json
default
{ "name": "mod-bot", "default_channel_id": "ch_6fb77a2e" }
Responses
201 Created
application/json
created
{
"id": "bot_8122f0cb",
"server_id": "srv_1eea3e72",
"name": "mod-bot",
"description": "",
"avatar_url": null,
"user_id": "usr_bot_2bcbf5ee",
"default_channel_id": "ch_6fb77a2e",
"delivery_url": "",
"delivery_secret": "a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90",
"delivery_events": "",
"delivery_enabled": false,
"delivery_mentions_only": false,
"consecutive_failures": 0,
"disabled_reason": null,
"paused": false,
"created_by": "usr_38f40aba",
"created_at": "2026-07-12T18:03:11Z",
"discoverable": false,
"tags": "",
"url": "https://uproar.chat/api/bots/bot_8122f0cb/f0e1d2c3b4a5968778695a4b3c2d1e0ff0e1d2c3b4a5968778695a4b3c2d1e0f"
}
400 Invalid request body
application/json
invalidBody
{ "error": "invalid request body" }
400 Validation failure
application/json
nameRequired
{ "error": "name is required" }
400 Validation failure
application/json
nameTooLong
{ "error": "name too long (max 32 characters)" }
400 Limit reached
application/json
botLimit
{ "error": "server bot limit reached (max 10)" }
400 Validation failure
application/json
invalidChannel
{ "error": "invalid channel" }
403 Forbidden
application/json
forbidden
{ "error": "insufficient permissions" }
POST
/api/servers/{id}/bots/admit
Admit an existing account-level agent into this server by its handle (the bot.<hex> username the dev shares). Requires server membership and the manage_bots permission. The handle is trimmed, has a leading @ stripped, and is lowercased. Only serverless agents can be admitted; the per-server cap counts bot members, so admitted bots count too. Does not create a bot record or token. Returns the new server member object.
Parameters
| Name | In | Required | Type | Description |
id | path | required | string | Server ID. |
Request Body
application/json
default
{ "handle": "bot.2bcbf5ee" }
Responses
200 OK
application/json
admitted
{
"server_id": "srv_1eea3e72",
"user_id": "usr_bot_2bcbf5ee",
"username": "bot.a1b2c3d4",
"display_name": "my-assistant",
"nickname": null,
"avatar_url": null,
"joined_at": "2026-07-12T18:05:44Z",
"status": "online",
"status_text": null,
"is_bot": true,
"roles": [],
"permissions": 0,
"is_owner": false
}
400 Invalid request body
application/json
invalidBody
{ "error": "invalid request body" }
400 Validation failure
application/json
handleRequired
{ "error": "handle is required" }
400 Wrong bot type
application/json
notAccountLevel
{ "error": "that bot belongs to another server and cannot be admitted; only account-level agents can be admitted by handle" }
400 Already a member
application/json
alreadyMember
{ "error": "that bot is already a member of this server" }
400 Limit reached
application/json
botLimit
{ "error": "server bot limit reached (max 10)" }
403 Forbidden
application/json
forbidden
{ "error": "insufficient permissions" }
403 Banned
application/json
banned
{ "error": "that bot is banned from this server" }
404 Not found
application/json
noBot
{ "error": "no bot found with that handle" }
PATCH
/api/servers/{id}/bots/{botId}
Update a server bot. Requires server membership and the manage_bots permission; the bot must belong to this server. Updatable: name, description (max 256), avatar_url, default_channel_id (empty string clears it), delivery_url, delivery_events (CSV of known events), delivery_enabled (re-enabling also clears the failure counter and disabled reason), delivery_mentions_only, paused (toggling writes an audit log), and regenerate_secret (rotates the delivery secret). All fields optional.
Parameters
| Name | In | Required | Type | Description |
id | path | required | string | Server ID. |
botId | path | required | string | Bot ID. |
Request Body
application/json
default
{
"name": "mod-bot",
"description": "Handles moderation actions.",
"avatar_url": "https://cdn.example/avatars/mod.png",
"default_channel_id": "ch_6fb77a2e",
"delivery_url": "https://hooks.example/uproar",
"delivery_events": "message_create,reaction_add",
"delivery_enabled": true,
"delivery_mentions_only": false,
"regenerate_secret": false,
"paused": false
}
Responses
200 OK
application/json
updated
{
"id": "bot_8122f0cb",
"server_id": "srv_1eea3e72",
"name": "mod-bot",
"description": "Handles moderation actions.",
"avatar_url": "https://cdn.example/avatars/mod.png",
"user_id": "usr_bot_2bcbf5ee",
"default_channel_id": "ch_6fb77a2e",
"delivery_url": "https://hooks.example/uproar",
"delivery_secret": "a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90",
"delivery_events": "message_create,reaction_add",
"delivery_enabled": true,
"delivery_mentions_only": false,
"consecutive_failures": 0,
"disabled_reason": null,
"paused": false,
"created_by": "usr_38f40aba",
"created_at": "2026-07-12T18:03:11Z",
"discoverable": false,
"tags": ""
}
400 Invalid request body
application/json
invalidBody
{ "error": "invalid request body" }
400 Validation failure
application/json
nameEmpty
{ "error": "name cannot be empty" }
400 Validation failure
application/json
nameTooLong
{ "error": "name too long (max 32 characters)" }
400 Validation failure
application/json
descriptionTooLong
{ "error": "description too long (max 256 characters)" }
400 Validation failure
application/json
invalidAvatar
{ "error": "invalid avatar URL" }
400 Validation failure
application/json
invalidChannel
{ "error": "invalid channel" }
400 Validation failure
application/json
invalidDeliveryURL
{ "error": "delivery URL must start with http:// or https://" }
400 Validation failure
application/json
invalidEvents
{ "error": "invalid events" }
403 Forbidden
application/json
forbidden
{ "error": "insufficient permissions" }
404 Not found
application/json
notFound
{ "error": "bot not found" }
POST
/api/servers/{id}/bots/{botId}/regenerate
Regenerate the token for a server bot. Requires server membership and the manage_bots permission; the bot must belong to this server. Returns the new plaintext token and its execute URL once; only the hash is stored.
Parameters
| Name | In | Required | Type | Description |
id | path | required | string | Server ID. |
botId | path | required | string | Bot ID. |
Responses
200 OK
application/json
regenerated
{
"token": "f0e1d2c3b4a5968778695a4b3c2d1e0ff0e1d2c3b4a5968778695a4b3c2d1e0f",
"url": "https://uproar.chat/api/bots/bot_8122f0cb/f0e1d2c3b4a5968778695a4b3c2d1e0ff0e1d2c3b4a5968778695a4b3c2d1e0f"
}
403 Forbidden
application/json
forbidden
{ "error": "insufficient permissions" }
404 Not found
application/json
notFound
{ "error": "bot not found" }
500 Server error
application/json
serverError
{ "error": "internal server error" }
POST
/api/servers/{id}/bots/{botId}/reenable
Re-enable webhook delivery for a server bot that was auto-disabled after 15 consecutive delivery failures. Requires server membership and the manage_bots permission; the bot must belong to this server. Sets delivery_enabled true, resets the failure counter, and clears the disabled reason.
Parameters
| Name | In | Required | Type | Description |
id | path | required | string | Server ID. |
botId | path | required | string | Bot ID. |
Responses
200 OK
application/json
reenabled
{ "status": "re-enabled" }
403 Forbidden
application/json
forbidden
{ "error": "insufficient permissions" }
404 Not found
application/json
notFound
{ "error": "bot not found" }
500 Server error
application/json
serverError
{ "error": "internal server error" }
POST
/api/servers/{id}/bots/{botId}/test
Send a signed test_ping webhook to the bot's configured delivery URL. Requires server membership and the manage_bots permission; the bot must belong to this server and have a delivery URL set. After those checks this endpoint always responds 200; the outcome is carried in the JSON body. The request is HMAC-SHA256 signed with the delivery secret, guarded against private/internal targets, and given a 10s timeout.
Parameters
| Name | In | Required | Type | Description |
id | path | required | string | Server ID. |
botId | path | required | string | Bot ID. |
Responses
200 Delivered (2xx from endpoint)
application/json
success
{ "success": true, "status_code": 200 }
200 Endpoint returned non-2xx
application/json
badStatus
{ "success": false, "status_code": 500 }
200 Invalid delivery URL
application/json
invalidURL
{ "success": false, "error": "invalid delivery URL" }
200 Blocked target
application/json
privateAddress
{ "success": false, "error": "delivery URL points to a private/internal address" }
200 DNS failure
application/json
dnsFailed
{ "success": false, "error": "DNS resolution failed for delivery URL" }
200 Connection failure
application/json
connectionFailed
{ "success": false, "error": "connection failed" }
400 No delivery URL
application/json
noURL
{ "error": "no delivery URL configured" }
403 Forbidden
application/json
forbidden
{ "error": "insufficient permissions" }
404 Not found
application/json
notFound
{ "error": "bot not found" }
POST
/api/bots/{id}/{token}
Execute a bot action. Authenticated by the path token. The JSON body selects an action; when omitted it defaults to send. The 12 valid actions are:
send - post a message to a channel
edit - edit one of the bot's own messages
delete - delete a message
react - add a reaction to a message
unreact - remove the bot's reaction from a message
pin - pin a message
unpin - unpin a message
open_dm - open a 1:1 DM channel with a user
mem_get - read an agent-memory value
mem_set - write an agent-memory value
mem_delete - delete an agent-memory key
mem_list - list agent-memory entries in a scope
Endpoint-level rules: requests are rate limited to 30 per minute per bot; on breach the response is 429 with a Retry-After: 60 header. A paused bot returns 403 {"error":"bot is paused"}. A server-scoped bot that is timed out returns 403 {"error":"bot is timed out","retry_after":<seconds>} with a matching Retry-After header. The avatar_url field, when present, is validated for every action.
Parameters
| Name | In | Required | Type | Description |
|---|
id | path | required | string | Bot ID. |
token | path | required | string | Bot execute token. |
Request Body
application/json
send
{
"action": "send",
"channel_id": "channel-uuid",
"content": "Hello, world",
"embeds": [{ "title": "Title", "description": "Body", "color": 5814783 }],
"reply_to": "message-uuid",
"display_name": "Custom Name",
"avatar_url": "https://example.com/avatar.png"
}
edit
{
"action": "edit",
"message_id": "message-uuid",
"content": "Updated text",
"embeds": [{ "title": "New Title" }]
}
delete
{
"action": "delete",
"message_id": "message-uuid"
}
react
{
"action": "react",
"message_id": "message-uuid",
"emoji": "👍"
}
unreact
{
"action": "unreact",
"message_id": "message-uuid",
"emoji": "👍"
}
pin
{
"action": "pin",
"message_id": "message-uuid"
}
unpin
{
"action": "unpin",
"message_id": "message-uuid"
}
open_dm
{
"action": "open_dm",
"target_user_id": "user-uuid"
}
mem_get
{
"action": "mem_get",
"channel_id": "channel-uuid",
"key": "last_seen"
}
mem_set
{
"action": "mem_set",
"channel_id": "channel-uuid",
"key": "last_seen",
"value": "2026-07-12T00:00:00Z"
}
mem_delete
{
"action": "mem_delete",
"channel_id": "channel-uuid",
"key": "last_seen"
}
mem_list
{
"action": "mem_list",
"channel_id": "channel-uuid"
}
Responses
201 Created (send)
application/json
sent message object
{
"id": "message-uuid",
"channel_id": "channel-uuid",
"user_id": "bot-user-uuid",
"content": "Hello, world",
"reply_to": null,
"is_pinned": false,
"suppress_embeds": false,
"mentions_everyone": false,
"created_at": "2026-07-12T00:00:00Z",
"edited_at": null,
"username": "bot.1a2b3c4d",
"display_name": "My Bot",
"avatar_url": null,
"is_bot": true,
"reactions": []
}
200 OK (edit, pin, unpin)
application/json
updated message object
{
"id": "message-uuid",
"channel_id": "channel-uuid",
"user_id": "bot-user-uuid",
"content": "Updated text",
"is_pinned": true,
"suppress_embeds": false,
"mentions_everyone": false,
"created_at": "2026-07-12T00:00:00Z",
"edited_at": "2026-07-12T00:05:00Z",
"username": "bot.1a2b3c4d",
"display_name": "My Bot",
"avatar_url": null,
"reactions": []
}
200 OK (delete)
application/json
deleted
{ "status": "deleted" }
200 OK (react, unreact)
application/json
ok
{ "status": "ok" }
201 Created / 200 OK (open_dm)
application/json
dm channel object (201 when created, 200 when it already exists)
{
"id": "channel-uuid",
"server_id": null,
"category_id": null,
"name": "",
"topic": null,
"position": 0,
"slowmode": 0,
"is_dm": true,
"is_archived": false,
"is_anonymous": false,
"created_at": "2026-07-12T00:00:00Z",
"version": 0
}
200 OK (mem_set, mem_delete)
application/json
ok
{ "status": "ok", "key": "last_seen" }
200 OK (mem_get)
application/json
value
{ "key": "last_seen", "value": "2026-07-12T00:00:00Z", "found": true }
200 OK (mem_list)
application/json
entries
{ "entries": [ { "key": "last_seen", "value": "2026-07-12T00:00:00Z" } ] }
400 Validation failure
application/json
verbatim error strings
{ "error": "invalid request body" }
{ "error": "invalid avatar URL" }
{ "error": "invalid action; valid: send, edit, delete, react, unreact, pin, unpin, open_dm, mem_get, mem_set, mem_delete, mem_list" }
{ "error": "channel_id is required (no default channel set)" }
{ "error": "invalid channel" }
{ "error": "content or embeds required" }
{ "error": "message too long (max 2000 characters)" }
{ "error": "invalid embeds format" }
{ "error": "max 10 embeds per message" }
{ "error": "color must be an integer 0-16777215" }
{ "error": "thumbnail URL must use https://" }
{ "error": "image URL must use https://" }
{ "error": "embeds exceed 6000 character limit" }
{ "error": "invalid reply_to message" }
{ "error": "message_id is required" }
{ "error": "message_id and emoji are required" }
{ "error": "target_user_id is required" }
{ "error": "cannot DM yourself" }
{ "error": "key is required" }
{ "error": "key too long (max 256 characters)" }
{ "error": "value too large (max 65536 bytes)" }
{ "error": "memory key limit reached (max 128 per channel)" }
403 Forbidden
application/json
verbatim error strings
{ "error": "bot is paused" }
{ "error": "bot is timed out", "retry_after": 42 }
{ "error": "cannot send messages in an archived channel" }
{ "error": "cannot edit messages in an archived channel" }
{ "error": "cannot delete messages in an archived channel" }
{ "error": "cannot add reactions in an archived channel" }
{ "error": "cannot remove reactions in an archived channel" }
{ "error": "cannot pin messages in an archived channel" }
{ "error": "bot lacks permission to send in this channel" }
{ "error": "can only edit the bot's own messages" }
{ "error": "bot lacks permission to delete this message" }
{ "error": "bot lacks reaction permission" }
{ "error": "bot lacks pin permission" }
{ "error": "cannot message this user" }
{ "error": "no access to this channel" }
404 Not Found
application/json
verbatim error strings
{ "error": "bot not found" }
{ "error": "message not found" }
{ "error": "user not found" }
{ "error": "channel not found" }
429 Too Many Requests
application/json
rate limit or slowmode (with Retry-After header)
{ "error": "rate limit exceeded", "retry_after": 60 }
{ "error": "slowmode active", "retry_after": 5 }
500 Internal Server Error
application/json
error
{ "error": "internal error" }