REST API v1 · Developers · API reference
Moderation API reference
All 9 Moderation endpoints of the BotGG REST API v1: scopes, parameters, fields, errors and ready-to-run curl, JavaScript and Python examples.
Developer docs
On this page
Endpoints
Paths are relative to https://console.botgg.app/api/v1. Send your API key as Authorization: Bearer botgg_…; IDs are strings.
| Endpoint | Scope | Description |
|---|---|---|
GET/guilds/{guildId}/moderation/cases | moderation:read | List moderation cases |
GET/guilds/{guildId}/moderation/cases/{number} | moderation:read | Get a moderation case |
POST/guilds/{guildId}/moderation/users/{userId}/warn | moderation:write | Warn a member |
POST/guilds/{guildId}/moderation/users/{userId}/timeout | moderation:write | Time out a member |
POST/guilds/{guildId}/moderation/users/{userId}/untimeout | moderation:write | Remove a member’s timeout |
POST/guilds/{guildId}/moderation/users/{userId}/kick | moderation:write | Kick a member |
POST/guilds/{guildId}/moderation/users/{userId}/ban | moderation:write | Ban a user |
POST/guilds/{guildId}/moderation/users/{userId}/unban | moderation:write | Unban a user |
GET/guilds/{guildId}/moderation/actions/{actionId} | moderation:write | Get the result of a moderation action |
Besides its own errors below, every endpoint can return these common ones:
List moderation cases
GET/guilds/{guildId}/moderation/cases
Newest first (deleted cases are left out). Pass before=<nextCursor> for the next page.
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
action | query | string | No | One of warn, timeout, untimeout, kick, ban, tempban, softban, unban, note, purge |
source | query | string | No | One of command, automod, escalation, antiraid, antinuke, console, api |
userId | query | string | No | Only cases against this user Discord ID (string) |
moderatorId | query | string | No | Discord ID (string) |
active | query | string | No | 1 = only running warnings / timeouts / tempbans One of 1, 0, true, false |
limit | query | integer | No | 1–100 · Default 25 |
before | query | integer | No | 1–999,999,999 |
Response
| Field | Type | Required | Description |
|---|---|---|---|
items | array of object | Yes | |
items[].number | integer | Yes | |
items[].action | string | Yes | One of warn, timeout, untimeout, kick, ban, tempban, softban, unban, note, purge |
items[].userId | string | Yes | The target |
items[].userTag | string | null | Yes | |
items[].moderatorId | string | Yes | |
items[].reason | string | null | Yes | |
items[].durationSeconds | integer | null | Yes | |
items[].evidence | array of string | Yes | |
items[].source | string | Yes | One of command, automod, escalation, antiraid, antinuke, console, api |
items[].active | boolean | Yes | |
items[].expiresAt | string | null | Yes | ISO 8601 date-time |
items[].resolvedAt | string | null | Yes | ISO 8601 date-time |
items[].dmStatus | string | null | Yes | One of sent, failed, off |
items[].apiKey | object | null | Yes | Set when the action was made with an API key |
items[].apiKey.id | integer | null | Yes | |
items[].apiKey.name | string | null | Yes | |
items[].createdAt | string | null | Yes | ISO 8601 date-time |
nextCursor | string | null | Yes |
{
"items": [
{
"number": 7,
"action": "warn",
"userId": "876543210987654321",
"userTag": "example",
"moderatorId": "234567890123456789",
"reason": "Tournament prize",
"durationSeconds": 3600,
"evidence": [
"example"
],
"source": "command",
"active": true,
"expiresAt": "2026-10-07T12:00:00.000Z",
"resolvedAt": "2026-10-07T12:00:00.000Z",
"dmStatus": "sent",
"apiKey": {
"id": 98765,
"name": "Example"
},
"createdAt": "2026-10-07T12:00:00.000Z"
}
],
"nextCursor": "98700"
}Examples
curl -s "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/cases?limit=25" \
-H "Authorization: Bearer $BOTGG_API_KEY"// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/cases?limit=25', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/cases",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
},
params={
"limit": 25,
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 409 | FEATURE_DISABLED |
| 503 | SERVICE_UNAVAILABLE |
Get a moderation case
GET/guilds/{guildId}/moderation/cases/{number}
With its timeline (created, reason / evidence edits, DM, expired, revoked, escalated).
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
number | path | string | Yes |
Response
| Field | Type | Required | Description |
|---|---|---|---|
number | integer | Yes | |
action | string | Yes | One of warn, timeout, untimeout, kick, ban, tempban, softban, unban, note, purge |
userId | string | Yes | The target |
userTag | string | null | Yes | |
moderatorId | string | Yes | |
reason | string | null | Yes | |
durationSeconds | integer | null | Yes | |
evidence | array of string | Yes | |
source | string | Yes | One of command, automod, escalation, antiraid, antinuke, console, api |
active | boolean | Yes | |
expiresAt | string | null | Yes | ISO 8601 date-time |
resolvedAt | string | null | Yes | ISO 8601 date-time |
dmStatus | string | null | Yes | One of sent, failed, off |
apiKey | object | null | Yes | Set when the action was made with an API key |
apiKey.id | integer | null | Yes | |
apiKey.name | string | null | Yes | |
createdAt | string | null | Yes | ISO 8601 date-time |
events | array of object | Yes | |
events[].type | string | Yes | |
events[].actorId | string | null | Yes | |
events[].data | any | Yes | |
events[].createdAt | string | null | Yes | ISO 8601 date-time |
{
"number": 7,
"action": "warn",
"userId": "876543210987654321",
"userTag": "example",
"moderatorId": "234567890123456789",
"reason": "Tournament prize",
"durationSeconds": 3600,
"evidence": [
"example"
],
"source": "command",
"active": true,
"expiresAt": "2026-10-07T12:00:00.000Z",
"resolvedAt": "2026-10-07T12:00:00.000Z",
"dmStatus": "sent",
"apiKey": {
"id": 98765,
"name": "Example"
},
"createdAt": "2026-10-07T12:00:00.000Z",
"events": [
{
"type": "example",
"actorId": "234567890123456789",
"data": null,
"createdAt": "2026-10-07T12:00:00.000Z"
}
]
}Examples
curl -s "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/cases/234567890123456789" \
-H "Authorization: Bearer $BOTGG_API_KEY"// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/cases/234567890123456789', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/cases/234567890123456789",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 404 | CASE_NOT_FOUND |
| 409 | FEATURE_DISABLED |
| 503 | SERVICE_UNAVAILABLE |
Warn a member
POST/guilds/{guildId}/moderation/users/{userId}/warn
Same as /warn: a warning case (counts toward escalation), DM per the Moderation settings. Acts in the name of the key’s creator, who must still have the matching Discord permission; the target must be below their and the bot’s highest role (never the owner, the bot or the creator). The bot runs it within a few seconds — poll GET …/moderation/actions/{actionId}. At most 10 actions a minute per key. Written to the audit log.
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
userId | path | string | Yes | The member’s Discord user ID · Discord ID (string) |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Required. Shown in the case, the mod log and the member’s DM 1–500 characters |
evidence | array of string | No | Up to 5 links up to 5 items |
Response
| Field | Type | Required | Description |
|---|---|---|---|
actionId | string | Yes | |
action | string | Yes | One of warn, timeout, untimeout, kick, ban, unban |
userId | string | Yes | |
status | string | Yes | One of pending, running, done, failed |
caseNumber | integer | null | Yes | |
error | string | null | Yes | Refusal code when failed (e.g. HIERARCHY_HIGHER, MODERATOR_MISSING_PERMISSION, NOT_BANNED, BOT_MISSING_PERMISSIONS) |
requestedAt | string | null | Yes | ISO 8601 date-time |
{
"actionId": "234567890123456789",
"action": "warn",
"userId": "876543210987654321",
"status": "pending",
"caseNumber": 10,
"error": "example",
"requestedAt": "2026-10-07T12:00:00.000Z"
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/warn" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason":"Tournament prize","evidence":["example"]}'// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/warn', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
body: JSON.stringify({
reason: 'Tournament prize',
evidence: [
'example',
],
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/warn",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
json={
"reason": "Tournament prize",
"evidence": [
"example",
],
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
Time out a member
POST/guilds/{guildId}/moderation/users/{userId}/timeout
Same as /timeout: Discord timeout for durationSeconds, a case, DM per the settings. Acts in the name of the key’s creator, who must still have the matching Discord permission; the target must be below their and the bot’s highest role (never the owner, the bot or the creator). The bot runs it within a few seconds — poll GET …/moderation/actions/{actionId}. At most 10 actions a minute per key. Written to the audit log.
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
userId | path | string | Yes | The member’s Discord user ID · Discord ID (string) |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Required. Shown in the case, the mod log and the member’s DM 1–500 characters |
durationSeconds | integer | Yes | 1 minute to 28 days 60–2,419,200 |
evidence | array of string | No | Up to 5 links up to 5 items |
Response
| Field | Type | Required | Description |
|---|---|---|---|
actionId | string | Yes | |
action | string | Yes | One of warn, timeout, untimeout, kick, ban, unban |
userId | string | Yes | |
status | string | Yes | One of pending, running, done, failed |
caseNumber | integer | null | Yes | |
error | string | null | Yes | Refusal code when failed (e.g. HIERARCHY_HIGHER, MODERATOR_MISSING_PERMISSION, NOT_BANNED, BOT_MISSING_PERMISSIONS) |
requestedAt | string | null | Yes | ISO 8601 date-time |
{
"actionId": "234567890123456789",
"action": "warn",
"userId": "876543210987654321",
"status": "pending",
"caseNumber": 10,
"error": "example",
"requestedAt": "2026-10-07T12:00:00.000Z"
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/timeout" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason":"Tournament prize","durationSeconds":3600,"evidence":["example"]}'// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/timeout', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
body: JSON.stringify({
reason: 'Tournament prize',
durationSeconds: 3600,
evidence: [
'example',
],
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/timeout",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
json={
"reason": "Tournament prize",
"durationSeconds": 3600,
"evidence": [
"example",
],
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
Remove a member’s timeout
POST/guilds/{guildId}/moderation/users/{userId}/untimeout
Same as /untimeout. Acts in the name of the key’s creator, who must still have the matching Discord permission; the target must be below their and the bot’s highest role (never the owner, the bot or the creator). The bot runs it within a few seconds — poll GET …/moderation/actions/{actionId}. At most 10 actions a minute per key. Written to the audit log.
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
userId | path | string | Yes | The member’s Discord user ID · Discord ID (string) |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Required. Shown in the case, the mod log and the member’s DM 1–500 characters |
Response
| Field | Type | Required | Description |
|---|---|---|---|
actionId | string | Yes | |
action | string | Yes | One of warn, timeout, untimeout, kick, ban, unban |
userId | string | Yes | |
status | string | Yes | One of pending, running, done, failed |
caseNumber | integer | null | Yes | |
error | string | null | Yes | Refusal code when failed (e.g. HIERARCHY_HIGHER, MODERATOR_MISSING_PERMISSION, NOT_BANNED, BOT_MISSING_PERMISSIONS) |
requestedAt | string | null | Yes | ISO 8601 date-time |
{
"actionId": "234567890123456789",
"action": "warn",
"userId": "876543210987654321",
"status": "pending",
"caseNumber": 10,
"error": "example",
"requestedAt": "2026-10-07T12:00:00.000Z"
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/untimeout" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason":"Tournament prize"}'// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/untimeout', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
body: JSON.stringify({
reason: 'Tournament prize',
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/untimeout",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
json={
"reason": "Tournament prize",
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
Kick a member
POST/guilds/{guildId}/moderation/users/{userId}/kick
Same as /kick: DM (before the kick) per the settings, then the kick and a case. Acts in the name of the key’s creator, who must still have the matching Discord permission; the target must be below their and the bot’s highest role (never the owner, the bot or the creator). The bot runs it within a few seconds — poll GET …/moderation/actions/{actionId}. At most 10 actions a minute per key. Written to the audit log.
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
userId | path | string | Yes | The member’s Discord user ID · Discord ID (string) |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Required. Shown in the case, the mod log and the member’s DM 1–500 characters |
evidence | array of string | No | Up to 5 links up to 5 items |
Response
| Field | Type | Required | Description |
|---|---|---|---|
actionId | string | Yes | |
action | string | Yes | One of warn, timeout, untimeout, kick, ban, unban |
userId | string | Yes | |
status | string | Yes | One of pending, running, done, failed |
caseNumber | integer | null | Yes | |
error | string | null | Yes | Refusal code when failed (e.g. HIERARCHY_HIGHER, MODERATOR_MISSING_PERMISSION, NOT_BANNED, BOT_MISSING_PERMISSIONS) |
requestedAt | string | null | Yes | ISO 8601 date-time |
{
"actionId": "234567890123456789",
"action": "warn",
"userId": "876543210987654321",
"status": "pending",
"caseNumber": 10,
"error": "example",
"requestedAt": "2026-10-07T12:00:00.000Z"
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/kick" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason":"Tournament prize","evidence":["example"]}'// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/kick', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
body: JSON.stringify({
reason: 'Tournament prize',
evidence: [
'example',
],
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/kick",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
json={
"reason": "Tournament prize",
"evidence": [
"example",
],
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
Ban a user
POST/guilds/{guildId}/moderation/users/{userId}/ban
Same as /ban: with durationSeconds a temporary ban the bot lifts automatically; deleteMessageSeconds deletes recent messages. Works for users who already left. Acts in the name of the key’s creator, who must still have the matching Discord permission; the target must be below their and the bot’s highest role (never the owner, the bot or the creator). The bot runs it within a few seconds — poll GET …/moderation/actions/{actionId}. At most 10 actions a minute per key. Written to the audit log.
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
userId | path | string | Yes | The member’s Discord user ID · Discord ID (string) |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Required. Shown in the case, the mod log and the member’s DM 1–500 characters |
deleteMessageSeconds | integer | No | Delete the user’s messages of the last N seconds (≤ 7 days) 0–604,800 |
durationSeconds | integer | null | No | Temporary ban (≤ 365 days); omit for a permanent ban 60–31,536,000 |
evidence | array of string | No | Up to 5 links up to 5 items |
Response
| Field | Type | Required | Description |
|---|---|---|---|
actionId | string | Yes | |
action | string | Yes | One of warn, timeout, untimeout, kick, ban, unban |
userId | string | Yes | |
status | string | Yes | One of pending, running, done, failed |
caseNumber | integer | null | Yes | |
error | string | null | Yes | Refusal code when failed (e.g. HIERARCHY_HIGHER, MODERATOR_MISSING_PERMISSION, NOT_BANNED, BOT_MISSING_PERMISSIONS) |
requestedAt | string | null | Yes | ISO 8601 date-time |
{
"actionId": "234567890123456789",
"action": "warn",
"userId": "876543210987654321",
"status": "pending",
"caseNumber": 10,
"error": "example",
"requestedAt": "2026-10-07T12:00:00.000Z"
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/ban" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason":"Tournament prize","deleteMessageSeconds":3600,"durationSeconds":3600,"evidence":["example"]}'// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/ban', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
body: JSON.stringify({
reason: 'Tournament prize',
deleteMessageSeconds: 3600,
durationSeconds: 3600,
evidence: [
'example',
],
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/ban",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
json={
"reason": "Tournament prize",
"deleteMessageSeconds": 3600,
"durationSeconds": 3600,
"evidence": [
"example",
],
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
Unban a user
POST/guilds/{guildId}/moderation/users/{userId}/unban
Same as /unban (ends a running tempban). 409 NOT_BANNED when the user isn’t banned. Acts in the name of the key’s creator, who must still have the matching Discord permission; the target must be below their and the bot’s highest role (never the owner, the bot or the creator). The bot runs it within a few seconds — poll GET …/moderation/actions/{actionId}. At most 10 actions a minute per key. Written to the audit log.
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
userId | path | string | Yes | The member’s Discord user ID · Discord ID (string) |
Idempotency-Key | header | string | No | Optional. Send a unique value (a UUID) and reuse it when you retry: the change happens only once. How it works |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Required. Shown in the case, the mod log and the member’s DM 1–500 characters |
Response
| Field | Type | Required | Description |
|---|---|---|---|
actionId | string | Yes | |
action | string | Yes | One of warn, timeout, untimeout, kick, ban, unban |
userId | string | Yes | |
status | string | Yes | One of pending, running, done, failed |
caseNumber | integer | null | Yes | |
error | string | null | Yes | Refusal code when failed (e.g. HIERARCHY_HIGHER, MODERATOR_MISSING_PERMISSION, NOT_BANNED, BOT_MISSING_PERMISSIONS) |
requestedAt | string | null | Yes | ISO 8601 date-time |
{
"actionId": "234567890123456789",
"action": "warn",
"userId": "876543210987654321",
"status": "pending",
"caseNumber": 10,
"error": "example",
"requestedAt": "2026-10-07T12:00:00.000Z"
}Examples
curl -s -X POST "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/unban" \
-H "Authorization: Bearer $BOTGG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason":"Tournament prize"}'// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/unban', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry this request
},
body: JSON.stringify({
reason: 'Tournament prize',
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os, uuid
import requests
res = requests.post(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/users/876543210987654321/unban",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # reuse it when you retry this request
},
json={
"reason": "Tournament prize",
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
Get the result of a moderation action
GET/guilds/{guildId}/moderation/actions/{actionId}
Only actions made with the same API key. done → caseNumber; failed → error (the bot re-checks everything right before acting).
Needs this feature enabled on the server: Moderation
| Name | In | Type | Required | Description |
|---|---|---|---|---|
guildId | path | string | Yes | Your server’s ID · Discord ID (string) |
actionId | path | string | Yes |
Response
| Field | Type | Required | Description |
|---|---|---|---|
actionId | string | Yes | |
action | string | Yes | One of warn, timeout, untimeout, kick, ban, unban |
userId | string | Yes | |
status | string | Yes | One of pending, running, done, failed |
caseNumber | integer | null | Yes | |
error | string | null | Yes | Refusal code when failed (e.g. HIERARCHY_HIGHER, MODERATOR_MISSING_PERMISSION, NOT_BANNED, BOT_MISSING_PERMISSIONS) |
requestedAt | string | null | Yes | ISO 8601 date-time |
{
"actionId": "234567890123456789",
"action": "warn",
"userId": "876543210987654321",
"status": "pending",
"caseNumber": 10,
"error": "example",
"requestedAt": "2026-10-07T12:00:00.000Z"
}Examples
curl -s "https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/actions/234567890123456789" \
-H "Authorization: Bearer $BOTGG_API_KEY"// Node.js 18+ (built-in fetch) — on your server, never in a browser
const res = await fetch('https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/actions/234567890123456789', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.BOTGG_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error}: ${data.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://console.botgg.app/api/v1/guilds/123456789012345678/moderation/actions/234567890123456789",
headers={
"Authorization": f"Bearer {os.environ['BOTGG_API_KEY']}",
},
timeout=10,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
print(data)Errors
| HTTP | Codes |
|---|---|
| 404 | MODERATION_ACTION_NOT_FOUND |
| 409 | FEATURE_DISABLED |
| 503 | SERVICE_UNAVAILABLE |