WhatsApp для сайта: заявки с формы — в WhatsApp менеджеру и клиенту
Заявка, которая до вечера лежит в почте, часто уже никому не нужна. Если отправлять её в WhatsApp, менеджер увидит её на телефоне сразу, а клиент получит подтверждение с вашего рабочего номера — и сможет ответить в тот же чат.
Разберём, как подключить WhatsApp API к сайту: схему, согласие клиента, формат телефона, защиту формы от ботов и готовые обработчики на PHP, Node.js и Python.
Схема: форма → ваш сервер → WhatsApp
- Посетитель заполняет форму: имя, телефон, вопрос, галочка согласия.
- Браузер отправляет форму на ваш сервер — PHP-скрипт на хостинге, Node.js или Python.
- Сервер проверяет данные и вызывает
POST /messages/sendдважды: менеджеру — текст заявки, клиенту — подтверждение. - Оба сообщения уходят с вашего номера, подключённого к wapi.kz. Ответ клиента придёт на тот же номер, на телефон менеджера.
Отправка с сайта в WhatsApp идёт только через сервер, не из браузера. Параметры запроса — в документации.
Менеджеру пишите на его личный WhatsApp — заявка придёт с обычным уведомлением. Если менеджеров несколько, ведите заявки в CRM — см. WhatsApp и CRM.
Сценарии
- Заявка с формы. Менеджеру: имя, телефон, вопрос, страница. Клиенту: «Здравствуйте, Айгерим! Получили вашу заявку, менеджер свяжется в рабочее время». Не обещайте в тексте сроков, которые не соблюдаете.
- Заказ в интернет-магазине. Клиенту: номер заказа, сумма, состав. Менеджеру: новый заказ и способ доставки. Для готовых платформ — страницы Tilda и WooCommerce, сценарии — в разделе для интернет-магазинов.
- Запись на услугу. Подтверждение сразу и напоминание накануне — см. напоминания о записи.
Кнопке «Написать в WhatsApp» со ссылкой wa.me API не нужен: там диалог начинает клиент. API нужен, когда пишет ваша система.
Согласие клиента
Добавьте в форму галочку «Согласен получать сообщения в WhatsApp по этой заявке». Она не должна стоять заранее. Подтверждение отправляйте, только если галочка отмечена, а сам факт согласия сохраняйте вместе с заявкой: время, страницу, текст галочки.
Имя и телефон — персональные данные. Как их собирать и где хранить, регулирует Закон РК «О персональных данных и их защите». Это не юридическая консультация, уточните у юриста; основные моменты — в гайде о хранении персональных данных в Казахстане.
Телефон: приводим к формату API
API ждёт номер цифрами с кодом страны: 77011234567. Посетители вводят «8 701 123 45 67», «+7 (701)…» или «701…». Номер с «8» в начале API отправит не туда, куда нужно, поэтому на сервере:
- оставьте только цифры;
- 11 цифр с «8» в начале — замените «8» на «7»;
- 10 цифр — допишите «7» спереди;
- всё, что короче 11 или длиннее 15 цифр, — ошибка, просите исправить.
Маска ввода в форме не заменяет проверку на сервере. Есть ли у номера WhatsApp, можно проверить через POST /contacts/check — до 200 проверок в сутки на номер.
Защита формы от спама
Боты заполняют формы случайными номерами, и без защиты ваш номер начнёт писать незнакомым людям: сгорит суточный лимит первых сообщений новым людям, вырастут ошибки доставки, упадёт индекс доверия. Если за час появится 40+ новых контактов, первые сообщения новым людям остановятся на 24 часа — и настоящие клиенты подтверждений не получат.
- Honeypot. Скрытое поле, например
website, которое человек не видит. Заполнено — это бот: отвечайте «ок» и ничего не отправляйте. - Лимит по IP. Например, не больше 3 заявок с одного адреса за 10 минут.
- Лимит по телефону. Одно подтверждение на номер в сутки, повторы — только менеджеру.
- Капча — если атака всё-таки идёт.
Учитывайте лимиты номера. На уровне «Хороший» — 150 первых сообщений новым людям в сутки, а в первые три дня после подключения — 20. Первые сообщения новым людям — не чаще раза в 5 секунд. Подробно — в разделе лимитов и в гайде как снизить риск блокировки.
Ключ — только на сервере
Никогда не вставляйте API-ключ в JavaScript страницы. Любой посетитель откроет инструменты разработчика, скопирует ключ и сможет писать с вашего номера.
Храните ключ в переменной окружения или в файле конфигурации вне публичной папки сайта. Для сайта выпустите отдельный ключ номера: он работает только с одним номером и не видит баланс. Если ключ утёк — отзовите его в карточке номера, остальные интеграции продолжат работать.
Обработчики на PHP, Node.js и Python
Форма отправляет поля name, phone, message, consent (галочка) и website (honeypot). Между двумя сообщениями — пауза из-за лимита 1 сообщение в секунду.
PHP: обычный хостинг
Для WhatsApp API на PHP хватает встроенного cURL, библиотеки не нужны. Нужен PHP 7.4 или новее.
<?php
// lead.php — заявка с формы → WhatsApp менеджеру и клиенту
const MANAGER_PHONE = "77010000001";
function wapi_send(string $to, string $text): bool {
$ch = curl_init("https://wapi.kz/api/v1/messages/send");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("WAPI_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode(["to" => $to, "text" => $text], JSON_UNESCAPED_UNICODE),
CURLOPT_TIMEOUT => 15,
]);
$data = json_decode((string) curl_exec($ch), true);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code !== 200) error_log("wapi: " . ($data["code"] ?? $code) . " — " . ($data["error"] ?? ""));
return $code === 200;
}
function normalize_phone(string $raw): ?string {
$d = preg_replace('/\D/', '', $raw);
if (strlen($d) === 11 && $d[0] === "8") $d = "7" . substr($d, 1);
if (strlen($d) === 10) $d = "7" . $d;
return (strlen($d) >= 11 && strlen($d) <= 15) ? $d : null;
}
if ($_SERVER["REQUEST_METHOD"] !== "POST") { http_response_code(405); exit; }
if (!empty($_POST["website"])) exit("ok"); // honeypot: бот
$file = sys_get_temp_dir() . "/lead_" . md5($_SERVER["REMOTE_ADDR"]);
$hits = json_decode(@file_get_contents($file) ?: "[]", true) ?: [];
$hits = array_filter($hits, fn($t) => $t > time() - 600);
if (count($hits) >= 3) { http_response_code(429); exit("Слишком много заявок, попробуйте позже"); }
$hits[] = time();
file_put_contents($file, json_encode(array_values($hits)));
$name = mb_substr(trim($_POST["name"] ?? ""), 0, 100);
$phone = normalize_phone($_POST["phone"] ?? "");
$msg = mb_substr(trim($_POST["message"] ?? ""), 0, 1000);
if ($name === "" || $phone === null) { http_response_code(400); exit("Проверьте имя и телефон"); }
wapi_send(MANAGER_PHONE, "Заявка с сайта\nИмя: $name\nТелефон: +$phone\nВопрос: $msg");
if (!empty($_POST["consent"])) {
sleep(2); // не чаще 1 сообщения в секунду
wapi_send($phone, "Здравствуйте, $name! Получили вашу заявку, менеджер свяжется в рабочее время.");
}
echo "ok";Node.js (Express)
import express from "express";
const app = express();
const hits = new Map();
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const normalizePhone = (raw) => {
let d = String(raw || "").replace(/\D/g, "");
if (d.length === 11 && d.startsWith("8")) d = "7" + d.slice(1);
if (d.length === 10) d = "7" + d;
return d.length >= 11 && d.length <= 15 ? d : null;
};
async function send(to, text) {
try {
const res = await fetch("https://wapi.kz/api/v1/messages/send", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.WAPI_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ to, text }),
});
if (!res.ok) console.error("wapi:", res.status, await res.text());
} catch (err) {
console.error("wapi:", err.message);
}
}
app.post("/lead", express.urlencoded({ extended: false }), async (req, res) => {
const { name = "", phone: raw, message = "", consent, website } = req.body;
if (website) return res.send("ok"); // honeypot
const recent = (hits.get(req.ip) || []).filter((t) => t > Date.now() - 600_000);
if (recent.length >= 3) return res.status(429).send("Слишком много заявок");
hits.set(req.ip, [...recent, Date.now()]);
const phone = normalizePhone(raw);
if (!name.trim() || !phone) return res.status(400).send("Проверьте имя и телефон");
res.send("ok"); // не держим посетителя
await send(process.env.MANAGER_PHONE, `Заявка с сайта\nИмя: ${name}\nТелефон: +${phone}\nВопрос: ${message}`);
if (consent) {
await sleep(2000);
await send(phone, `Здравствуйте, ${name}! Получили вашу заявку, менеджер свяжется в рабочее время.`);
}
});
app.listen(3000);За прокси включите app.set("trust proxy", 1), иначе req.ip — адрес прокси.
Python (Flask)
import os, re, time
import requests
from flask import Flask, request
app = Flask(__name__)
H = {"Authorization": f"Bearer {os.environ['WAPI_KEY']}"}
hits = {}
def normalize_phone(raw):
d = re.sub(r"\D", "", raw or "")
if len(d) == 11 and d.startswith("8"):
d = "7" + d[1:]
if len(d) == 10:
d = "7" + d
return d if 11 <= len(d) <= 15 else None
def send(to, text):
try:
r = requests.post("https://wapi.kz/api/v1/messages/send", headers=H,
json={"to": to, "text": text}, timeout=15)
if r.status_code != 200:
app.logger.error("wapi: %s %s", r.status_code, r.text[:300])
except requests.RequestException as e:
app.logger.error("wapi: %s", e)
@app.post("/lead")
def lead():
f = request.form
if f.get("website"):
return "ok" # honeypot
ip = request.remote_addr
recent = [t for t in hits.get(ip, []) if t > time.time() - 600]
if len(recent) >= 3:
return "Слишком много заявок", 429
hits[ip] = recent + [time.time()]
name, phone = f.get("name", "").strip(), normalize_phone(f.get("phone"))
if not name or not phone:
return "Проверьте имя и телефон", 400
send(os.environ["MANAGER_PHONE"], f"Заявка с сайта\nИмя: {name}\nТелефон: +{phone}\nВопрос: {f.get('message', '')}")
if f.get("consent"):
time.sleep(2)
send(phone, f"Здравствуйте, {name}! Получили вашу заявку, менеджер свяжется в рабочее время.")
return "ok"Две одновременные заявки могут получить rate_limited (429). При большом потоке складывайте заявки в очередь и отправляйте отдельным процессом.
Частые вопросы
Можно ли отправлять WhatsApp прямо из JavaScript на странице?
Нет. Ключ окажется в коде страницы, и любой сможет писать с вашего номера. Браузер отправляет форму на ваш сервер, а сервер — запрос в wapi.kz.
Подойдёт ли обычный хостинг на PHP?
Да, если на нём есть cURL и разрешены исходящие https-запросы. Пример выше работает без библиотек.
Что будет, если у клиента нет WhatsApp?
Сообщение не доставится, а ошибки доставки снижают индекс доверия номера. Если таких заявок много, проверяйте номер через POST /contacts/check до подтверждения. Менеджеру заявка уходит в любом случае.
Сколько стоят заявки с сайта в WhatsApp?
Номер — 9 900 ₸ в месяц, платы за сообщения и запросы нет. Ограничение — лимиты номера, а не деньги. Подробнее — на странице цен.
Подтверждение не пришло клиенту — что проверить?
Формат телефона, подключён ли номер в кабинете и код ошибки в логах сервера. Частые причины разобраны в гайде о решении проблем с API.
С чего начать?
Зарегистрируйтесь, подключите номер (около 5 минут), выпустите для сайта ключ номера и проверьте форму на своём телефоне.
Подключите WhatsApp к своим системам
9 900 ₸ в месяц за номер, без платы за сообщения. Сервер в Казахстане, оплата Kaspi или счётом на ТОО/ИП. Подключение — около 5 минут.