Формы сайтов — Tilda, WordPress, свой сайт — часто присылают заявку письмом: «Имя: Мария, Телефон: …». Правило emailForms говорит Gratio, какие письма считать заявками и какие поля в них искать. Письмо совпало с правилом — в уведомлении и в getEmail есть разобранная заявка: имя, телефон в виде +7…, почта, ваши поля и каких обязательных не хватает.
Без правил ни одно письмо заявкой не считается.
Правило
Правила задают в setSettings списком — по правилу на форму или сервис:
| Поле | Тип | Описание |
|---|---|---|
name |
string | название правила — попадёт в form.rule. По умолчанию «Форма сайта» |
from |
array, string | отправитель: адрес или его часть — noreply@tilda.ws, @school.ru. Без from правило не работает — иначе оно ловило бы всю почту |
subject |
array, string | тема содержит одно из. Пусто — любая тема |
excludeSubject |
array, string | темы, которые не заявка, хотя форма та же: «Вакансия», «Франшиза» |
fields |
object | свои поля: ключ → метки в письме, {"child_age": ["Возраст ребёнка"]}. Дополняют стандартные |
required |
array | обязательные поля. Пусто — нужен телефон или почта |
- Новый список
emailFormsзаменяет прежний целиком. Убрать все правила —"emailForms": []. from,subjectиexcludeSubjectсравниваются без учёта регистра и сохраняются в нижнем регистре.- Правила проверяются по порядку, срабатывает первое подходящее. Тема из
excludeSubject— письмо не заявка, а письмо человека:kind: "personal". - Правила действуют на новые письма сразу. getEmail применяет их к любому письму, в том числе пришедшему раньше правила.
Стандартные поля
Эти поля ищутся всегда — свои метки в fields для того же ключа добавляются к ним:
| Ключ | Метки |
|---|---|
name |
Имя, ФИО, Ваше имя, Как вас зовут, Контактное лицо, Name, Your name |
phone |
Телефон, Контактный телефон, Номер телефона, Ваш телефон, Мобильный телефон, Phone, Tel, Тел |
email |
E-mail, Email, Эл. почта, Электронная почта, Почта, Ваш e-mail, Mail |
message |
Сообщение, Комментарий, Вопрос, Текст сообщения, Ваш вопрос, Message, Comment |
city |
Город, City |
Как разбирается письмо
- Поле — строка «Метка: значение». Метка — в начале строки, перед ней можно поставить
•,*или-. Регистр метки не важен. - Значение — до следующей известной метки или до конца письма, может занимать несколько строк. Последнее поле забирает весь хвост письма, поэтому служебные строки в конце (Request ID, адрес страницы) добавьте в
fieldsсвоими ключами. - Таблица в html — строка «Имя | Мария» — читается как «Имя: Мария».
- Пустые значения и заглушки — «-», «—», «не указано», «нет», «null» — поле не заполняют.
- Сначала разбирается текстовая версия письма, а если полей в ней нет — html.
- Телефон приводится к виду
+79991234567, почта — к нижнему регистру.
Результат — form
| Поле | Тип | Описание |
|---|---|---|
rule |
string | название сработавшего правила |
fields |
object | заполненные поля: ключ → значение как в письме |
missing |
array | обязательные поля, которых нет в письме; без required — ["phone"], если нет ни телефона, ни почты |
complete |
boolean | true — все обязательные поля на месте |
phone |
string | телефон в виде +79991234567; null — не найден или не похож на номер |
email |
string | почта из заявки в нижнем регистре; null — нет |
name |
string | имя из заявки; null — нет |
Где видна заявка:
| Где | Что |
|---|---|
| Уведомление Письмо | messageData.emailMessageData.form; chatId — почта из заявки |
| getEmail | kind: "form" и объект form |
chatId уведомления о заявке — почта из полей формы, без неё — адрес из Reply-To письма. Нет ни того, ни другого — чат form:<idMessage>: ответить на него письмом нельзя, sendMessage ответит 400 no_account. Свяжитесь по телефону из form.phone. В конверте письма (getMessages, getEmail) chatId — как у любого письма: Reply-To или отправитель.
Пример
Форма записи на пробное занятие присылает письма с noreply@tilda.ws. Нужны телефон и возраст ребёнка, а ещё — курс и номер заявки на сайте.
Шаг 1. Правило
curl
curl -X POST "$GRATIO_URL/waInstance$GRATIO_ID/setSettings/$GRATIO_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"emailForms": [
{
"name": "Заявка с сайта",
"from": [
"noreply@tilda.ws"
],
"subject": [
"Заявка"
],
"excludeSubject": [
"Вакансия"
],
"fields": {
"child_age": [
"Возраст ребёнка"
],
"course": [
"Курс",
"Направление"
],
"request_id": [
"Request ID"
],
"page": [
"Page"
]
},
"required": [
"phone",
"child_age"
]
}
]
}'
Python
import os
import requests
BASE = f"{os.environ['GRATIO_URL']}/waInstance{os.environ['GRATIO_ID']}"
TOKEN = os.environ["GRATIO_TOKEN"]
url = f"{BASE}/setSettings/{TOKEN}"
payload = {
"emailForms": [
{
"name": "Заявка с сайта",
"from": ["noreply@tilda.ws"],
"subject": ["Заявка"],
"excludeSubject": ["Вакансия"],
"fields": {
"child_age": ["Возраст ребёнка"],
"course": ["Курс", "Направление"],
"request_id": ["Request ID"],
"page": ["Page"],
},
"required": ["phone", "child_age"],
},
],
}
response = requests.post(url, json=payload, timeout=60)
print(response.status_code, response.json())
Node.js
const { GRATIO_URL, GRATIO_ID, GRATIO_TOKEN } = process.env;
const base = `${GRATIO_URL}/waInstance${GRATIO_ID}`;
const url = `${base}/setSettings/${GRATIO_TOKEN}`;
const payload = {
emailForms: [
{
name: "Заявка с сайта",
from: ["noreply@tilda.ws"],
subject: ["Заявка"],
excludeSubject: ["Вакансия"],
fields: {
child_age: ["Возраст ребёнка"],
course: ["Курс", "Направление"],
request_id: ["Request ID"],
page: ["Page"],
},
required: ["phone", "child_age"],
},
],
};
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
console.log(response.status, await response.json());
PHP
<?php
$base = getenv('GRATIO_URL') . '/waInstance' . getenv('GRATIO_ID');
$url = $base . '/setSettings/' . getenv('GRATIO_TOKEN');
$payload = [
'emailForms' => [
[
'name' => 'Заявка с сайта',
'from' => ['noreply@tilda.ws'],
'subject' => ['Заявка'],
'excludeSubject' => ['Вакансия'],
'fields' => [
'child_age' => ['Возраст ребёнка'],
'course' => ['Курс', 'Направление'],
'request_id' => ['Request ID'],
'page' => ['Page'],
],
'required' => ['phone', 'child_age'],
],
],
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
]);
$response = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), PHP_EOL;
print_r($response);
Ответ 200 OK:
{"saveSettings": true}
Шаг 2. Письмо формы
Тема «Заявка с сайта school.ru», отправитель noreply@tilda.ws, текст:
Request details:
Имя: Мария Иванова
Телефон: 8 (999) 123-45-67
Email: Maria@Client.ru
Возраст ребёнка: 9 лет
Курс: Робототехника
Комментарий: Можно на пробное в эту субботу?
Request ID: 4815162342
Page: https://school.ru/probnoe
Шаг 3. Разобранная заявка
В уведомлении о письме — собеседник по почте из формы:
"senderData": {
"chatId": "maria@client.ru", "chatName": "Мария Иванова", "chatType": "user",
"sender": "maria@client.ru", "senderName": "Мария Иванова", "senderType": "user",
"senderContactName": "", "senderPhoneNumber": 0
}
И заявка в messageData.emailMessageData.form:
{
"rule": "Заявка с сайта",
"fields": {
"name": "Мария Иванова",
"phone": "8 (999) 123-45-67",
"email": "Maria@Client.ru",
"child_age": "9 лет",
"course": "Робототехника",
"message": "Можно на пробное в эту субботу?",
"request_id": "4815162342",
"page": "https://school.ru/probnoe"
},
"missing": [],
"complete": true,
"phone": "+79991234567",
"email": "maria@client.ru",
"name": "Мария Иванова"
}
Без меток Request ID и Page в fields строки хвоста попали бы в message: «Можно на пробное в эту субботу? Request ID: 4815162342 Page: …».
Заявка без возраста ребёнка — неполная:
{"rule": "Заявка с сайта", "fields": {"name": "Олег", "phone": "89997654321", "course": "Программирование"},
"missing": ["child_age"], "complete": false, "phone": "+79997654321", "email": null, "name": "Олег"}
Шаг 4. Обработка
Полную заявку — в CRM, неполную — менеджеру:
def on_notification(body: dict) -> None:
if body["typeWebhook"] != "incomingMessageReceived":
return
email = body["messageData"].get("emailMessageData") or {}
form = email.get("form")
if not form:
return # обычное письмо
if already_seen(body["eventId"]): # ваша таблица обработанных событий
return
if form["complete"]:
create_lead(name=form["name"], phone=form["phone"], email=form["email"], fields=form["fields"])
else:
ask_manager(missing=form["missing"], folder=email["mailbox"], uid=email["uid"])
create_lead и ask_manager — ваши функции. По mailbox и uid менеджер откроет письмо методом getEmail.
Что дальше
- Настройки ящика — остальные ключи
email…. - Письмо — уведомление о письме и
emailMessageData. - getEmail — письмо целиком с
kindиform.
