Контакты, аудитории и рассылки
Каждая команда для адресной книги, аудиторий, рассылок и списка подавления, с разобранными примерами.
Как они связаны
Четыре пространства имён охватывают людей, которым вы пишете. Контакты это адресная книга рабочего пространства, аудитории это именованные списки контактов, рассылка отправляет одно письмо всем в нескольких аудиториях, а список подавления хранит адреса, на которые рабочее пространство не отправляет. Каждая команда вызывает один метод SDK, поэтому страницы SDK описывают те же вызовы подробнее.
- У контакта нет идентификатора. Ключом, который принимает каждая команда
contacts, служит его адрес, без пробелов по краям и в нижнем регистре, поэтому[email protected]и[email protected]это один контакт. У аудитории идентификаторaud_, у рассылкиbrd_, а у записи о подавлении тот идентификатор, который выводитsuppressions list. - Каждый контакт входит в аудиторию по умолчанию, пока он существует. Эту аудиторию нельзя удалить, очистить или проредить, и её
builtinравенdefault. - Адресная книга принадлежит рабочему пространству, поэтому каждый участник и каждый ключ читают и пишут одну и ту же.
- Каждое пространство имён откликается и на форму единственного числа, как в
openemail contact get, и работают обычные псевдонимы:ls,show,new,editиrm. Вsuppressions, где глаголы называютсяaddиremove,newведёт кadd, аrmкremove.
openemail <namespace> <verb> --help показывает каждый флаг с типом, scope, эндпоинт и то, что возвращает команда. Добавьте --json, чтобы получить ту же страницу как данные.
Контакты
Адресная книга рабочего пространства: люди, которым участник писал из редактора приложения, плюс все, кого сохранили вручную. Приходящая почта никого не добавляет, как и отправка через API или CLI.
| Команда | Что делает |
|---|---|
| openemail contacts list | Одна страница сохранённых контактов, сначала те, с кем переписывались недавно. --source оставляет контакты manual или auto, а --q ищет по именам и адресам |
| openemail contacts get <email> | Один контакт со всеми аудиториями, в которые он входит |
| openemail contacts create --email <value> | Сохранить новый контакт с --name, --notes и --audience-ids. Адрес, который уже есть в книге, отклоняется с 409 contact_exists |
| openemail contacts update <email> | Изменить --name или --notes, где null очищает значение. Сам адрес изменить нельзя |
| openemail contacts delete <email> | Удалить контакт вместе с заметками, фото и членством и скрыть адрес, чтобы редактор не записал его снова |
| openemail contacts set-audiences <email> --audience-ids <a,b> | Сделать так, чтобы контакт входил ровно в эти аудитории. Аудитория по умолчанию всегда сохраняется |
| openemail contacts list-people | Все со страницы «Контакты»: сохранённые контакты и, с threads:read, каждый адрес, замеченный в почте, с числом цепочек. --sort, --q, --email и --blocked сужают список |
| openemail contacts save <email> | Сохранить адрес, оставить адрес, записанный при отправке, или вернуть удалённый. Никогда не ошибка, в каком бы состоянии ни был адрес |
| openemail contacts delete-many <emails...> | Удалить и скрыть от 1 до 200 адресов за один вызов |
| openemail contacts set-photo <email> <data> | Загрузить фото из файла или из stdin через -: PNG, JPEG, WebP или GIF до 5 МБ |
| openemail contacts remove-photo <email> | Убрать фото и удалить сохранённое изображение |
| openemail contacts block <email> | Внести адрес в список блокировки рабочего пространства, чтобы почта с него отклонялась. Плюс-тег отбрасывается |
| openemail contacts unblock <email> | Снять каждое правило списка блокировки, которое блокирует адрес, включая правило на весь домен |
| openemail contacts list-threads <email> | Цепочки, которые адрес писал или в которых писали ему, во всех папках. --q ищет внутри них |
| openemail contacts activity <email> | Почта, полученная с адреса и отправленная на него за период, 90 дней, если --minutes не говорит иначе, с цепочками, ждущими ответа, и медианным временем ответа в каждую сторону |
Аудитории
Именованные списки контактов, до 100 в рабочем пространстве. Прежде чем адрес попадёт в аудиторию, он должен быть контактом, кроме как через import-contacts, который сохраняет новые адреса по ходу дела.
| Команда | Что делает |
|---|---|
| openemail audiences list | Одна страница аудиторий, сначала аудитория по умолчанию, остальные новые сверху, каждая с contactCount |
| openemail audiences growth | Как росли аудитории за период, 30 дней, если --days или --minutes не говорит иначе: вступления и отписки по интервалам и итоги |
| openemail audiences get <id> | Одна аудитория со свежим contactCount |
| openemail audiences create --name <value> | Создать пустую аудиторию с необязательным --description. Имена не обязаны быть уникальными |
| openemail audiences update <id> | Изменить --name или --description. Состав не затрагивается |
| openemail audiences delete <id> | Удалить аудиторию, сохранив её контакты. Аудиторию по умолчанию удалить нельзя |
| openemail audiences empty <id> | Вынуть все контакты и сохранить аудиторию с её идентификатором, именем и описанием |
| openemail audiences list-contacts <id> | Одна страница контактов аудитории с тем, когда каждый вступил и отписался ли он. --sort, --q, --source и --statuses сужают её |
| openemail audiences add-contact <id> --email <value> | Добавить в аудиторию один существующий контакт. Добавление того, кто уже там есть, ничего не меняет |
| openemail audiences remove-contact <id> <email> | Вынуть один контакт. Контакт, которого нет в аудитории, даёт 404 |
| openemail audiences add-contacts <id> --emails <a,b> | Добавить до 200 существующих контактов и сообщить в missing адреса, которые не являются контактами |
| openemail audiences remove-contacts <id> --emails <a,b> | Вынуть до 200 контактов и сообщить о тех, которых в ней не было |
| openemail audiences import-contacts <id> --contacts <json|@file|-> | Импортировать до 500 строк { email, name }, сохраняя адреса, которые ещё не являются контактами |
Рассылки
Одно письмо всем в не более чем 10 аудиториях, отправленное отдельной копией каждому человеку, с заполненными полями подстановки и ссылкой для отписки. Каждая копия это обычное письмо со своим идентификатором msg_, событиями и вебхуками.
| Команда | Что делает |
|---|---|
| openemail broadcasts preview --audience-ids <a,b> | Посчитать, до кого дошла бы рассылка этим аудиториям и кого она пропустила бы как отписавшихся или подавленных. Ничего не отправляет |
| openemail broadcasts send --audience-ids <a,b> --from <value> | Отправить с --subject и --html или --text либо с сохранённым --template, сейчас или в --scheduled-at |
| openemail broadcasts list | Одна страница рассылок, новые сверху, с живыми счётчиками. --audience-id оставляет те, что отправлены этой аудитории |
| openemail broadcasts get <id> | Одна рассылка со статусом и живыми счётчиками: эту команду опрашивают, пока идёт отправка |
| openemail broadcasts stats <id> | Итоги по доставке, отказам, открытиям, кликам и отпискам и ряд по интервалам --grain, по часу, если не указано иное |
| openemail broadcasts list-recipients <id> | Кому ушла каждая копия и что с ней стало. --filter оставляет одну группу, например bounced или not_opened |
| openemail broadcasts get-recipient <id> <email-id> | Копия одного человека, с темой, HTML и текстом ровно в том виде, в каком он их получил |
| openemail broadcasts cancel <id> | Остановить рассылку, которая запланирована, стоит в очереди или ещё отправляется. Ушедшие копии отозвать нельзя |
Подавления
Адреса, на которые это рабочее пространство не отправляет: жёсткие отказы и жалобы, записанные по мере появления, и любой адрес, который вы добавили вручную. Отправка на такой адрес отклоняется для этого получателя до того, как что-то уйдёт.
| Команда | Что делает |
|---|---|
| openemail suppressions list | Одна страница списка, новые сверху. --reason оставляет bounce, complaint или manual, а --q ищет |
| openemail suppressions get <id> | Одна строка: адрес, причина, подробности, которые нёс отказ или жалоба, и можно ли её удалить |
| openemail suppressions add --email <value> | Прекратить отправку на адрес. Добавление адреса, который уже есть, возвращает его строку |
| openemail suppressions remove <id> | Снова разрешить почту на адрес. Жёсткий отказ удалить нельзя |
Список подавления и список блокировки это разные списки. suppressions add останавливает почту, уходящую на адрес, а contacts block отклоняет почту, приходящую с него.
Scope
Большинству команд нужен scope чтения или записи их пространства имён. Нескольким нужен другой, потому что они читают или меняют что-то ещё:
| Scope | Команды |
|---|---|
| contacts:read | contacts list, get и list-people |
| contacts:write | contacts create, update, delete, save, delete-many, set-photo и remove-photo, а также audiences import-contacts вместе с audiences:write |
| audiences:read | audiences list, growth, get и list-contacts, а также broadcasts preview, так что ключ, который не может отправлять, всё равно может показать число |
| audiences:write | Все остальные команды audiences и contacts set-audiences. contacts create --audience-ids нужен он вместе с contacts:write |
| threads:read | contacts list-threads и activity, а также адреса, замеченные в почте, в list-people |
| settings:read | suppressions list и get |
| settings:write | suppressions add и remove, а также contacts block и unblock |
| emails:read | broadcasts list, get, stats, list-recipients и get-recipient |
| emails:send | broadcasts send, которому нужен ещё и audiences:read, и broadcasts cancel |
- Ключ, ограниченный определёнными адресами или доменами, читает и пишет ту же адресную книгу, что и любой другой ключ. Он видит только рассылки, отправленные с адреса или домена, который ему выдан, получает из
list-peopleодни сохранённые контакты и получает отказ 422capability_unsupportedотcontacts list-threads,activity,blockиunblock, а также отsuppressions addиremove. - Вход через браузер участника, у которого есть доступ только к некоторым адресам, получает отказ 422
capability_unsupportedв каждой командеcontacts,audiencesиbroadcasts.suppressions addотклоняет вход через браузер любого, кроме владельца рабочего пространства.
Разобранные примеры
Соберите аудиторию из файла, а затем посчитайте, до кого дошла бы рассылка ей. import-contacts сохраняет адреса, которые ещё не контакты, и повторный запуск ничего не создаёт и не добавляет дважды.
[ { "email": "[email protected]", "name": "Ada Lovelace" }, { "email": "[email protected]", "name": "Grace Hopper" }, { "email": "[email protected]" }]AUDIENCE=$(openemail audiences create --name 'Product updates' --json | jq -r .id)openemail audiences import-contacts "$AUDIENCE" --contacts @contacts.jsonopenemail broadcasts preview --audience-ids "$AUDIENCE"Проверьте рассылку с --dry-run, который выводит запрос и ничего не отправляет, а затем отправьте её. Рассылка создаётся сразу и отправляется в фоне, поэтому опрашивайте get, чтобы следить за ней. В этом теле нет {{unsubscribeUrl}}, поэтому каждая копия получает однострочный футер для отписки.
{ "audienceIds": ["aud_4c1b8e2a7d9f05c36b4e8a71"], "from": "Acme <[email protected]>", "subject": "{{firstName|Hello}}, the September release is out", "html": "<p>Hi {{firstName|there}},</p><p>Here is what changed this month.</p>", "scheduledAt": "2026-10-01T09:00:00Z"}openemail broadcasts send --data @broadcast.json --dry-runBROADCAST=$(openemail broadcasts send --data @broadcast.json --yes --json | jq -r .id)openemail broadcasts get "$BROADCAST"openemail broadcasts stats "$BROADCAST" --grain dayПосмотрите, до кого рассылка не дошла. --ndjson выводит по одному получателю в строке, а --all --json один документ со всеми страницами.
openemail broadcasts list-recipients brd_5a8c1e3f7b2d94a06c8e1f3b --filter bounced --ndjson | jq -r .emailopenemail broadcasts list-recipients brd_5a8c1e3f7b2d94a06c8e1f3b --filter not_opened --all --json | jq ".items | length"openemail suppressions list --reason bounce --all --max 50Скопируйте подписанных участников одной аудитории в другую. jq превращает поток в тело, которое принимает add-contacts, а --data - читает его из stdin. --max 200 ограничивает его 200 адресами, которые принимает один вызов.
openemail audiences list-contacts aud_9f2c4b7e1a0d63d84c5f2e7b --statuses subscribed --max 200 --ndjson \ | jq -s '{ emails: map(.email) }' \ | openemail audiences add-contacts aud_1c4e7a9b2d0f36e85a7c1b4d --data -Удалите все контакты одного домена, записанные редактором. delete-many принимает до 200 адресов за вызов, поэтому xargs -n 200 делит более длинный список. Сначала проверьте пакеты с --dry-run, потому что отмены нет.
openemail contacts list --source auto --all --ndjson \ | jq -r 'select(.email | endswith("@old-vendor.example")) | .email' > leaving.txtxargs -n 200 openemail contacts delete-many --dry-run < leaving.txtxargs -n 200 openemail contacts delete-many --yes < leaving.txtПрекратите отправку на адрес, снова разрешите другой и заблокируйте отправителя. removable говорит, какие строки примет suppressions remove.
openemail suppressions add --email [email protected]openemail suppressions list --q [email protected] --json | jq -r '.items[] | select(.removable) | .id'openemail suppressions remove 7b1e2c3d-4f5a-4b6c-8d7e-9f0a1b2c3d4e --yesopenemail contacts block [email protected]Подтверждения и коды подтверждения
Эти команды просят подтверждения в терминале перед запуском:
| Пространство имён | Просит подтверждения |
|---|---|
| contacts | delete, delete-many, remove-photo и unblock |
| audiences | delete, empty, remove-contact и remove-contacts |
| broadcasts | send и cancel |
| suppressions | remove |
--yesподтверждает за вас. Без присмотра, с--jsonили--no-input, в CI или без терминала, команда, которая спросила бы, останавливается сRefusing to run unattended. Pass --yes to confirm.и кодом выхода2.--dry-runвыводит запрос, который отправила бы команда, и завершается с кодом0, ничего не спрашивая и ничего не меняя.- При входе через браузер
audiences deleteсначала спрашивает код подтверждения, как веб-приложение.--yesникогда его не пропускает, а без присмотра команда останавливается с кодом выхода4. Заранее запуститеopenemail verifyили используйте API-ключ, у которого код не спрашивают никогда. audiences emptyникогда не спрашивает код подтверждения, поэтому проверьте идентификатор, прежде чем передавать--yes.
Постраничный вывод
Каждая команда, которая выводит список, читает одну страницу. Если осталось ещё, передайте выведенный курсор в --cursor с теми же фильтрами или прочитайте всё:
--allчитает все страницы и передаёт элементы потоком: таблицей в терминале и по одному JSON-объекту в строке при передаче в конвейер или с--ndjson.--max <n>останавливается после стольких элементов и подразумевает--all.--jsonвыводит один документ{ items, hasMore, nextCursor }, в том числе с--all.- Неверный или устаревший курсор даёт 400
invalid_cursor. Начните заново без него.
| Команда | Размер страницы |
|---|---|
| openemail contacts list | От 1 до 200, 50, если --limit не говорит иначе |
| openemail contacts list-people | От 1 до 100, 25, если --limit не говорит иначе |
| openemail contacts list-threads | От 1 до 100, 25, если --limit не говорит иначе |
| openemail audiences list | От 1 до 100, 25, если --limit не говорит иначе |
| openemail audiences list-contacts | От 1 до 200, 50, если --limit не говорит иначе |
| openemail broadcasts list | От 1 до 100, 25, если --limit не говорит иначе |
| openemail broadcasts list-recipients | От 1 до 200, 50, если --limit не говорит иначе |
| openemail suppressions list | От 1 до 100, 25, если --limit не говорит иначе |
Полезно знать
contacts createотклоняет адрес, который уже есть в книге, с 409contact_exists, поэтому повтор никогда не перезапишет имя, которое кто-то исправил.contacts saveникогда не отказывает: он сохраняет, оставляет или возвращает адрес, в каком бы состоянии тот ни был.contacts deleteпринимает и адрес, который встречался только в почте, и это убирает человека изlist-people. Почта остаётся. Отмены нет: если снова сохранить адрес, контакт начнётся без имени, без заметок и без аудиторий, кроме аудитории по умолчанию.- Адрес это сама суть контакта, поэтому
contacts updateне может его изменить. Перенос контакта этоdeleteиcreate. contacts set-photoчитает изображение из файла или из stdin через-. Передайте--content-type, напримерimage/jpeg: без него изображение может уйти какapplication/octet-stream, и сервер отклонит его с 422invalid_image.broadcasts send --scheduled-atпринимает время ISO 8601, например2026-10-01T09:00:00Z, или длительность ISO 8601, напримерPT2HилиP1D, до 365 дней вперёд. Короткие задержки, которые принимаетsend --at, например2h, здесь отклоняются.- Поля подстановки работают в
--subject,--htmlи--text:{{firstName}},{{lastName}},{{name}},{{email}}и{{unsubscribeUrl}}, у каждого может быть запасное значение после черты, как в{{firstName|there}}. Тело без{{unsubscribeUrl}}получает однострочный футер для отписки. Шаблон отправляется как есть, поэтому ставьте ссылку в шаблон. - Рассылка сверяется с месячным лимитом отправок тарифа до того, как что-либо будет записано, и каждая копия считается одной отправкой. Рассылка, которую лимит не покрывает, отклоняется с 429
send_quota_exceeded, и после неё ничего не остаётся. - Передавайте свой
--idempotency-keyвbroadcasts send, если скрипт может повторить этот шаг. С тем же ключом в ответ приходит уже созданная рассылка, а новая не отправляется. - Контакт, отписавшийся от рассылки, остаётся в аудитории с заполненным
unsubscribedAt, и следующие рассылки этой аудитории его пропускают.audiences list-contacts --statuses unsubscribedперечисляет таких. - Жёсткий отказ остаётся в списке подавления.
suppressions removeотклоняет его с 409suppression_not_removable, аremovableв каждой строке сообщает об этом заранее.