> ## Documentation Index
> Fetch the complete documentation index at: https://docs.riggery.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Send email

> Отправить одно письмо через ваш почтовый сервер.

Отправляет одно письмо. Хост, логин и **От** остаются на узле. Секреты — `{{secret:mailbox-password}}`. Пароль не уходит в модель и не попадает в результат.

Ящик это не читает. Для чтения — [IMAP fetch](/ru/graph/tools/imap-fetch).

<h2 id="input">
  Вход
</h2>

Вкладка инспектора **Настройки**. Правила пустого поля на графе: [Предыдущие узлы](/ru/graph/previous-nodes). **Вставить значение** есть у **Хоста**, **Пользователя**, **Пароля**, **От**, **Кому**, **Темы**, **Текста**, **HTML** и **Сообщения**.

У инструмента на агенте **Кому**, **Тема**, **Текст** и **HTML** могут быть **Решает агент** или **Фиксировано**. **Хост**, **Порт**, **Шифрование**, **Пользователь**, **Пароль**, **От** и **Сообщение** остаются на узле.

| Поле | Обязательно | Пустое | Заметки |
| - | - | - | - |
| **Хост** | Да | Ошибка | Сервер исходящей почты, например `smtp.example.com`. Частный адрес отклоняется. |
| **Порт** | Да | По умолчанию: `587` | |
| **Шифрование** | Да | По умолчанию: **STARTTLS** | **STARTTLS** или **TLS**. Без шифрования отправить нельзя. |
| **Пользователь** | Да | Ошибка | |
| **Пароль** | Да | Ошибка | Подсказка секрета. Не кладите настоящий пароль в граф. |
| **От** | Да | Ошибка | Один ящик, например `news@example.com`. |
| **Кому** | Если не задано **Сообщение** | Ошибка | Один ящик. Второй адрес или имя с адресом роняют шаг. |
| **Тема** | Если **Сообщение** её не даёт | Ошибка | |
| **Текст** | Нужен **Текст** или **HTML** | — | Обычный текст. |
| **HTML** | Нужен **Текст** или **HTML** | — | HTML-тело. |
| **Сообщение** | Если не задано **Кому** | — | JSON предыдущего узла. Ключи `to`, `subject`, `text` и `html`. Заполненное поле на этом узле заменяет тот же ключ. |

Перевод строки в **Кому**, **От** и **Теме** убирается. Если после этого ящиков больше одного, шаг падает.

<h2 id="output">
  Выход
</h2>

На следующем узле **Предыдущие узлы** показывает **`Send email`: Result** (`{{Send email.text}}`, пока имя уникально).

| Выход | В меню | Тип | Следующий узел получит |
| - | - | - | - |
| Result | Да | JSON-текст | `{ "ok": true, "to": "person@example.com", "messageId": "<id@example.com>", "error": null }` |

Отказ адреса не роняет запуск:

```json theme={null}
{ "ok": false, "to": "person@example.com", "messageId": null, "error": "Recipient rejected (550)" }
```

Нет хоста, заблокированный хост, сбой TLS или неверный логин роняют запуск. Тела и пароля в результате нет.

<h2 id="many">
  Много адресов
</h2>

Один запуск отправляет одну страницу записей. Страницу ограничивают [Read records](/ru/graph/tools/state-query) и [Loop](/ru/graph/loop). Следующий запуск стартует триггер [Cron](/ru/graph/triggers/cron). Список загрузите во вкладке [State](/ru/instance/state). Если файл не помещается целиком, второй импорт идёт в ту же коллекцию. Адрес — колонка. Ключ записи не может содержать `@`.

Внутри Loop, по порядку. Первый узел Code назовите **Prepare**. Его `input` — текущая строка (`input.data` — ваши колонки).

```javascript theme={null}
const row = input.data;
return {
  collection: "recipients",
  where: [{ path: "email", eq: row.email }],
  set: [{ path: "status", value: "sending" }],
  to: row.email,
  subject: "Hello",
  text: "Hello " + row.name,
};
```

`email`, `status` и `name` — колонки вашего файла. **Update records** читает этот JSON. **Collection** на этом узле можно оставить пустым, если `collection` уже в JSON. См. [Update records](/ru/graph/tools/state-update).

Дальше **Send email**. **Сообщение** — `{{Prepare.text}}`. **Хост**, **Пользователь**, **Пароль** и **От** — через `{{secret:Name}}`.

Следующий Code назовите **Finish**. Его `input` — Result выше.

```javascript theme={null}
return {
  collection: "recipients",
  where: [{ path: "email", eq: input.to }],
  set: [{ path: "status", value: input.ok ? "sent" : "error" }],
};
```

Второй **Update records** читает этот JSON.

Снаружи Loop узел **Read records** фильтрует статус, который вы ещё не отметили, а **Limit** равен `100`. Строки со статусом `sending` не попадают в следующий запуск, в том числе если запуск остановился после того, как сервер принял письмо, и до **Finish**.

<h2 id="limits">
  Лимиты
</h2>

Один адресат. Без вложений. **Тема** до 998 символов. **Текст** и **HTML** до 256000 символов каждый. Более длинное тело роняет шаг.
