API FormToMail предназначен для отправки форм с вашего сайта на электронную почту и Telegram.
Базовый URL: https://api.formtomail.ru. Авторизация — заголовком Authorization: Bearer ВАШ_КЛЮЧ (ключ публичный, берётся в кабинете). Бесплатный тариф принимает запросы с любых ресурсов; на платном включается CORS-политика и запросы идут только с указанного домена.
Метод: POST https://api.formtomail.ru/send
const response = await fetch("https://api.formtomail.ru/send", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ title: "Новая заявка", body: { "Имя": "значение поля", "Телефон": "значение поля" } }) }); const body = await response.json();
{ "statusCode": 200, "message": "Успешно отправлено" }
{ "statusCode": 400, "message": "Обслуживание сайта завершено" }
{ "statusCode": 403, "message": "Срок обслуживания истек" }
Состав заказа отправляйте одним полем Корзина.
Простой способ таблица, но в Telegram теги таблицы вырезаются и всё схлопывается в строку — поэтому для обоих каналов сразу используйте вариант 2.
const cart = [ { name: "Футболка, M", qty: 2, price: 1490 }, { name: "Кроссовки, 42", qty: 1, price: 8990 } ]; const rows = cart.map((item) => "<tr><td>" + item.name + "</td><td>" + item.qty + "</td><td>" + item.price + "</td><td>" + (item.qty * item.price) + "</td></tr>" ).join(""); const total = cart.reduce((s, i) => s + i.qty * i.price, 0); const cartHtml = "<table border=\"1\" cellpadding=\"8\" cellspacing=\"0\"><tr><th>Товар</th><th>Кол-во</th><th>Цена</th><th>Сумма</th></tr>" + rows + "<tr><td colspan=\"3\">Итого</td><td>" + total + "</td></tr></table>"; const response = await fetch("https://api.formtomail.ru/send", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ title: "Новый заказ", body: { "Имя": "Иван Иванов", "Телефон": "+7 900 000-00-00", "Корзина": cartHtml } }) }); const body = await response.json();
Пример результата — так корзина придёт на почту:
| Товар | Кол-во | Цена | Сумма |
|---|---|---|---|
| Футболка, M | 2 | 1490 | 2980 |
| Кроссовки, 42 | 1 | 8990 | 8990 |
| Итого | 11970 | ||
Текст внутри <pre>. Так таблица одинаково выглядит везде. Цифры прижаты вправо пробелами, длинные названия переносятся по словам.
const cart = [ { name: "Футболка, M", qty: 2, price: 1490 }, { name: "Кроссовки беговые мужские, черные, 42", qty: 1, price: 8990 } ]; const row = (n, q, p, s) => n.padEnd(16) + " | " + q.padStart(3) + " | " + p.padStart(5) + " | " + s.padStart(5); const sep = "-".repeat(38); // длинные названия — перенос по словам const wrap = (text) => { const out = []; let line = ""; for (const word of String(text).split(" ")) { const next = line ? line + " " + word : word; if (next.length <= 16) line = next; else { if (line) out.push(line); line = word; } } if (line) out.push(line); return out; }; const lines = [row("Товар", "Кол", "Цена", "Сумма"), sep]; let total = 0; for (const item of cart) { const sum = item.qty * item.price; total += sum; const parts = wrap(item.name); parts.forEach((part, i) => { lines.push(i === 0 ? row(part, String(item.qty), String(item.price), String(sum)) : row(part, "", "", "")); }); } lines.push(sep, row("Итого", "", "", String(total))); const cartHtml = "<pre>" + lines.join("\n") + "</pre>"; const response = await fetch("https://api.formtomail.ru/send", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ title: "Новый заказ", body: { "Имя": "Иван Иванов", "Телефон": "+7 900 000-00-00", "Корзина": cartHtml } }) }); const body = await response.json();
Пример результата — так корзина придёт на почту и в Telegram:
Товар | Кол | Цена | Сумма -------------------------------------- Футболка, M | 2 | 1490 | 2980 Кроссовки | 1 | 8990 | 8990 беговые мужские, | | | черные, 42 | | | -------------------------------------- Итого | | | 11970
Добавьте в форму скрытое поле-ловушку и отправляйте его значение отдельным полем honeypot в JSON. Люди его не видят и оставляют пустым, а боты заполняют. Если поле непустое — сервер вернёт успех, но письмо не отправит. Проверок на клиенте делать не нужно.
<form id="form"> <input id="name" placeholder="Иван Иванов"> <input type="text" name="company_website" autocomplete="off" tabindex="-1" aria-hidden="true" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;"> <button>Отправить</button> </form> <script> document.getElementById("form").addEventListener("submit", async (event) => { event.preventDefault(); const response = await fetch("https://api.formtomail.ru/send", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ body: { "Имя": document.getElementById("name").value }, honeypot: document.querySelector('[name="company_website"]').value }) }); console.log(await response.json()); }); </script>
Метод: POST https://api.formtomail.ru/send
encodeURIComponent перед отправкойconst SUPPORTED_EXTENSIONS = ["jpg", "jpeg", "png", "webp", "bmp", "gif", "mov", "mp4", "pdf", "xls", "xlsx", "doc", "docx"];
const formData = new FormData(); formData.append("title", "Новая заявка"); formData.append("body", JSON.stringify({ "Имя": "значение поля", "Телефон": "значение поля" })); // Пример добавления файлов const SUPPORTED_EXTENSIONS = ["jpg", "jpeg", "png", "webp", "bmp", "gif", "mov", "mp4", "pdf", "xls", "xlsx", "doc", "docx"]; const filesInput = document.getElementById("files"); const files = Array.from(filesInput.files); for (const file of files) { const extension = file.name.split(".").pop()?.toLowerCase(); if (!extension || !SUPPORTED_EXTENSIONS.includes(extension)) { continue; // неподдерживаемый тип } formData.append("attachments", file, encodeURIComponent(file.name)); } const response = await fetch("https://api.formtomail.ru/send", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY" }, body: formData, }); const body = await response.json();
{ "statusCode": 200, "message": "Успешно отправлено" }
{ "statusCode": 400, "message": "Отправить можно не более 10 файлов" }
{ "statusCode": 403, "message": "Срок обслуживания истек" }
Добавьте в форму скрытое поле-ловушку и отправляйте его значение отдельным полем honeypot через FormData. Люди его не видят и оставляют пустым, а боты заполняют. Если поле непустое — сервер вернёт успех, но письмо не отправит. Проверок на клиенте делать не нужно.
<form id="form"> <input id="name" placeholder="Иван Иванов"> <input type="text" name="company_website" autocomplete="off" tabindex="-1" aria-hidden="true" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;"> <button>Отправить</button> </form> <script> document.getElementById("form").addEventListener("submit", async (event) => { event.preventDefault(); const formData = new FormData(); formData.append("body", JSON.stringify({ "Имя": document.getElementById("name").value })); formData.append("honeypot", document.querySelector('[name="company_website"]').value); const response = await fetch("https://api.formtomail.ru/send", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY" // Content-Type не указываем — браузер сам выставит multipart/form-data с boundary }, body: formData }); console.log(await response.json()); }); </script>
multipart/form-data с boundary.Webhook нужен, чтобы заявки сразу попадали в вашу систему: российские CRM (Битрикс24, amoCRM, retailCRM, Мегаплан), 1С и МойСклад, Google Sheets, свой backend или Telegram-бота — везде, где есть HTTP API, принимающий JSON. Не нужно забирать письма с почты и переносить данные вручную: форма с сайта за секунды оказывается в воронке продаж, таблице или чате.
Если оплачен тариф с хуком, каждая форма параллельно уходит JSON'ом на ваш URL: Authorization: Bearer <ваш токен>. JSON для хука вы готовите сами и передаёте полем webhook вместе с формой. Файлы в хук не входят — они идут только на почту и в Telegram.
// JSON для хука клиент собирает сам и передаёт полем webhook вместе с формой const webhook = { client_name: document.getElementById("name").value, phone: document.getElementById("phone").value, source: "landing" }; const response = await fetch("https://api.formtomail.ru/send", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ title: "Новая заявка", body: { "Имя": document.getElementById("name").value, "Телефон": document.getElementById("phone").value }, webhook: webhook }) });
200 за <2с и проверять Bearer.Примечание: если хук включён, а поле webhook не передано — на ваш API уходит объект формы как есть (title + body).