Редактирование и удаление сообщений Discord Webhook (PATCH и DELETE)
Как редактировать и удалять сообщения, отправленные через Discord webhook. Рабочие PATCH и DELETE на Python, JavaScript и curl — включая получение message ID.
Большинство туториалов про webhook’и заканчивается на «отправили сообщение». Но в реальности часто нужно его обновить (live-статус, прогресс билда, эволюция инцидента) или удалить (опечатка, отозванный алерт). Всё это можно делать одним webhook URL — без бота — но форма запроса нестандартная.
В гайде — точные PATCH и DELETE для webhook’а, плюс трюк, который многие пропускают: чтобы получить message ID, при отправке нужно ?wait=true.
Три endpoint’а
Имея webhook URL https://discord.com/api/webhooks/{webhook.id}/{webhook.token}:
| Действие | Метод | URL |
|---|---|---|
| Отправить сообщение | POST | /webhooks/{id}/{token} |
| Отредактировать | PATCH | /webhooks/{id}/{token}/messages/{message.id} |
| Удалить | DELETE | /webhooks/{id}/{token}/messages/{message.id} |
Для edit и delete нужен message ID. По умолчанию Discord его не возвращает — надо запросить.
Шаг 1: получить message ID
Добавь ?wait=true к URL POST. Тогда Discord вернёт полный объект сообщения (включая id) со статусом 200 вместо 204.
import requests
WEBHOOK_URL = "https://discord.com/api/webhooks/ID/TOKEN"
r = requests.post(
WEBHOOK_URL + "?wait=true",
json={"content": "Билд стартовал…"},
timeout=10,
)
r.raise_for_status()
message_id = r.json()["id"]
print(f"Отправлено id={message_id}")
Без ?wait=true приходит 204 No Content с пустым телом — сообщение ушло, но handle’а на него нет.
Шаг 2: редактирование
PATCH той же JSON-формой, что и POST. Поля, которые передаёшь — перезаписываются; поля без передачи остаются как были.
edit_url = f"{WEBHOOK_URL}/messages/{message_id}"
r = requests.patch(
edit_url,
json={"content": "Билд завершён за 2м 14с"},
timeout=10,
)
r.raise_for_status()
Можно править:
contentembeds(заменяет все embed’ы — пустой массив очищает)components(кнопки, select-меню)attachments(см. ниже)allowed_mentions
Нельзя менять username, avatar_url, tts — они фиксируются в момент отправки.
Live-статус борд: пример
import time
import requests
WEBHOOK_URL = "https://discord.com/api/webhooks/ID/TOKEN"
# Стартовое сообщение
r = requests.post(
WEBHOOK_URL + "?wait=true",
json={
"embeds": [{
"title": "Деплой в процессе",
"description": "⏳ Шаг 0 / 4 — подготовка",
"color": 0xfee75c,
}]
},
)
mid = r.json()["id"]
steps = ["сборка Docker-образа", "запуск тестов", "пуш в registry", "rolling restart"]
for i, step in enumerate(steps, 1):
time.sleep(5) # реальная работа
requests.patch(
f"{WEBHOOK_URL}/messages/{mid}",
json={
"embeds": [{
"title": "Деплой в процессе",
"description": f"⏳ Шаг {i} / {len(steps)} — {step}",
"color": 0xfee75c,
}]
},
)
# Финал
requests.patch(
f"{WEBHOOK_URL}/messages/{mid}",
json={
"embeds": [{
"title": "Деплой завершён",
"description": "✅ Все 4 шага выполнены",
"color": 0x57f287,
}]
},
)
Получается одно сообщение в Discord, которое обновляется на месте — без спама в чат.
Шаг 3: удаление
import requests
WEBHOOK_URL = "https://discord.com/api/webhooks/ID/TOKEN"
message_id = "1234567890123456789"
r = requests.delete(f"{WEBHOOK_URL}/messages/{message_id}", timeout=10)
r.raise_for_status() # 204 No Content при успехе
Удаление одноразовое — отменить нельзя. Webhook удаляет сообщения и старше 14 дней (в отличие от bulk-delete через bot API).
JavaScript / Node.js
const WEBHOOK = process.env.WEBHOOK_URL;
// Отправка + capture id
async function send(payload) {
const res = await fetch(`${WEBHOOK}?wait=true`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`Send failed: ${res.status}`);
return (await res.json()).id;
}
// Edit
async function edit(messageId, payload) {
const res = await fetch(`${WEBHOOK}/messages/${messageId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`Edit failed: ${res.status}`);
}
// Delete
async function del(messageId) {
const res = await fetch(`${WEBHOOK}/messages/${messageId}`, { method: 'DELETE' });
if (!res.ok) throw new Error(`Delete failed: ${res.status}`);
}
// Использование
const id = await send({ content: 'Работаем…' });
await new Promise(r => setTimeout(r, 5000));
await edit(id, { content: 'Готово за 5с' });
curl
WEBHOOK="https://discord.com/api/webhooks/ID/TOKEN"
# Отправка с capture ID
MID=$(curl -s -H 'Content-Type: application/json' \
-d '{"content":"Initial"}' \
"$WEBHOOK?wait=true" | jq -r '.id')
# Edit
curl -X PATCH \
-H 'Content-Type: application/json' \
-d '{"content":"Updated"}' \
"$WEBHOOK/messages/$MID"
# Delete
curl -X DELETE "$WEBHOOK/messages/$MID"
jq упрощает парсинг ответа. Без него: python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])'.
Редактирование вложений
Не очевидно. Два случая:
Случай 1: убрать все вложения. В PATCH body — attachments: [].
Случай 2: добавить новый файл или заменить. multipart/form-data (как при загрузке), а в payload_json — массив attachments с тем, что оставить:
# Оставить существующее attachment id=123, добавить новый файл
files = {"files[0]": ("new.png", open("new.png", "rb"), "image/png")}
payload_json = '{"attachments":[{"id":"123"},{"id":"0"}]}'
requests.patch(
f"{WEBHOOK_URL}/messages/{mid}",
data={"payload_json": payload_json},
files=files,
)
id нового файла = индекс в files[N] (т. е. files[0] → id: "0").
Треды
Если отправляли в тред (?thread_id={tid}), ?thread_id= нужно и на edit/delete:
requests.patch(
f"{WEBHOOK_URL}/messages/{mid}?thread_id={tid}",
json={"content": "Обновлённое сообщение треда"},
)
Иначе Discord не находит сообщение и возвращает 404 Unknown Message.
Частые ошибки
| Статус | Причина |
|---|---|
| 404 Unknown Message | Неверный message ID, неверный webhook, пропущен ?thread_id= |
| 401 Invalid Webhook Token | Webhook пересоздан или отозван |
| 403 Cannot edit message authored by another user | Пытаетесь редактировать чужое сообщение |
| 400 Cannot send empty message | После edit должно остаться хотя бы одно: content, embeds, attachments, components |
Сохранение message ID
Для долгоживущих сервисов (статус-страницы, дашборды) сохраняйте ID в БД или файл:
from pathlib import Path
import json, requests
STATE = Path("/var/lib/myapp/discord_state.json")
def get_or_create_status_message():
if STATE.exists():
data = json.loads(STATE.read_text())
return data["message_id"]
r = requests.post(WEBHOOK_URL + "?wait=true", json={"content": "Status: starting"})
mid = r.json()["id"]
STATE.write_text(json.dumps({"message_id": mid}))
return mid
Теперь можно PATCH’ить то же сообщение между перезапусками процесса.
Попробуйте в конструкторе
Конструктор Discord Webhook даёт копируемый message ID после каждой отправки и поддерживает edit/delete из UI — удобно для прототипирования update-флоу до автоматизации.
Дополнительно — scheduled messages и Python.
Источники
- Discord docs: Edit Webhook Message
- Discord docs: Delete Webhook Message
Попробуйте в нашем инструменте
Открыть конструктор Discord Webhook