Пошаговые инструкции Для начинающих 13 мин

Как вернуть файл из Webhook в n8n: Respond to Webhook

Соберите JSON-файл и верните его браузеру как вложение. 12 шагов, готовый workflow, заголовки скачивания и проверка Test URL.

n8n webhook Respond to Webhook

Webhook может не только принять заявку, но и отдать файл в ответ на запрос. Сделаем учебную ссылку, которая собирает JSON-каталог и возвращает его как вложение. Проверим не только HTTP 200, но и заголовки, содержимое файла и одноразовый тестовый URL.

Результат инструкции

Локальный GET-запрос запустит workflow. Три вымышленные карточки товаров превратятся в один catalog-demo.json, после проверки байтов Respond to Webhook отправит файл клиенту. В ответе будут Content-Type: application/json; charset=utf-8 и Content-Disposition: attachment; filename="catalog-demo.json".

Используем Test URL и не публикуем workflow. Это учебная выгрузка с фиксированными данными: она не читает вашу базу и не зависит от параметров запроса. Нейросеть, внешние API и ключи не нужны. Производственный сервис скачивания с авторизацией и доступом к настоящим документам здесь не создаём.

Что понадобится

  • Работающий локальный n8n и доступ к новому workflow. Проверено на n8n 2.42.4, Webhook 2.1, Convert to File 1.1 и Respond to Webhook 1.4.
  • Code на JavaScript для учебных данных и проверок. Сам файл создаёт штатный Convert to File, отправляет Respond to Webhook.
  • Браузер или curl для GET-запроса. Пример команды использует localhost:5679; подставьте фактический Test URL своего экземпляра.

Установить n8n локально через Docker

Шаг 1. Импортируйте готовую схему

Скачать n8n-webhook-file.json

Создайте отдельный workflow, выберите Import from File и загрузите JSON. В схеме шесть узлов:

text
Download Request → Sample Rows → Check Rows → Make JSON → Check File → Send File

Download Request — Webhook, Make JSON — Convert to File, Send File — Respond to Webhook. Остальные три узла — Code. В файле нет credentials и ID чужого workflow, состояние неактивное. Не нажимайте Publish или Activate: для примера достаточно тестового ожидания.

Шаг 2. Настройте Webhook на ответ отдельным узлом

В Download Request задайте следующие параметры:

Поле Значение Зачем
HTTP Method GET Скачивание обычным запросом
Path ezgpt-file-download Путь учебного endpoint
Respond Using Respond to Webhook node Ответ сформирует Send File
Authentication None Только локальный тест с вымышленными данными

None здесь допустим только в описанном локальном учебном сценарии. Не выносите такой endpoint с настоящими данными в интернет. Имя пути не является паролем. Не добавляйте запрос SQL, имя файла или путь к документу из query-параметра: пример возвращает строго заданный каталог.

В Webhook переключитесь на Test URL и скопируйте адрес. Пока слушатель не запущен, запрос к нему может возвращать 404. Production URL — другой адрес, он не должен работать у нашего неопубликованного workflow.

Шаг 3. Подготовьте карточки товаров

В Sample Rows выберите Mode: Run Once for All Items, Language: JavaScript. Вставьте:

javascript
const rows = [
  {"id":"001","title":"Блокнот","quantity":2,"active":true,"note":null,"tags":["офис","бумага"]},
  {"id":"002","title":"Чай «Утро»","quantity":0,"active":false,"note":"Проверить остаток","tags":[]},
  {"id":"003","title":"Папка A4","quantity":5,"active":true,"note":"Строка 1\nСтрока 2","tags":["архив"]}
];
return rows.map(json => ({ json }));

Источник выдаёт три отдельных items. ID — строки с ведущими нулями, quantity — числа, active — boolean, note — строка либо null, tags — массив строк. Вторая карточка специально содержит 0, false и пустой массив. Данные запроса из Webhook в каталог не копируются.

Шаг 4. Проверьте записи до создания файла

В Check Rows установите Run Once for All Items, JavaScript и вставьте:

javascript
const items = $input.all();
if (items.length < 1 || items.length > 20) throw new Error('row_count_invalid');
const ids = new Set();
for (const { json: row } of items) {
  if (Object.keys(row).sort().join('|') !== 'active|id|note|quantity|tags|title') {
    throw new Error('row_fields_invalid');
  }
  if (typeof row.id !== 'string' || !/^\d{3}$/.test(row.id) || ids.has(row.id)) {
    throw new Error('id_invalid_or_duplicate');
  }
  if (typeof row.title !== 'string' || !row.title.trim() || row.title.length > 100) {
    throw new Error('title_invalid');
  }
  if (!Number.isSafeInteger(row.quantity) || row.quantity < 0) throw new Error('quantity_invalid');
  if (typeof row.active !== 'boolean') throw new Error('active_invalid');
  if (row.note !== null && (typeof row.note !== 'string' || row.note.length > 200)) {
    throw new Error('note_invalid');
  }
  if (!Array.isArray(row.tags) || row.tags.length > 3 || row.tags.some(t => typeof t !== 'string' || !t.trim() || t.length > 30)) {
    throw new Error('tags_invalid');
  }
  ids.add(row.id);
}
return items.map(item => ({ json: item.json }));

Проверка допускает 1–20 карточек с точными полями, уникальными строковыми ID и корректными типами. Неизвестные поля, строковое количество, повтор ID и отсутствующий note остановят выполнение. Это учебный договор, а не ограничение JSON или Webhook.

В Settings узлов оставьте On Error: Stop Workflow. Continue On Fail, Retry On Fail, Always Output Data и Execute Once не включайте. Ошибку подготовки файла нельзя превращать в успешную выгрузку с пустым объектом.

Шаг 5. Создайте один JSON-файл

В Make JSON выберите Operation: Convert to JSON, Mode: All Items to One File, Put Output File in Field: export. В Options задайте File Name: catalog-demo.json, Format: On, Encoding: utf8, Add Byte Order Mark (BOM): Off.

На выходе этого узла нужен один item с одним binary-полем export. В нём лежит общий JSON-массив из трёх карточек. JSON самого item может быть пустым — файл находится на вкладке Binary.

Не выбирайте Each Item to Separate File. Respond to Webhook не отправляет несколько отдельных файлов по одному HTTP-запросу таким способом. Если нужен набор файлов, сначала спроектируйте общий архив; наш Check File отклоняет несколько входных items.

Подробно о создании JSON-файла через Convert to File

Шаг 6. Проверьте байты перед ответом

В Check File установите Run Once for All Items, JavaScript и вставьте:

javascript
const items = $input.all();
if (items.length !== 1) throw new Error('one_file_required');
const item = items[0];
if (Object.keys(item.binary ?? {}).join('|') !== 'export') throw new Error('export_binary_required');
if (item.binary.export.fileName !== 'catalog-demo.json') throw new Error('file_name_invalid');
const bytes = await this.helpers.getBinaryDataBuffer(0, 'export');
if (bytes.length < 1 || bytes.length > 16384) throw new Error('file_size_invalid');
if (bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) throw new Error('bom_not_expected');
const text = bytes.toString('utf8');
if (!Buffer.from(text, 'utf8').equals(bytes)) throw new Error('utf8_invalid');
const expected = $('Check Rows').all().map(i => i.json);
if (text !== JSON.stringify(expected, null, 2)) throw new Error('file_content_mismatch');
return [{ json: {}, binary: item.binary }];

Код читает реальные байты binary через helper n8n и сравнивает их с форматированным JSON из Check Rows. Проверяет один файл, имя, размер до 16384 байт, UTF-8 без BOM и точное содержимое. Затем передаёт исходный binary.export дальше.

Ограничение размера действует после создания файла. Это не лимит трафика входящего Webhook, не антивирус и не проверка фактов в карточках. Если вы меняете форматирование или кодировку, нужно одновременно пересмотреть договор проверки, а не просто удалить Check File.

Шаг 7. Настройте отправку Binary File

В Send File задайте:

Поле Значение Что отправляется
Respond With Binary File Байты файла, не JSON item
Response Data Source Specify Myself Поле выбираем явно
Input Field Name export То же имя, что в Make JSON
Options → Response Code 200 Код успешного ответа

Input Field Name — имя binary-поля export, не имя catalog-demo.json. Если написать data, Send File не найдёт нужный файл. Не выбирайте First Incoming Item: после Check File JSON пустой, поэтому вместо каталога можно получить {} с успешным статусом.

Один HTTP-запрос имеет один ответ. Не ставьте несколько Respond to Webhook в последовательности в надежде отправить каждый файл отдельно: этой схемой так не делают. Мы заранее собираем все карточки в один документ.

Шаг 8. Добавьте заголовки скачивания

В Send File откройте Options → Response Headers и добавьте три пары Name / Value:

  • Content-Type: application/json; charset=utf-8
  • Content-Disposition: attachment; filename="catalog-demo.json"
  • Cache-Control: no-store

Content-Type описывает формат тела. Content-Disposition с attachment предлагает клиенту сохранить ответ как файл и задаёт имя. Без него тот же JSON может открыться как текст; HTTP 200 и правильные байты при этом ещё не доказывают настройку скачивания.

Имя файла здесь фиксированное и безопасное для учебного примера. Не вставляйте в заголовок произвольную строку из запроса. Cache-Control: no-store адресован HTTP-клиентам и промежуточным кэшам; он не отключает хранение executions и binary внутри n8n.

Шаг 9. Запустите ожидание тестового запроса

Сохраните workflow и нажмите Execute workflow, чтобы Download Request ждал событие. Можно использовать ожидание тестового события в самом Webhook. Убедитесь, что выбран именно Test URL и редактор показывает ожидание запроса.

Не запускайте Send File отдельно как обычный ручной узел: без входящего HTTP-запроса ему некому отправить ответ. После тестового запроса слушатель нужно запускать заново перед следующей проверкой.

Пример Test URL для локального экземпляра на порту 5679: http://localhost:5679/webhook-test/ezgpt-file-download. Если в редакторе другой порт, домен или протокол, используйте адрес из редактора. Внешний компьютер не сможет открыть ваш localhost: он указывает на него самого.

Шаг 10. Сделайте GET и сохраните ответ

Откройте скопированный Test URL в браузере: ожидается скачивание catalog-demo.json. Для проверки заголовков через Windows PowerShell выполните в отдельной учебной папке, пока слушатель ждёт запрос:

powershell
curl.exe --fail --show-error --max-time 15 --dump-header webhook-headers.txt --output catalog-demo.json "http://localhost:5679/webhook-test/ezgpt-file-download"

Команда создаёт webhook-headers.txt и catalog-demo.json в текущей папке и может перезаписать одноимённые файлы. Используйте пустую учебную папку, чтобы не принять старую копию за новый ответ. Если первым запросом уже открыли ссылку в браузере, перед curl снова включите ожидание.

Не отправляйте POST: наш Webhook настроен на GET. Не подставляйте Production URL вместо тестового адреса. Добавлять --location и отключать проверку TLS в этом примере не нужно.

Шаг 11. Сверьте заголовки и содержимое

В webhook-headers.txt ожидается успешный HTTP-статус и следующие значения заголовков. Регистр их имён может отличаться; это не полный список ответа:

text
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Disposition: attachment; filename="catalog-demo.json"
Cache-Control: no-store

Откройте catalog-demo.json в редакторе с UTF-8. В исходном примере файл занимает 521 байт, без BOM и без дополнительного переноса в конце. Он должен содержать:

json
[
  {
    "id": "001",
    "title": "Блокнот",
    "quantity": 2,
    "active": true,
    "note": null,
    "tags": [
      "офис",
      "бумага"
    ]
  },
  {
    "id": "002",
    "title": "Чай «Утро»",
    "quantity": 0,
    "active": false,
    "note": "Проверить остаток",
    "tags": []
  },
  {
    "id": "003",
    "title": "Папка A4",
    "quantity": 5,
    "active": true,
    "note": "Строка 1\nСтрока 2",
    "tags": [
      "архив"
    ]
  }
]

Скачать эталон n8n-webhook-file-demo.json

Эталон имеет другое имя, но те же байты. Если вы изменили Sample Rows, размер и содержимое закономерно изменятся. Восстановите источник для точной сверки исходного примера. Проверяйте и тело, и заголовки: пустой объект {} с кодом 200 — не наш файл.

В редакторе откройте выполненный Send File. Binary export остаётся частью данных выполнения. Результат на выходе узла в редакторе и HTTP-тело, полученное браузером, — разные представления; файл нужно проверять на стороне клиента.

Шаг 12. Проверьте повтор, неправильное поле и ошибочные данные

Сразу повторите запрос к Test URL, не включая новое ожидание. В проверенном экземпляре ожидается 404: тестовый слушатель уже использован. Снова нажмите Execute workflow, выполните GET и убедитесь, что скачивание повторяется. Это свойство тестового режима, не кэш файла.

В Send File временно измените Input Field Name на data. Запустите новое ожидание и GET: выполнение должно завершиться ошибкой, а клиент не должен получить эталонный файл. Верните export.

Замените quantity второй карточки на строку "0", снова включите ожидание и сделайте GET. Check Rows должен остановиться с quantity_invalid; в проверенном тестовом режиме ошибка до отправки ответа вернула HTTP 500, а не файл каталога. Верните число 0 и повторите основной запуск.

После восстановления должны одновременно выполняться четыре условия: запрос GET, все шесть узлов отработали, клиент получил attachment с именем catalog-demo.json, а содержимое равно трём исходным карточкам. Один зелёный узел или статус 200 не заменяет эту проверку.

Если файл не скачивается

Симптом Что проверить Правильное состояние
404 у Test URL Ожидание и метод Активный тестовый слушатель, GET
404 у Production URL Публикацию workflow В статье production не включаем
HTTP 500, binary не найден Input Field Name в Send File export
HTTP 200 и пустой объект Respond With Binary File
JSON открыт текстом Content-Disposition attachment с фиксированным именем

Что не подключать к этой версии

Не публикуйте учебный endpoint с персональными данными, финансовыми документами или произвольным выбором файла по query. Перед production нужны авторизация, проверка прав на конкретную выгрузку, ограничения запросов и размера, а также правила хранения и журналирования. Фиксированный путь и no-store не заменяют эти меры.

Мы не обещаем мгновенную выгрузку больших баз: клиент ждёт формирования ответа, а прокси и сервер имеют свои тайм-ауты. Для долгой подготовки файла нужен отдельный процесс с проверкой готовности, не бесконечное ожидание HTTP. Публикацию, proxy, Cloud и большие выгрузки здесь не тестировали.

Принять JSON и вернуть обычный ответ через Webhook

Скачать файл в другой workflow через HTTP Request

Что проверено редакцией

17 октября 2026 года схема проверена на n8n 2.42.4 в отдельном Docker-экземпляре с binary-режимом filesystem. Выполнены реальные GET-запросы к тестовым Webhook после регистрации слушателя через API. Все 19 проверок прошли: основной точный скачиваемый workflow, одна запись, обратный порядок, Unicode, нули, false и null; дубли, числовой ID, строковое количество, пропущенное поле, отдельные файлы вместо общего, неверное binary-поле и BOM; JSON-ответ вместо Binary File и отсутствие attachment. Проверены 404 без слушателя, неверный POST, одноразовый Test URL, неактивный Production URL и ошибка 500 при отсутствии узла ответа в выбранном режиме. У успешных файлов сверены точные байты, Content-Type, Content-Disposition и Cache-Control. Для ошибок до ответа подтверждён 500 без эталонного файла; успешный JSON-ответ без файла разобран отдельно. Проверены настройки по официальной документации и коду узлов. Учебный контейнер был доступен только на loopback и остановлен, существующие workflows не менялись. Клики редактора, ручное скачивание браузером, production-публикация, авторизация, Cloud, прокси и большие реальные выгрузки не тестировались. Обложка — редакционная AI-иллюстрация, не снимок интерфейса.

Частые вопросы

Почему Test URL сработал только один раз?

В проверенном тестовом режиме слушатель снимается после запроса. Перед следующим GET снова включите ожидание. Production URL — отдельный режим, не замена кнопке теста.

Почему вместо файла пришёл пустой объект?

Проверьте Respond With: Binary File. First Incoming Item отправит JSON входного item, а после Check File он пуст. Сам файл лежит в binary.export.

Зачем Content-Disposition, если JSON уже сформирован?

Тело ответа определяет содержимое, а attachment и filename подсказывают клиенту скачать его с заданным именем. Без заголовка клиент может показать JSON как текст.

Можно вернуть PDF или ZIP?

Respond to Webhook умеет отправлять binary-файл, но создание и проверка PDF или ZIP требуют другого workflow. Эта инструкция проверена только для маленького JSON-каталога.

no-store запрещает n8n хранить файл?

Нет. Этот HTTP-заголовок не управляет сохранением executions и binary внутри n8n. Сроки хранения и доступ к данным выполнения настраиваются отдельно.

Дальше по теме

Похожие материалы