Retry и DLQ в n8n: безопасные HTTP-запросы без потерь и дублей ¶
Обновлено: 2026-05-30
Импортируйте workflow, замените credentials и прогоните тестовый payload до включения production.
Проблема: внешний API может вернуть 429, 502 или timeout именно в момент, когда заявка уже принята бизнесом. Если n8n просто падает на HTTP Request, событие теряется или вручную перезапускается с риском дубля.
Решение: строим retry/DLQ-слой в n8n: отделяем временные ошибки от постоянных, добавляем backoff, сохраняем payload и idempotency key, отправляем alert владельцу и даём команде безопасный способ переиграть событие.
Проблема: почему HTTP Request в n8n теряет события при 429 и 5xx ¶
Типичная интеграция выглядит безобидно: Webhook принимает заявку, HTTP Request отправляет её в CRM или склад, а дальше workflow завершается. Но production живёт иначе: API ограничивает частоту запросов, прокси рвёт соединение, токен истекает, а downstream-сервис отвечает 502 после того, как часть операции уже выполнена.
Без retry и DLQ команда не понимает, какие события действительно обработаны, какие можно повторить, а какие нужно исправлять вручную. Эта статья решает конкретную боль интеграторов: как сделать HTTP Request в n8n устойчивым к временным сбоям и при этом не создать дубли при повторной отправке.
Архитектура workflow retry/DLQ для HTTP-интеграции ¶
| Нода | Роль | Что проверить |
|---|---|---|
| Webhook or Trigger | получает бизнес-событие | event_id, source, payload, дедупликационный ключ |
| Prepare API request | собирает endpoint, headers и body | нет секретов в тексте execution, timeout задан явно |
| HTTP Request | отправляет запрос во внешний API | response code, response body, retryable network errors |
| Classify response | разделяет success, retry и DLQ | 429/5xx/timeout не смешаны с 400/422 |
| Retry with backoff | повторяет временные ошибки | attempt_count, next_retry_at, max attempts |
| Dead-letter queue | сохраняет необработанное событие | payload, причина, владелец, ссылка на execution |
Главная SEO-и практическая разница между “retry” и “просто перезапустить workflow” — наличие контекста. В DLQ должен лежать исходный payload, статус API, число попыток, idempotency key и понятная причина, чтобы повтор был безопасным.
Контракт входных данных для API-запроса ¶
{
"event_id": "lead-10492",
"source": "landing-webhook",
"target": "crm-api",
"idempotency_key": "landing:lead-10492",
"customer": {
"name": "Мария",
"phone": "+7 (916) 555-12-34"
},
"request": {
"method": "POST",
"url": "https://api.example.ru/leads",
"body": {
"name": "Мария",
"phone": "+79165551234"
}
},
"attempt": 1
}
`event_id` и `idempotency_key` обязательны. Если внешний API поддерживает заголовок `Idempotency-Key`, передавайте туда тот же ключ. Если не поддерживает — храните ключ в Postgres и не повторяйте бизнес-действие вслепую.
Code Node: классификация ответа, retry и DLQ route ¶
const src = $json.body ?? $json;
const status = Number(src.http_status ?? src.statusCode ?? 0);
const attempt = Number(src.attempt ?? 1);
const maxAttempts = Number(src.max_attempts ?? 5);
const idempotencyKey = String(src.idempotency_key ?? src.event_id ?? '').trim();
if (!idempotencyKey) throw new Error('Missing idempotency_key for retry workflow');
const retryableStatus = [408, 409, 425, 429, 500, 502, 503, 504];
const isNetworkError = String(src.error ?? '').toLowerCase().includes('timeout');
const retryable = retryableStatus.includes(status) || isNetworkError;
const permanentFailure = status >= 400 && status < 500 && !retryable;
const delaySeconds = Math.min(900, Math.pow(2, attempt) * 15);
let route = 'success';
if (retryable && attempt < maxAttempts) route = 'retry';
if (retryable && attempt >= maxAttempts) route = 'dlq';
if (permanentFailure) route = 'dlq';
return [{
json: {
route,
idempotency_key: idempotencyKey,
attempt,
max_attempts: maxAttempts,
next_attempt: attempt + 1,
retry_after_seconds: delaySeconds,
status,
reason: src.error ?? src.response?.message ?? 'classified_by_retry_policy',
original_payload: src.original_payload ?? src
}
}];
Почему 400 и 500 нельзя обрабатывать одинаково
400/422 обычно означают ошибку данных и должны уходить в DLQ с описанием. 429/502/503 чаще временные: их можно повторять с backoff, но только с idempotency key и лимитом попыток.
Готовый workflow JSON: скачать и импортировать ¶
Скачать готовый workflow JSON Скачать тестовый payload
{
"name": "Nodbot - HTTP Request retry and DLQ policy",
"nodes": [
{
"name": "Webhook input",
"type": "n8n-nodes-base.webhook",
"purpose": "Принять событие и idempotency key"
},
{
"name": "Prepare HTTP request",
"type": "n8n-nodes-base.code",
"purpose": "Собрать endpoint, headers, body и timeout"
},
{
"name": "Send HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"purpose": "Отправить запрос во внешний API"
},
{
"name": "Classify API response",
"type": "n8n-nodes-base.code",
"purpose": "Разделить success/retry/DLQ"
},
{
"name": "Retry gate",
"type": "n8n-nodes-base.if",
"purpose": "Проверить лимит попыток и статус"
},
{
"name": "Write DLQ record",
"type": "n8n-nodes-base.postgres",
"purpose": "Сохранить payload, reason и execution URL"
},
{
"name": "Respond",
"type": "n8n-nodes-base.respondToWebhook",
"purpose": "Вернуть безопасный результат"
}
],
"connections": "Webhook input → Prepare HTTP request → Send HTTP Request → Classify API response → Retry gate → Write DLQ record → Respond"
}
Пошаговая настройка retry, backoff и DLQ в n8n ¶
- Импортируйте workflow и замените endpoint внешнего API.
- Добавьте idempotency key в payload и, если API поддерживает, в header `Idempotency-Key`.
- Создайте таблицу DLQ с полями idempotency_key, payload, reason, attempt, status.
- Настройте retry policy: 429/5xx/timeout повторять, 400/422 отправлять в DLQ.
- Добавьте Telegram/Slack alert только после записи DLQ, чтобы не потерять контекст.
Тесты перед production и проверка повторов ¶
curl -X POST "https://YOUR-N8N-DOMAIN/webhook/retry-dlq-http-request" \
-H "Content-Type: application/json" \
--data @retry-dlq-http-request-payload.json
- Сымитируйте 200 OK и проверьте, что запись не попадает в DLQ.
- Сымитируйте 429 и убедитесь, что route = retry, а delay растёт.
- Сымитируйте 422 и проверьте route = dlq без повторов.
- Запустите один и тот же event_id дважды и проверьте idempotency.
- Отключите внешний API и убедитесь, что после max_attempts событие попадает в DLQ.
Production-риски retry и dead-letter queue ¶
- Retry без idempotency. Повтор может создать вторую сделку, списание или заявку.
- Бесконечные повторы. Workflow забивает очередь и маскирует настоящую ошибку данных.
- DLQ без payload. Нельзя безопасно восстановить событие после инцидента.
- Alert до записи DLQ. Уведомление пришло, но контекст потерян при сбое.
- Все ошибки считаются временными. 400/422 будут повторяться зря и увеличивать нагрузку.
Полезные ссылки и смежные workflow ¶
См. также webhook idempotency to Postgres, Error Workflow → Telegram alert, retry/DLQ pattern и ЮKassa → CRM с idempotency. Официальные документы: n8n HTTP Request node, n8n error handling и HTTP status codes.
Критерии готовности ¶
- Retryable-статусы отделены от ошибок данных.
- Каждый повтор использует idempotency key.
- DLQ хранит исходный payload, reason, status и attempt_count.
- Есть лимит попыток и понятный backoff.
- Команда знает, как безопасно переиграть DLQ-событие.
Nodbot настроит retry/DLQ-слой в n8n: классификацию ошибок, idempotency, журнал восстановления, alert и безопасные повторы.
Настроить надежный HTTP workflow