Перейти к документации
CLI

Шаблоны, правила и вебхуки

Каждая команда `templates`, `rules` и `webhooks`: сохранённые тела, которые вы отправляете по slug, правила, которые раскладывают приходящую почту, и подписанные события для вашего сервера.

Три пространства имён

Эти три пространства имён позволяют ящику работать без присмотра. templates хранит тела, которые вы отправляете много раз, rules раскладывает почту по мере поступления, а webhooks сообщает вашему серверу, что произошло. Каждая команда это метод SDK под своим именем в kebab-case, поэтому webhooks.rotateSecret становится openemail webhooks rotate-secret, и она читает аргументы и флаги, как любая другая команда ресурса.

Пространство имёнТакжеДля чтения нужноДля изменений нужно
templatestemplatetemplates:readtemplates:write, а для send ещё и emails:send
rulesrulerules:read, включая testrules:write
webhookswebhookwebhooks:readwebhooks:write, включая test и replay-delivery

На этой странице перечислены все команды и то, что стоит знать, прежде чем писать для них скрипт. Чтобы увидеть каждый аргумент и флаг с типом, нужными scope, эндпоинтом и тем, что он возвращает, запустите openemail <namespace> <verb> --help. Добавьте --json, чтобы получить ту же страницу в виде JSON.

Справка
openemail templates --helpopenemail rules create --helpopenemail webhooks replay-delivery --help --json

Шаблоны

Тела, сохранённые один раз и отправляемые много раз, с версиями, предпросмотром и типизированными props. Каждая команда, принимающая <id-or-slug>, принимает идентификатор tpl_ или slug. Slug никогда не меняется при переименовании шаблона, поэтому закрепляйте в скриптах slug.

КомандаЧто делает
openemail templates listПоказать шаблоны, недавно обновлённые сверху. --status оставляет черновые, активные или архивные, --search ищет по именам, slug и темам, а --sort выбирает порядок
openemail templates get <id-or-slug>Прочитать шаблон с его головной версией целиком, включая тело
openemail templates create --name <value>Создать шаблон и его первую версию. Он остаётся черновиком, если не передан --publish, а --starter берёт за основу стартовый дизайн
openemail templates update <id-or-slug>Изменить имя, slug, описание или статус либо тело черновика. Отправки продолжают использовать опубликованную версию, пока вы не опубликуете
openemail templates duplicate <id-or-slug>Скопировать головную версию в новый шаблон, который начинается как черновик
openemail templates replace-content <id-or-slug>Заменить тело на тело стартового дизайна (--starter) или другого шаблона (--from-template-id). Просит подтверждения
openemail templates delete <id-or-slug>Удалить шаблон и все его версии. Просит подтверждения
openemail templates list-versions <id-or-slug>Показать версии, новые сверху, без их тел
openemail templates get-version <id-or-slug> <version>Прочитать одну версию с её телом, не трогая черновик
openemail templates publish <id-or-slug>Опубликовать черновик, чтобы отправки использовали его. Публикация головной версии, которая уже опубликована, ничего не меняет
openemail templates restore-version <id-or-slug> <version>Вернуть тело более старой версии в черновик. Просит подтверждения
openemail templates delete-version <id-or-slug> <version>Удалить одну версию. Опубликованная версия, головная и единственная версия отклоняются. Просит подтверждения
openemail templates list-startersПоказать встроенные стартовые дизайны
openemail templates get-starter <slug>Прочитать один стартовый дизайн целиком, с деревом блоков и отрисованным предпросмотром
openemail templates list-fontsПоказать веб-шрифты, которые может загружать шаблон
openemail templates renderОтрисовать тело, которое нигде не сохранено, из --html или --document
openemail templates preview <id-or-slug>Отрисовать сохранённый шаблон с --props и --slots, включая черновики, не отправляя его
openemail templates get-analytics <id-or-slug>Отправки, открытия и клики за период, по дням, по источникам и по версиям
openemail templates list-sends <id-or-slug>Отдельные письма, отправленные шаблоном, новые сверху, постранично
openemail templates send <id-or-slug> --from <value> --to <a,b>Отправить письмо, отрисованное из опубликованной версии или из той, которую закрепляет --template-version

У шаблона есть головная версия, которая остаётся черновиком, пока в ней есть неопубликованные правки, и опубликованная версия, которую использует отправка без --template-version. create без --publish, правка тела через update, replace-content и restore-version пишут в черновик, поэтому получатели не увидят ничего нового до publish.

  • Архивный шаблон отказывается отправлять с template_archived. publish снова делает его активным.
  • В рабочем пространстве может быть не больше 200 шаблонов, включая архивные, поэтому освободить место можно только удалением.
  • delete отклоняется с template_in_use, пока запланированная рассылка или рассылка в очереди всё ещё ссылается на шаблон.

Правила

Условия и действия, которые проверяются на приходящей почте в том порядке, который показывает rules list. Правило действует только на почту, пришедшую, пока оно включено. Ни одна команда не применяет правило к почте, которая уже в ящике, а увидеть, что оно поймало бы, позволяет rules test. Идентификаторы правил начинаются с rul_.

КомандаЧто делает
openemail rules listПоказать правила в порядке выполнения. --enabled или --no-enabled оставляет один вид
openemail rules get <id>Прочитать одно правило с matchCount и lastMatchedAt
openemail rules create --name <value> --conditions <json|@file|-> --actions <json|@file|->Создать правило в конце порядка. Оно включено, если не передан --no-enabled
openemail rules update <id>Изменить правило. --conditions и --actions заменяют весь список, а --position перемещает только это правило
openemail rules delete <id>Удалить правило. То, что оно уже сделало, остаётся в list-runs. Просит подтверждения
openemail rules reorder <rule-ids...>Задать порядок всех правил сразу, назвав каждое правило ровно один раз
openemail rules test <id>Пробно прогнать правило по почте, которая уже в ящике. Ничего не меняет и работает для выключенного правила
openemail rules list-runsЧто правила на самом деле сделали с приходящей почтой, новые сверху. --rule-id и --thread-id сужают список

--conditions это список объектов { field, op, value }, объединённых через --match all или --match any, где value всегда строка, а negate: true обращает одно условие. --actions это список объектов { type, value }, применяемых по порядку. Правило содержит от 1 до 20 условий и от 1 до 10 действий, а в ящике может быть не больше 100 правил.

  • Поля условий: from, from_domain, envelope_from, to, cc, bcc, recipient, reply_to, delivered_to, subject, body, header, list_id, attachment_name, attachment_type, has_attachment, attachment_size, message_size, spam, hour и weekday.
  • Операторы: matches, contains, equals, starts_with, ends_with, gt и lt. gt и lt работают только с числовыми полями, а has_attachment и spam принимают только equals с true или false.
  • Типы действий: label, remove_label, archive, mark_read, star, spam, trash, forward, reply, block_sender и reject. label и remove_label принимают идентификатор метки, например USER_RECEIPTS, forward принимает адрес, а reply идентификатор или slug шаблона.
  • from_domain совпадает и с поддоменами, а hour и weekday читаются в UTC, причём воскресенье это 0.
  • Правило с действием reject должно также проверять envelope_from, иначе оно отклоняется с reject_needs_envelope.

Вебхуки

Эндпоинты на вашем сервере, которые получают подписанные события ящика, с их секретами подписи, журналом доставок и журналом аудита каждого изменения. Идентификаторы эндпоинтов начинаются с whe_, а идентификаторы доставок с whd_.

КомандаЧто делает
openemail webhooks listПоказать эндпоинты рабочего пространства, новые сверху, с их состоянием
openemail webhooks get <id>Прочитать один эндпоинт. Секрет подписи никогда не входит в чтение
openemail webhooks create --url <value>Зарегистрировать эндпоинт HTTPS. Команда выводит секрет подписи, и это единственный раз, когда вы его видите
openemail webhooks update <id>Изменить URL, события, списки разрешённых или то, включён ли он. Каждый список заменяет сохранённый
openemail webhooks delete <id>Удалить эндпоинт и его журнал доставок. Просит подтверждения
openemail webhooks rotate-secret <id>Выпустить новый секрет подписи. Старый сразу перестаёт работать. Просит подтверждения
openemail webhooks test <id>Отправить подписанное синтетическое событие email.sent и сообщить, как прошла доставка
openemail webhooks list-deliveries <id>Попытки доставки одного эндпоинта, новые сверху. --status, --since и --until сужают список
openemail webhooks get-delivery <id> <delivery-id>Одна попытка целиком: отправленное тело, ответ вашего сервера, каждая попытка события и будет ли принят повтор
openemail webhooks replay-delivery <id> <delivery-id>Снова отправить одно сохранённое событие на эндпоинт, сейчас
openemail webhooks list-workspace-deliveriesПопытки доставки по всем эндпоинтам или по тем, что названы в --endpoint-ids
openemail webhooks list-activity <id>Журнал аудита одного эндпоинта: кто создал, изменил, проверил, повторил доставку или удалил его
openemail webhooks list-workspace-activityЖурнал аудита всех эндпоинтов, включая удалённые

Если не указать --event-types, эндпоинт получает набор по умолчанию: события email.*, кроме email.replied. email.replied, события domain.* и события suppression.* доходят до него, только если вы их назовёте. --address-allowlist и --domain-allowlist сужают эндпоинт до некоторых адресов или доменов так же, как сужают API-ключ.

  • В рабочем пространстве может быть 10 эндпоинтов, если поддержка не подняла этот предел.
  • Эндпоинт, у которого 100 доставок подряд не прошли, сервер выключает, а webhooks update <id> --enabled возвращает его.
  • При входе через браузер прочитать доставку через get-delivery может только владелец рабочего пространства. Любой другой получает owner_only и код выхода 4.

Проверить шаблон, затем опубликовать

templates preview отрисовывает ровно то, что дала бы отправка с теми же значениями, включая черновики, и ей нужен только templates:read, так что её может запустить даже ключ только для чтения. Отсутствующее обязательное свойство она сообщает как предупреждение там, где send отказался бы, поэтому валите сборку при любом предупреждении. publish безопасен при каждом развёртывании, потому что публикация уже опубликованной головной версии ничего не меняет.

CI
draft=$(openemail templates get order-shipped --json | jq .latestVersion)openemail templates preview order-shipped --template-version "$draft" \  --props '{"orderId":"AC-4192","customer":"Ada"}' --json | jq -e '.warnings == []'openemail templates publish order-shipped

Отправка из шаблона

Закрепите версию, чтобы переработка, опубликованная завтра, не изменила то, что отправляет этот код, и передайте ключ идемпотентности, взятый из того, что вызвало отправку, чтобы повтор после потерянного ответа воспроизвёл первое письмо, а не отправил второе. --dry-run выводит метод, URL, заголовки со скрытыми учётными данными и тело, ничего не отправляет и завершается с кодом 0. Чтобы отправить, запустите снова без --dry-run.

Терминал
openemail templates send order-shipped \  --from 'Acme <[email protected]>' \  --to [email protected] \  --template-version 5 \  --props '{"orderId":"AC-4192","customer":"Ada"}' \  --idempotency-key order-shipped:AC-4192 \  --dry-run

Проверить правило до запуска

Создайте правило выключенным, пробно прогоните его по недавней почте и включите, когда оно ловит то, что вы имели в виду. При входе через браузер rules create и rules update спрашивают код подтверждения, который скрипт ввести не может, поэтому сначала запустите openemail verify. Следующие 60 минут этот профиль выполняет их без вопросов.

conditions.json
[  { "field": "from_domain", "op": "equals", "value": "stripe.com" },  { "field": "has_attachment", "op": "equals", "value": "true" }]
actions.json
[  { "type": "label", "value": "USER_RECEIPTS" },  { "type": "archive" }]
Терминал
openemail verifyrule=$(openemail rules create --name 'Stripe receipts' \  --conditions @conditions.json --actions @actions.json --no-enabled --json | jq -r .id)openemail rules test "$rule" --days 30 --limit 100openemail rules update "$rule" --enabled

Читайте предупреждения rules test раньше его совпадений. field_unevaluable значит, что условие читает то, чего в сохранённой почте уже нет, и проверка не смогла его оценить, а forward_unverified значит, что адрес пересылки размещён не здесь. wouldApply перечисляет то, что объявляет правило: пересылка на адрес, который не подтвердился, всё равно не сработает, когда придёт настоящая почта.

Поставить правило первым и понять, почему письмо переместилось

rules reorder принимает каждое правило ящика ровно один раз. Если правило пропущено или названо дважды, вызов отклоняется, и ничего не сдвигается. rules list возвращает идентификаторы в порядке выполнения, поэтому поставьте нужное правило перед остальными.

Терминал
first=rul_4f1c9a2b7d3e8f6a0b5c1d2eopenemail rules reorder "$first" $(openemail rules list --all --ndjson \  | jq -r --arg first "$first" 'select(.id != $first) | .id')openemail rules list-runs --thread-id CAHk7pQ2x9LmZ4 --json | jq '.items[] | {ruleName, actions, failures}'

list-runs это запись того, что произошло на самом деле. Каждая строка это одно правило, совпавшее с одним письмом, с действиями, которые сработали, и в failures с теми, что ящик отклонил, например ответ отправителю, которому уже отвечали в тот день. Каждая строка хранит имя, которое было у правила в тот момент, поэтому --rule-id работает и для правила, которое вы с тех пор удалили.

Зарегистрировать вебхук и убедиться, что он работает

webhooks create показывает секрет подписи один раз, и никакая следующая команда его больше не покажет. С --json он есть в JSON в stdout, а напоминание сохранить его уходит в stderr, так что вывод по-прежнему разбирается. webhooks test отправляет подписанное синтетическое событие email.sent, на что бы ни был подписан эндпоинт, и никакой почты не отправляется.

Терминал
openemail verifyopenemail webhooks create --url https://hooks.acme.com/openemail \  --event-types email.received,email.bounced,email.complained \  --description 'Support desk sync' --json > endpoint.jsonjq -r .secret endpoint.jsonopenemail webhooks test "$(jq -r .id endpoint.json)" --json | jq .deliveryrm endpoint.json

Положите секрет в хранилище секретов, прежде чем удалять файл. test завершается с кодом 0, даже когда ваш сервер не справился, поэтому читайте delivery.status: delivered для ответа 2xx и failed для всего остального, включая перенаправление, поскольку перенаправления никогда не выполняются. responseCode, равный null, значит, что ответ не пришёл вообще.

Найти неудавшиеся доставки и отправить одну снова

После сбоя на вашей стороне перечислите, что не прошло, по всем эндпоинтам, проверьте, что повтор будет принят, и отправьте событие снова. Повтор несёт тот же идентификатор события, поэтому получатель, который отбрасывает уже обработанные идентификаторы, воспримет его как знакомое событие.

Терминал
openemail webhooks list-workspace-deliveries --status failed --since 2026-09-26T00:00:00Z --all --ndjson \  | jq -r '[.endpointId, .id, .eventType, (.responseCode // "no answer")] | @tsv'openemail webhooks get-delivery whe_3f9c2a7b1e4d8f60a5c7b92d whd_8c1e4a7f2b9d3e6a0c5f1b28 --json | jq .replayRefusalopenemail webhooks replay-delivery whe_3f9c2a7b1e4d8f60a5c7b92d whd_8c1e4a7f2b9d3e6a0c5f1b28
  • --since и --until принимают момент в ISO 8601.
  • У неудавшейся строки, в nextAttemptAt которой стоит время, ещё впереди автоматический повтор.
  • replayRefusal равен null, когда повтор уйдёт, а иначе называет причину отказа, например webhook_disabled, пока эндпоинт выключен.
  • Повторы идут по одному событию. Команды, которая заново отправила бы все неудавшиеся доставки, нет.

Коды подтверждения

При входе через браузер четыре из этих команд, прежде чем что-то изменить, спрашивают код подтверждения, как веб-приложение: rules create, rules update, webhooks create и webhooks update. У API-ключа код не спрашивают никогда. Все остальные команды на этой странице выполняются без кода, включая удаления и webhooks rotate-secret.

  • В терминале CLI присылает на почту шестизначный код или, если включён двухфакторный вход, просит код из приложения-аутентификатора, а затем выполняет команду один раз.
  • Без присмотра, с --json или --no-input, в CI или без терминала, ввести код некому, поэтому команда останавливается с кодом выхода 4 и ничего не меняет. Сначала запустите openemail verify, и 60 минут этому профилю код не понадобится.
  • --yes подтверждает удаление, но никогда не пропускает код.

Подтверждения и пробные запуски

Семь команд здесь что-то удаляют или перезаписывают, поэтому сначала просят подтверждения: templates delete, templates delete-version, templates replace-content, templates restore-version, rules delete, webhooks delete и webhooks rotate-secret. Без присмотра каждая останавливается с кодом выхода 2, если вы не передадите --yes.

Терминал
$ openemail webhooks delete whe_3f9c2a7b1e4d8f60a5c7b92d --no-input✗ Refusing to run unattended. Pass --yes to confirm.$ openemail webhooks delete whe_3f9c2a7b1e4d8f60a5c7b92d --yes

--dry-run выводит первый запрос, который что-то изменил бы, и завершается с кодом 0, не отправляя его и не прося подтверждения. С --json выводится один документ { dryRun, request }. rules test, templates render и templates preview ничего не меняют, но это запросы POST, поэтому пробный запуск выводит их вместо выполнения.

Постраничный вывод

  • templates list, templates list-versions, rules list, rules list-runs и каждая команда webhooks list… читают по одной странице, 25 строк, если --limit не просит до 100. Терминал показывает --cursor, который нужно передать для следующей страницы.
  • --all читает все страницы, --max <n> останавливается после стольких строк, а --ndjson выводит по одному JSON-объекту в строке. С --json список выводит один документ { items, hasMore, nextCursor }, в том числе с --all.
  • Передавайте курсор обратно с теми же фильтрами и сортировкой, с которыми он пришёл. Всё остальное отклоняется как invalid_cursor с кодом выхода 7.
  • templates list-sends листает вместо этого по номеру, через --page и --page-size, сообщает total и не имеет --all. Номера страниц сдвигаются, пока уходит почта, поэтому сужайте период через --days или --minutes, а не листайте далеко.
  • templates list-starters и templates list-fonts возвращают весь каталог сразу, а rules reorder возвращает все правила простым списком в новом порядке.
  • В ящике может быть не больше 100 правил, поэтому rules list --limit 100 всегда возвращает все правила одной страницей.

Флаги, на которые стоит взглянуть ещё раз

  • --template-version это поле тела version, переименованное, потому что --version выводит версию CLI. Аргумент <version> у get-version, restore-version и delete-version это номер версии, а не идентификатор tplv_.
  • --conditions, --actions, --document, --slots, --props и другие флаги JSON принимают JSON встроенно, из файла через @path или из stdin через -. --data принимает так же всё тело, а любой флаг, переданный вдобавок, переопределяет свой ключ.
  • --html принимает саму разметку, а не файл, поэтому --html @page.html отправляет текст @page.html. Передайте --html "$(cat page.html)" или поместите html в файл, который отдаёте --data.
  • rules update --conditions и --actions заменяют весь список, как и webhooks update --event-types, --address-allowlist и --domain-allowlist. Прочитайте текущее значение, измените его и отправьте целиком.
  • Пустой --event-types это ошибка использования. Чтобы вернуть эндпоинт на набор по умолчанию, отправьте --data '{"eventTypes":[]}', а чтобы остановить его доставки, передайте --no-enabled.
  • --expected-version у templates update, replace-content и restore-version принимает головную версию, которую вы прочитали. Если с тех пор головную версию сдвинул кто-то другой, команда останавливается с кодом выхода 6 и version_conflict и ничего не записывает.
  • rules update <id> --no-enabled выключает правило и сохраняет его место в порядке, и так правило ставят на паузу, не удаляя его.

Ваши входящие,
на ваших условиях.

Почтовая инфраструктура для бизнеса, ИИ, агентов и личной почты. Создана для масштаба, приватности и контроля. Всё, что должно было быть в почте с первого дня.

OpenEmail

Почтовая инфраструктура для бизнеса, ИИ, агентов и личной почты. Создана для масштаба, приватности и контроля. Всё, что должно было быть в почте с первого дня.

© 2026 OpenEmail. Все права защищены.