CEvent::Send - метод в 1С-Битрикс для отправки писем прямо из кода: из обработчика формы, из агента, из вебхука или из кастомного компонента. Я подключаю его каждый раз, когда стандартной привязки почтового события к действию вроде оформления заказа не хватает и письмо нужно отправить по своей логике, например после подтверждения оплаты через вебхук платёжной системы или при смене статуса лида в CRM. В статье разберу, как устроен метод CEvent::Send, какие параметры он принимает, что нужно подготовить в админке перед вызовом и на каких ошибках спотыкаются чаще всего.
Как работает отправка почтовых событий в Битриксе
В основе лежит связка из трёх сущностей. Тип события, EVENT_NAME, это условный идентификатор рассылки, например ORDER_NEW или FEEDBACK_FORM. Шаблон сообщения привязан к конкретному сайту и содержит тему, отправителя и тело письма, где переменные вида #EMAIL# или #NAME# заменяются на реальные значения. И сам вызов CEvent::Send, который передаёт в этот шаблон конкретные данные и запускает отправку.
Письмо не обязательно уходит в тот же момент, когда отработал код. В настройках почты в админке есть опция очереди для email-рассылок: если она включена, письма попадают в таблицу очереди и рассылаются агентом по расписанию, а не мгновенно при вызове. Частоту обработки очереди агентом можно посмотреть и изменить в списке агентов Битрикса, обычно это интервал в несколько минут, поэтому даже при включённой очереди задержка между вызовом кода и фактической отправкой обычно не превышает нескольких минут, а не часов.
Каждый тип события существует в своём экземпляре на каждом сайте отдельно. Если в мультисайтовом проекте письмо должно уходить с трёх разных доменов с разным брендингом, шаблон создаётся для каждого сайта персонально, а не один на всех.
Синтаксис и параметры CEvent::Send
Метод принимает семь параметров, из которых обязательны первые три.
<?php
CEvent::Send(
$event_name,
$site_id,
$arFields,
$duplicate,
$message_id,
$files,
$language_id
);
Параметр event_name задаёт код типа события из справочника почтовых событий, например ORDER_NEW. Параметр site_id принимает ID сайта строкой или массив ID сайтов, если письмо должно уйти по нескольким шаблонам сразу. Массив arFields содержит данные, которые подставятся в переменные шаблона: ключи массива должны совпадать с именами полей, заданными в типе события. Четвёртый параметр называется duplicate и к профилю пользователя отношения не имеет: он управляет тем, отправлять ли копию письма на адрес, указанный в настройках главного модуля как адрес для дублирования всех исходящих сообщений, по умолчанию Y. Пятый, message_id, это идентификатор почтового шаблона, по которому уйдёт письмо: если он не задан или равен пустой строке, письма отправятся по всем шаблонам, привязанным к типу события и к сайту. Шестой, files, принимает массив идентификаторов файлов, которые используются классом CFile, либо массив абсолютных путей до файлов. Седьмой, language_id, задаёт идентификатор языковой версии.
Метод существует в Битриксе очень давно и исторически вызывался как метод объекта: New CEvent, затем Send. Сейчас в актуальных версиях ядра его вызывают статически, CEvent::Send, оба варианта рабочие, но новый код проще писать через статический вызов без создания лишнего объекта. Возвращает метод идентификатор созданного почтового события, то есть число, а не булево значение. И означает это только то, что событие создано, а не то, что письмо долетело до почтового ящика получателя, это разные вещи и путать их не стоит.
Полезно тестировать шаблон сразу в админке, не трогая код: у каждого шаблона почтового события есть кнопка отправки тестового письма с подстановкой произвольных значений в переменные. Это быстрее, чем гонять реальный код ради проверки вёрстки письма.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
Как подготовить почтовое событие перед вызовом из кода
Прежде чем звать CEvent::Send, в админке нужно завести две вещи.
Сначала тип события: раздел Настройки, Почта, Почтовые события, вкладка Типы событий. Здесь задаётся код события и список полей, которые он умеет принимать, скажем EMAIL, NAME, ORDER_ID.
Затем шаблон сообщения для нужного сайта: та же секция, вкладка Почтовые события. В шаблоне прописывается тема письма, отправитель, ответный адрес и тело письма с переменными в виде #ORDER_ID# или #NAME#, которые должны буква в букву совпадать с ключами массива arFields в коде. Разойдётся регистр или название поля, и в письме на месте переменной останется пустое место или сам текст плейсхолдера.
В шаблоне можно указать не только адрес отправителя, но и его отображаемое имя, а также адрес для ответа, если он должен отличаться от адреса отправки. Для случаев, когда нужна ещё и копия письма на внутренний адрес компании, такой адрес прописывается в дополнительных заголовках шаблона, а не передаётся через код.
Вызов CEvent::Send из агентов и вебхуков
CEvent::Send одинаково хорошо работает и в обработчике, который выполняется в рамках обычного запроса, и в фоновом агенте Битрикса. Разница только в контексте: в агенте, который проверяет брошенные корзины раз в час, важно не отправить одно и то же письмо повторно при следующем запуске, поэтому перед вызовом CEvent::Send стоит отмечать заказ или лид отдельным флагом, что письмо уже отправлено, и проверять этот флаг перед следующей отправкой.
В обработчике вебхука, например при подтверждении оплаты от Т‑Банка, письмо логично отправлять сразу после проверки подписи запроса и смены статуса заказа, не откладывая до следующего запуска агента. Здесь же, если процесс завязан на СДЭК или другую службу доставки, тем же вызовом можно отправить письмо с номером трек-кода, как только он появился в заказе.
Если помимо письма нужно продублировать событие в Telegram-бота на aiogram или передать его во внешний сценарий на n8n для дальнейшей маршрутизации, я обычно развожу эти вызовы: CEvent::Send отвечает только за письмо, а уведомление в мессенджер или запрос во внешний сервис уходит отдельным вызовом сразу после него, чтобы падение одного канала не тянуло за собой другой.
Примеры отправки писем через CEvent::Send
Базовая отправка после обработки формы обратной связи выглядит так.
<?php
$arEventFields = array(
"EMAIL" => $email,
"NAME" => $name,
"MESSAGE" => $message,
);
CEvent::Send("FEEDBACK_FORM", SITE_ID, $arEventFields);
Отправка с вложением, например с готовым PDF-отчётом, требует предварительно сохранить файл через CFile и передать его ID шестым параметром, а пятый, message_id, оставить пустой строкой.
<?php
$file = CFile::MakeFileArray($_SERVER["DOCUMENT_ROOT"]."/upload/reports/report.pdf");
$fileID = CFile::SaveFile($file, "mail_attachments");
$arEventFields = array(
"EMAIL" => $email,
"NAME" => $name,
);
CEvent::Send("REPORT_READY", SITE_ID, $arEventFields, "N", "", array($fileID));
Такой вызов встраивается в любую точку кода: в обработчик вебхука от Т‑Банка после подтверждения оплаты, в агент, который раз в час проверяет статусы заказов, или в обработчик события OnSaleOrderSaved.
Если письмо должно завязываться на нестандартную бизнес-логику, которой в модулях Битрикса из коробки нет, такими доработками занимается доработка и поддержка 1С-Битрикс, у меня эта услуга стоит от 3 000 рублей в час.
Типичные ошибки при работе с CEvent::Send
Почти все проблемы с CEvent::Send сводятся к рассинхрону между кодом и настройками в админке, а не к ошибкам самого метода.
- Нет шаблона для нужного сайта. Метод отработает без исключений, но письмо никуда не уйдёт, потому что Битрикс просто не найдёт активный шаблон, привязанный к переданному site_id.
- Поля в arFields не совпадают с переменными в шаблоне. Если в коде передано ORDER_NUM, а в шаблоне написано ORDER_ID, в письме останется пустое место или буквально текст плейсхолдера.
- Передан не тот site_id. В мультисайтовых проектах легко перепутать константу SITE_ID с ID сайта, для которого реально создан шаблон, особенно если код вызывается из общего модуля, подключённого сразу к нескольким сайтам.
- Шаблон настроен как обычный текст, а тело письма содержит HTML-разметку. Тогда получатель увидит теги вместо оформленного письма.
- Отправка больших вложений при выключенной очереди. Без очереди файл готовится и уходит синхронно в момент вызова CEvent::Send, и если вложение тяжёлое, пользователь на фронте ждёт ответ от сервера дольше, чем нужно.
- Повторная отправка из-за отсутствия защиты от дублей. Если вызов CEvent::Send стоит внутри обработчика, который может сработать дважды на одном и том же событии, скажем, вебхук от платёжной системы прилетел повторно, письмо уйдёт получателю два раза. Спасает простая проверка статуса или отдельного флага перед вызовом.
- Пустой EMAIL в arFields. Метод не проверяет валидность адреса и не бросает ошибку на пустое значение, он просто передаст пустую строку в шаблон, и письмо в итоге может уйти в никуда или вовсе не сформироваться корректно на уровне почтового сервера.
Как проверить, что письмо действительно отправлено
Первым делом смотрю историю самого события в админке: в разделе Почтовые события у каждого типа события есть список отправленных писем с датой постановки в очередь и датой фактической отправки. Если запись есть, а письмо не дошло, дело почти всегда в почтовом сервере хостинга или в том, что письмо осело в спаме у получателя.
Второй шаг, если записи в истории вообще нет, вернуться к предыдущему разделу и проверить, что шаблон для нужного типа события и сайта существует и активен. Метод CEvent::Send не бросает исключение и не пишет ошибку в лог, если подходящего шаблона просто нет, он молча ничего не отправляет.
И третий шаг, актуальный на shared-хостинге, проверить настройки исходящей почты сервера: SPF и DKIM для домена отправителя. Без них письма от корректно настроенного почтового события всё равно будут падать в спам у Яндекса и Mail.ru, и это уже не имеет отношения к коду.
Проверять доставку удобно на реальном почтовом ящике на стороннем домене, а не только на корпоративной почте: некоторые почтовые серверы внутри организации доверяют своему же домену и пропускают письма, которые у обычного получателя на Яндексе или Gmail улетят в спам из-за не настроенных SPF и DKIM.
1С-Битрикс регулярно обновляется, поэтому перед внедрением я сверяюсь с актуальной документацией.
Частые вопросы
В чём разница между CEvent::Send и функцией mail() в PHP
mail() отправляет письмо сразу с тем текстом, что передан в аргументах, без шаблонов и без очереди. CEvent::Send работает через связку тип события плюс шаблон, привязанный к сайту: текст письма редактируется в админке без правок кода, а сама отправка может уйти в очередь и обрабатываться агентом по расписанию, что удобнее при массовых рассылках.
Можно ли отправить письмо без создания шаблона в админке
Нет. Если для переданных event_name и site_id не найден активный шаблон сообщения, CEvent::Send просто ничего не отправит и не покажет ошибку. Шаблон обязателен для каждой пары события и сайта.
Как отправить письмо с вложением через CEvent::Send
Файл сначала сохраняется в файловую систему Битрикса через CFile::SaveFile, полученный ID передаётся массивом в шестой параметр files, а пятый параметр message_id при этом остаётся пустой строкой. Сам файл в шаблоне никак не описывается, вложение крепится автоматически при отправке.
Почему CEvent::Send не сообщает об ошибке, а письмо не приходит
Чаще всего это несовпадение site_id с сайтом, для которого создан шаблон, отсутствие самого шаблона или проблема на стороне почтового сервера хостинга уже после того, как письмо поставлено в очередь. Метод подтверждает только то, что почтовое событие создано, а не факт доставки получателю.