API, Betta 0.153 2017-07-17

Формат протокола

Вызов

Адрес для вызова

https://api.sendsay.ru/

Транспорт - HTTP 1.0/1.1 over TLSv1.2/v1.1/v1 - RFC 2616 http://tools.ietf.org/html/rfc2616, RFC 5246 http://tools.ietf.org/html/rfc5246

Основной протокол - TLSv1.2

Рекомендуемые расширения - SNI и NPN/ALPN

Возможная альтернатива - TLSv1.1

Поддерживается, но не рекомендуется - TLSv1

Настоятельно рекомендуется уметь обрабатывать статусы 30x возможные при работе по HTTP.

Имя api.sendsay.ru имеет несколько ip-адресов. При невозможности соединения с каким-то из этих адресов необходимо повторять запрос с использованием других.

Метод вызова

Метод вызова - POST.

Максимальный размер запроса - в настоящий момент 400 Мбайт.
Свяжитесь со Службой Поддержки если этого вам мало.

Не забывайте, что у метода POST есть два типа кодирования данных:

- application/x-www-form-urlencoded - все параметры вызова передаются в теле POST запроса в кодировке urlencode (RFC 1738 http://tools.ietf.org/html/rfc1738)

- multipart/form-data - (RFC 2388 http://tools.ietf.org/html/rfc2388) - каждый параметр вызова передаётся теле POST запроса в отдельной части и urlencoding не применяется

При любом из этих типов кодирования передаваемые данные передаются в теле POST запроса.

Параметры POST запроса https-вызова

Описание формата JSON вы найдёте в RFC 7159 http://tools.ietf.org/html/rfc7159

При передаче этих параметров как application/x-www-form-urlencoded вы должны сначала закодировать их значения с помощью urlencode-кодирования и только потом собирать из названий и значений параметров строку запроса.

Примерный алгоритм сборки запроса для вызова с application/x-www-form-urlencoded приведён в разделе "Общие замечания".

Проверка request.id

Не смотря на то, что параметр request.id не обязателен и служит, в целом, только для облегчения отладки, есть одно его применение позволяющее избежать нежелательных последствий.

Если request.id не пустой и action входит в число контролируемы, то повторный вызов с таким же request.id в пределах 15 минут заканчивается сразу ошибкой.

Контролируемые вызовы:

issue.send
issue.send.multi
member.import
member.import.probe

вверх

Последовательность обработки

Последовательность обработки вызовов не гарантирована, так как они выполняются параллельно.

Если вам требуется твёрдая уверенность в том, что вызов Б нормально воспользуется результатами более раннего вызова А, то вы должны дождаться явного окончания вызова А и только потом посылать вызов Б.

Например, два вызова anketa.quest.add в случае, когда они сделаны для одной и той же анкета и второй вызов послан до окончания первого, могут дать три разных результата
- добавятся оба вопроса
- только первый
- только второй

Так же следует помнить о порядке обработки при удалении сложных объектов. Например, если удаляемый объект ссылается на другие необходимые ему объекты и они так же при этом должны быть удалены, то сначала следует удалить основной объект и явно дождаться результата и только после этого удалять объекты на которые он ссылался. Иначе, при параллельной обработке может сложиться ситуация, когда вспомогательный объект не будет удалён, так как ещё будет существовать основной (вызов удаления вспомогательного объекта дошёл до обработки ранее завершения удаления основного).

Не заданные параметры api-вызова

Если явно не указано иное, то необязательный параметр служащий для указание логики работы будет трактоваться как имеющий значение 0 или логическое значение "нет" если он отсутствует или задан, но пустой.

Если явно не указано иное, то необязательный параметр служащий для указания текстовых строк будет трактоваться как имеющий пустое значение если он отсутствует.

Ответ

Ответ выдаётся в формате JSON и кодировке UTF-8

Синхронность

Если иного не описано, то вызов является синхронным - запрошенное действие будет выполнено (или не выполнено при ошибке) к моменту ответа на вызов.

Если вызов помечен как асинхронный, то успешный ответ обозначает что запрошенное действие принято и будет выполнено позже в порядке обработки очереди запросов на такое же действие.

Асинхронный вызов возвращает track.id - номер запроса для отслеживания с помощью track.get

Общие поля ответа


{

 "request.id" : "то что было в запросе в параметре request.id" 

,"duration" : null или "время обработки запроса в секундах" 

}

Нормальный ответ


{

 <общие поля>

,<поля специфические для конкретного запроса>

}

Формат ответа при ошибке

При наличии ошибки (ошибок) препятствующих полному или частичному выполнению запроса в ответе появится массив errors со списком описаний каждой ошибки.

Обратите внимание, что, зависимости от вызова и самой ошибки, поле explain может быть не только строкой, но и массивом и объектом.

Часть вызовов может возвращать массив warnings аналогичный структуры для того что бы сообщить о событиях помешавших выполнить вызов в какой-либо его части, но не позволяющих считать что вызов ошибочен.


{

 <общие поля>

."errors" :  [
              {
               "id" : "код ошибки-1" 

              ,"explain" : "возможное более развёрнутое описание-1" 

              ,<возможно поля специфические для конкретного запроса-1>
              }
             ,{
               "id" : "код ошибки-2" 

              ,"explain" : "возможное более развёрнутое описание-2" 

              ,<возможно поля специфические для конкретного запроса-2>
              }

              ......

             ]
}

вверх

Специальный ответ "Перенаправление"

При наличии в ответе поля REDIRECT необходимо

Так же стоит иметь счётчик перенаправлений что бы избежать зацикливания.

В данный момент ограничение максимальной глубины перенаправлений десятью выглядит достаточным.


{
 <общие поля>

,"REDIRECT" : "/xxx/yyy" 

}

вверх

Специальный ответ "Смена пароля"

Получение ответа с ошибкой "error/auth/failed" и уточнением "force_change_password" означает, что для продолжения работы данной комбинацией login/sublogin требуется сменить пароль.

Это может быть вызвано как автоматическими событиями (например истёк срок действия пароля), так и прямой установкой такого требования через вызов user.set или sys.password.set

Для продолжения работы необходимо:


{
 <общие поля>

,"errors"   : [ 
               {
                "id: : "error/auth/failed" 

               ,"explain" : "force_change_password" 
               }
              ]
}

вверх

Кэширование

Часть вызовов поддерживает кэширование ответов, что позволяет быстрее получать ответ если вы уверены, что данные не изменились и не придумывать свою собственную систему кэширования. В данный момент это вызовы stat.uni и member.list.count

Кэширование применяется только для вызовов завершившихся без ошибок.

Для использования кэширования в вызове передаётся параметр cache с как минимум значениями mode и key.

В ответ вызова использующего кэширование с режимами "use", "fetch" и "refresh" будет добавляться параметр cache с информацией о результате использования кэша.

Если вызов асинхронный, то параметр cache будет отсутствовать в ответе на основной вызов:

Если вызов асинхронный, то при режиме "fetch" и при отсутствии данных основной вызов сразу завершиться с hit == 0, а асинхронная часть даже не начнётся и её действия не будут выполнены. Проверяйте на наличие hit и его равенство 0 для установления факта не вызова асинхронной части.

Вызов:

{
 "action: "xxx" 

,"cache" : {
            "mode" : режим -- обязательно
                           --
                           -- ignore  - не использовать
                           --
                           -- use     - при наличии кэша - ответ из него
                           --         - при отсутствии - получить новый результат и закэшировать его
                           --
                           -- refresh - получить новый результат и закэшировать его
                           --
                           -- fetch   - при наличии кэша - ответ из него
                           --         - при отсутствии - зависит от вызова, но в общем случае нет даже самих ключей ответа
                           --                            проверяйте сначала на hit == 0
                           --         - в зависимости от вызова, не требуется передача всех или почти всех параметров необходимых
                           --           для работы вызова так как, при отсутствии данных в кэше, они не вычисляются заново.

            ,"key" : ключ кэша -- обязательно
                               --
                               -- до 64 печатных символов ASCII кроме пробела
                               -- для каждого вызова набор уникальных ключей свой
                               -- можно использовать один и тот же ключ с разными вызовами, они не будут смешаны
                               --
                               -- каждый ключ виден всем суб-логинам одного обшего логина - один может что-то посчитать
                               -- и закэшировать, а остальные воспользоваться результатом

            ,"ttl" : желаемое максимальное время жизни кэша в секундах
                       -- не обязательно, применимо для use и refresh при записи к кэш
                       --
                       -- при отсутствии - теоретически время не ограничено
                       --
                       -- практически - любая запись в кэше может перестать существовать в любой момент
                       -- и данные будут получены путём обычного выполнения запроса
           }

 <прочие параметры вызова>
}

В ответ на "refresh":

{
 ....

 "cache" : {
            "hit" : 0

           ,"created" : "YYYY-MM-DD hh:mm:ss" -- дата и время занесения записи в кэш

           ,"expired"  : null или "YYYY-MM-DD hh:mm:ss" -- дата и время окончания времени жизни записи.
                                                        --  null - бесконечно, но прочитайте выше примечания к параметру ttl
           }

 ....
}

При отсутствии данных в кэше в ответе на "use" и "fetch" будет:

{
 ....

 "cache" : {
            "hit" : 0
           }

 ....
}

При использовании данных иэ кэша в ответе на "use" и "fetch" будет:

{
 ....

 "cache" : {
            "hit" : 1

           ,"created" : "YYYY-MM-DD hh:mm:ss" -- дата и время занесения записи в кэш

           ,"expired"  : null или "YYYY-MM-DD hh:mm:ss" -- дата и время окончания времени жизни записи.
                                                        --  null - бесконечно, но прочитайте выше примечания к параметру ttl
           }

 ....
}

вверх

Возвращаемое значение

Часть вызовов имеет возможность указать, что результат их работы можно сохранить как отчёт или загрузить на клиентский сервер.

Такие вызовы поддерживают не обязательный параметр result содержащий один или несколько способов использования результата.


 <другие параметры вызова>

,"result" : [
             { "type" : .... способ-сохранения-результата-1 }
            ,{ "type" : .... способ-сохранения-результата-2 }
            ............
            ]

Если параметр отсутствует, задан как пустой скаляр или задан как пустой массив, то выбирается значение по умолчанию "response".

Если запрошенное действие "response" или "none", то работа вызова происходит синхронно и его результат возвращается сразу в ответе.

Иначе вызов возвращает track.id - номер запроса для отслеживания с помощью track.get, а само действие производится асинхронно.

Возможные действия с результатом приведены ниже.

Во всех случаях использования http(s)/ftp(s) ссылок в них работает кастомизация текущим временем {DT...} или случайным числом {RND} как описано в "Кастомизированные ссылки". Такая же кастомизация работает для параметра filename. Таким образом, при использовании вызовов в "Действиях по расписания" имеется возможность получать каждый раз разные ссылки и имена файлов.

Везде где можно указать format можно указать для него не обязательные параметры:

- compress - zip, bzip2, gzip или пусто (не сжимать). по умолчанию не сжимать для xlsx и сжимать zip для csv, html и json

- separator - разделитель полей для формата csv. по умолчанию - запятая. игнорируется для xlsx, html и json

Отправить результат на электронную почту

После вычисление результата на адреса указанные в массиве to высылается письмо с прикреплённым файлом.

Название файла задаётся в необязательном параметре filename, а при его отсутствии генерируется автоматически.

Формат файла задаётся в необязательном параметре format, а при его отсутствии используется csv

Можно использовать черновик выпуска для настройки содержимого письма указав его номер в необязательном параметре draft.

    { "type": "email_file", "to" : [ <email1>, .... ], "draft": <draft_id>, "filename": <filename>, "format": <xlsx|csv|html|json> }, 

Сохранить в Отчёты

После вычисление результата он сохраняется в хранилище отчётов.

Срок хранения файла - 90 дней, после чего он автоматически удаляется.

Название файла задаётся в необязательном параметре filename, а при его отсутствии генерируется автоматически. Название файла в дальнейшем доступно в результатах отслеживания запроса через track.get

Формат файла задаётся в необязательном параметре format, а при его отсутствии используется csv.

    { "type": "save", "to": <filename>, "format": <xlsx|csv|html|json> },

Загрузить на внешний ресурс пo http(s)

После вычисление результата он передаётся по ссылке указанной в to.

Для загрузки по http(s) используется метод POST с типом multipart/form c именем переменной указанным в name (не обязательно, по умолчанию "file") и именем файла указанным в filename (не обязательно, по умолчанию генерируется автоматически).

Формат файла задаётся в необязательном параметре format, а при его отсутствии используется csv

    { "type": "url_file", "to" : <http(s)-url>, "name" : "<post-arg-name>", "filename": <post-filename>, "format": <xlsx|csv|html|json> },

Загрузить на внешний ресурс пo ftp(s)

После вычисление результата он загружается по ссылке указанной в to.

Для загрузки по ftp(s) указывается сервер и каталог на сервере (параметр to) в который загружается файл с именем указанным в filename (не обязательно, по умолчанию генерируется автоматически).

Формат файла задаётся в необязательном параметре format, а при его отсутствии используется csv

    { "type": "url_file", "to" : <ftp(s)-url>, "filename": <filename>, "format": <xlsx|csv|html|json> }

Отправить уведомление на электронную почту

После вычисление результата на адреса указанные в массиве to высылается письмо с уведомлением об окончании расчёта.

Обратите внимание, что сам результат в письме не содержится и его получение должно быть предусмотрено отдельно (email_file,save,url_file).

Можно использовать черновик выпуска для настройки содержимого письма указав его номер в необязательном параметре draft.

    { "type": "email_notify", "to" : [ <email1>, .... ], "draft": <draft_id> }, 

Отправить уведомление по sms

После вычисление результата на номера телефонов указанные в массиве to высылается сообщение с уведомлением об окончании расчёта.

Обратите внимание, что сам результат в письме не содержится и его получение должно быть предусмотрено отдельно (email_file,save,url_file).

    { "type": "sms", "to" : [ <cellphone1>, <cellphone2> ] },

Уведомить по внешней ссылке

После вычисление результата вызывается ссылка указанная в to (методом GET).

Обратите внимание, что сам результат в вызове не содержится и его получение должно быть предусмотрено отдельно (email_file,save,url_file).

    { "type": "url_notify", "to" : <url> },

Вернуть в ответе

Результат вычисляется синхронно и возвращается в ответе.

Это действие по умолчанию. Для его использования просто не указывайте ничего.

    { "type": "response" },

Не возвращать

Это действие имеет смысл только для вызова Универсальной статистики при задании пересчёта кэша и позволяет сократить размер ответа.

Для всех других вызовов трактуется как "response".

    { "type": "none" },

вверх

Несколько запросов за один вызов

Все способы авторизации работают только в основном запросе и игнорируются во вложенных.

Запросы выполняются последовательно.

Запросы изолированы друг от друга и ничего не знают о результатах работы своих соседей. Т.е. схема "в одном запросе создать объект, а в следующем его изменить" в общем случае работать не будет.

{
 "action" : "batch" 

,"stop_on_error" : 0|1 -- прекращать выполнение после первого же запроса закончившегося с ошибками
                       -- не обязательно. по умолчанию 0
,"do" : [

               { один запрос }

              ,{ другой запрос }

              ....
             ]
}

ответ

{
 <общие поля>

,"result" : [

               { один ответ }

              ,{ другой ответ }

              ....
             ]
}

вверх

Проверка доступности и авторизации

Пинг без авторизации


{

 "action" : "ping" 

}

ответ


{

 <общие поля>

,"pong" : "что-то, более-менее каждый раз разное" 

}

вверх

Пинг с авторизацией


{

 "action" : "pong" 

}

ответ


{

 <общие поля>

,"ping" : "что-то, более-менее каждый раз разное" 

}

вверх

Авторизация

Настоятельно рекомендуется для вашей же безопасности при реализации автоматической работы по API создавать для этих целей отдельный суб-логин с максимально органиченными правами !

Авторизация по логину и паролю

Полученный в ответ номер сессии должен передаваться во всех последующих запросах в request в параметре "session" (кроме запросов "ping" и "login" в которых он игнорируется).

Время жизни сессии несколько часов. Рекомендуется завершать явным вызовом "logout".

В случае окончания времени действия сессии вы получите ответ об ошибке авторизации, а запрошенное действие выполнено не будет.

Для продолжения работы будет необходимо:


{

 "action" : "login" 

,"login"  : "общий логин" 

,"sublogin" : "личный логин" 

,"passwd" : "пароль" 

}

ответ


{

 <общие поля>

,"session" : "номер сессии" 

,"login" : "общий логин для которого выдана авторизация" 

,"sublogin" : "личный логин для которого выдана авторизация" 

}

вверх

Авторизация AGSES-карт. Запрос.

Авторизация биометрических карт AGSES происходит в два этапа.

На этапе запроса вы получаете flicker-код, на этапе ответа сообщаете код полученный клиентом от своей карты
и, в случае успеха, в ответ получаете номер сессии.


{

 "action" : "login.agses.challenge" 

,"card" : номер карты, наличие лидирующих 0 не обязательно

}

ответ


{

 <общие поля>

,"flicker" : код фликера

,"hedgeid" : номер запроса

}

вверх

Авторизация AGSES-карт. Ответ.

Сессия, полученная в этом вызове точно такая же по своим свойствам как и сессия
от обычного вызова login

Храните её в надёжном, cухом, светлом месте вдали от детей.


{

 "action" : "login.agses.response" 

,"card" : номер карты, наличие лидирующих 0 не обязательно

,"hedgeid" : номер запроса

,"response" : ответ клиента

}

ответ


{

 <общие поля>

,"session" : "номер сессии" 

,"login" : "общий логин для которого выдана авторизация" 

,"sublogin" : "личный логин для которого выдана авторизация" 

}

вверх

Авторизация разовая

Настоятельно рекомендуется, для вашей же безопасности, не реализовывать автоматическую работу по API с использоватением этого вызова !
Не ленитесь реализовать нормальную работу с помощью сессий, вместо простой постоянной пересылки логина и пароля !

Единственный разумный повод использования разовой авторизации - смена пароля в ответ на ошибку force_change_password.

Указанный параметр передаётся во параметрах вызова вместо "session" и может использоваться с любым вызовом требующим авторизации.

Авторизация заканчивается в окончанием вызова и завершения вызовом "logout" не требует.

Процессы порождённые асинхронными вызовами продолжают работать-и-работать до их естественного завершения.


{

 "one_time_auth" : {

                    "login"  : "общий логин" 

                   ,"sublogin" : "личный логин" 

                   ,"passwd" : "пароль" 

                   }

}

вверх

Окончание работы

Текущая сессия становится не действительной


{

 "action" : "logout" 

}

ответ


{

 <общие поля>

}

вверх

Логины и пароли в ссылках

Многие вызовы api могут получать данные осуществляя доступ по http(s)/ftp(s) к сторонним серверам.

Хорошей практикой будет требовать логин-пароль при доступе к таким данным.

Однако использование стандартной для такого случая схемы записи ссылки с указанием логина-пароля прямо в ней

http://login:password@host.tld/bla/bla

хоть и, разумеется поддерживается, но не является полностью безопасным - например, при работе нескольких
сотрудников с аккаунтом, все они смогу увидеть логин-пароль если он указан в действиях по расписанию или
в отложенных выпусках.

Создайте одни раз объект "Внешняя авторизация" (вызов authext.create, type = 0, login = "метка для памяти", "token" = "логин:пароль")
и используйте его номер в ссылках в виде

http://authext:777@host.tld/bla/bla

вверх

Отслеживание асинхронных запросов

Список асинхронных запросов

Список всех или отобраных по фильтру запросов.

Информация о запросах старше двух месяцев регулярно автоматически удаляется из системы.

Информация о запросах issue.send/personal хранится ещё меньше - два дня.

По умолчанию, не выдаются запросы issue.send/personal. Для их получения явно указывайте action = "issue.send/list".

{

 "action" : "track.list" 

,"filter" : { -- обязателен как минимум один параметр фильтра

             "action" : "код api-вызова"  или [ "код-1","код-2"... ]

             -- или
            ,"status" : интересующее состояние запроса -- одно число или список чисел но в массиве
             -- или
            ,"!status" : состояние запроса которое исключить -- одно число или список чисел но в массиве

            ,"dt.from" : "YYYY-MM-DD hh:mm:ss" -- самая ранняя дата создания запроса на отслеживание

            ,"status.dt.from" : "YYYY-MM-DD hh:mm:ss" -- самая ранняя дата изменени статуса запроса на отслеживание

            ,"reltype" : число - фильтр по reltype

            ,"relref" : число - фильтр по relref
            }
}

ответ

{
 <общие поля>

 "list" : [
            {
             объект как возвращается track.get
            }

           .........
          ]
}

Описание асинхронного запроса

{

 "action" : "track.get" 

,"id"     : номер трекера

}

ответ

{
 obj : {
        "id" : номер запроса

       ,"dt" : дата-время создания (YYYY-MM-DD hh:mm:ss)

       ,"action" : -- тип запроса
                    "issue.send" 
                    "issue.send/personal" 
                    "stat.uni" 
                    "member.list" 
                    "member.list.count" 
                    "member.import" 
                    "member.sendconfirm" 
                    "member.update" 
                    "member.delete" 
                    "member/activate" 
                    "stat.activity" 
                    "stat.issue" 
                    "stat.group.portrait" 
                    "stat.group.common" 
                    "issue.split.create" 
                    "stoplist.add" 
                    "stoplist.delete" 
                    "sequence.member.start" 
                    "sequence.member.pause" 
                    "sequence.member.resume" 
                    "sequence.member.stop" 
                    "email.test" 
                    "email.cleanerror" 

       ,"status" : состояние запроса
                    -- -7 - действие приостановлено
                    -- -6 - действие не прошло модерацию
                    -- -5 - действие на премодерации
                    -- -4 - отложенное действие (например отложенный выпуск рассылки)
                    -- -3 - отменён
                    -- -2 - закончился ошибкой
                    -- -1 - закончился успешно
                    --  0 - принят
                    --  1 - запущен
                    --  2 - начата обработка
                    --  3 - сортировка
                    --  4 - форматирование
                    --  5 - генерация отчёта
                    --  6 - антиспам проверка
                    -- остальное - зависит от типа запроса

       ,"status.dt" : дата-время установки текущего состояния (YYYY-MM-DD hh:mm:ss)

       ,"error"  : код ошибки    -- не обязательно
                                 -- смысл зависит от типа запроса

       ,reltype" : ...
       ,relref" : ...

   -- дополнительные параметры в зависимости от запроса и его состояния

      ,"param" : {

   -- === issue.send ===
   -- === issue.send/personal ===

                   ,"group.id" : код группы

                     -- характеристики выпуска, если они уже известны

                   ,"issue.id" : номер выпуска

                   ,"issue.dt" : время начала выпуска

                   ,"issue.size" : количество смсок или примерный размер письма в байтах

                   ,"issue.members" : количество получателей для которых уже сформированы письма

                   ,"issue.format" : "email" , "sms" или "viber" 

                   -- в состоянии "начата обработка" примерная информация о достигнутом прогрессе

                   ,"eta" : {

                             "rest"        : примерное количество секунд до окончания

                            ,"perc"        : примерный процент обработки

                            ,"dt.finished" : примерное дата-время окончания

                            ,"dt.updated   : дата-время последнего обновления информации eta
                            }  

                   -- информация о номере письма для Транзакционных писем после завершения работы

                   ,"letter" : номер письма -- пусто если адресу не было отправлено письмо (например подписчик отписался или какие-то ошибки выпуска)

                   -- отчёт и статистика после завершения работы для Экспресс-Выпуска

                   ,"report_file" : "название файла" -- если были ошибки, то название файла с отчётом для вызова rfs.file.get

                   ,"statistic" : {
                                   "total"       : всего строк
                                  ,"taked"       : верных строк
                                  ,"erroneous"   : строк с ошибками
                                  ,"repeated"    : повторов адресов
                                  ,"only_unique" : 0|1 -- был ли выпуск запущен с запретом повторной
                                                       -- рассылки по дублирующимся адресам
                                  }

   -- === stat.uni ===

                    "report_file" : "название файла" -- пусто до окончания запроса
                                                     -- после окончания - название файла с отчётом для вызова rfs.file.get
                                                     -- или пусто если заказывалась высылка на почту
   -- === stat.activity ===
   -- === stat.issue    ===

                    "group.id" : "код группы" -- пусто если считалось без указания группы
                                              -- иначе код группы указанной для расчёта

                   ,"report_file" : "название файла" 

   -- === stat.group.portrait ===
   -- === stat.group.common   ===
   -- === member.list         ===

                    "group.id" : "код группы" -- пусто если считалось без указания группы
                                              -- иначе код группы указанной для расчёта

                   ,"report_file" : "название файла" 

                   ,"eta" : { в состоянии "начата обработка" примерная информация о достигнутом прогрессе }

   -- === member.import ===

                    "group.id" : "код группы" -- пусто если при импорте не указывалась автосоздание новой
                                              -- или заполнение существующей группы иначе код автосозданой
                                              -- существующей группы

                   ,"eta" : { в состоянии "начата обработка" примерная информация о достигнутом прогрессе }

                   -- отчёты и статистика после завершения работы

                   ,"report_file" : "название файла" -- файл в формате xlsx с отчётом в хранилище отчётов если были ошибки.
                                                     -- первые 1000 ошибок для просмотра человеком

                   ,"report_file.json" : "название файла" -- файл в формате json запакованый zip с отчётом в хранилище отчётов если были ошибки.
                                                          -- все ошибки в машиноразбираемом виде

                   ,"report_file.csv" : "название файла" -- файл в формате csv запакованый zip с отчётом в хранилище отчётов если были ошибки.
                                                         -- все ошибки в машиноразбираемом виде

                   ,"statistic" : {
                                   "inserted"    : добавлено новых адресов
                                  ,"updated"     : обновлено адресов
                                  ,"economed"    : без изменений (вносимые данные совпали с имеющимися)
                                  ,"erroneous"   : строк с ошибками
                                  ,"unconfirmed" : новых адресов внесённых без подтверждения
                                  ,"repeated"    : повторов адресов
                                  }

   -- === member/activate ===

                    "group.id" : "код группы" -- код активируемой группы

                   ,"eta" : { в состоянии "начата обработка" примерная информация о достигнутом прогрессе }

                   -- статистика после завершения работы

                   ,"statistic" : {
                                   "done       : активировано адресов
                                  ,"notneed"   : активация не требовалась скольки адресам 
                                  ,"overlimit" : не активировано адресов из-за исчерпания лимита
                                  }

   -- === member.list.count  ===
   -- === member.sendconfirm ===
   -- === member.update ===
   -- === member.delete ===
   -- === stoplist.add ===
   -- === stoplist.delete ===
   -- === group.snapshot ===
   -- === group.clean ===
   -- === sequence.member.start ===
   -- === sequence.member.pause ===
   -- === sequence.member.resume ===
   -- === sequence.member.stop ===
   -- === email.test ===
   -- === email.cleanerror ===

                    "group.id" : "код группы" -- пусто если считается без указания группы
                                              -- иначе код группы указанной для расчёта

                   ,"eta" : { в состоянии "начата обработка" примерная информация о достигнутом прогрессе }

   -- === issue.split.create  === 

                    -- отслеживается не "создание" (как формально следует из названия)
                    -- а процесс формирования списка получателей и назначения им того или иного варианта
                    -- соответственно, трекер отслеживает тестирование в состояние "подготавливается к запуску" 

                    "split.id" : "код группы" -- номер сплит-тестирования

                   ,"eta" : { в состоянии "начата обработка" примерная информация о достигнутом прогрессе }

                  }
       }
}

вверх

Подписчики

Идентификация подписчика

Каждый подписчик имеет свой набор связанных с ним данных и идентифицируется в системе своими адресом электронной почты - email, и/или номером мобильного телефона - msisdn, и/или клинетским идентификатором - csid.

Именно их вы указываете в параметре "email" и данных импортирования.

При сохранении, каждому идентификатору система назначает свой внутренний целочисленный номер. Он постоянен (даже если подписчика удалить и потом создать заново) и доступен вам как member.id

Так как указание адреса или телефона в открытом виде не всегда допустимо, то система предоставляет клиенту две возможности по их сокрытию - "Клиентский индентификатор" и "Заменитель адреса" описанные ниже.

Использование адреса электронной почты

Система полностью поддерживает национальные доменные имена (IDN) и национальные имена получателей. Вы можете без проблем использовать адреса не только из домена .рф (например проверка@тест.рф), но и из любых других национальных доменов (например 企业@企业.企业) и записывать из без всяких ухищерений (типа xn--кодирования) просто как есть.

Система нормализует email приводя их к написанию маленькими буквами (да, формально это не по стандарту, но такова практика всех почтовых систем).

Так же производится проверка email-адреса на соответствие правилам RFC и правильности указания его домена верхнего уровня.

Национальные почтовые адреса по умолчанию включены для всех аккаунтов созданных начиная с 2016-07-26. Для аккаунтов созданных ранее возможность можно включить через параметр email.utf8 вызова sys.settings.set. Если к моменту включения вы уже имели в базе адреса с xn--кодированием, то обратитесь в Службу Поддержки для их преобразования.

Использование номера мобильного телефона

Система нормализует номера телефонов к виду +7XXXYYYYYYY

Так же производится проверка номера телефона на наличие в реестре "Российской системы и плана нумерации" Федерального агентства связи.

Использование клиентского идентификатора

Клиентский идентификатор регистрозависим и нормализуется только удалением начальных и конечных пробельных символов. Остальное в нём - ваше дело. Для системы это просто набор символов.

Скорее всего у вас есть свой уникальный идентификатор подписчика. Если вы будете вносить его вместе с прочими данными подписчика, то сможете включать его в отчёты на ряду или вместо email.

Но кроме такого простого использования вам доступен более продвинутый способ - система может следить за уникальностью вашего клиентского идентификатора при внесении и изменении данных и вы можете указывать его в Экспресс-Выпуске и Транзакционных Письмах вместо реального адреса.

Возможность системы использовать клиентский идентификатор в выпуске рассылки место настоящего адреса позволяет реализовать сложные сценарии работы с рассылками без раскрытия адресов третьим лицам.

Например, по вашему поручению, подрядчик собирает какие-то данные о посетителях вашего сайта и должен периодически делать по ним рассылки. Он не знает адресов ваших посетителей, а знает только их идентификаторы, что в обычной ситуации не позволит ему произвести рассылку.

Если же вы подключите в Службе Поддержки возможность делать выпуски и импорты по клиентскому идентификатору, то подрядчик сможет производить Экспресс-Выпуски и Транзакционные Письма с использованием известного ему индентификатора который система сама превратит в адрес получателя.

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

Хранение заменителя адреса

Вы можете хранить в системе не настоящие email-адреса, а их заменители. Главное, что бы по заменителю адреса вы могли однозначно понять о каком вашем подписчике идёт речь.

Во всех отчётах и данных системы будет использоваться именно заменитель.

А реальный адрес необходимо будет указывать только при выпуске рассылки (к сожалению, при использовании заменителя адреса вам будет доступен только Экспресс-Выпуск и Транзакционные Письма).

Данная возможность настраивается по обращению в Службу Поддержки.

Мультиканальность

Одному подписчику могут соответствовать несколько идентификаторов по которым вы получите доступ к одному и тому же набору данных.

Думайте об этом как о Змее-Горыныче: несколько голов (идентификаторов) с одним телом (набором данных).

В данный момент доступны идентификаторы трёх видов: email-адрес, номер мобильного телефона и клиентский идентификатор.

По какой бы голове-идентификатору вы не вели работу с подписчиков вы получаете доступ к одному и тому же телу-набору данных.

У набора данных должен быть хотя бы один идентификатор связанный с ним (тело без головы не живёт).

Участие подписчика в группах-списках учитывается по идентификаторам. Разные идентификаторы могут быть внесены в разные группы-списки (впрочем, в одну и туже - тоже).

Действия совершаемые подписчиком (доставка письма, сообщения; чтение письма; переход по ссылке; отписка; подтверждение внесения в базу; ошибки доставки и прочие) запоминаются за тем его идентификатором от которого произведены эти действия.

Например, если у тела, есть почтовая голова с фатальными ошибками доставки, то из рассылки email такое тело будет исключаться, но на рассылку sms это не повлияет.

Благодаря поддержке нескольких идентификаторов у одного подписчика вам доступны разнообразные сценарии мультиканального взаимодействия с ним. Например "Выслать письмо, а тем кто за 10 дней так и не прочёл и не сделал ни одного перехода выслать смс".

Форматы хранения

В данный момент имеется два формата хранения - текущий, с моделью Анкета-Вопрос-Ответ (АВО) и новый, с моделью хранения Ключи Данных (КД).

Формат АВО проверен временем, но имеет несколько недостатков делающих его не пригодным для реализации сложных проектов.

Формат КД разработан как замена АВО и может быть использован в любых проектах.

Формат "Анкета-Вопрос-Ответ"

Формат АВО хранит данные в жестко структурированной форме "Анкет" которые состоят из "Вопросов" разных типов.

Данные о подписчике могут быть представлены только как "Ответы" на "Вопросы Анкет".

Формат АВО имеет только одно преимущества

- при доступе к данным и изменении данных система следит за тем, что указаны существующие Анкета и Вопрос и проверяет соответствие данных типу Вопроса.

Недостатки формата АВО более многочислены и печальны

- ограничен набор символов которые можно хранить

- не возможно хранить несколько ответов на один Вопрос (массив)

- не возможно хранить структуры данных произвольной сложности - иерархия данных только двухуровневая Анкета-Вопрос

- в перспективе формат будет полностью заменён на КД и объявлен устаревшим

Формат "Ключи Данных"

(Изучите отдельную главу описывающую ключи данных)

Новый формат КД разработан для замены форматы АВО и поддержки хранения сложных структур данных

- Данные формата АВО полностью доступны через КД

- Нет ограничения на набор хранимых данных

- Нет ограничений на сложность иерархии хранения данных и их сочетание (например "адреса подписчика" могут представлять собой массив из объектов хранящих структуру одного адреса по компонентам (страна, гоород, улица, дом, квартира, координаты), а компоненты адреса в свою очередь там же быть объектами - "координаты" это "широта" и "долгота").

- Нет ограничений на формат данных

- У разных подписчиков по одному и тому же ключу данных можно хранить разные стуктуры и типы данных

- Доступ к данным в выпуске рассылки осуществляется как и прежде путём прямой записи [% anketa.ключ.данных %] или через функцию datakey()

Cистемная анкета member

Через системную анкету member вы можете получить дополнительную служебную информацию о подписчике

В формате "Ключи Данных" она имеет вид

{
 "member" : {
             -- информация от наборе данных (теле) подписчика, общая для всех его идентификаторов (голов)

            "create" : {
                        "time" : "2015-09-12 18:53:39" -- дата создания (Ys) набора данных
                       ,"host" : "1.1.1.1"             -- источник создания набора данных
                       }

           ,"update" : {
                        "time" : "2015-09-12 18:53:39" -- дата последнего изменения (Ys) набора данных
                       ,"host" : "1.1.1.1"             -- источник последнего изменения набора данных
                       }

             -- информация о том идентификаторе (голове) через который прочитаны данные, своя для каждого идентификатора

           ,"addr_type" : "email"    -- тип идентификатора (email, msisdn, csid)

           ,"email" : "test@test.ru" -- идентификатор

           ,"domain" : "test.ru"     -- домен почтового адреса если идентификатор email

           ,"id" : "1380900"         -- внутренний номер идентификатора в системе

           ,"haslock" : 0,  -- все блокировки идентификатора в одном параметре. может ли  получать письма, sms или viber
                           -- 0 - нет            - может
                           -- 1 - отписался      - не может
                           -- 2 - не подтверждён - не может
                           -- 4 - имеет фатальные ошибки доставки - не может получать письма
                           -- прочие значения ( 3,5,6,7 ) - одновременное наличие нескольких указанных блокировок

           ,"confirm" : { -- состояние подтверждения внесения в базу
                        "lock" : 0,                    -- 0 - подтверждено, 1 - нет
                       ,"time" : "2015-09-12 18:53:39" -- дата подтверждения (Ys)
                       ,"host" : "1.1.1.1"             -- источник подтверждения
                       }

           ,"error" : { -- состояние ошибок доставки
                       "lock" : 0                       -- заблокирован из-за ошибок доствки (1) или нет (0)
                      ,"date" : "2015-09-12 18:53:39"   -- дата последней ошибки доставки (Ys) (удаляется при первой же успешной доставке)
                      ,"str"  : "name=test.ru type=A: Host not found" -- описание последней ошибки доставки (удаляется при первой же успешной доставке)
                      ,"error" : 1                      -- общее число ошибок доставки произошедших подряд (устанавливается в 0 при первой же успешной доставке)
                     }

           ,"lockremove" : 0 -- адрес отписан или в стоп-листе (1) или нет (0)

           }

}

В формате АВО анкета member содержит теже поля, но с названиями адаптированными к формату анекета.вопрос. Например member.confirm.time

Проверить существование подписчика


{
  "action": "member.exists" 

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn
}

ответ


{
  <общие поля>

 "list" : {
           "адрес электронной почты или номер телефона" : 0|1|null -- подписчик существует (1) или нет (0) в базе, null - ошибка в адресе
                                                                   -- в случае невалидного адреса запись в warnigns
          },

,"warnings" : [ cписок предупреждений про невалидные адреса ]

}

Все подписчики с данным идентификатором

Одна и та же строка символов может быть использована как идентификатор подписчика для разных типов идентификаторов. Так же усложняет жизнь то, что идентификаторы типа email и msisdn хранятся в нормализованном виде.

Это означает, что по строке нельзя однозначно сказать какого типа идентификатор она представляет. Например "test@test.RU" может быть и email-адресом "test@test.ru" и идентификатором типа csid "test@test.RU".

Данный вызов позволяет получить для заданной строки все типы идентификаторов соответствующих и соответствующих ей (с учётом нормализации) подписчиков имеющихся в системе.

{
  "action": "member.find" 

 ,"email": "идентификатор подписчика" 

}

ответ

{
  <общие поля>

 "list" : [

           "тип-1" 

          ,"тип-2" 

           ......

          ]
}

Cоздать подписчика / Установить ответы подписчика (КД)

При отсутствии адреса в базе он автоматически создаётся.


{
  "action" : "member.set" 

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

 ,"source" : "ip-адрес оригинального запроса" 
              -- если оригинальный инициатор запроса вы сами - ваш ip, иначе ip-адрес откуда вам пришёл запрос

 ,"if_exists" : "error" -- не обязательно
                 -- error     - при наличии адреса в базе возвращается ошибка

 ,"newbie.confirm": "подписчик должен подтвердить внесение в базу (1|0)" 
                     -- используется только при внесении email-адресов
                     -- действует лимит внесения без подтверждения
                     -- подробнее описанный в вызове member.import

 ,"newbie.letter.confirm" : "номер шаблона информационного письма" 
                           -- высылается если новый адрес до этого отсутствовал в базе, и внесён с необходимостью подтверждения
                           -- если параметр пуст или отсутствует, то ничего выслано не будет
                           -- но необходимость подтвердить регистрацию никуда не денется и выслать письма вы сможете позже через member.sendconfirm
                           -- этот параметр НЕ зависит от newbie.confirm - если у вас кончился лимит внесения без подтверждения, то импорт
                           -- продолжит вносить уже с подтверждением и этот параметр будет использоваться 
                           -- при внесении номеров телефонов ни какие уведомления не высылаются
                           -- информационное письмо должно иметь заполненный адрес отправителя и не находиться на модерации

 ,"newbie.letter.no-confirm" : "номер шаблона информационного письма" 
                           -- высылается если новый адрес до этого отсутствовал в базе, и внесён с без необходимости подтверждения
                           -- если параметр пуст или отсутствует, то ничего выслано не будет
                           -- при внесении номеров телефонов ни какие уведомления не высылаются
                           -- информационное письмо должно иметь заполненный адрес отправителя и не находиться на модерации

-- записываемые данные

,"datakey" : [

  [ "ключ-данных-1", "режим-1", "новое-значение-1", "тип-1" ]
  [ "ключ-данных-2", "режим-2", "новое-значение-2", "тип-2" ]

 ........
]

-- "режим" имеет следующие значения:
--
--     "set"       - полностью и безусловно заменить имеющиеся данные новыми
--                   если значение присваивается элементу массива, и элемент ещё не существовал, и он не первый, то в массиве появится необходимое количество
--                   элементов (со значением null) предшествующих присваемому
--
--     "update"    - заменить имеющиеся данные новыми только указанный ключ уже есть
--                   ("есть" это именно есть - null, пустая строка, путой массив, пустой объект находящиеся по указанному ключу -это "ключ есть")
--                   иначе изменение игнорируется
--                   поведение при присвоении не первому элементу массива аналогично set (при условии что замена реально была произведена) 
--
--     "insert"    - добавить данные только если указанного ключа ещё нет
--                   ("нет" это именно нет - null, пустая строка, путой массив, пустой объект находящиеся по указанному ключу -это "ключ есть")
--                   иначе изменение игнорируется
--                   поведение при присвоении не первому элементу массива аналогично set (при условии что данные реально были добавлены)
--
--     "merge"     - "set" по каждому ключу нового значения в сущестующее, а именно:
--                   * если имеющиеся данные это объект и новое значение тоже объект, то каждый ключ и его величина из нового значения добавляются при отсутствии
--                   или его величина заменяют собой величину существующего такого же ключа в имеющихся данных если ключ в них уже есть
--                   * если данных по указанному ключу нет и новое значение объект, то новое значение создаст объект по указанному ключу
--                   * в других случаях вызов завершится ошибкой
--
--     "merge_update" - "update" по каждому ключу нового значения в сущестующее, а именно:
--                   * если имеющиеся данные это объект и новое значение тоже объект,то величина каждого ключа из нового значения заменяют собой величину
--                   существующего такого же ключа в имеющихся данных. ключи нового значения отсутствующие в имеющихся данных игнорируются.
--                   * если данных по указанному ключу нет и новое значение объект, то новое значение будет проигнорировано
--                   в других случаях вызов завершится ошибкой
--
--     "merge_insert" - "insert" по каждому ключу нового значения в сущестующее, а именно:
--                   * если имеющиеся данные это объект и новое значение тоже объект, то каждый ключ и его величина из нового значения добавляются при отсутствии
--                   такого ключа в имеющихся данных. ключи нового значения уже существующие в имеющихся данных игнорируются. 
--                   * если данных по указанному ключу нет и новое значение объект, то новое значение создаст объект по указанному ключу
--                   * в других случаях вызов завершится ошибкой
--
--     "push"      - если имеющиеся данные это массив и новое значение тоже массив, то новый данные добавляются в конец имеющегося массива
--                   если данных по указанному ключу нет и новое значение массив, то новое значение создаст массив по указанному ключу
--                   в других случаях вызов завершится ошибкой
--
--     "unshift"   - если имеющиеся данные это массив и новое значение тоже массив, то новый данные добавляются в начало имеющегося массива
--                   если данных по указанному ключу нет и новое значение массив, то новое значение создаст массив по указанному ключу
--                   в других случаях вызов завершится ошибкой
--
--     "delete"    - данные будут удалены, "значение" и "тип" игнорируются и должны или отсутствовать или быть пустыми
--                 - если удалялся элемент массива и он был последним, то размер массива уменьшится на 1
--                 - если удалялся элемент массива и он не был последним, то размер массива не уменьшится, а удаляемый элемент получит значение null

-- "тип" не обязателен и предназначен для контроля соответствия "нового-значения" формату данных.
--  В данный момент поддерживается только тип данных "дата-время" так как не верно заданные данные этого типа могут вызвать ложные срабатывания/не срабатывания
--  в фильтре групп оперции сравнения дат.
--
--  возможные значения "тип" 
--
--    отсутствует - проверки не производится
--
--    ""          - проверки не производится
--
--    null        - проверки не производится
--
--    "dt:LR"     - производится проверка и нормализация "значения" как даты для указанного диапазона точности от L до R
--                - где L и R - символы Y M D h m s задающие диапазон компонентов даты
--                - например "dt:Ys" - дата от года до секунды, "dt:Mh" - дата от месяца до часа
--                - год должен всегда задаваться четырмя цифрами
--                - остатальные компоненты - одной или двумя и они будут автоматически нормализованы до двух цифр
--                - например дата "1971-5-4 3:2:1" при типе "dt:Ys" станет "1971-05-04 03:02:01" 
--
--    другие значения - вызов завершится с ошибкой

-- внимательно изучите примеры в разделе "Общие замечания" 

-- для управления участием указанного идентификатора в группах-списках с помощью псевдо-ключа данных "-group" используйте точное указание группы "-group.КОДГРУППЫ" и:
--   * режим "delete" для удаления из группы
--   * режим "set" и новое значение "1" для добавления в группу
--   * в других случаях вызов завершится ошибкой

-- для удаления ошибок доставки используйте ключ данных "member.error.error" и режим "delete", в других случаях вызов завершится ошибкой

-- для перевода подписчика в состояние "Требуется подтверждение внесения в базу" используйте ключ данных "member.lockconfirm", режим "set" и значение точно "1", в других случаях вызов завершится ошибкой

-- все прочие изменения по ключам данных "member.*" и "-group.*" в данный момент игнорируются, но могут стать ошибками в будущем. лучше не делайте их.

-- если изменяемый ключ данных не существует, то он будет создан (с учётом влияния "режима" 

-- если не существует часть или весь путь изменяемого ключа данном, то все отсутствующие части пути так же будут созданы.

-- в контексте создания не существующих частей пути, "не существует" это реально не существует, или существует, но равен null.

-- попытки использовать объект или скалярную величину как массив приведут к завершению вызова с ошибкой

-- попытки использовать массив или скалярную величину как объект приведут к завершению вызова с ошибкой

-- данные или изменяются/удаляются для всех указанных ключей (если не было ошибок) или не изменяются/удаляются вообще (при любой ошибке)

-- одновременное с созданием подписчика / внесением данных подписчика добавление к нему дополнительных идентификаторов

-- аналогично последовательному вызову member.set и потом нужное число раз member.head.attach
-- но при данном использовании атомарно - или все действия по созданию/внесению и присоединению
-- завершаться успешно или же любая ошибка отменяет все изменения

 "head_attach" : [ -- не обязательно

                   { -- параметры аналогичны одноимённым из вызова member.head.attach
                    "head" : "дополнительный присоединяемый идентификатор" 
                   ,"head_add_type" : "тип дополнительного присоединяемого идентификатора
                   ,"newbie.confirm" : "режим подтверждения" 
                   }
                  .......
                 ]

-- необязательный

 ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

-- по умолчанию, считается "return_fresh_obj" : "0" 
-- при "1" данные возвращаются как при запросе member.get c "datakey" : "*" 

}

ответ


{

 <общие поля>

,"newbie"  : 0|1 -- новый подписчик - 0 - уже был в базе, 1 - внесён в базу

-- если "return_fresh_obj" : "1" 

-- данные от запроса "member.get" соответствующего адреса

}

вверх

Получить ответы подписчика (ДК)

Обратите внимание, что в зависимости от того, что вы выбираете и имеются ли в ключе шаблоны, "данные для ключа" могут быть и скаляром и массивом и объектом и null.

Обратите внимание, что значением для того, что было создано в модели АВО и называется там "вопрос с выбором" будет объект, а не массив. Ключами массива будут кода ответов, значениями - null.

Обратите внимание, что количество ошибок доставки имеет ключ member.error.error (совметимо со stat.uni), а не member.error как в АВО.

Псевдо ключ "-group" описывает участие именно указанного адреса в группах списка (как описано ниже в member.get для формата АВО)


{

  "action" : "member.get" 

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

-- один из способов указания интересующих ключей данных

-- или список ключей

 ,"datakey" : [
               "ключ данных-1" 
              ,"ключ данных-2" 
               .....
              ]

-- или один ключ

 ,"datakey" : "ключ данных" 

-- или специальный вариант одного ключа для получения всех данных

 ,"datakey" : "*" 

}

ответ

{

    <общие поля>

-- при запросе со списком ключей

 ,"datakey" : {
               "ключ данных-1" : данные для ключа-1
              ,"ключ данных-2" : данные для ключа-2
              ......
              }

-- при запросе с одним ключом или с "*" 

 ,"datakey" : данные для ключа

}

Cоздать подписчика / Установить ответы подписчика (АВО)

При отсутствии адреса в базе он автоматически создаётся.

Все попытки изменения системной анкеты member игнорируются, за исключением ответа error.

Установка member.error точно в "0" удаляет запись о проблемах доставки и снимает блокировку адреса из-за ошибок доставки если таковая была.

Установка member.lockconfirm точно в "1" переводит подписчика в состояние "Требуется подтверждение внесения в базу"


{

  "action" : "member.set" 

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

 ,"source" : "ip-адрес оригинального запроса" 
              -- если оригинальный инициатор запроса вы сами - ваш ip, иначе ip-адрес откуда вам пришёл запрос

 ,"if_exists" : "error|update|overwrite" 
                 -- правило изменения ответов анкетных данных. не действует на псевдоанкету "-group" 
                 -- error     - при наличии адреса в базе возвращается ошибка
                 -- update    - если ответ на изменяемый вопрос уже есть то он остаётся неизменным
                 -- overwrite - ответ заменяется/создаётся в любом случае

  ,"newbie.confirm": "подписчик должен подтвердить внесение в базу (1|0)" 
                     -- используется только при внесении email-адресов
                     -- действует лимит внесения без подтверждения
                     -- подробнее описанный в вызове member.import

  ,"newbie.letter.confirm" : "номер шаблона информационного письма" 
                           -- высылается если новый адрес до этого отсутствовал в базе, и внесён с необходимостью подтверждения
                           -- если параметр пуст или отсутствует, то ничего выслано не будет
                           -- но необходимость подтвердить регистрацию никуда не денется и выслать письма вы сможете позже через member.sendconfirm
                           -- этот параметр НЕ зависит от newbie.confirm - если у вас кончился лимит внесения без подтверждения, то импорт
                           -- продолжит вносить уже с подтверждением и этот параметр будет использоваться 
                           -- при внесении номеров телефонов ни какие уведомления не высылаются
                           -- информационное письмо должно иметь заполненный адрес отправителя и не находиться на модерации

  ,"newbie.letter.no-confirm" : "номер шаблона информационного письма" 
                           -- высылается если новый адрес до этого отсутствовал в базе, и внесён с без необходимости подтверждения
                           -- если параметр пуст или отсутствует, то ничего выслано не будет
                           -- при внесении номеров телефонов ни какие уведомления не высылаются
                           -- информационное письмо должно иметь заполненный адрес отправителя и не находиться на модерации

  ,"obj" : {

            -- управление анкетными данными подписчика

            -- ank   - код анкеты

            -- quest - код ответа

            -- val   - значение ответа или код ответа если вопрос с выбором из списка

            -- значение не указанных вопросов не меняется

            "ank1" : {

                       -- установка значения ответа обычного вопроса

                       "quest" :"val" 

                       -- установка значения ответа для вопроса с типом "дата" в зависимости от заданной в анкете точности

                       "quest" : "YYYY-MM-DD" 
                       "quest" : "YYYY-MM-DD hh" 
                       "quest" : "YYYY-MM-DD hh:mm" 
                       "quest" : "YYYY-MM-DD hh:mm:ss" 

                       -- установка значения ответа у вопроса с множественным выбором

                      ,"quest" : [ "val", "val", "val"....]

                       -- установка значения ответа у вопрос с выбором одного из нескольких - всё равно массив

                      ,"quest" : [ "val" ]

                       -- удаление ответа

                      ,"quest" : null

                      }

          ,"ank2" : {

                      ..............

                     }

          -- управление участием указанного идентификатора в группах-списках с помощью псевдо-анкеты

          -- параметр "if_exists" не влияет на эту анкету

          ,"-group" : {

                  -- 0 - удалить из группы

                  -- 1 - добавить в группу

                  -- участие в неперечисленных группах не меняется

                  "id1" : "(0|1)" 

                  ,"id2" : "(0|1)" 

                   .............

                  ,"idN" : "(0|1)" 

                }

          }

-- необязательный

 ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

-- по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ


{

 <общие поля>

,"newbie"  : 0|1 -- новый подписчик - 0 - уже был в базе, 1 - внесён в базу

-- если "return_fresh_obj" : "1" 

 данные от запроса "member.get" соответствующего адреса

}

вверх

Получить ответы подписчика (АВО)


{

  "action" : "member.get" 

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

}

ответ


{

    <общие поля>

-- ответы на вопросы анкет

  ,"obj" : { 

            -- ank   - код анкеты

            -- quest - код ответа

            -- val   - значение ответа или код ответа если вопрос с выбором из списка

            "ank1" : {

                       -- обычный вопрос

                       "quest" : "val" 

                       -- вопрос с множественным выбором

                      ,"quest" : [ "val", "val", "val"....]

                       -- вопрос с выбором одного из нескольких - всё равно массив

                      ,"quest" : [ "val" ]

                      }

          ,"ank2" : {

                      ..............

                     }

          -- псевдо-анкета описывающая участие в группах-списках

          ,"-group": { -- перечисляются кода только тех групп-списков, в которых именно этот идентификатор состоит

              "id1" : 1

             ,"id2" : 1

                .............

             ,"idN": 1

            }

          }

}

Удалить подписчика

Адреса и данные реально удаляются из базы !

Запрос с указанием списка по умолчанию синхронный.

Запрос с указанием группы по умолчанию асинхронный. Используйте sync = 1 если вам реально нужен ответ с описанием какие адреса как были обработаны.

Асинхронные запросы возвращают номер трекера для отслеживания.

В зависимости от параметра wipe вызов работает по разному в случае наличия у подписчика нескольких идентификаторов.

При wipe = 0 (по умолчанию)

При wipe = 1

При любом wipe

В отличии от member.head.detach возможно удаление единственного (последнего) идентификатора.

В отличии от member.head.detach генерируется триггерное событие "Подписчик удалён".

Удаление идентификатора приведёт к прекращению его обработки в тригерных последовательностях. Однако не сразу, а при очередной обработке событий связанных с удалённым идентификатором.


{

  "action" : "member.delete" 

 ,"wipe" : 0|1 -- способ удаления. по умолчанию 0

-- указание подписчиков одним из способов

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

или

 ,"list" : [

            "идентификатор подписчика" 

           ,"идентификатор подписчика" 
            ........

           ]

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

или

 ,"group" : код группы участники которой будут удалены

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"group.filter" : [
                    фильтр отбора как у group.filter.set
                   ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"url" : ссылка на файл со списком адресов. по одному на строке. возможно сжатие zip.

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

               ,"filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

}

ответ


{

 <общие поля>

-- для асинхронного запроса

,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для синхронного запроса

,"list" : {
           "адрес-1" : 0|1 -- результат удаления - 1 - удалён, 0 - нет (например адрес ошибочен или отсутствует)

          ,"адрес-2" : 0|1

           .................
          }

}

вверх

Список идентификаторов

Выводится список всех идентификаторов и сопутствующая им информация для указанного пользователя


{

 "action" : "member.head.list" 

 ,"email" : "один из идентификаторов cуществующего пользователя" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

}

ответ


{

 <общие поля>

,"list" : [

            {

             "email" : "идентификатор пользователя" -- email, номер телефона,....

            ,"addr_type" : "email|msisdn|csid" -- тип идентификатора

            -- структуры состояния головы как описано в member.get

            ,"has_lock" : ...

            ,"lockerror" : ...

            ,"confirm" : { ... }

            ,"error" : { ... }

            }

            ...

           ]

}

вверх

Присоединение идентификатора

К уже существующему пользователю указанному в email присоединяется идентификатор указанный в head.

Присоединяемый идентификатор не должен быть связан с другим существующим пользователем.

Возможно присоединение идентификаторов во время импорта пользователей.


{

 "action" : "member.head.attach" 

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

 ,"head" : "присоединяемый идентификатор" 

 ,"head_addr_type" : email|msisdn|csid -- тип присоеденяемого идентификатора. не обязательно, система сама распознает email или msisdn

 ,"newbie.confirm": "подписчик head должен подтвердить внесение в базу (1|0)" 
                     -- используется только при внесении email-адресов
                     -- действует лимит внесения без подтверждения
                     -- подробнее описанный в вызове member.import
}

ответ


{

 <общие поля>

}

вверх

Удаление идентификатора

Идентификатор указанный в email удаляется от пользователя и становится более ни с кем не связан.

Так же прекращаетяс участие указанного идентификатора в группах-списках в которых он состоял.

Данные подписчика и другие идентификаторы остаются за пользователем.

Статистика по действиям (участие в выпусках, доставки, переходы, чтения и прочее) остаётся за удалённым идентификатором.

После удаления у пользователя должен остаться ещё хотя бы один другой идентификатор (для удаления всего пользователя есть member.delete).

В отличии от member.delete не возможно удаление единственного (последнего) идентификатора.

В отличии от member.delete не генерируется триггерное событие "Подписчик удалён".

Возможно удаление идентификаторов во время импорта пользователей.

{

 "action" : "member.head.detach" 

 ,"email"  : "удаляемый один из идентификаторов существующего пользователя" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

}

ответ

{

 <общие поля>

}

вверх

Замена идентификатора

К уже существующему пользователю указанному в email присоединяется идентификатор указанный в head.

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

Этот вызов - аналог последовательного исполнения member.head.attach и member.head.detach, но является атомарным - любая ошибка полностью отменяет все изменения.


{

 "action" : "member.head.replace" 

 ,"email": "текущий идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип текущего идентификатора. не обязательно, система сама распознает email или msisdn

 ,"head" : "новый идентификатор" 

 ,"head_addr_type" : email|msisdn|csid -- тип нового идентификатора. не обязательно, система сама распознает email или msisdn

 ,"newbie.confirm": "подписчик head должен подтвердить внесение в базу (1|0)" 
                     -- используется только при внесении email-адресов
                     -- действует лимит внесения без подтверждения
                     -- подробнее описанный в вызове member.import
}

ответ


{

 <общие поля>

}

вверх

Участие подписчика в группах-фильтрах

Списков групп-фильтров в которых состоит подписчик

А участие в группах-списках и так известена через member.get

{
 "action" : "member.where" 

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

-- не обязательно, ограничение проверки 

 ,"group" : "код группы" -- одной

-- или

 ,"group" : [ "код группы", ... ] -- несколькими группами

}

ответ

{

 <общие поля>

,"list" : {
           "адрес" : [ список кодов групп-фильтров в которые адрес входит ]

          -- или

          ,"адрес" : null -- адрес не существует или синтаксически не верен

          }

}

Список подписчиков

НЕ СОВМЕСТИМОЕ ИЗМЕНЕНИЕ С 17 ИЮЛЯ 2017 ГОДА - при result=response размер выдачи не более 1000 строк.

Если формат фильтра у группы group или заданный непосредственно в group.filter использует формат ДК с оператором has c параметром save, то в результатах вызова будут так же результаты всех срабатываний save.

{

  "action" : "member.list" 

-- источник адресов, одно из двух или вообще ничего (тогда по всем адресам вообще)

 ,"group" : "идентификатор группы" 

-- или

 ,"group.filter" : [ фильтр отбора как у group.filter.set ]

-- для всех источников

 ,"addr_type": "тип выбираемых адресов подписчиков (email | msisdn | csid)" 
                -- при отсутствии определяется по типу адресов в указанной группе
                -- если указан, то должен совпадать с типом адресов в группе, если и та указана
                -- если не указан и не указана группа, то выбираются еmail-адреса

 ,"member.haslock" : "код" -- учёт состояния блокировки подписчика
                   -- пусто или отсутствует - не учитывать состояние блокировки. может ли  получать письма, sms или viber
                   -- -1 - есть хоть какая-то блокировка       - не может
                   --  0 - нет блокировки                      - может
                   --  1 - заблокирован так как отписался      - не может
                   --  2 - заблокирован так как не подтверждён - не может
                   --  4 - заблокирован так как имеет фатальные ошибки доставки - не может
                   --  прочие значения ( 3,5,6,7 ) - одновременное наличие нескольких указаных блокировок
                   --
                   -- формально этот параметр можно заменить дополнительными условиями в фильтре группы
                   -- но его использование позволяет 
                   --  * не меняя фильтра групп узнавать сколько в ней всего участников и сколько из них могут получать письма
                   --  * не создавая группы узнавать это же для всех свои адресов вообще
                   --  * выполнять запрос быстрее по сравнению с таким же условием в фильтре группы

-- формат вывода, одно из трёх или вообще ничего
-- если формат вывода не используется, то выводится только адрес (member.email) или номер телефона (member.cellphone)

 ,"format" : "идентификатор формата вывода" -- использование существующего формата

-- или 

 ,"format" : [ -- указание формата в формате Анкета-Вопрос-Ответ прямо в запросе
               { "anketa" : "код анкеты", "quest" : "код вопроса" }
              ........
             ]

-- или 

 ,"format" : [ -- указание объекта Формат прямо в запросе

              -- запрос просто по ключу данных
               { "datakey" : "ключ данных" }

              -- запрос по ключу из результатов работы save в условии has используемого в вызове фильтра
              -- результат undef если save не срабатывал или массив из результатов получения данных по
              -- ключу данных относительно каждого значения из результата save с указанным кодом
              ,{ "save" : "код сохранения", datakey" : "относительный ключ данных" }

              -- запрос значение меток из условий фильтрации

              ,{ "label" : "имя метки" }

              ........
             ]

 ,"sort" : "коданкеты.кодвопроса" 
             -- сортировка по указаному полю (не важно будет оно в итоговых данных или нет)
             -- если параметр отсутствует или пуст, то выдача идёт в неком внутреннем порядке
             -- сортировка по произвольной анкете/полю может резко увеличить время выполнения запроса
             -- так как для его выполнения надо отсортировать всю выборку
             -- но сортировка по ниже перечисленным полям практически не влияет на скорость выполнения
             --   member.id
             --   member.email       - при выборке адресов (email)
             --   member.cellphone   - при выборке телефонов (msisdn)
             --   member.domain      - сортировка по домену и внутри него по адресу
             --                      - при выборке телефонов эквивалентно просто member.cellphone
             --   member.create.time - дата и время создания
             --   member.update.time - дата и время последнего изменения данных по API

  ,"sort.order" : "asc|desc" -- направление сортировки asc - по возрастанию (по умолчанию), desc - по убыванию

  ,"result" : [ способ возврата результата. смотрите общее описание ]

  -- в первой строке выводить заголовок, содержащий для каждой колонки код анкеты и вопроса (id) или их названия (name)
  -- или указанные названия если параметр задан как массив
  -- если параметр отсутствует или пуст, то такая строка не добавляется

  ,"caption" :"id|name" 

--или 

  ,"caption" :[ "имя 1", "имя 2" ... ]

---- дополнительные параметры запроса зависящие от result

-- *НЕ СОВМЕСТИМОЕ ИЗМЕНЕНИЕ С 17 ИЮЛЯ 2017 ГОДА*

-- ДО 17 ИЮЛЯ 2017 ГОДА

-- для response

  ,"page" : "номер страницы" -- при отсутствии выдаётся весь список. должны быть указаны оба параметра или ни одного

  ,"pagesize": "размер страницы" 

-- ПОСЛЕ 17 ИЮЛЯ 2017 ГОДА

-- для response

  ,"page" : "номер страницы" -- при отсутствии выдаётся 1000 строк
                             -- должны быть указаны оба параметра или ни одного
                             -- если размер страницы больше 1000 то ошибка

  ,"pagesize": "размер страницы" 

}

ответ

{

    <общие поля>

-- для result != response

  ,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для result = response

  ,"order" : [ -- описание порядка колонок выводимых результатов

              {

                anketa      : код анкеты

               ,anketa.name : название анкеты

               ,quest       : код вопроса

               ,quest.name  : названи вопроса

              }

             ....

           ]

   ,"list" : [ -- по одной записи для каждого адреса

              [ значения запрошеных полей в порядке указанном в параметре ответа order ]

             ,[ значения запрошеных полей в порядке указанном в параметре ответа order ]

             ,[ значения запрошеных полей в порядке указанном в параметре ответа order ]

             .....

           ]

  ,"save" : { -- только при result=response

              "aдрес" : { 
                           результат (если таковой был) работы save в условии has фильтра
                           В описании фильтра формата КД описано когда и чем это полезно
                        }
            }

  ,"label" : { -- только при result=response

              "aдрес" : { 
                           список сработавших меток и их значений
                           В описании фильтра формата КД описано когда и чем это полезно
                        }
            }

}

вверх

Количество подписчиков

Расчёт быстр если ведётся по "всем", по группам-спискам, по группам-фильтрам состоящим из групп-списков.

Расчёт по группе-фильтру может быть долог - в зависимости от сложности фильтра и общего числа подписчиков.

По умолчанию вызов синхронный. Используйте sync = 0, что бы сделать его асинхронным.

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


{

  "action" : "member.list.count" 

 ,"group" : "идентификатор группы" 
            -- задает фильтр по списку подписчиков группы
            -- если пусто или отсутствует, то подсчёт идёт по всем подписчикам

 ,"cache" : { параметры кэширования }

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

}

ответ


{

  <общие поля>

-- для асинхронного запроса

,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для синхронного запроса

 ,"obj" : {
            "total" : всего подписчиков

           ,"active" : количество подписчиков которые могут участвовать в рассылках писем или смс

           ,"locked" : уникальное количество заблокированных - не могут быть в рассылке.

           -- подробности по причинам блокировок (у одного подписчика может быть больше одной блокировки)

           ,"locked.unsubscribed" : в том числе заблокированные так как отписались

           ,"locked.confirm" : в том числе заблокированные так как не подтвердили внесение в базу

           ,"locked.stoplist" : в том числе заблокированные так как находятся в стоп-листе

           ,"locked.hardbounced" : в том числе заблокированные так как имеют фатальные ошибки доставки

           -- при подсчёте по всем подписчикам, дополнительно появляются поля

           ,"total.email" : всего именно email

           ,"total.msisdn" : всего именно телефонов

           ,"total.csid" : всего именно клиентских идентификаторов

           ,"active.email" : способных получать именно адреса

           ,"active.msisdn" : способных получать именно телефоны

           }

}

вверх

Внесение списка подписчиков

Вызов member.import.probe предназначет для проверки что думает система импорта о ваших входных данных.

В ответ будет сообщено как произошло авто-определение кодировки (сharset), разделителя столбцов, определилась ли первая строка данных как строка конфигурации (firstline), и каким ответом каких анкет приписаны столбцы данных

Если вы знаете зарание ответы на эти вопросы, то можете сразу указать эти данные при вызове тогда выше указание будет иметь приоритет над авто-определением системы.

Вызов member.import реально импортирует данные, описаные выше параметры будет определены автоматически если их не указать сразу при вызове.

Вызов member.import асинхронный - получение положительного ответа означает, что задание на импорт поставлено в очередь. Это не означает успешность импорта - он может не состоятся из ошибок возникших позже. Следить за его исходом можно по возвращаемому номеру асинхронного запроса.

Обратите внимание: Все возможные проблемы (фатальные или нет - смотрите по cannot_import) связанные именно с импортом (настройки, данные) при вызове member.import.probe сообщаются только через параметр warnings. А параметр errors служит только для сообщения о проблемах вызова только с точки зрения API. При вызове же самого импорта, не фатальные ошибки остаются по прежнему в warnings, а фатальные переносятся в errors. Это может выглядеть не логично, но это так из-за назначения вызова member.import.probe - предупредить вас о возможных ошибках, а не выполнять само импортирование.

Вызов позволяет импортировать данные модели КД при использовании источника данных в формате JSON-объект.

Вызов member.import сохраняет отчёты о строчках которые он не смог обработать. Имена файлов с отчётами можно получить из трекера.


{

  "action" : "member.import" 

или 

  "action" : "member.import.probe" 

** данные импортирования

  ,"addr_type": "тип вносимых адресов подписчиков (email | msisdn | csid)" 

и одни из источников данных:

   ,"users.list": адреса и данные для импортирования -- непосредственно в JSON или в СSV или XLSX
                                                     -- подробнее в разделе "Форматы данных для импортирования и Экспресс-Выпуска" 

   ,"encoding" : "base64" -- не обязательно.
                          -- пусто или отсутствует - данные в users.list представлены непосредственно
                          -- base64                - данные в users.list закодированы в base64
или

   ,"users.url": "URL по которому находятся список адресов и данных для импортирования
                  -- данные забираются в момент вызова
                  -- Внешиение ссылки http / https / ftp / ftps / sftp или из хранилища загрузок rfs://upload/путь-до-файла

                  -- Для запросов по SOAP (например к Siebel) схемы soap:// и soaps:// - обратитесь с Службу Поддержки
                  -- для получения дополнительной информации

                  -- Для запросов из Google Big Query настройте внешнюю авторизация для схемы gbd:// (описано в вызовах authext.*)
                  -- Данные получаются полной выборкой из указанной в урле вида gbd://authext:AUTHEXT_ID@DATASET_ID/TABLE_ID таблицы.
                  -- При использовании схемы gbd:// обязательно должен быть указано описание получаемых столбцов через параметр caption (описан ниже)

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

или

  ,"uid": "идентификатор уже загруженных данных" 
          -- возвращается после первого вызова member.import.probe, используется далее вместо cамих данных

** прочие параметры

  ,"charset": "кодировка символов ( koi8-r | cp1251 | utf-8 | utf-7 | utf-16 | mac-cyrillic )",

  ,"separator": "разделитель символов ("," | ";" | "|" | "t")", -- t - табуляция

  ,"firstline": "использовать первую строку как строку конфигурации ( 1 | 0 )",

** явное описание столбцов для модели АВО (не обязательны)

  ,"caption":  [ -- по порядку следования столбцов в данных. приписывают каждый столбец одному вопросу одной анкеты.
                 -- приоритетнее чем описание из первой строки данных для импортирования
                 -- обязателен при получении данных из Google Big Query   

                -- при addr_type = email  колонка с адресом это анкета "member" вопрос "email" 

                -- при addr_type = msisdn колонка с номером телефона это анкета "member" вопрос "cellphone" 

                -- при addr_type = csid  колонка с клиентским идентификатором это анкета "member" вопрос "csid" 

                {

                 "anketa": "id  анкеты",

                 "quest": "id вопроса в анкете",

                  или 

                 "ignore": "1" -- игнорировать столбец

                 -- вычислять при внесении. к содержимое колонки не заменит имеющиеся данны, а будет прибавлено к ним.
                 -- для задания через первую строку данных для импортирования добавьте знак "=" перед названием колонки

                  "calc" : 1

                 },

                ......

              ]

** явное описание столбцов для модели КД (не обязательны)

  ,"caption":  [ -- по порядку следования столбцов в данных. приписывают каждый столбец одному вопросу одной анкеты.
                 -- приоритетнее чем описание из первой строки данных для импортирования

                -- при addr_type = email  колонка с адресом это member.email

                -- при addr_type = msisdn колонка с номером телефона это member.cellphone

                -- при addr_type = csid  колонка с клиентским идентификатором это member.csid

                {

                 "datakey": "ключ данных колонки", -- как в member.set

                 "mode" : "режим внесения", -- как в member.set

                 "type" : "тип данных", -- как в member.set

                  или 

                 "ignore": "1" -- игнорировать столбец

               -- вычислять при внесении. к содержимое колонки не заменит имеющиеся данны, а будет прибавлено к ним.
               -- для задания через первую строку данных для импортирования добавьте знак "=" перед названием колонки

                  "calc" : 1

                 },

                ......

              ]

** пробный импорт

  ,"action": "member.import.probe",

** импорт

  ,"action": "member.import",

  ,"if_exists": "overwrite|ignore|error" -- обязателен
                -- что делать если адрес уже существует 
                -- overwrite - перезаписывать данные (этот вариант не применим для КД)
                -- ignore    - не вносить
                -- error     - не вносить и считать строку ошибочной

  ,"newbie.confirm" : "подписчик должен подтвердить внесение в базу (1|0)",

                      -- по умолчанию - внесение без необходимости подтверждения подписчиком (0)

                      -- внесение email без подтверждения ограничено величиной member.noconfirm.rest
                      -- (узнать можно через sys.settings.get) при её исчерпании остальные адреса
                      -- вносятся с подтверждением.

                      -- если количество адресов в базе превышает member.tarif.limit, то величина member.noconfirm.rest
                      -- равна нулю, иначе она равна member.noconfirm.limit за вычетом количества уникальных email
                      -- адресов внесённых в текущем месяце

                      -- на внесение номеров телефонов параметр newbie.confirm не влияет (они всегда вносятся без подтверждения)

  ,"auto_group" : {
                  -- автоматически создать группу-список для импортируемых адресов
                  -- или дополнить любую существующую группу-список импортируемыми адресами

                  -- отсутствие всего параметра auto_group означает ни создавать новую ни пополнять существующую группу
                  -- наличие auto_group но без id и без name означает создание группы со стандартным кодом
                  -- и со стандартным названием. код созданый группы можно узнать используя возвращаемый номер track.id
                  -- и вызов track.get

                  "id" : "идентификатор группы-списка для пополнения" 
                         -- при отсутствии такой группы она создаётся автоматически
                         -- если параметр пуст или отсутствует то используется стандартный
                         -- код вида importYYYYYMMDDhhmmss

                 ,"name" : "название группы" 
                            -- название для создаваемой группы.
                            -- если параметр пуст или отсутвует то используется стандартное "Внесены <дата-время импорта>" 

                 },

   ,"clean_group" : 0|1 -- очищать (1) или нет (0) группу-список указанную в auto_group перед началом импортирования
                      -- позволяет не дополнять группу, а полностью заменять её участников
                      --
                      -- сначал проверяются все возможные причины по которым импорт может не начаться. если они есть, то импорт заканчивается ошибкой и очистка не происходит
                      -- иначе импорт начинается с очистки списка участников, а внесение новых происходит только после этого
                      --
                      -- не путайте удаление адреса из списка участников с удалением адреса из базы - это разные вещи
                      --
                      -- при одновременном импортировании в одну группу несколькими вызовами у которых хоть у одного clean_group=1 результат
                      -- зависит от порядка выполнения и может быть неожиданным. для однозначного результата всегда выполняйте импорт с cleangroup=1
                      -- только по завершении всех предыдущих импортов в ту же самую группу и не запускайте новый импорт в такую группу до завершения текущего.

   ,"format" : " id-формата" -- дополнить данные каждого вносимого адреса данным из формата (игнорируется для КД пока форматы не получат поддрежку КД)
                             -- отсутствие или пусто - не дополнять

   ,"sequence.event" : 0|1 -- будет ли внесение/изменение данных вызывать срабатывание событийных действий
                           -- отсутствие или пусто - вызова событий не будет

   ,"result" : [ способ возврата результата. смотрите общее описание ] -- данный вызов всегда асинхронный и способ "response" означает сохранение отчёта в Хранилище Отчётов
}

ответ

{
   <общие поля>

-- только для member.import.probe

  ,"uid": "идентификатор уже загруженных данных" 

  ,"charset": "кодировка символов ( koi8-r | cp1251 | utf-8 | utf-7 | utf-16 | mac-cyrillic )" 

  ,"separator": "разделитель символов ("," | ";" | "|" | "t")" 

  ,"firstline": "использовать первую строку как строку конфигурации ( 1 | 0 )" 

  ,"caption":  [ -- поля конфигурации

                {

               -- при импорте по моделе АВО для колонки которой найдено соответствие анкете/вопросу

                 "name": "отображаемое имя колонки",

                 "anketa": "id  анкеты",

                 "quest": "id вопроса в анкете",

               -- при импорте по моделе АВО для колонки которой не найдено соответствие

                 "name": "неопределенное значение из элемента строки конфигурации или null",

               -- при импорте по моделе ДК

                 "datakey": "ключ данных колонки",

                 "mode" : "режим внесения",

                 "type" : "тип данных",

               -- при импорте по моделе ДК для колоник с динамическим ключём данных

                 "dynamic": "1",

               -- для колонки для которой конфигурации задано игнорирование

                  "ignore" : 1

               -- для колонки для которой конфигурации задано вычислять при внесении

                  "calc" : 1

                 }

              ]

  ,"rows" : [ -- данные первых 15 значащих строк разбитые по полям с учёном указанного разделителя

             [ "колонка-1-строки-1" ,"колонка-2-строки-1" ,"колонка-3-строки-1" ]

            ,[ "колонка-1-строки-2" ,"колонка-2-строки-2" ,"колонка-3-строки-3"]

            .......

           ]

  ,"cannot_import" : 0|1  -- может ли быть выполнен импорт с текущими параметрами запроса
                         -- 1 - может
                         -- 0 - нет

                         -- Именно этот параметр, а не описанный далее параметр warnings сигнализирует о возможности/невозможности импорта,
                         -- т.к. параметр warnings может содержать и не влияющие на запуск процесса импорта предупреждения - например о том,
                         -- что в первых строках файла есть неверно указанные адреса подписчиков, что не говорит о том, что во всем
                         -- списке подписчиков будет так.

   ,"warnings" : [ -- предупреждения о проблемах при анализе данных
                   -- в первую очередь проверяйте параметр cannot_import
                   -- если он показывает, что импорт невозможен, то тут содержится описание почему

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

                  { "id":"код ошибки", "explain": "доп. информация (скаляр, массив или хэш)" }

                  ....

                ]

-- только для member.import

  ,"queue_position" : "номер в очереди импортирования" 

  ,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

}

вверх

Массовое изменение данных подписчиков

Вызов предназначен для единнобразного изменения данных сразу многих адресов по правилам описаных в "формате заполнения".

Запрос с указанием списка по умолчанию синхронный.

Запрос с указанием группы по умолчанию асинхронный. Используйте sync = 1 если вам реально нужен ответ с описанием какие адреса как были обработаны.

Асинхронные запросы возвращают номер трекера для отслеживания.

Все попытки изменения системной анкеты member игнорируются, за исключением ответа error.

Установка member.error в "0" удаляет запись о проблемах доставки и снимает блокировку адреса из-за ошибок доставки если таковая была.

В данный момент вызов не позволяет менять данные по модели КД так как это происходит через указание формата, а форматы пока не совместимы с КД.

Но вы можете использовать вызов импорта подписчиков.


{

 "action" : "member.update" 

 ,"format" : код формата заполнения или универсального, из данных которого будут применены изменения

 ,"if_has" : что делать, если изменяемый пункт анкеты уже заполнен

             -- "overwrite" - заменить старое значение новым из формата

             -- "merge"     - объединить старое и новое значения

             -- "skip"      - оставить старое значение

 ,"if_hasnt" : что делать, если изменяемый пункт анкеты ещё не заполнен

             -- "set"  - заполнить указанным в формате значением

             -- "skip" - оставить пункт незаполненным

-- указание подписчиков одним из способов

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

или

 ,"list" : [

            "идентификатор подписчика" 

           ,"идентификатор подписчика" 

            ........

           ]

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

или

 ,"group" : код группы к участникам которой будут применены изменения

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"group.filter" : [
                    фильтр отбора как у group.filter.set
                   ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"url" : ссылка на файл со списком адресов. по одному на строке. возможно сжатие zip.

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

                "filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный
}

ответ


{

  <общие поля>

-- для асинхронного запроса

,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для синхронного запроса

,"list" : {
           "адрес-1" : 0|1 -- результат изменения - 1 - изменение применено, 0 - нет (например адрес ошибочен или отсутствует)

          ,"адрес-2" : 0|1

           .................
          }

}

Пригласить подписчиков

По указанных адресам высылается напоминание о необходимости подтвердить внесение в вашу базу. Адреса уже подтвердившие внесение игнорируются.

Запрос с указанием списка по умолчанию синхронный.

Запрос с указанием группы по умолчанию асинхронный. Используйте sync = 1 если вам реально нужен ответ с описанием какие адреса как были обработаны.

Асинхронные запросы возвращают номер трекера для отслеживания.


{

 "action" : "member.sendconfirm" 

 ,"letter" : "код информационного письма" -- обязательно
                                          -- информационное письмо должно иметь заполненный адрес отправителя и не находиться на модерации

-- указание подписчиков одним из способов

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

или

 ,"list" : [

            "идентификатор подписчика" 

           ,"идентификатор подписчика" 

            ........

           ]

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

или

 ,"group" : код группы, участникам которой будут высланы повторные напоминания о подтверждении регистрации

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"group.filter" : [
                    фильтр отбора как у group.filter.set
                   ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"url" : ссылка на файл со списком адресов. по одному на строке. возможно сжатие zip.

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

                "filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }
 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный
}

ответ


{

  <общие поля>

,"issue.id" : номер выпуска -- для group, group.filter, url и stat.uni высылаемые письма будут оформлены отдельным выпуском рассылки
                            -- номер которого и будет возвращён. это позволяет изучить статистику по высланным приглашениям

-- для асинхронного запроса

,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для синхронного запроса

,"list" : {
           "адрес-1" : 0|1 -- результат высылки - 1 - выслано, 0 - нет (например адрес ошибочен или отсутствует или не нуждается в подтверждении)

          ,"адрес-2" : 0|1

           .................
          }
}

вверх

Подтвердить внесение в базу

При успешном подтверждении подписчик становится доступным для участия в рассылкам (при отсутствии других противопоказаний к этому).

Если требуется обратное - заново установить состояние "Требуется подтверждение внесения в базу" - используйте вызовы member.set или member.update с установкой member.lockconfirm в "1".


{

  "action" : "member.confirm" 

 ,"email" : "email-адрес подписчика" 

 ,"cookie" : "код подтверждения" 

}

ответ


{

 <общие поля>

 ,"error" : "error/member/wrongcookie" - не верный код. отсутстви ошибки - подтверждение выполнено или не требовалось

}

вверх

Информация об адресе

Вызовы возвращает информацию об адресе не связанную с существованием подписчика и даже в случае если подписчик уже удалён.

{
 "action" : "email.get" 

,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn
}

ответ

{
 <общие поля>
   "obj" : {

-- информация об адресе по структуре аналогичная ключу member вызова member.get

      "member" : {
         "id" : "номер адреса",  -- он же номер подписчика

         "addr_type" : "тип адреса", -- email,msisdn,csid

         "email" : "адрес",

         "haslock" : "наличие блокировки", -- 0 - нет, 1 - отписался/в стоп листе, 4 - ошибки доставкиб 5 = 1 + 4

-- информация об ошибках доставки

         "error" : {
            "lock" : 0 -- нет блокировки из-за ошибок доставки, 1 - есть

             -- при наличии, информация об ошибках

            ,"date" : "2015-09-12 18:53:39"   -- дата последней ошибки доставки (Ys) (удаляется при первой же успешной доставке)
            ,"str"  : "name=test.ru type=A: Host not found" -- описание последней ошибки доставки (удаляется при первой же успешной доставке)
            ,"error" : 1                      -- общее число ошибок доставки произошедших подряд (устанавливается в 0 при первой же успешной доставке)

         },

-- инофрмация об отписке/стоп листе

        "lockremove" : 0|1,  -- адрес отписан или в стоп-листе (1) или нет (0)

        "stoplist" : {
                       -- в стоп-листе владельца аккаунта если ключ присутствует в stoplist
                       "owner" : { 
                                  "dt" : "2015-11-17 15:02:45", -- дата внесения (Ys)
                                  "source" : "101.101.201.1"    -- источник
                                },

                       -- в стоп-листе так как подписчик отписался если ключ присутствует в stoplist
                       "member" : { 
                                  "dt" : "2015-11-17 15:02:45", -- дата внесения (Ys)
                                  "source" : "101.101.201.1"    -- источник
                               }
                      }
    }      
  }
}

вверх

Проверка адресов

Вызов проверяет список адресов на синтаксическую верность, даёт нормализованый вариант написания и (если указано) на то, что про этот адрес думает первичный MX обслуживающий домен.

В ответе ключами списка являются адреса из исходного списка в неизменном виде. Номарлизованый вид доступен в параметре email.

Если проверка SMTP не указана, то в ответе не будет частей связаных с её результатами.

Стадии на которых проверка SMTP закончилась ошибкой (параметр status):

  resolver - ошибка запроса DNS. описание ошибки в параметре message

  nodomain - домен не существует

  nomxa    - у домена нет ни одной записи MX или А

  connref  - не удалось соединиться с MX

  banner   - ошибка в первичном банере

  helo     - ошибка в ответ на HELO

  mailfrom - ошибка в ответ на MAIL FROM

  rcptto   - ошибка в ответ на RCPT TO

Для понимания как работает SMTP и что значат все эти странные слова полезно изучить RFC 5321.

{

  "action" : "email.test" 

  "smtp.test" : "проверять доступность по smtp" -- не обязательное поле
                                                -- 0 - не проверять (по умолчанию)
                                                -- lite - проверять, но без проверки существования адреса
                                                -- full - проверять, включая проверку существования адреса
                                                --
                                                -- !!! Полная проверка "full" не всегда даёт результат
                                                -- Многие системы отвечают "существует" на любой адрес
                                                -- Многие системы примут такую проверку за спам-активность
                                                -- Используйте такую проверку только если понимаете что делаете

 ,"smtp.timeout" : "таймаут в секундах" -- не обязательное поле. по умолчанию 15.

 ,"auto_group" : { -- не обязательно
                  -- автоматически создать группу-список для не прошедших проверку подписчиков
                  -- или дополнить любую существующую группу-список не прошедшими проверку подписчиков

                  -- в группу заносят только те адреса, у которых нет синтакстисеческих ошибок но есть ошибки smtp и в базе уже есть подписчик с таким адресом.

                  -- отсутствие всего параметра auto_group означает ни создавать новую ни пополнять существующую группу
                  -- наличие auto_group но без id и без name означает создание группы со стандартным кодом
                  -- и со стандартным названием. код созданый группы можно узнать используя возвращаемый номер track.id
                  -- и вызов track.get

                  "id" : "идентификатор группы-списка для пополнения" 
                         -- при отсутствии такой группы она создаётся автоматически
                         -- если параметр пуст или отсутствует то используется стандартный
                         -- код вида importYYYYYMMDDhhmmss

                 ,"name" : "название группы" 
                            -- название для создаваемой группы.
                            -- если параметр пуст или отсутвует то используется стандартное "Внесены <дата-время импорта>" 

                 }, 

  ,"clean_group" : 0|1 -- очищать (1) или нет (0) группу-список указанную в auto_group перед началом проверки. не обязательно

-- указание адресов одним из способов

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

или

 ,"list" : [

            "идентификатор подписчика" 

           ,"идентификатор подписчика" 

            ........

           ]

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

или

 ,"group" : код группы, участникам которой будут высланы повторные напоминания о подтверждении регистрации

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"group.filter" : [
                    фильтр отбора как у group.filter.set
                   ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"url" : ссылка на файл со списком адресов. по одному на строке. возможно сжатие zip.

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

                "filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }
 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

}

ответ


{

    <общие поля>

   "list" : { -- ключ - оригинальное значение адреса из запроса

              " missing@CityCat.ru" : { "email" : "missing@citycat.ru" -- нормализованая форма адреса

                                       ,"syntax" : "ok" -- результат синтаксической проверки адреса

                                       ,"smtp"  : {

                                              "status"  : "rcptto" -- стадия возникновения ошибки

                                             ,"domain"  : "citycat.ru" -- домен для которого определялся первичный MX

                                             ,"mx"      : "smtp.citycat.ru" -- первичный MX используемый для теста

                                             ,"ip"      : "81.9.34.192" -- ip-адрес использованного MX

                                             ,"ptr"     : [             -- список имён соответствующих ip-адресу
                                                           "cat192.subscribe.ru" 
                                                          ]

                                             ,"code"    : "550" -- код SMTP-ошибки (000 - тайм-аут)

                                             ,"dsn"     : "5.1.1" -- DSN-код SMTP-ошибки (при наличии в ответе)

                                             ,"message" : "<missing@citycat.ru>... User unknown" 
                                                           -- текст SMTP-ошибки или ошибки DNS

                                                  }

                                       }
             ,"     PRO@subscribe.ru   " :  { "email" : "pro@subscribe.ru" 

                                             ,"syntax" : "ok" 

                                             ,"smtp" : {
                                                          "status" : "ok" 

                                                         ,"domain" : "subscribe.ru" 

                                                         ,"mx"     : "smtp.subscribe.ru" 

                                                         ,"ip"     : "81.9.34.192" 

                                                         ,"ptr"    : [
                                                                      "cat192.subscribe.ru" 
                                                                     ]
                                                       }

                                            }

             ,"123@test@test.ru  " :   { "email" : null

                                       ,"syntax" : "error/email/multydog" -- код ошибки

                                      }

         }

}

вверх

Удаление ошибок доставки

Удаляются записи об ошибках доставки указанных адресов.

С подписчика, при наличии, снимается блокировка из-за ошибок доставки.

Но наличие подписчика не обязательно (в отличии от вызова member.set который работает именно с подписчиками)

{

  "action" : "email.cleanerror" 

-- указание адресов одним из способов

 ,"email": "идентификатор подписчика" 

 ,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn

или

 ,"list" : [

            "идентификатор подписчика" 

           ,"идентификатор подписчика" 

            ........

           ]

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

или

 ,"group" : код группы, участникам которой будут высланы повторные напоминания о подтверждении регистрации

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"group.filter" : [
                    фильтр отбора как у group.filter.set
                   ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"url" : ссылка на файл со списком адресов. по одному на строке. возможно сжатие zip.

 ,"addr_type" : email|msisdn|csid -- тип идентификаторов в списке. не обязательно, система сама распознает email или msisdn

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

                "filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }
 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

}

ответ

{

    <общие поля>

   "list" : { -- ключ - оригинальное значение адреса из запроса

             "email1" =>  результат -- null - адрес синтаксически не верен
                                    -- 1 - ошибки доставки удалены, подписчик разблокирован (если был заблокирован)
                                    -- 0 - не было ошибок доставки
            ,"email2" => ...

         }

}

вверх

Группы адресов - Сегментирование аудитории

Группы позволяют сформировать сегменты аудитории по самым разным критерия персональных данных занесённых в базу и по накопленой статистике о певедении подписчика.

Группы-списки это списки адресов формируемые вами в явном виде. Участие адреса в такой группе не зависит от его персональных данных и статистики. Выпуск и подсчёт статистик по таким группам несколько быстрее так как, список адресов предопределён.

Группа-фильтр это динамический список адресов. Участие в такой группе описывается фильтром применяемым к персональных данным и статистике поведения. Участие адреса в такой группе каждый раз устанавливается в момент проверки в реальном времени. За счёт этого выпуск и подсчёт статистик по таким группам несколько медленние из-за фильтрации данных.

Фильтр группы-фильтра поддерживает оба формата хранения - и АВО и КД.

Cписок групп


{

 "action" : "group.list" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "код группы" 

            ,"name" : "название" 

            ,"type" : "тип группы" -- list или filter

            ,"addr_type" : "тип адресов" -- email или msisdn

            ,"create.date" : "дата и время создания" -- Ys, null

            ,"update.date" : "дата и время последнего изменения" -- Ys, null

            ,"reltype" : ...

            ,"relref" : ...
            }

            ...

           ]

}

Создать группу

При создании группы типа filter она первоначально получит пустой набор правил отбора.

И до явной установки желаемых правил отбор по такой группе всегда будет давать пусто.


{

  "action" : "group.create" 

 ,"id" : "смысловой код группы. символы a-zA-Z0-9" 
         -- например "clients2011" 
         -- не обязателен. при отсутвии будет назначен автоматически

 ,"name" : "название группы" -- обязательно

 ,"type" : "list | filter" -- обязательно. тип группы.

                           -- list - группа является заранее создаваемым списком

                           -- filter - группа является набором фильтров для отбора по всей базе

 ,"addr_type" : "email | msisdn" -- обязательно, тип адресов на работу с которыми будет ориентирована группа

                                 -- email - адреса электронной почты

                                 -- msisdn - номера телефонов
 ,"reltype" : ...

 ,"relref" : ...
}

ответ


{

 <общие поля>

 , "id" : "код созданой группы" 

}

вверх

Прочитать группу

Обратите внимание, что при вызове со списком групп, сообщения об ошибочных кодах групп будут возвращены не в errors (как это было бы при одиночном вызове), а в warnings, так как в целом считается что вызов закончился успешно.


{

  "action" : "group.get" 

 ,"with_filter" : 0 | 1 -- вернуть в ответе так же и фильтр группы

 ,"id" : "код группы" - для одной группы

-- или

 ,"id" : [ "код группы", "код группы", ... ]  - для списка групп

-- или

 ,"id" : [ "*" ] - для всех групп
}

ответ


{

 <общие поля>

-- для одной группы

 ,"obj" : {

             ,"id" : "код группы" 

             ,"name" : "название группы" 

             ,"type" : "тип группы" -- list или filter

             ,"addr_type" : "тип адресов" -- email или msisdn

             ,"filter" : [ -- если запрошено
                          фильтр группы как в вызове group.filter.get 
                         ]

             ,"create.date" : "дата и время создания" -- Ys, null

             ,"update.date" : "дата и время последнего изменения" -- Ys, null

             ,"reltype" : ...

             ,"relref" : ...
          }

-- или для нескольких групп

 ,"list" : [
            {
             содержимое obj для одной группы
            }

           ,{
             содержимое obj для другой группы
            }

            .....
           ]

 ,"warnings" : [ -- может отсутствовать
                массив аналогичный по структуре массиву errors
                содержит ошибки для тех кодов групп из списка, которые ошибочны по тем или иным причинам
               ]

}

вверх

Изменить группу

Не указанные параметры не изменяются


{

  "action" : "group.set" 

 , "id" : "код группы" 

 ,"name" : "название группы" - не обязательно

 ,"reltype" : ...

 ,"relref" : ...

--  необязательный

 ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

--  по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ


{

 <общие поля>

}

если "return_fresh_obj" : "1" 

ответ -- как на запрос чтения "action" : "group.get" соответствующей анкеты

вверх

Удалить группу


{

  "action" : "group.delete" 

 , "id" : "код группы" 

}

ответ


{

 <общие поля>

}

вверх

Получить правила фильтрации группы

Только для групп с типом filter


{

  "action" : "group.filter.get" 

 , "id" : "код группы" 

}

ответ


{

 <общие поля>

 ,"filter" : [

               массив описывающий фильтр

             ]

}

вверх

Установить правила фильтрации группы

Только для групп с типом filter


{

  "action" : "group.filter.set" 

 ,"id" : "код группы" 

 ,"filter" : [

              массив описывающий фильтр

             ]

}

ответ


{

 <общие поля>

}

вверх

Совпадение одного адреса с фильтром группы

Вызов в основном полезен для отладки сложных фильтров - по результату label можно видеть ход выполнения условий, а по safe - результат работы итератеров has.

{
 "action" : "group.filter.match" 

,"id" : кодгруппы

,"email": "идентификатор подписчика" 

,"addr_type" : email|msisdn|csid -- тип идентификатора. не обязательно, система сама распознает email или msisdn}
}

Ответ

{
 <общие поля>

 "match" : 0 | 1 | null -- с фильтром не совпало, совпало, ошибка фильтра

,"safe" :  { ... } -- данные сохранённые итератерами has в фильтре

,"label" : { ... } -- метки сохранённые в фильтре при проходе по условиям
}

вверх

Снимок группы / Расширить группу-список

В указанную группу вносятся адреса из другой группы, условия созданного на ходу фильтра или прочих возможных источников.

Так как источником может быть группа-фильтр или просто фильтр, то вы получаете неизменный "снимок" состояния группы на момент вызова.

Это полезно, например, если требуется провести несколько статистических анализов (не надо для каждого анализа заново искать подписчиков по фильтру и их состав не изменен) или для быстрого выхода рассылки с очень сложным филтром (можно заранее сделать снимок, скажем ночью, и потом днём очень быстро по нему выпустить рассылку).

Так же с помощью этого вызова можно добавлять группу-список участников из других групп, условий фильтра или прочих доступных источников

{
 "action" : "group.snapshot" 

  "to" : { -- получатель данных

          "id" : "код группы" -- группа-список в которую будут внесены адреса
                              -- обратите внимание, что это не вызов импорта списков !
                              -- если адреса указанные в источниках email, list, stat.uni на момент
                              -- вызова отсутствуют в базе, то они не будут созданы и добавлены.
                              -- для этого есть импорт подписчиков.

         ,"clean" : 0|1 -- очистить группу перед началом внесения
         }

 ,"from" : { -- источник данных одно из

         "email" : "адрес подписчика" 

        -- или

          "list" : [

                    "адрес подписчика" 

                   ,"адрес подписчика" 

                    ........

                   ]

         ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

        -- или

          "group" : код группы

         ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

        -- или

          "group.filter" : [
                            фильтр отбора как у group.filter.set
                           ]

         ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

        -- или

         "stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

                "filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }

         ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный}
   }

ответ

{

 <общие поля>

-- для асинхронного запроса

,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для синхронного запроса -- ничего дополнительного

}

вверх

Очистить группу-список

Вызов удаляет из участников группы-списка заданные адреса.

Сами адреса в базе остаются ! Удалением адресов из базы занимается вызов member.delete

{
  "action" : "group.clean" 

 ,"id" : "код группы" -- группа-список из которой будут удалены адреса

-- указание подписчиков одним из способов

 ,"all" : 1 -- все адреса. быстрый синхронный вызов.
            -- для получения трекинга или асинхронности используйте источник "group" c таким же значением что и "id" (более медленно)

или

 ,"email" : "адрес подписчика" -- вызов синхронный

или

 ,"list" : [

            "адрес подписчика" 

           ,"адрес подписчика" 

            ........

           ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

или

 ,"group" : код группы участники которой будут удалены

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"group.filter" : [
                    фильтр отбора как у group.filter.set
                   ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"url" : ссылка на файл со списком адресов. по одному на строке. возможно сжатие zip.

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

                "filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

}

ответ

{

 <общие поля>

-- для асинхронного запроса

,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для синхронного запроса -- ничего дополнительного

}

вверх

Анкеты

Анкеты предназначены для описания структуры и иерархии данных для формата АВО.

Список анкет


{

 "action" : "anketa.list" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "system" : "системная, да, нет (1, 0)" -- системные анкеты не доступны для изменения

             ,"id" : "уникальный идентификатор" 

             ,"name" : "название",

            }

            ...

           ]

}

Чтение анкеты


{

 "action" : "anketa.get" 

,"id"     : код-анкеты

}

ответ


{

 <общие поля>

,"obj" : {

          "id" : код-анкеты,

          "param" : {

                     "name"     : название анкеты,

                     "system"   : анкета системная да/нет

                    },

          "order" : [ -- порядок вопросов

                     "код-вопроса3",

                     "код-вопроса8",

                     .....

                     "код-вопроса4" 

                    ]

          "quests" : { -- вопросы анкеты

             "код-вопроса" : {

                             "id" : код-вопроса,

                             "@"  : номер по порядку,

                             "name" : формулировка вопроса,

                             "type" : тип вопроса,

                             --  для типа "dt" 

                             "subtype" : под-тип вопроса
                                        --  yd  - Дата и время с точностью от года до дня
                                        --  yh  - Дата и время с точностью от года до часа
                                        --  ym  - Дата и время с точностью от года до минуты
                                        --  ys -  Дата и время с точностью от года до секунды

                             --  для типа "free" 

                             "width" : ширина поля в байтах для типа free

                             --  для типа "1m" или "nm" 

                             "answers" : { -- ответы вопроса

                                          "код ответа1" : "название ответа1",

                                          "код ответа2" : "название ответа2",

                                           .....

                                          "код ответа3" : "название ответа3",

                                         },

                             "order" : [ -- порядок ответов

                                        "код ответа3",

                                        "код ответа1",

                                        .....

                                        "код ответа2" 

                                       ],

                            "form" : {
                                       -- свойства вопроса при использовании анкеты в форме как анкеты для первичного приёма данных
                                     }
                            },

             ............

         },

}

вверх

Удаление анкеты


{

    "action" : "anketa.delete" 

   ,"id" : "уникальный идентификатор анкеты" 

}

ответ


{

    <общие поля>

}

вверх

Создание анкеты


{

     "action" : "anketa.create" 

    ,"name" : "Название анкеты",

     необязательные:

    ,"copy_from" : "уникальный идентификатор анкеты (не нужен для создаваемой с нуля, а не копируемой анкеты)" 

    ,"id" : "уникальный код анкеты" 

    ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

    по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ


{

  <общие поля>

,"id" : "код анкеты" 

}

если "return_fresh_obj" : "1" 

ответ -- как на запрос чтения "action" : "anketa.get" соответствующей анкеты

вверх

Сохранение анкеты


{

    "action" : "anketa.set" 

    ,"id" : "идентификатор анкеты" 

    ,"name" : "название анкеты" 

    необязательный

    ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

    по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ


{

    <общие поля>

}

если "return_fresh_obj" : "1" 

ответ -- как на запрос чтения "action" : "anketa.get" соответствующей анкеты

вверх

Добавления нового вопроса анкеты


{

   "action" : "anketa.quest.add" 

  ,"anketa.id" : "уникальный идентификатор анкеты вопроса" 

  ,"return_fresh_obj" : 0|1  -- вернуть новый объект анкета -- да (1), нет (0, по умолчанию)

-- добавление одного вопроса

  ,obj : { -- описание вопроса

          "name" : "Текст вопроса" 

         ,"type" : "Тип вопроса" 
                 -- free - свободный ввод
                 -- dt   - дата и время
                 -- 1m   - выбор одного из списка
                 -- nm   - выбор нескольких из списк
                 -- int  - целое число

         -- параметры, определяемые типом вопроса:

         -- для type=free:

              ,"width" : "количество символов для свободного ввода" -- обязателен,  > 0

         -- для  type=dt:

              ,"dtsubtype" : "точность для даты и времени" -- обязателен
                              -- yd - от года до дня.     в данных записывается и выводится в формате "YYYY-MM-DD" 
                              -- yh - от года до часа.    в данных записывается и выводится в формате "YYYY-MM-DD hh" 
                              -- ym - от года до минуты.  в данных записывается и выводится в формате "YYYY-MM-DD hh:mm" 
                              -- ys - от года до секунды. в данных записывается и выводится в формате "YYYY-MM-DD hh:mm:ss" 

         -- для type=1m или nm (выбор из списка):

              ,"answers" : { -- ответы вопроса 

                            "код ответа 1" : "название ответа1" 

                           ,"код ответа 2" : "название ответа2" 

                           ..... 

                           ,"код ответа N" : "название ответаN" 

                          }

             ,"order" : [ -- порядок ответов

                           "код ответа 3" 

                          ,"код ответа 8" 

                          .....

                          ,"код ответа 4" 
                        ]

         -- необязательные для любого типа

         ,"id" : "уникальный идентификатор вопроса" 

         -- свойства вопроса при использовании анкеты в форме как анкеты для первичного приёма данных
         -- не обязательно
        ,"form" : {

             -- Видимость в форме

            "hidden" : 0|1 -- 0 - по умолчанию - поле в форме будет полем ввода 
                              1 - поле в форме будет иметь тип hidden и значение default

            -- Обязательность заполнения

           ,"mustbe" : 0|1 -- 0 - по умолчанию - поле не обязательно для заполнения (игнорируется для hidden=1)
                           --     при первоначальном отображении формы в поле будет значение default
                           -- 1 - поле обязательно для заполнения

            -- Значение по умолчанию

            ,"default" : "значение по умолчанию" -- для всех вопросов кроме 1m и nm

            ,"default" : [код ответа] -- для вопросов 1m

            ,"default" : [код ответа1 , код ответа 2, ... ] -- для вопросов nm

            -- В какой вопрос какой анкеты-хранилища копируется результат при подтверждении заполнения формы
            -- параметры или оба отсутствуют или оба указаны и не пустые
            -- анкета не может быть системной или показывать на саму себя
            -- тип вопроса в который будет идти копирование должен совпадать с типом вопроса в obj
            -- для вопросов 1m и nm все возможные ответы текущего вопроса должны быть в ответах вопроса в который будет идти копирование

            ,"trans.ank"   : "код анкеты" 
            ,"trans.quest" : "код вопроса" 
          }
        }

-- добавление нескольких вопросов. любая ошибка отменяет все изменения. вопросы буду или все добавлены или все не добавлены

  ,obj : [
          { описание вопроса-1 }
         ,{ описание вопроса-2 }
         ,...............
         ]
}

ответ


{

  <общие поля>

 ,"obj" : { объект анкета как из ответа anketa.get } -- если "return_fresh_obj" : "1" 

-- в зависимости от вида параметра obj в вызове

 ,"id" : "id-добавленного вопроса" 

-- или

 ,"id" : [ -- порядок id соответствует порядку объектов в параметре obj в вызове
          "id-добавленного вопроса-1" 
         ,"id-добавленного вопроса-2" 
         .......
         ]

}

вверх

Изменение вопроса анкеты


{

   "action" : "anketa.quest.set" 

  ,"anketa.id" : "уникальный идентификатор анкеты вопроса" 

  ,"return_fresh_obj" : 0|1  -- вернуть новый объект анкета -- да (1), нет (0, по умолчанию)

-- добавление одного вопроса

  ,"obj" : { -- описание вопроса

            "id" : "уникальный идентификатор вопроса" 

           ,"name" : "Текст вопроса" 

           -- параметры, определяемые типом вопроса:

           -- для type=free:

                ,"width" : "количество символов для свободного ввода, обязателен,  > 0" 

           -- для type=1m или nm:

                ,"answers" : { -- ответы вопроса 

                              "код ответа 1" : "название ответа 1" 

                             ,"код ответа 2" : "название ответа 2" 

                             ..... 

                             ,"код ответа N" : "название ответа N" 

                             }

               ,"order" : [ -- порядок ответов

                           "код ответа 3" 

                          ,"код ответа 8" 

                          .....

                          ,"код ответа 4" 
                          ]

        ,"form" : {
           -- свойства вопроса при использовании анкеты в форме как анкеты для первичного приёма данных
           -- не обязательно
           -- при указании "form" в запросе все параметры вопроса связанные с ним заменяются на новые указанные или удаляются
        }

-- изменение нескольких вопросов. любая ошибка отменяет все изменения. вопросы буду или все изменены или все не изменены

  ,obj : [
          { описание вопроса-1 }
         ,{ описание вопроса-2 }
         ,...............
         ]
}

ответ


{

    <общие поля>

 ,"obj" : { объект анкета как из ответа anketa.get } -- если "return_fresh_obj" : "1" 

-- в зависимости от вида параметра obj в вызове

 ,"id" : "id-добавленного вопроса" 

-- или

 ,"id" : [ -- порядок id соответствует порядку объектов в параметре obj в вызове
          "id-добавленного вопроса-1" 
         ,"id-добавленного вопроса-2" 
         .......
         ]
}

вверх

Удаление вопроса анкеты

Удалении нескольких вопроса транхакционно - удаляются или все (не ошибок) или ни ничего (хоть одна ошибка).


{

     "action" : "anketa.quest.delete" 

    ,"anketa.id" : "уникальный идентификатор анкеты вопроса" 

    ,"id" : "уникальный идентификатор вопроса" 

     -- или

    ,"id" : [ "id1", "id2", ..... ]

--- необязательный

    ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

    по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ


{

    <общие поля>

}

если "return_fresh_obj" : "1" 

ответ -- как на запрос чтения "action" : "anketa.get" соответствующей анкеты

вверх

Изменение позиции вопроса анкеты


{

     "action" : "anketa.quest.order" 

     ,"anketa.id" : "уникальный идентификатор анкеты вопроса" 

     ,"order" : [ -- новый порядок вопросов

          "код-вопроса3" 

         ,"код-вопроса8" 

          .....

         ,"код-вопроса4" 

     ]

    необязательный

    ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

    по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ


{

    <общие поля>

}

если "return_fresh_obj" : "1" 

ответ -- как на запрос чтения "action" : "anketa.get" соответствующей анкеты

вверх

Удаление ответа вопроса анкеты

Для вопросов типа "1m" и "nm"


{

    "action" : "anketa.quest.response.delete" 

   ,"anketa.id" : "уникальный идентификатор анкеты вопроса" 

   ,"quest.id" : "уникальный идентификатор вопроса" 

   ,"id" : "уникальный идентификатор ответа" 

   необязательный

   ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

   по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ


{

    <общие поля>

}

если "return_fresh_obj" : "1" 

ответ -- как на запрос чтения "action" : "anketa.get" соответствующей анкеты

вверх

Изменение позиции ответа вопроса

Для вопросов типа "1m" и "nm"


{

    "action" : "anketa.quest.response.order" 

    ,"anketa.id" : "уникальный идентификатор анкеты вопроса" 

    ,"id" : "уникальный идентификатор вопроса" 

    ,"order" : [ -- новый порядок ответов

         "код-ответа3" 

        ,"код-ответа8" 

        .....

        ,"код-ответа4" 

]

    необязательный

    ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

    по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ


{

    <общие поля>

}

если "return_fresh_obj" : "1" 

ответ -- как на запрос чтения "action" : "anketa.get" соответствующей анкеты

Выпуски рассылки

Варианты выпуска рассылок

Все рассылки (и email и sms и viber) выпускаются через вызов issue.send

Есть четыре варианта его использования.

Группа-спискок Группа-фильтр Экспресс-Выпуск Транзакционное письмо
Адреса получателей Зарнее созданые в базе список Динамический поиск по базе адресов данные которых удовлетворяют фильтру Указываются при выпуске или забираются по ссылке Указывается при выпуске
Данные для персонализации Из базы Из базы Указываются при выпуске Из базы если не указаны при выпуске
Количество переменных Не ограничено Не ограничено Не ограничено Не ограничено
Шаблонизатор Продвинутый Продвинутый Продвинутый Продвинутый
Собственные функции-плагины к шаблонизатору Да Да Да Да
Сложные вложенные структуры данных Да, в модели ДК Да, в моделе ДК Да Да
Подтверждение от владельца адреса Требуется Требуется Требуется Не обязательно
Разбор жалоб на спам Согласно договора Согласно договора Согласно договора С особым пристрастием
Лимит в месяц Не ограничено Не ограничено Не ограничено Первоначально равен максимально допустимому количеству адресов в базе. Далее в зависимости от репутации

Отослать выпуск

Этот вызов асинхронный - получение положительного ответа означает, что задание на рассылку поставлено в очередь. Используйте возвращаемый track.id для отслеживания выпуска.

Это не гарантирует выпуск - он может не состоятся по причинам которые не проверить на момент вызова (например недоступность на момент преобразования ссылки для которой производится преобразование для учёта переходов).

Индивидуальные данные персонализации для каждого адреса берутся из сохранённых в базе (группа-список, группа-фильтр) или из указанных прямо при выпуске (Экспресс-Выпуск,Транзакционные письма) и доступны через префикс anketa шаблонизватора.

Данные формата АВО автоматически доступны как преобразованные в формат ДК.

Данные персонализации для каждого получателя могут быть дополнены внешними данными (формат ДК) в процессе выпуска. Подробнее в разделе "Внешние данные для персонализации"

Из списка участников группы и списка Экспресс-Выпуска система автоматически исключает адреса не подтвердившие внесение в базу, отписавшиеся, находящиеся в стоп-листе или имеющие постоянные ошибки доставки.
Вам не надо предпринимать ни каких дополнительных усилий для этого - всё происходит автоматически.

Для каждого письма, sms или viber система отслеживает статус доставки - доставлено/не доставлено (и с какой ошибкой), а для каждой sms - дополнительно: размер, предполагаемый оператор и стоимость доставки. Это доступно через объект deliv вызова stat.uni (из этого следует что из deliv можно так же получить просто список участников выпуска).

Для каждого успешно доставленного письма система отслеживает факты открытия и факты перехода по каждой ссылке (если учёт переходов не был отключён при выпуске). Время каждого открытия/перехода и ip-адрес доступны через объекты read и click вызова stat.uni.

Так же система отслеживает вид устройства, операционную систему, браузер и его версию если произошло чтение и переход из письма. Это доступно через объект gadget вызова stat.uni.

При установке на страницах сайта специального счётчика, возможно отслеживание пути по сайту и достижения целевых страниц после перехода из письма - подробност в разделе "Целевые Страницы".

Каждое письмо/sms/viber имеют уникальный идентификатор состоящий из номера выпуска и номера письма в выпуске. Поэтому вы можете получить описанную выше статистику с точностью до каждого письма/sms/viber каждого выпуска для каждого адресата.

До тех пор, пока у вас в каждом выпуске одни адрес или телефон получателя встречается только один раз (а это может быть не так для Экспресс-Выпусков, Персональных писем и выпусков по группам с фильтрами со сложной группировкой), допустимо считать уникальным идентификатором письма для такого выпуска номер выпуска и адрес получателя.

Кроме системного уникального номера письма каждому письмо можно присвоить клиентскую метку состоящую из одного или нескольких полей. Для этого настраивается список ключей данных которые запоминаются в каждом выпуске для каждого получателя (отсутствующие в конкретном выпуске или пустые данные не в счёт). Они доступные через Универсальную Статистику и позволяют присваивать письмам удобные для анализа клиента метки. Для настройки этой возможности обратитесь в Службу Поддержки.

Благодаря поддержке нескольких идентификаторов у одного подписчика вам доступны разнообразные сценарии мультиканального взаимодействия с ним. Например "Выслать письмо, а тем кто за 10 дней так и не прочёл и не сделал ни одного перехода выслать смс".


{
   "action" : "issue.send",

   "name" : "Название выпуска", -- не обязательно, при отсутствии используется тема письма или имя отправителя sms
                                -- из-за особенностей учёта выходов транзакционных писем personal данный параметр
                                -- для них будет работать не так как ожидается и в, целом, бесполезен

* параметры содержимого выпуска. Для Транзакционных писем - только указание черновика и прикреплённых файлов.

    "letter" : {

*** для sms

                * содержимое сообщения

                или указание параметров

                "from.name" : "Имя отправителя" -- обязителен и имя отправителя должно быть в списке уже промодерированных имён (вызовы issue.smssender.*)

               ,"message": {

                           "sms"  : "sms cообщение" 

                          }

                или черновик из которого их взять. при указании черновика другие параметры содержимого сообщения игнорируются и берутся только из черновика

                "draft.id": "номер черновика содержимое которого даст выпуск" -- должен иметь sms-версию

*** для viber

                "button.url"   : "ссылка с кнопки" -- обязательно если нужна кнопка после сообщения

               ,"button.text" : "надпись на кнопке" -- не обязательно, по умолчанию "Узнать" 

               ,"message": {

                          "viber"  : "viber cообщение" -- текст без форматирования

                          }

               ,"attaches": [  -- не обязательно, одна и только одна ссылка на возможную картинку отображаемую между текстом и кнопкой

                              -- при выпуске по черновику, прикрепляемые файлы из черновика добавляются к указанным в списке
                              -- если одноимённый файл или ccылка тут отсутствуют

                             { "url"  : "Внешниение ссылки http / https / ftp / ftps / sftp" },

                             -- или

                             { "url"  : "персонализованный url - для каждого свой" }, -- подробнее в разделе "Персонализация выпуска" 

                или черновик из которого их взять. при указании черновика другие параметры содержимого сообщения игнорируются и берутся только из черновика

                "draft.id": "номер черновика содержимое которого даст выпуск" -- должен иметь viber-версию

*** для email

                * содержимое письма

                или указание параметров

                "subject" : "Тема письма" -- обязательно не пусто

               ,"from.name" : "Имя отправителя" 

               ,"from.email" : "Адрес отправителя (email)" -- обязательно не пустой для текстовых писем; адрес должен быть в списке уже подтверждённых адресов (вызовы issue.emailsender.*)
                                                           --
                                                           -- Использование в качестве адреса отправителя адреса принадлежащие Почте Mail.ru (mail.ru, bk.ru, inboc.ru, list.ru, mail.ua) - запрещено настройками Mail.Ru
                                                           -- Рассылка с таким адресом не выйдет. Черновик - не сохранится.
                                                           ---Более подробную информацию вы можете прочитать по адресу https://corp.mail.ru/ru/press/releases/9593/

               ,"reply.name" : "Имя для обратного адреса для ответа" 

               ,"reply.email" : "Обратный адрес для ответа (email)"  -- адрес должен быть в списке уже подтверждённых адресов (вызовы issue.emailsender.*)
                                                                     -- возможен учёт и статистика ответов получателей
                                                                     -- для этого необходимо активировать через Службу Поддержки настройку "Перехват ответов на письма выпуска" 
                                                                     -- статистика доступна через deliv.replyed.* вызова stat.uni

               ,"to.name" : "Имя получателя" -- в этом поле уместна персонализация для подстановки имени и фамилии получателя

               ,"message": { -- одна или обе не пустые версии письма

                           "html" : "html-версия письма" 

                          ,"text" : "текстовая версия письма" 
                          }

                или черновик из которого их взять. при указании черновика другие параметры содержимого письма игнорируются и берутся только из черновика

                "draft.id": "номер черновика содержимое которого даст выпуск", -- должен иметь html и/или текстовую версию

                * генерация текстовой версии из html

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

                "autotext" : "0|1|ширина" -- 0 - отключено (по умолчанию)
                                          -- 1 - включено, ширина строки 80 символов
                                          -- ширина - включено, ширина строки - как указано

                * прикреплённые файлы письма

                -- по умолчанию, mime-тип файла определяется по расширению

                -- по умолчанию, кодировкой текстовых файлов text/* считается utf-8

                -- следующие имена файлов зарезервированы для внутренних нужд и не должны использоваться
                --
                --  все начинающиеся с _
                --
                --  message.text
                --
                --  index.html
                --  index.htm
                --  index.shtml
                --  index.shtm
                --
                --  attach.html
                --  attach.htm
                --  attach.shtml
                --  attach.shtm
                --
                --  message.html
                --  message.htm
                --  message.shtml
                --  message.shtm

               ,"attaches": [ 

                              -- при выпуске по черновику, прикрепляемые файлы из черновика добавляются к указанным в списке
                              -- если одноимённый файл или ccылка тут отсутствуют

                             {
                              "name" : "имя файла",

                             ,"content": "содержимое файла закодированное base64",

                             ,"encoding" : "base64",

                             ,"mime-type" : "тип атача", -- не обязательно, заменяет тип установленный по расширению имени атача

                             ,"charset" : "набор символов атача", -- не обязательно, заменяет используемое по умолчанию utf-8
                             },

                             { 
                              "name" : "имя файла",

                             ,"content": "содержимое файла",

                             ,"mime-type" : "тип файла", -- не обязательно, заменяет тип установленный по расширению имени файла

                             ,"charset" : "набор символов файла" -- не обязательно, заменяет используемое по умолчанию utf-8
                             },

                             { 
                              "url"  : "url откуда заборать. Внешиение ссылки http / https / ftp / ftps / sftp или из хранилища загрузок rfs://upload/путь-до-файла " 

                             ,"mime-type" : "тип атача", -- не обязательно, заменяет тип установленный по расширению имени атача или полученный в ответ на запрос

                             ,"charset" : "набор символов атача", -- не обязательно, заменяет используемое по умолчанию utf-8 или полученный в ответ на запрос
                             },

                             { "url"  : "персонализованный url откуда забирать - для каждого свой" }, -- подробнее в разделе "Персонализация выпуска" 

                             -- генерация персонального pdf-документа на ходу. подробнее в разделе "Персонализация выпуска" 

                             { "pdf"  : "номер черновика"}

                             -- генерация персонального excel-документа на ходу. подробнее в разделе "Персонализация выпуска" 

                             { "xlsx"  : "номер черновика"}

                             ...

                            ]
               }

* параметры способа выпуска

    "group" : "id группы | masssending - если это Экспресс-Выпуск | personal - Транзакционное письмо",
              -- старый параметр gid переименован в group для совместимости с другими вызовами
              -- можно продолжать указывать gid - он будет использоваться при отсутствии group

              -- если фильтр группы описан в формате КД и в нём используется оператор has с параметром save,
              -- то один участник группы может попасть в рассылку несколько раз в зависимости от количества
              -- срабатываний has/save и параметра выпуска "multiple".
              -- В описании фильтра формата КД описано когда и чем это полезно.

   "group.exclude" : [ "id-группы1", "id-группы2" .... ] -- Группы списки для исключения получателей
                     -- Если адрес получателя присутствует хотя бы в одной из указанных групп-списков,
                     -- то он исключается из выпуска
                     -- Эта возможность позволяет, например, реализовать "стоп-лист по тематикам" - в письме
                     -- размещается дополнительная ссылка "Не хочу получать эту тематику" ведущая на Форму
                     -- А в выпусках "этой тематики" указывается исключить группу-список "Заполнившие Форму" 

    "sendwhen": "Когда выпустить (now - сейчас | later - отложенный | delay - задержаный | save - отложить на хранение | test - тестовый)",
                -- при использовании в "Действиях по расписанию" допустимы только now и test
                -- Тестовый выпуск не доступен для Экспресс-Выпуска, Транзакционных писем, SMS и Viber

    "dkim.id" : "номер проверенного dkim-ключа или 0" -- если строго "0", то будет использоваться общий системный ключ
                                                -- если пусто/не задано/невалиден, то будет взят из черновика
                                                 - если в черновике строго "0", то будет использоваться общий системный ключ
                                                -- если в черновике не задан или на момент выпуска выбранный ключ из черновика окажется не проверенным,
                                                -- то будет использоваться самый свежий из ключей имеющихся для домена отправителя (from.email из вызова или черновика)
                                                -- если ключа точно по домену не будет, то домен будет сокращён на один уровеньт и поиск будет повторён. и так до домена второго уровня  включительно
                                                -- если и ключа по домену и его сокращённым вариантам не будет или на момент выпуска выбранный ключ по домену окажется не проверенным,
                                                -- то будет использоваться значение установленное через sys.settings.set
                                                -- если и его нет или не проверено, то будет использоваться общий системный ключ

    "campaign.id" : "код кампании" 

-- или

    "campaign.id" : [ "код кампании 1", "код кампании 2" ...]

                   -- Код кампании в которую будет включён выпуск 
                   -- пусто или не указан - будет включён в кампанию, если она указана в черновике
                   -- 0 - не будет включён ни в какую кампанию, даже указанную в черновику
                   --     не может быть указан с другими кодами если их несколько

                   -- Код кампании (указаный или полученый из черновика) игнорирует если выпуск выходит
                   -- в рамках АБ-тестирования или тригерной рассылки

                   -- При успешном создании задания отложенного выпуска, он автоматически вносится в кампании указанные в campaign.id
                   -- При выходе отложенного выпуска ранее заданный campaign.id игнорируется и выпуск вносится только в те кампании,
                   -- в которых на момент выпуска состоит задание отложенного выпуска сработавшее в кроне естественным путём (cron.runonce не считается)

* при отложеном выпуске (later) указывается дополнительно дата, час и минута не ранее которых выпускать 

    -- Если такие же дату-время уже запланирован выпуск по такой же группе, то новое задание принято не будет.
    -- ИСКЛЮЧЕНИЕ - Экспресс-Выпуск и Транзакционные Письма.
    -- Выпуски по этим группам могут быть запланированы на одну и ту же дату в любом количестве.

    "later.time" : "YYYY-MM-DD hh:mm" -- рекомендуемая точность - 5 минут. Т.е. mm из набора 0,5,10,15,20,25,30,35,40,45,50,55
                                      -- другие значение mm допустимы, но фактически при выпуске могут быть округлены системой
                                      -- до ближайшего большего значения из указанного набора

* при задержаном выпуске (delay) указывается дополнительно на сколько минут задержать выпуск

    -- Так как "задержка" реализуется созданием отложенного выпуска на "текущее время + delay.time",
    -- то действуют ТАКИЕ ЖЕ ОГРАНИЧЕНИЯ, что и при использовании later.time

    "delay.time" : "mm" 

* для Email - при выпуске теста (test) указываются адреса получателей. не более пяти.

    "mca": [

            "e@mail.1",

            "e@mail.2",

             ......

           ]

* для Email - параметры преобразования ссылок для учёта перехода по ним в системе 

    "relink" : 0|1 -- Преобразовывать ссылки автоматически
                   -- 1 - да, 0 - нет

    "relink.param" : {
                      -- если relink = 1, а какой то из параметров relink.param не указан,
                      -- то считается что: link=1, image=0 и test=1 соответственно для всех рассылок кроме personal
                      -- для personal это: link=1, image=0 и test=0 - объяснение ниже 

                      "link" : 0|1 -- преобразовывать ссылки тега <A>
                                   -- 1 - да, 0 - нет
                                   -- преобразуются ссылки со схемами: http(s)://, ftp(s)://, viber://, fb-messenger://, skype://

                      "image": 0|1 -- преобразовывать ссылка на внешние картинки для тега <IMG>
                                   -- и фоновых изображений в <BODY> и <TABLE>
                                   -- 1 - да, 0 - нет
                                   -- преобразуются ссылки со схемами: http(s)://, ftp(s)://, viber://, fb-messenger://, skype://

                      "test": 0|1 -- проверять существование адресов (только если адрес подлежит преобразованию)
                                  -- при включённой проверке выпуск не выйдет если ссылка
                                  -- не действительна (ответ не 200, не 301 и не 302)
                                  -- 1 - да, 0 - нет

                                  -- настройка "test"=1 не действует на домены vk.com, vkontake.ru, ok.ru, my.mail.ru, facebook.com, fb.com, instagram.com, twitter.com,
                                  -- pintrest.com, youtube.com, rutube.com, rutube.com, linked.in, odnoklassniki.ru, ok.ru, telegram.me 
                                  -- и их мобильные версии с префиксом "m" и версии с префиксом "www" 
                                  -- ссылки с их использованием не проверяются на существование из-за непредсказуемости результата
                                  -- указывайте таким ссылкам явно x-do-link-test=1 для осуществления проверки

                                  -- никогда не проверяются ссылки с персонализицией, и со схемами viber://, fb-messenger://, skype://

                                  -- Обратите внимание, что массовая отсылка выпусков Персональных Писем, может вызвать
                                  -- проблему выпуска из-за неудавшейся проверки адресов. Причина - система анализа частоты
                                  -- запросов сайта для которого проверяется ссылка может воспринять многократные постоянные
                                  -- запросы несколько подряд раз за короткое время (а именно так это будет выглядеть при массовой
                                  -- отсылке Персональных писем) как атаку на сайт и не давать проверить ссылку.
                                  -- По этому, по умолчанию, для personal проверка ссылок не производится.
                                  -- Включить её для всего выпуска вы всегда можете задав параметр test в явном виде.
                                  -- Включить её для отдельных выпусков вы всегда можете атрибутом x-do-link-test

                      -- Глобальные настройки из relink.param можно переопределить для любого
                      -- конкретного тега A и IMG указав в них не стандартные атрибуты
                      -- "x-do-link-relink" и "x-do-link-test" влияющие на преобразование
                      -- и тестирование по следующим правилам:
                      --
                      -- Атрибут отсутствует или пуст - учитывается глобальная настройка
                      -- Атрибут = 1 - делать действие не смотря на глобальную настройку
                      -- Атрибут = 0 - не делать действие не смотря на глобальную настройку
                      --
                      -- В отличи от параметра "test", зависящего от "link" и "image",
                      -- явное указание атрибута x-do-link-test="1" всегда вызывает проверку ссылки
                      -- не смотря на то, что преобразование для неё может и не проводиться
                      -- из-за настроек link/image/x-do-link-relink
                     }

* для Viber - параметры преобразования ссылок для учёта перехода по ним

-- Аналогично как для Email но действует только на ссылку button.link
-- По этому не имеет варианта image и не применимы замечания про атрибуты x-do-relink-*

    "relink" : 0|1 -- Преобразовывать ссылки автоматически
                   -- 1 - да, 0 - нет

    "relink.param" : {
                      "link" : 0|1 

                      "test": 0|1
                     }

* для Email - параметр преобразования ссылок для отслеживания перехода по ним с помощью сторонних сервисов

    "link.qsid": "Параметр для отслеживания переходов по ссылкам через сторонние сервисы" 
                  -- добавляется ко всем ссылкам через query-string
                  -- значение должно быть уже url-encoded
                  -- при выпуске по черновику, если этот параметр пуст, то берётся link.qsid из черновика
                  -- обычно содержит utm-метки и используется для отслеживания через сервисы типа Google Analitics
                  -- не имеет ни какого отношения к параметра relink*

* для Viber - параметр преобразования ссылок для отслеживания перехода по ним с помощью сторонних сервисов
     -- Аналогично как и для Email но действует только на ссылку button.link

    "link.qsid": "Параметр для отслеживания переходов по ссылкам через сторонние сервисы" 

* список получателей и данные персонализации для Экcпресс-Выпуска
  -- подробнее в разделе "Форматы данных для импортирования и Экспресс-Выпуска" 
  -- данные персонализации могут быть дополнены внешними данными в процессе выпуска
  -- подробнее в разделе "Внешние данные для персонализации" 

    "users.list": адреса и данные персонализации  -- непосредственно в СSV или XLSX

или

    "users.list" : {
                    "encoding" : "base64" 
                   ,"content" : "адреса и данные персонализации в base64" -- вариант для CSV и XLSX которые не получается передать в двоичном виде
                   },   

или

    "users.list" : [  -- данные в формате JSON-массив
                    { данные одного получателя }
                    ..............
                   ]

или

    "users.list" : {  -- данные в формате JSON-объект-ABO
                    "caption" : [ описание столбцов ]
                   ,"rows" : [
                              [ данные одного получателя ]
                              ..............
                             ]
                   }

или

    "users.url": "url"  -- по которому находятся список адресов с данными для персонализации
                        -- внешиение ссылки http / https / ftp  / ftps / sftp или из хранилища загрузок rfs://upload/путь-до-файла 

                        -- Для запросов по SOAP (например к Siebel) схемы soap:// и soaps:// - обратитесь с Службу Поддержки
                        -- для получения дополнительной информации

                        -- Для запросов из Google Big Query настройте внешнюю авторизация для схемы gbd:// (описано в вызовах authext.*)
                        -- Данные получаются полной выборкой из указанной в урле вида gbd://authext:AUTHEXT_ID@DATASET_ID/TABLE_ID таблицы.
                        -- Одна из колонок таблицы должна называться email - она послужит источником адресов получателей и доступно при персонализации текста письма под стандартным именем anketa.member.email
                        -- Название остальных колонок не важно, но оно должно начитаться с латинской буквы и продолжаться латинскими буквами и цифрами.
                        -- Для использования данных в персонализации используйте подстановки вида [% anketa.t.НАЗВАНИЕ-КОЛОНКИ-ТАБЛИЦЫ %] для всех колонок таблицы кроме email

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

* параметры списка получателей для Экпресс-Выпуска

    "only_unique": 0|1 -- Следить за уникальностью адресов в списке
                       -- 1 - да,  повторно встреченные адреса исключаются из выпуска
                       -- 0 - нет, повторно встреченные адреса участвуют в рассылке
* параметры объединения Экпресс-Выпусков

    accumulate" : 0|1 -- только для Экспресс-Выпуска
                      -- если Экспресс-Выпуск с такими же именем (параметр "name") когда-либо уже выпускался,
                      -- то этот запускаемый выпуск будет оформлен не как новый, а как продолжение уже вышедшего.
                      -- полезно в случае если ваши CRM не может выдать весь реестр для рассылки за раз,
                      -- а только порциями.
                      -- с учётом параллельности приёма и выпуска заданий на рассылку, настоятельно рекомендуется
                      -- выпустить сначала первую порцию и потом уже последующие можно запускать параллельно

* учёт множественных совпадений итератор в фильтре

    "multiple" : 0|1 -- что делать с множественными совпадениями итератора
                     -- 0 - по умолчанию, выпустить одно письмо
                     -- 1 - по одному письму на совпадение
                     -- В описании фильтра формата КД описано когда и чем это полезно.

* получатель для Транзакционных писем

    "email" : "адрес получателя или номер телефона получателя" 
                                 -- данные для персонализации берутся из базы
                                 --
                                 -- данные персонализации могут быть дополнены внешними данными в процессе выпуска
                                 -- подробнее в разделе "Внешние данные для персонализации" 
                                 --
                                 -- при этом способе указания получателя он проверяется на возможность отсылки ему письма
                                 -- (синтаксическая верность адреса, стоп-лист, ошибки доставки, отписался) и вы сразу получите
                                 -- в ответ описание ошибки если она есть
                                 -- при  других способах задания получателя, проверка на возможность выпуска происходит
                                 -- только при формировании рассылки

или

    "users.list": один адрес и данные персонализации  -- непосредственно в JSON (два варианта) или в СSV или XLSX
                                                      -- подробнее в разделе "Форматы данных для импортирования и Экспресс-Выпуска" 
                                                      -- при указании только адреса данные персонализации берутся из базы
                                                      -- при нескольких адресах используется только первый
                                                      -- данные персонализации могут быть дополнены внешними данными в процессе выпуска
                                                      -- подробнее в разделе "Внешние данные для персонализации" 

* Ограничение на размер тиража

 -- не обязательно, по умолчанию - "100%" 

 ,"users.slice => "число" - тираж составит не более "число" первых получателей. число - целое число больше нуля

 или

 ,"users.slice" => "процент%" - тираж составит указанный процент от всех получателей. процент - целое число больше нуля и меньше 101

* Для email и viber ограничение на частоту отправки
  --
  -- все ограничения касаются только первой попытки отправки сообщения.
  -- если сообщение не принято сразу, то ограничения могут не быть соблюдены.

  -- при совместном использовании с tz_observance, ограничение на частоту отправки действует независимо в каждом
  -- часовом поясе

 ,"tz_limit" {

   "default" : {

      -- ограничение на частоту отправки сообщений
      -- за rate минут будет передано в доставку в не более number сообщений

      -- если параметр отсутствует или пуст или обе величины равны 0,
      -- то ограничения нет

      "batch" : { -- или отсутствуют или указаны оба значения
                 "number" : "сколько cообщений" -- обязательно, >= 2000
                ,"rate"   : "за сколько минут"  -- обязательно, >= 0
                }

     }
  }

* Для email и viber учёт часового пояса получателя

  -- Не обязательно, при отсутствии учёта часового пояса нет и всё выходит сразу (если нет прочих настроек)

  -- При выпуске с учётом временной зоны, письма будут заранее сформированы, но их первая попытка
  -- доставки начнётся не ранее, чем во временной зоне получателя начнётся указанный час
  -- по его локальному времени.
  --
  -- Источником данных может быть один или несколько пунктов анкеты подписчика.
  --
  -- Берётся первое определившееся время по порядку указания источников.
  --
  -- Часовой пояс не удалось определить ни по одному из источников, то письмо начинает
  -- доставляться сразу (с учётом tz_limit)
  --
  -- В качестве указания на часовой пояс понимаются названия большинства стран и крупных городом
  -- в английском и русском написании (регистр не важен)
  --
  -- А так же, числовые смещения относительно UTC в виде
  --
  --           +hhmm  -hhmm  UTC+hhmm  UTC-hhmm
  --           +hh    -hh    UTC+hh    UTC-hh
  --           +h     -h     UTC+h     UTC-h  
  --           hh
  --           h
  --
  -- Используйте вызов sys.settings.get, что бы проверить понимает ли система ваш способ записи.
  -- Если нет - обратитесь в Службу Поддержки за расширением нашего списка городов и стран.
  -- 
  -- Во избежание путаницы с пониманием часовых поясов, настоятельно рекомендуется планировать
  -- такие рассылки не менее чем за 24 часа и учитывать особенности России - текущий час в Москве
  -- по всех других часовых поясах страны (за исключением Калининграда) уже прошёл и настанет
  -- там через через 15-23 часа в зависимости от удалённости.

  -- при совместном использовании с tz_limit, ограничение на частоту отправки действует независимо в каждом
  -- часовом поясе

 ,"tz_observance" : { -- не обязательно

                    "hour" : "час" -- от 00 до 23
                                   -- час по местному времени для начала доставки писем
                                   -- не обязательно, по умолчанию - текущий час для выпуска "сейчас" 
                                   -- или час запуска для отложенного выпуска

                   ,"source" : [ -- обязательно. источники данных о временной зоне подписчика
                                 -- в его анкете. один и болеее.
                                "ключ-данных" 
                               ,"ключ-данных" 
                               ,"ключ-данных" 
                               ........
                               ]
                   }

* Для sms ограничение на частоту и время отправки

  -- все ограничения касаются только первой попытки отправки сообщения в sms-центр.
  -- если sms-центр не принял сообщение сразу или принял, но внутри себя
  -- по какой-то причине не пытался доставить сразу, то ограничения могут не быть соблюдены.

  -- допустимы временные зоны относительно GMT от -23 до +23 и default. знаки "+" и "-" обязательны.

  -- временная зона телефонного номера сотовой связи определяется по базе Федерального Агентства Связи.

 ,"tz_limit" {

   "default" : { -- настройки по умолчанию для не описанных зон или отсутствующих описаний с описанной зоне

      -- ограничение на частоту отправки сообщений
      -- за rate минут будет передано в доставку в sms-центр не более number сообщений
      --
      -- если параметр отсутствует или пуст или обе величины равны 0,
      -- то ограничения нет

      "batch" : { -- или отсутствуют или указаны оба значения
                 "number" : "сколько cообщений" -- обязательно, >= 0
                ,"rate" : "за сколько минут"    -- обязательно, >= 0
                }

      -- ограничение по времени отправки
      -- *все даты и часы по местному времени временной зоны !!!*

     ,"time" : {
                 "dt" : "YYYY-MM-DD" -- отправка начнётся не ранее указанного дня.
                                     -- если не указано или пусто или в прошлом,
                                     -- то используется текущая даты зоны MSK/MSD

                ,"hour" : [ час, час, час, ... ] -- разрешённые часы местного времени (0-23) в которые будет осуществлять отправка 
                                                 -- если не указано или пусто, то разрешены часы с 9 по 21
                                                 -- подумайте, стоит ли расширять этот диапазон на утреннее и ночное время !
                                                 --
                                                 -- порядок следования часов важен для первого дня отправки !
                                                 -- запись
                                                 --        {
                                                 --         "dt"   : "2012-09-17" 
                                                 --        ,"hour" : [ 20, 21, 22, 23, 06 08 07 ]
                                                 --        }
                                                 -- задаёт разрешённые часы с 6, 7, 8, 20, 22 и 23 и начало рассылки 17го декабря
                                                 -- но именно 17го она начнётся не в 6 часов, а в 20, так как 20 указано первым
                                                 -- а 18го и далее - начиная с 6
                                                 --
                                                 -- разрешается весь час - последняя отправка в нём может быть
                                                 -- даже в 59 минут 59 секунд, что может выглядеть как уже "не разрешённый" час
                                                 -- т.к. сообщение будет доставлено уже явно в следующем часе.
                }

   }

  ,"+4" : { -- ограничение для временной зоны GMT+4

       -- полностью отсутствующий или пустой компонент берётся из default

      "batch" : { .... } -- используйте 0 для rate и number что-бы снять ограничение из default

     ,"time"  : { .... } -- *дата и часы по местному времени временной зоны !!!*
                         --
                         -- если задано только dt, то действуют стандартные разрешённые часы с 9 по 21
                         -- подумайте, стоит ли расширять этот диапазон на утреннее и ночное время !
                         --
                         -- если задано только time, то то используется текущая даты зоны MSK/MSD
                         --
                         -- если не задано ни dt ни time, то действуют ограничения из default

   }

  ,"-2" : { -- ограничение для зоны GMT-2
    ..........
  }

  ..........

 }

* данные произвольной структуры одинаковые для всех подписчиков

 -- эти данные трактуются в по моделе КД

 -- эти данные будут доступны для шаблонизатора выпуска как переменные с именем совпадающим с именем ключа хэша

 -- эти данные могут быть дополнены из черновика. указанные здесь имеют больший приоритет. 

 -- эти данные могут быть дополнены внешними данными в процессе выпуска - подробнее в разделе "Внешние данные для персонализации" 

 -- в настоящий момент зарезервированы имена:
 --   anketa - данные о конкретном подписчике
 --   lenta  - данные для подстановки rss/atom каналов
 --   date   - интерфейс к функциям работы с датой и временем
 --   form   - форма опроса

 -- во избежание пересечений с новыми стандартными именами рекомендуется
 -- свои имена начинать с большой буквы

,"extra" : {
            "Key1" : [ 12, 34 ,56 ] -- пример использования [% Key1[2] %]

           ,"City" : { -- пример использования [% City.rus.long %]
                      "rus" : {
                               "long"  : "Санкт-Петербург" 
                              ,"short" : "СПБ" 
                     ,"eng" : {
                               "long"  : "Leningrad" 
                              ,"short" : "LED" 
                              }
                     }

           }

}

ответ

{

    <общие поля>

   ,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*
                       --
                       -- после выпуска Транзакционного письма полученный письмом номер "letter" будет в отчёте track.get
                       -- для данного track.id. Используя letter в stat.uni можно получить информацию по данному конкретному
                       -- Транзакционному письму.

}

Список отложенных выпусков


{

  "action" : "issue.later.list",

 ,"from" : "YYYY-MM-DD" -- от даты (не обязательно)

 ,"upto" : "YYYY-MM-DD" -- до даты (не обязательно)

 ,"group" : [ -- фильтр по группам (не обязательно)

               код-группы-1

              ,код-группы-2

               ... 

             ] 

 ,"format" :  "email|sms|viber|html|text"  -- фильтр по формату (не обязательно).
                                     -- email это "html или text" 

 ,"draft" : номер черновика" -- фильтр по черновику (не обязательно)
                             -- параметр отсутствует - не фильтровать
                             -- параметр присутствует но пустой - все выпуски не по черновикам
}

ответ


{

  <общие поля>

 ,"list" :  [

              {

                 "id" : "код выпуска" 

                ,"create.date" : "YYYY-MM-DD hh:mm:ss" -- дата постановки задания

                ,"track.id" : "номер трекера выпуска" 

                ,"status" : "статус задания" -- hold  - отложено на хранение
                                             -- later - отложено для выпуска
                                             -- error - ошибка выпуска

                -- если статус "отложено из-за ошибки" 

                ,"status.reason" : код ошибки

                -- если статус "отложено для выпуска" 

                ,"later.time" : "YYYY-MM-DD hh:mm" -- запланированная дата выпуска

-- базовые параметры выпуска

                ,"name" : название выпуска

                ,"group" : код группы для которой отложен выпуск

                ,"letter" : {
                              "draft.id" : код черновика используемого для выпуска

                             ,"subject" : "тема выпуска" -- для sms совпадает с именем отправителя
                             ,"from.email" : адрес отправителя
                             ,"from.name" : имя отправителя
                            }

              },

             ...

            ]

}

Прочитать отложенный выпуск


{
 "action" : "issue.later.get",

 "id" : "код выпуска",
}

ответ


{

  <общие поля>

 ,"obj" : {
                 "id" : "код выпуска" 

                ,"create.date" : "YYYY-MM-DD hh:mm:ss" -- дата постановки задания

                ,"track.id" : "номер трекера выпуска" 

                ,"status" : "статус задания" -- hold  - отложено на хранение
                                             -- later - отложено для выпуска
                                             -- error - ошибка выпуска

                -- если статус "отложено из-за ошибки" 
                ,"status.reason" : код ошибки

                ,"track.id" : номер - номер асинхронного запроса выданный выпуску при issue.send

                <......... параметры issue.send использованные при задании выпуска .......>

-- все эти параметры совпадают c issue.send и значения obj можно использовать для создания нового выпуска
-- необходимо будет добавить action
-- уточнить значения sendwhen и later.time
-- и настоятельно рекомендуется удалить "лишнее" : id, create.date, status, status.reason, format, name, issue.date
-- но для просто смены даты выпуска или отсылки немедленно используйте специализированный выпуск issue.later.send

-- при выпуске по черновику перед вызовом issue.send необходимо проверить параметры
-- subject, from.name и from.email черновика, так как они не обязательны в черновике
-- но обязательны при выпуске

         }
}

Изменение даты выхода отложенного выпуска

Если на новую дату выпуска уже запланирован выпуск по такой же группе, то изменение даты закончится ошибкой.

Исключение - Экспресс-Выпуск и Транзакционные Письма. Выпуски по этим группам могут быть запланированы на одну и ту же дату в любом количестве.


{

 "action" : "issue.later.send",

 "id" : "код выпуска",

 "issue.date" : "YYYY-MM-DD hh:mm" -- Новая дата выпуска
                                   -- Если параметр отсутствует или указана дата в прошлом,
                                   -- то выпуск выйдет сразу

}

ответ


{

  <общие поля>

}

Удаление отложенного выпуска

Трекер выпуска остаётся и помечается как "Действие отменено"


{

 "action" : "issue.later.delete",

 "id" : "код выпуска" 

}

ответ


{

  <общие поля>

}

Список формирующихся и доставляющихся рассылок

{
 "action" : "issue.running.list" 
}

ответ

{
  <общие поля>

  "list" : [
      {
         "id" : "номер задания",

         "issue.id" : "номер выпуска если уже назначен",

         "gid" : "код группы" 

         -- состояние формирования выпуска

         "processing" : 1, -- выпуск формируется

         "track.id" : "номер трекера если назначен",

         "track.status" : "состояние трекера если известно",

          -- состояние доставки выпуска

         "delivering" : 1, -- выпуск доставляется

         "later" : "дата и время" -- запланированное время запуска последней
                                  -- части растянутого выпуска (Ys)

         "paused" : 1, -- в доставке есть приостановленный пользователем тираж

         "spam" : 1, -- в доставке есть тираж приостановленный из-за спама
      },

     ...
     ]
}

Приостановить выпуск рассылки

Приостанавливается формирование и доставка всех имеющихся на момент вызова выпусков или указанного одного выпуска

Вызов делает буквально то, что описано. Новые выпуски будут продолжать приниматься и выпускаться. Для влияния на них используйте issue.paused вызова sys.settings.get/set

Возобновление возможно в течении 3 дней после чего данные о приостановленном выпуске удаляются.

{
 "action" : "issue.running.pause" 

-- одно из

 ,"id" : "all" -- все

-- или

 ,"id" : "идентификатор задания" -- указанный
}

ответ

{
  <общие поля>
}

Возобновить выпуск рассылки

Возобновляется формирование и доставка всех имеющихся приостановленных на момент вызова выпусков или указанного одного приостановленного выпуска.

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

{
 "action" : "issue.running.resume" 

-- одно из

 ,"id" : "all" -- все

-- или

 ,"id" : "идентификатор задания" -- указанный
}

ответ

{
  <общие поля>
}

Досрочно начать выпуск отложенных частей рассылки

Досрочно начинается доставка всех отложенных частей (не важно на какое время) для всех имеющихся на момент вызова выпусков или указанного одного выпуска

{
 "action" : "issue.running.start" 

-- одно из

 ,"id" : "all" -- все

-- или

 ,"id" : "идентификатор задания" -- указанный
}

ответ

{
  <общие поля>
}

Прекратить выпуск рассылки

Прекращается без возможности возобновления формирование и доставка всех имеющихся на момент вызова выпусков или указанного одного выпуска

Трекер становится "Отменено"

{
 "action" : "issue.running.delete" 

-- одно из

 ,"id" : "all" -- все

-- или

 ,"id" : "идентификатор задания" -- указанный
}

ответ

{
  <общие поля>
}

Список адресов email-отправителей

Адреса указываемые при выпуске рассылки как "адрес отправителя" или как "обратный адрес" должны быть подтверждены перед использованием.

При каждом вызове issue.emailsender.set на используемый в нём адрес высылается письмо со ссылкой подтверждения.

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

{

  "action" : "issue.emailsender.list" 

}

ответ

{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"name" : "адрес" 

            ,"onmoderation" : 0|1 -- 0 - адрес одобрен, 1 - адрес ждёт подтверждения

            ,"create.date" : "дата и время создания" -- Ys, null

            ,"update.date" : "дата и время последнего изменения" -- Ys, null
            }

            ...

           ]

}

Чтение адреса email-отправителя

{

  "action" : "issue.emailsender.get" 

  ,"id" : "идентификатор" 

}

ответ

{
  <общие поля>

  "obj" : {

            ,"id" : "идентификатор" 

            ,"name" : "адрес" 

            ,"onmoderation" : 0|1

            ,"create.date" : "дата и время создания" -- Ys, null

            ,"update.date" : "дата и время последнего изменения" -- Ys, null
          }

}

Создание или изменение адреса email-отправителя

{
  "action" : "issue.emailsender.set" 

  ,"obj" : {

      ,"name" : "адрес" 

           }

 необязательные

  ,"id" : "идентификатор" -- если не указан, создается новый

  ,"return_fresh_obj": "" -- вернуть объект в формате issue.emailsender.get

}

ответ

{

 <общие поля>

 ,obj  { ... } -- объект в формате issue.emailsender.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление адреса email-отправителя

{

  "action" : "issue.emailsender.delete" 

  ,"id" : "идентификатор" 

}

ответ

{

 <общие поля>

}

Список имён sms-отправителей

{

  "action" : "issue.smssender.list" 

}

ответ

{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"name" : "отправитель" 

            ,"onmoderation" : 0|1 -- 0 - имя одобрено, 1 - имя ещё на модерации

            ,"create.date" : "дата и время создания" -- Ys, null

            ,"update.date" : "дата и время последнего изменения" -- Ys, null
            }

            ...

           ]

}

Чтение имени sms-отправителя

{

  "action" : "issue.smssender.get" 

  ,"id" : "идентификатор sms-отправителя" 

}

ответ

{
  <общие поля>

  "obj" : {

              "id" : "идентификатор sms-отправителя" 

             ,"name" : "отправитель" 

            ,"onmoderation" : 0|1

            ,"create.date" : "дата и время создания" -- Ys, null

            ,"update.date" : "дата и время последнего изменения" -- Ys, null
          }

}

Создание или изменение имени sms-отправителя

Создание или изменённо имя автоматически попадает на модерацию

Если имя состоит из одних только цифр, то его длина не должна превышать 15 символов.

Иначе имя может содержать от 1 до 13 символов больших и маленьких латинских букв и цифр.

{
  "action" : "issue.smssender.set" 

  ,"obj" : {

       "name" : "отправитель" 

           }

 необязательные

  ,"id" : "идентификатор sms-отправителя" -- если не указан, создается новый

  ,"return_fresh_obj": "" -- вернуть объект в формате issue.smssender.get

}

ответ

{

 <общие поля>

 ,obj  { ... } -- объект в формате issue.smssender.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление имени sms-отправителя

{

  "action" : "issue.smssender.delete" 

  ,"id" : "идентификатор sms-отправителя" 

}

ответ

{

 <общие поля>

}

Мультиканальные выпуски

Мультиканальный выпуск позволяет выпускать рассылку с учётом наличия у подписчика идентификаторов email и телефона в их различных сочетаниях.

Типичные задачи: "Выпустить в данной группе письмо для тех у кого указан email, а если email-а нет но есть телефон, то тогда послать viber, а если нет то тогда послать sms" (вы экономите sms отправляя вместо него письмо и viber тем кому это возможно) или же "Выпустить в данной группе по всем телефонам и email-ам" (вы оповещаете подписчиков максимально широким способом).

Мультиканальный выпуск происходит по одной группе (по одному и тому же списку при Экспресс-Выпуске), но по разным каналам доставки в зависимости от указанного способа выпуска и наличия у получателя email и/или номер телефона.

При Экспресс-выпуске, для указания наличия у получателя email используйте колонку с названием member.email, а для sms и viber- member.cellphone. Обе колонки должны присутствовать одновременно. Это отличается от привычного для обычного выпуска или импорта правила, что только в реестре может быть только одна такая колонка одновременно.

Используйте пустое значение в соответствующей ячейке, если у получателя нет адреса или телефона.

Выпустить мультивыпуск

Вызов очень похож на обычный выпуск рассылки, за исключением того, что на каждый канал доставки указывается свой текст писма/сообщения

Параметры оставленные без описания совпадаются с одноимёнными из issue.send

{
 "action" : "issue.send.multi" 

 ,"name" : "название мультивыпуска" -- не обязательно
                                    -- из-за особенностей учёта выходов транзакционных писем personal данный параметр
                                    -- для них будет работать не так как ожидается и в, целом, бесполезен

 ,"group" : 

 ,"group.exclude" : 

 ,"sendwhen": 

 ,"campaign.id" : 

 ,"later.time" :  -- в отличии от обычного выпуска, мультивыпуски любой одной и той же группы могут быть отложены на одно и тоже время

 ,"delay.time" :

 ,"mca": 

 ,"users.slice" :

 ,"users.list" :

 ,"users.url" 

-- автоподбор для personal

 ,"email" : "адрес получателя" 
          -- если тип указанного адреса не подходит под какой-то из указанных в issue канал доставки,
          -- то будут взяты все идентификаторы (головы) связанные с данным адресом и из них
          -- выбран первый подходящий по типу и доступный для рассылки
          -- если ничего подходящего нет, то данный канал доставки будет пропущен

-- точное указание соответствия для personal

 ,"email" : [ "адрес получателя" , "адрес получателя" , "адрес получателя" ... ]
           -- адреса будут использованы по порядку по одному для каждого соответствующего канала из issue
           -- если тип указанного адреса не подходит под свой канал доставки или не доступен для рассылки,
           -- то данный канал доставки будет пропущен

 ,"only_unique" :

 ,"multiple" :

 ,"extra" :

 "issue" : [ -- два или более элемента описывающие режим и текст сообщения для используемых каналов доставки

   { -- email

    "mode" : "all" или "rest" -- режим выпуска.
                              -- all  - по всем кто может получать данный канал
                              -- rest - по тем кто не попал в предыдущие каналы и могут получать данный
                              --
                              -- не обязателено для первого элемента "issue" - для него всегда all
                              -- обязательно для второго и последующих

    ,"letter" : {
                 "draft.id" 

                 --------------

                 ,"subject" :

                 ,"from.name" :

                 ,"from.email" :

                 ,"reply.name" :

                 ,"reply.email" :

                 ,"to.name" :

                 ,"message" :

                 ,"attaches" :

                 ,"autotext" :
                }

    ,"dkim.id" :

    ,"relink" :

    ,"relink.param" :

    ,"link.qsid": 

    ,"tz_limit" 

    ,"tz_observance" 
   }

  ,{ -- viber

    "mode" : "all" или "rest" 

    ,"letter" : {
                 "draft.id" 

                 --------------

                "button.url"   :

               ,"button.text" :

               ,"message" :

               ,"attaches":

    ,"relink" :

    ,"relink.param" :

    ,"link.qsid": 

    ,"tz_limit" 

    ,"tz_observance" 
   }

  ,{ -- sms

    "mode" : "all" или "rest" 

    ,"letter" : {
                 "draft.id" 

                 --------------

                ,"from.name" 

                ,message : { }
                }

    ,"tz_limit" 
   }

 ] 

}

ответ

{

 <общие поля>

 "id" : "номер мульти-выпуска" -- если номер выпуска null, то это означает, что при выпуске personal c указанием получателя в "email" ни одному каналу доставки
                               -- не удалось подобрать адрес получателя. следовательно мультивыпуск не создан так как не может быть выпущена ни одна рассылка.
                               -- причины по каждому каналу перечислены в "list" 

,"list" : [ -- информация по каждому элементу "issue" в том же порядке как заданы в запросе

           {
            "channel"  : "email|sms|viber" -- канал выпуска  

           ,"track.id" : "номер трекера созданного  выпуска для данного канала" 
           }

           -- или

           {
            "channel"  : "email|sms|viber" -- канал выпуска  

           ,"errors" :  [ ... ] -- возможно при выпуске personal
                                -- означает, что для данного канала выпуска не нашлось подходящего идентификатора получателя
                                -- или все найденные не доступны для рассылки или в режиме rest и без него нашли что выпускать
                                -- и, следовательно, для данного канала выпуск рассылки не был запущен вообще
           }

          ]

}

Специфические для вызова ошибки

Общая фатальная ошибка - данный канал указан в issue повторно. N - индекс в issue

{ "id" : "wrong_arg", "explain" : "issue/channel", "detail" : N }

Ошибка в результате одного из каналов - канал с режимом rest сразу исключён, так как уже заранее ясно что ему достанет 0 получателей.

{ "id" : "channel/excluded", "explain" : "rest" }

Cписок мультивыпусков

{

 "action" : "issue.multi.list" 

}

ответ

{

  <общие поля>

 ,"list" : [

             {

                "id"   : "идентификатор мультивыпуска" 

               ,"name" : "название мультивыпуска" 

               ,"dt"   : "дата мультивыпуска" 

               ,"group" : "группа мультивыпуска" 

             }

             ...

           ]

}

Чтение мультивыпуска

{

 "action" : "issue.multi.get" 

 ,"id" : "идентификатор мультивыпуска" 

}

ответ

{

  <общие поля>

 ,"obj" : {

                "id"   : "идентификатор мультивыпуска" 

               ,"name" : "название мультивыпуска" 

               ,"dt"   : "дата мультивыпуска" 

               ,"group" : "группа мультивыпуска" 

               ,"issue" : [
                           -- описание выпусков вышедших в рамках данного мультивыпуска
                           {
                             "id" : "номер выпуска" 

                            ,"dt" : "дата выпуска (Ys)" 

                            ,"channel" : "email|sms|viber" -- канал выпуска

                            ,"members" : "число получателей" 
                            },

                           ........

                          ]

           }
}

Персонализация выпуска

Данные для персонализации

Текст выпуска может быть персонализирован для каждого получателя его личными данными.

Для Экспресс-Выпуска данные для персонализации указываются совместно со списком получетелей.

Для Транзакционных Писем данные могут быть взяты или из хранимых в системе или указаны при выпуске.

Для прочих рассылок данные персонализации берутся из сохранённых в системе при импорте подписчиков (member.import) и индивидульно внесённых для отдельных адресов (member.set).

Для управления персонализацией создан язык ProScript - просто но эффективный язык персонализации с богатыми возможностями.

Для простой подстановки данных воспользуйтесь командами подстановки [% anketa.ключ-данных %]

В руководстве по ProScript размещённом на сайте https://sendsay.ru описаны более продвинутые возможности - условные операторы, циклы, вызовы функций, работа с датой и временем.

Данные персонализации могут быть дополнены из внешнего источника (например с вашего сайта) прямо во время выпуска - это описано ниже в этой главе.

Так же, при выпуске можно указать данные для персонализации уникальные для всего выпуска - параметр выпуска extra.

Персональные Excel-документы

Каждый получатель выпуска рассылки может иметь в письме лично для него сформированные и персонализированные его данными электронные таблицы Excel.

Для этого при выпуске просто указывается что требуется прикреплять индивидуальные Excel-файлы и на базе какого html-черновика их строить.

Система автоматически сгенерирует из черновика персонализованную электронную таблицу и превратит еёв уникальный для каждого Excel-файл.

Правила формирования

Из персонализованного html-кода черновика извлекается каждая таблица и размещается одной таблице на лист документа.

Таблицы со всеми пустыми ячейками игнорируются.

Вложенные таблицы игнорируются.

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

Свойствами документа, листа, строки и ячейки можно управлять через атрибуты data-xlsx-* тегов table/tr/th/td как описано далее

Свойства документа

Свойства документа устанавливаются в любом теге table. Повторное указание свойства в другой таблице заменяет предыдущее.

data-xlsx-title - заголовок

data-xlsx-subject - тема

data-xlsx-author - автор

data-xlsx-manager - менаджер

data-xlsx-company - компания

data-xlsx-category - категория

data-xlsx-keywords - ключевые слова

data-xlsx-comments - комментарий

data-xlsx-status - статус

Свойства листа

Свойства листа устанавливаются в теге table

data-xlsx-name - название листа (по умолчанию порядковый номер таблицы с единицы)

data-xlsx-infocus - 1 если этот лист должен быть отбражён при открытии документа. по умолчанию - первый лист

data-xlsx-selection - пусто или координаты ячейки которая будет выбрана при открытии документа (два числа через запятую - строка,столбец. счёт от нуля)

data-xlsx-autofilter - пусто или диапазоны выделения для автофильтра (диапазон - две координаты через запятую) т.е. строка чисел через запятую которая разбивается на четвёрки (диапазоны) которые разбиваются на пары (координаты)). Пример: data-xlsx-autofilter="2,3,6,8,34,6,38,9" - два диапазона: от 2,3 до 6,8 и от 34,6 до 38,9

Свойства строки

Свойства строки устанавливаются в теге tr

data-xlsx-row_height - высота строки (по умолчанию - автоподбор)

Свойства ячейки

Свойства ячейки устанавливаются в тегах th и td

data-xlsx-contents - если задано, то заменяет содержимое полученое из html-ячейки

data-xlsx-type - тип ячейки. string (по умолчанию), number, data_type. Для data_type содержимое должно быть вида YYYY-MM-DD hh:mm:ss

data-xlsx-display_format - формат отображения для number и data_type. Для data_type по умолчанию "DD/MM/YYYY HH:MM:SS"

data-xlsx-formula - формула для вычисления содержимого ячейки при просмотре документа

data-xlsx-сolumn_width - ширина строки (по умолчанию - автоподбор)

data-xlsx-format-bold - жирный шрифт (1, по умолчанию для th) или нет (0, по умолчанию для tr)

data-xlsx-format-italic - наклонный шрифт (1) или нет (0, по умолчанию)

data-xlsx-format-color - цвет шрифта. прямое указание вида "#RRGGBB" или одно из black, white, red, lime, blue, yellow, magenta, cyan, brown, green, navy, purple, silver, gray, pink, orange

data-xlsx-format-size - размер шрифта

data-xlsx-format-align - горизонтальное выравнивание содержимого ячейки: none (по умолчанию), left, center, right, fill, justify, center_across

data-xlsx-format-valign - вертикальное выравнивание содержимого ячейки: bottom (по умолчанию), top, vcenter, vjustify

data-xlsx-format-text_wrap - разрешить (1) перенос длинного текста или нет (0, по умолчанию)

data-xlsx-format-pattern - фон ячейки: none (по умолчанию), solid, medium_gray, dark_gray.light_gray, dark_horizontal, dark_vertical, dark_down, dark_up, dark_grid, dark_trellis, light_horizontal, light_vertical, light_down, light_up, light_grid, light_trellis, gray_125, gray_0625

data-xlsx-format-fg_color - цвет переднего плана ячейки (имеет смысл только если pattern не none)

data-xlsx-format-bg_color - цвет фона ячейки (имеет смысл только если pattern не none)

data-xlsx-format-border - четыре стиля бордюра для сторон ячейки через запятую в порядке лево,право,верх,низ: none, thin, medium, dashed, dotted. thick, double, hair, medium_dashed, dash_dot, medium_dash_dot, dash_dot_dot, medium_dash_dot_dot, slant_dash_dot

data-xlsx-format-border_color - четыре цвета бордюра для сторон ячеек через запятую

Персональные PDF-документы

Каждый получатель выпуска рассылки может иметь в письме лично для него сформированные и персонализированные его данными pdf-файлы.

Для этого при выпуске просто указывается что требуется прикреплять индивидуальные pdf и на базе какого html-черновика их строить.

Система автоматически сгенерирует из черновика персонализованный документ и превратит его в уникальный для каждого pdf-файл.

Для подключения этой возможности обратитесь в службу поддержки.

Персональные прикрепляемые файлы

Каждый получатель выпуска рассылки может иметь в письме прикрепляемые файлы получаемые по ссылке которая персонализируется в момент формирования письма.

Такая ссылка различается по наличию команд(ы) ProScript в ссылке на прикрепляемый файл.

Например

http://test.ru/secret/file/[% anketa.member.email %].png

Система автоматически получит каждый файл и прекрепит его к нужному письму.

Для подключения этой возможности обратитесь в службу поддержки.

Обратите внимание, что:

Вставка штрих-кода

В текст письма можно вставить штрих-код формата Code128.

Такой штрих-код используется на многих платёжных документах и понимается платёжными терминалами.

Для вставки штрих-кода используйте функцию шаблонизатора barcode_code128().


[% barcode_code128(код) %]

[% barcode_code128(код, параметр1, значение1, параметр2, значение2, ... ) %]

Пример с настройками по умолчанию, проверенный на реальных платёжных терминалах.


<img src="[% barcode_code128(anketa.barcode.T) %]" alt="[% anketa.barcode.T %]" width="280">

где anketa.barcode.T это переменная содержащая код для штрих-кода. Замените её имя на настоящее используемое у вас.

Вы можете влиять на внешний вид штриш-кода с помощью следующих параметров

 width            (автоматически) - Ширина изображения в пикселах
 height           (автоматически) - Высота изображения в пикселах
 border           (0)             - Размер чёрного бордюра вокруг штрих-кода
 scale            (1)             - Маштаб одного штриха кода в пикселах
 font             ("medium")      - Шрифт текста ("giant", "large", "medium", "small", "tiny")
 show_text        (1)             - Повторить цифры штрих-кода текстом под изображением
 font_margin      (2)             - Отступ в пикселах вокруг текста
 font_align       ("center")      - Выравнивание текста ("left", "right", "center")
 top_margin       (2)             - Отступ в пикселах выше штрих-кода
 bottom_margin    (2)             - Отступ в пикселах ниже штрих-кода и текста
 left_margin      (5)             - Отступ в пикселах ниже штрих-кода и текста
 right_margin     (5)             - Отступ в пикселах ниже штрих-кода и текста
 padding          (20)            - Размер в пикселах белой рамки до и после штрих-код

Внешние данные персонализации выпуска

Зачем это надо ? Например раздача пользователям уникальных кодов для последующего их использования на вашем сайте или магазине.

Для персонализации выпуска данные из анкет подписчиков и данные общих параметров (extra) можно дополнить данными из внешнего источника получаемыми динамически по ходу выпуска рассылки.

Это реализуется путём использования специальны функций шаблонизатора в тексте и/или шаблоне выпуска или указанием в специальном ключе параметра выпуска extra:

Указание в шаблоне

[% external_anketa("url","ignore_error","0|1","timeout":"секунды","dk":"dk1 dk2 dk3") %]
[% external_extra("url","ignore_error","0|1","timeout":"секунды") %]

[% external_extra("url","method","get","ignore_error","0|1","timeout":"секунды") %]

Кавычки у всех параметров обязательны.

Сами параметры (кроме url) - не обязательны

Тайм-аут по умолчанию 3 минуты, максимально - 10 минут.

С точки зрения шаблонизатора эти функции возвращают пустой результат, но меняют набор данных для использования в персонализации.

Указание в extra

{
 "action" : "issue.send" 

 .....

 "extra" : {

  ..........

            --- указание дополнительный ссылок для внешних данных для общих параметров
            --- аналогично функции шаблонизатора текста выпуска [% external_extra() %]
            --- однако обрабатывает до него и можно использовать шаблонизатор
            --- доступны данные "extra" с уже добавленным туда ключём param содержащим информацию о выпуске
            --  + туда с обработкой с каждого урла добавляются его данные
            --- т.е. при обработке url-1 для шаблонизатора доступны данные issue.send:extra
            --- а при обработке url-2 для шаблонизатора уже доступны данные issue.send:extra + данные url-1

            "external_extra" : [

                     {
                      "url" : "урл-1" 
                     ,"method" : ..
                     ,"ignore_error" : ...
                     -- и прочие возможные параметры external_extra()
                     -- обязателен так же только url
                     }                 

                    ,{
                      "url" : "урл-2" 
                      .....
                     }

                     .....
             ]

  ..........

            --- указание дополнительный ссылок для внешних анкетных данных
            --- аналогично функции шаблонизатора текста выпуска [% external_anketa() %]
            --- и по порядку вызов идут до урлов полученных из external_anketa()

            "external_extra" : [

                     {
                      "url" : "урл-1" 
                     ,"method" : ..
                     ,"ignore_error" : ...
                     и прочие возможные параметры external_extra()
                     }                 

                    ,{
                      "url" : "урл-2" 
                      .....
                     }

                     .....
             ]

 }
}

Обработка

Вызовы указанных адресов производятся методом POST с передачей cgi-параметра request содержащего json-utf8 данные о текущем выпуске.

Ответ ожидается в json-utf8.

При вызове передаются (если не указан метод get) следующие данные позволяющие идентифицировать, что это за выпуск:

 {
  "some.id" : "......." - некая строка до 32 символов однозначно идентифицирующая выпуск (это НЕ номер выпуска который он получит позже)

 ,"login" : "..." - аккаунт для которого формируется выпуск

 ,"group.id" : "..." - код группы для которой формируется выпуск

 ,"track.id" : 123 - номер трекера для track.get присвоенный выпуску

 ,"draft.id" : 123 - номер черновика на основе которого формирутеся выпуск или null

 ,"seq.id" :  123 - номер последовательности вызвавшей этот выпуск или null

 ,"variant.id" : 123 - номер варианта А/Б-тестирования вызвавшего этот выпуска или null

 ,"form.id" : 123 - номер формы вызвавшей этот выпуск или null

 ,"campaign.id" : 123 - номер кампании в рамках которой выходит этот выпуск или null

 ,"extra":  { ... } -- данные параметра extra вызова issue.send или null

- только для external_anketa

  -- без параметра dk

 ,"users.list" : [
                  список адресов для которых ожидаются дополнительные анкетные данные
                 ]

  -- c параметром dk

  -- данные персонализации для адресов для ключей данных указанных в dk 
  -- позволяет производить более подробный анализ на стороне клиента
  -- (например выдавать промо-коды не всем в списке, а только в зависимости от какой-то переменной)  

  -- в dk через пробел перечисляются ключ данных информация по которым интересует
  -- не забывайте ключ member.email для получения собственно адреса :)

 ,"users.list" : [
                  [ "email1-dk1-data", "email1-dk2-data", ... ]
                  [ "email2-dk1-data", "email2-dk2-data", ... ]
                 ,[ ... ]
                  .........  
                 ]
 }

Если вам не нужны данные о текущем выпуске, то можно указать использование метода GET.

Дополнение общих параметров

Общие параметры указываемые в ключе extra вызова issue.send дополняются результатами обработки external_extra().

В выпуске может быть несколько использований external_extra() - все они будет обработаны один раз перед началом формирования писем по порядку расположения в тексте. Место расположения в данный момент не важно. Дополненные параметры действуют с самого начала текста.

Ошибка получения данных или разбора полученного json или не соответствия типа json фатальна - выпуск прекращается.

Ответ должен быть json-объектом.

Результаты всех external_extra() сначала объединяются между собой по ключам первого уровня. Более поздние данные имеют приоритет и заменяют собой имеющиеся.

Итоговый результат объединяется по ключам первого уровня с данными указанными в extra. Данные уже имеющиеся в extra имеют приоритет и не заменятся внешними.

Пример для выпуска с двумя вызовами:

данные extra из issue.send

{
 "nol" : "ноль" 
}

данные первого вызова external_extra()

{
 "odin" : "адын" 

,"dva" : "ещё адын" 

,"abv" : {
          "a" : "А" 
         ,"b" : "Б" 
         ,"v" : "В" 
         }

,"gde" : {
          "g" : "Г" 
         ,"d" : "Д" 
         ,"e" : "Е" 
         }
}

данные второго вызова external_extra()

{
 "nol" : "два" 

,"dva" : "два" 

,"gde" : {
          "х" : "Х" 
         }
}

результат

{
 "nol" : "ноль"       -- данные их extra приоритетнее

,"odin" : "адын"      -- нет во втором вызове external_extra()

,"dva" : "два"        -- есть во втором вызове external_extra()

,"abv" : {            -- нет во втором вызове external_extra()
          "a" : "А" 
         ,"b" : "Б" 
         ,"v" : "В" 
         }

,"gde" : {            -- есть во втором вызове external_extra() - просто замена без попыток объединения ключей
          "х" : "Х" 
         }
}

Дополнение анкетных данных

Не работает в тестовых копиях.

Анкетные данные пользователей дополняются результатами обработки external_anketa().

В выпуске может быть несколько использований external_anketa() - все они будет обработаны один раз перед началом формирования писем по порядку расположения в тексте. Место расположения в данный момент не важно. Дополненные параметры действуют с самого начала текста.

Ошибка получения данных или разбора полученного json или не соответствия типа json фатальна для группы получателей указаных в выпуске, но сам выпуск продолжается в предположении что проблема разовая и следующие получатели смогут успешно пополнить свои данные.

Однако ошибка при получении данных для первой группы фатальна и выпуск прекращается.

Для одного выпуска вызовов каждого external_anketa() может быть несколько и они могут поступать одновременно - в зависимости от количества получателей выпуск формируется в несколько потоков, а получатели обрабатываются порциями. В данный момент обычный размер порции составляет 1000 адресов.

В параметре users.list передаются адреса текущей группы получателей или указанные данные на основе списка из параметра dk.

Ответ должен быть json-массивом содержащим json-объекты или null.

Внешние данные для пользователя могут быть любой структуры - не обязательно двухуровневые объекты как у анкетных данных.

Данные приписываются пользователю по порядку их расположения в массиве - первому адресу в users.list добавляются данные первого элемента массива ответа, второму - второго и так далее.

Элементом массива может быть null - соответствующий получатель не получит дополнительных данных.

Массив в ответе может быть короче исходного - оставшиеся адреса не получат дополнительных данных.

Массив в ответе может быть длиннее исходного - лишние элементы игнорируются.

Пример:


users.list                       ответ

[                                [
 "1@test.ru"                      { данные для 1@test.ru }

,"2@test.ru"                     ,null -- нет данных для 2@test.ru 

,"3@test.ru"                      { данные для 3@test.ru }
                                 ]
,"4@test.ru"                      -- нет данных для 4@test.ru так как ответ короче
]

Результаты всех external_anketa() для каждого получателя сначала объединяются между собой по ключам первого уровня. Более поздние данные имеют приоритет и заменяют собой имеющиеся.

Итоговый результат объединяется по ключам второго уровня с данными персонализации получателя. Данные уже имеющиеся в анкете менее приоритетны и заменятся внешними при их наличии. Внешние данные для анкеты member игнорируются.

Пример для выпуска:

данные анкеты одного пользователя

{
 "member" : {
             "email" : "test@test.ru" 
            ,"id"    : 123
            }

,"info" : {
           "firstname" : "Имя" 
          ,"lastname"  : "Фамилия" 
          }

,"param" : {
            "raz" : "Раз" 
            "dva" : "Два" 
           }
}

данные вызова external_anketa() для этого пользователя

{

 "member" : {
             "email" : "xxxx@test.ru" 
            }

,"info" : {
          ,"lastname"  : "фамилиЁ" 
          }

,"param" : [ "Три", "Четыре" ]

,"secret" : "12345" 
}

результат

{
 "member" : {
             "email" : "test@test.ru"  -- не замененно так как member не изменяем
            ,"id"    : 123
            }

,"info" : {
           "firstname" : "Имя" 
          ,"lastname"  : "фамилиЁ" -- внешние данные при объединение объектов по ключам второго уровня
          }

,"param" : [ "Три", "Четыре" ] -- полностью заменено данными первого уровня так как внешние данные не объект

,"secret" : "12345" -- новые внешние данные
}

вверх

Данные о товарах Яндекс.Маркет (формат YML)

Если у вас есть данные о ваших товарах в формате YML (Яндекс.Маркет), то вы можете сделать их доступными в выпуске одной командой

[% external_extra("url","format","yml") %]

(url может быть задан только константой)

Для ускорения работы и выпуска рассылки необходимо заказать в Службе Поддержки подключение каждого уникального веб-адреса с данными yml (это бесплатно).

В резлуьтате ваши данные о товарах станут доступны в шаблонизаторе с префиксом yml.

Каждый конкретный товар будет доступен как [% yml.<id>.* %]

где <id> - параметр id тега offer описывающего товар

все значения параметров тега offer и значения всех тегов внутри offer доступны как одноимённые параметры товара

все значения тега param доступны как yml.<id>.param.<p>

где <p> транслитерированное значение параметра name тега param

Например товара

<offer id="123" type="vendor.model" available="true" bid="1" group_id="136010368">
  <url>http://www.xxxxxx.ru/xxxx</url>
  <price>1749.0000</price>
  <currencyId>RUR</currencyId>
  <caategoryId>490</categoryId>
  <market_category>Дом и дача/Дом и интерьер/Текстиль/Шторы</market_category>
  <picture>http://media.xxxxx.xx/products/641by641/13/60/10/XXXXXXXXXXXXXX.jpg</picture>
  <delivery>true</delivery>
  <local_delivery_cost>0</local_delivery_cost>
  <typePrefix>Шторы, занавески</typePrefix>
  <vendor>La Interieurs</vendor>
  <vendorCode>136010368</vendorCode>
  <model>Занавеска с вышивкои по низу</model>
  <description>- Качество VALEUR SURE. Качественная отделка. Со сборкои 60 мм (3 варианта высоты). 91% полиэстера, 9% льна. Красивая вышивка по низу. Простои уход: стирка при 40°, не нужно гладить. Расстояние от отделки до низа 30 см (для размеров 240 и 260 см). Размер в см.</description>
  <sales_notes>Минимальный заказ 1500 руб</sales_notes>
  <manufacturer_warranty>true</manufacturer_warranty>
  <param name="country_of_origin">Франция</param>
  <param name="Пол">OTHER</param>
  <param name="Возраст">OTHER</param>
  <param name="Цвет">белый</param>
  <param name="Размер" unit="FR">240 x 175 см</param>
</offer>

Будет доступен через yml.123

Его параметр type тего offer через yml.123.type

[% id = "123" %]
[% yml.$id.type %]

Его значение тега model через yml.123.model

[% id = "123" %]
[% yml.$id.type %]

Его значение Размер через yml.123.param.Razmer

[% id = "123" %]
[% yml.$id.param.Razmer %]

Его значение дополнительного параметра unit у параметра Размер через yml.123.param.Razmer_unit

[% id = "123" %]
[% yml.$id.param.Razmer_unit %]

вверх

Динамический контент

Описание

Динамический контент позволяет вам задавать содержимое части выпуска или всего выпуска как текст забираемый готовым со страницы какого-либо сайта или собираемый на основе новостей из различных видов источников с помощью Лент Новостей.

Во всех ссылках поддерживается авторизация HTTP-Basic - укажите её обычным образов в самой ссылке.

Во всём содержимом подставляемым в текст письма с помощью динамического контента работает шаблонизатор - подставляемы части могут содержать любые его команды.

Готовые страницы сайта

"Готовая страница сайта" это доступная веб-страница любого сайта.

При использовании в выпуске рассылки команды шаблонизатора [% wget("url") %] указаная страница забирается с сайта и подставляется в текст письма. Это происходит в момент формирования текста выпуска рассылки и по этому адрес страницы может быть задан только как константа.

От страницы используется только содержимое между тегами <body></body>.

На странице можно дополнительно расставить метки фильтрации позволяющие указать только определённyю часть/части которую надо использовать в выпуске.

Такие части отмечаются в своём начале добавлением строки начинающейся с <!-- issueBegin --> и в конце добавлением строки начинающейся с <!-- issueEnd -->

В случае неудачи при получении страницы (не найдена, проблема связи и т.д.) выпуск не выйдет.

Лента Новостей

"Лента новостей" это набор от одного до бесконечности каналов новостей, сообщения из которых собираются и могут быть использованы для формирования индивидуальных писем или общих рассылок.

Один источник новостей может входить в несколько лент - фактически он опрашивается один раз в 1-2 часа и все найденные свежие новости собираются в базу и становятся независимо доступны во всех лента и использование определенной новости в одной из них никак не повлияет на ее состояние в другой ленте.

Одна и та же лента может использоваться в выпуске несколько раз. Например первый раз для формирования "меню" из списка заголовков новостей, а второй для вывода полной новости.

В выпуске может быть использовано несколько лент.

Лента хранит отметку времени последнего использования в рассылке и основываясь на нём система включает в письма только те новости, которые были получены после этой даты.

После формирования рассылки (или индивидуального письма) отметка времени у всех использованных в рассылке лент устанавливается в текущее время. Таким образом новости одной ленты уже использованные в рассылке повторно использованы не будут.

Старые новости регулярно удаляются из базы как только проходит 15 дней с момента получения новости.

Использование лент новостей в Индивидуальных письмах

Индивидуальные письма подписчикам формируются из новостей накопившихся в их личных лентах которые к подписчикам привязаны владельцем ПРО при их создании (lenta.set).

Письмо формируется по указанному в ленте расписанию с оформлением по указанному шаблону выпуска с использованием новостей накопившихся с прошлой рассылки.

Если свежих новостей нет, то письмо не высылается.

Это позволяет организовать индивидуальное оповещение клиентов персонализированными подборками информации. Например, организовать на сайт подписку на результаты поиска по каталогу товаром - пользователи будут автоматически получать обновления в интересующих их разделах.

Предоставляются простые базовые шаблоны которые можно модифицировать на свой вкус. Они доступны по адресу https://sendsay.ru/resources/help/lenta/drafts.html

Использование лент новостей в рассылках

Для использовании в рассылке новостей одной или нескольких лент надо воспользоваться функциям шаблонизатора news() или lenta().

Функция news() не требует специальной обработки и результатом её работы становится содержимо полей "content" всех свежих новостей. Это упрощает работу с лентами если готового источника у вас ещё нет и вы можете легко формировать его как нравится. Или когда имеющийся источник выдаёт в "content" новости то, что надо для выпуска.

Функция lenta() только присваивает переменной lenta специальную структуру данных описывающую все свежие на данный момент новости и их источники. Для их отображение требуется использование в теле письма оператора [% FOREACH %] для обработки каждой новости. Это сложнее, но вы можете оформить вывод новостей самым гибким образом.

Обе функции могут работать как с заранее созданной лентой новостей (указывается её номер), так и напрямую с каналом новостей (указывается его url).

При указании url механизм исключения уже использованных новостей не применим и в выпуск всегда попадают все новости канала имеющиеся в нём на момент выпуска.

Если свежих новостей нет ни в одной используемой в шаблоне ленте и новостей нет вообще ни в одном используемом в шаблоне источнике указанным напрямую, то выпуск рассылки не высылается.

По умолчанию новости отсортированы в порядке убывания и даты публикации. Задать свой порядок и группировку по источникам можно с помощью щаблонизатора - рассмотрите пример "Новости по источникам" по адресу https://sendsay.ru/resources/help/lenta/drafts.html

Базовые шаблоны от индивидуальных писем применимы и тут - как отправная точка для кастомизации под ваши нужды.

Доступные источники новостей для лент новостей

Структура переменной lenta

Структура данных ленты в индивидуальных письмах и после вызовов lenta() и news() содержит информаци о ленте новостей, всех источниках новостей новости которых попали в ленту и содержимое всех свежих новостей.

Лента новостей
"lenta" : {
       "id" : "id ленты в базе" 

      ,"name" : "название ленты" 

      -- описания всех источников ленты, чьи новости попали в письмо
      ,"sources" : {
                    "id-источника" : { описание источника }
                   ,"id-источника" : { описание источника }
                   ......
                   }

      -- id источников упорядоченные так же как упорядочены новости
      ,"sources_order" : [ "id-источника", "id-источника" .........]

      -- описание новостей в заказаном порядке (по умолчанию по убыванию даты публикации новости)
      ,"news" : [
                 { описание новости }
                ,{ описание новости }
                ,{ описание новости }
                ]

}
Описание одного источника
{
 "id" : "id источника в базе" 

 -- индексы в lenta.news[] новостей именно этого источника в порядке совпадающем с порядком lenta.news[]
,"news" : [ индекс, индекс, индекс ....]

 -- параметры источника как получены из описания RSS/Atom-канала при его последнем анализе
 -- названия параметров совпадают с названиями тегов XML

,"title" : "название источника" 

,"descr" : "описание источника" 

,"link" : "ссылка на обычную версию источника" 

,"logo" : "ссылка на логотип источника" 

,"author" : "информация об авторе" 

,"copyright" : "информация об авторских правах" 

}
Описание одной новости
{
 "ID" : "id новости в базе" 

,"source" : "id в базе источника из которого получена новость. ключ в lenta.sources{}" 

,"n" : "номер по порядку в lenta.news[]" 

 -- параметры новости как получены из описания RSS/Atom-канала при его последнем анализе
 -- названия параметров в основном совпадают с названиями тегов XML

,"title" : "название источника",

,"content" : "содежимое новости" -- тег <description> или <yandex:full-text>

,"link" : "ссылка на обычную версию новости" -- тег <link>, если пусто и isPermaLink="true" и guid похож на урл, то guid

,"dt" : "YYYY-MM-DD hh:mm:ss" -- дата публикации новости (тег <pubDate> или <dc:creator>)

,"id" : "идентификатор новости" -- тег <guid>

,"author" : "информация об авторе новости" 

,"category" : { -- категории новости
               "домен" : {
                          "категория" : "метка" 
                         ,....
                         }
              ,...
              }

,"enclosure" : [ -- приложения к новости на основании тегов <enclosure> и <media:thumbnail>
                 -- обычно так указывают картинки и звуковые файлы
                {
                 "url" : "ссылка" 
                ,"type" : "mime-type" 
                ,"length" : "длина" 
                }
               ,....
               ]

 -- дополнительные параметры полученые из dt

,"ymd" : "YYYY-MM-DD" 

,"hm" : "hh:mm" 

}

Черновики выпусков

Эти вызовы поменяли формат принимаемых и возвращаемых данных на совместимый с issue.send

Поля старого формата ответов будут ещё некоторое время возвращаться для обратной совместимости.

Старый формат изменения данных будет ешё некоторое время приниматься для обратной совместимости.

Список черновиков


{

  "action" : "issue.draft.list" 

 ,"filter" : { -- не обязательно

              "reltype"  : число  -- не обязательно
                                  -- при наличии ограничивает список только черновиками с указанным reltype

             ,"relref"   : число  -- не обязательно
                                  -- при наличии ограничивает список только черновиками с указанным relref
             }
}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "код черновика" 

            ,"alias" : "альтернативный идентификатор" 

            ,"format" : "viber|sms|html|text" -- формат черновика (для черновиков с несколькими текстами указывается только один)

            ,"name" : "название" 

            ,"template" : "0|1" -- признак шаблона (шаблон - заренее предустановленный черновик с оформлением)

            ,"create.date" : "дата и время создания" -- Ys, null

            ,"update.date" : "дата и время последнего изменения" -- Ys, null

            ,"public_preview' : "ccылка просмотра черновика без пароля" 

            ,"reltype" : ...

            ,"relref" : ...
            }

            ...

           ]

}

Чтение черновика


{

  "action" : "issue.draft.get",

  "id" : "код черновика или альтернативный идентификатор" 
}

ответ


{

  <общие поля>

  "obj" : {

      "id" : "код черновика" 

      ,"alias" : "альтернативный идентификатор" 

      ,"name" : "название черновика" 

      ,"create.date" : "дата и время создания" -- Ys, null

      ,"update.date" : "дата и время последнего изменения" -- Ys, null

      ,"public_preview' : "ccылка просмотра черновика без пароля" 

      ,"reltype" : ...

      ,"relref" : ...

      ,"template" : "0|1" -- признак шаблона (шаблон - заранее предустановленный черновик с оформлением)

      -- У предустановленных черновиков (при template = 1)

      ,"template.thumbnail" : "http://.." -- расположение (URL) изображения шаблона

      -- Параметры влияющие на выпуск не имеющие аналогов в issue.send

      ,"issue_exclude_filter" : .....

      ,"issue_member_list" : .....

      ,"unsub_list" : .....

      ,"ignore_stoplist" : .....

      -- Cодержимое письма и настройки выпуска (аналогично issus.send) 

      ,"letter" : {
                   параметры содержимого письма
                  } 

      ,"relink" : .....

      ,"relink.param" : .....

      ,"multiple" : .....

      ,"tz_limit" : .....

      ,"tz_observance" : .....

      ,"users.slice" : .....

      ,"only_unique" : .....

      ,"link.sqid" : .....

      ,"campaign.id" : .....

      ,"dkim.id" : .....

      ,"extra" : .....

      ,"group" : .....

      -- список используемых в черновике переменных персонализации с указанием где используются

      ,"variables": {
                    "email" { 
                             "header": [
                                        "header_var1",
                                        "header_var2",
                                        "header_var3" 
                                         .......
                                       ],
                             "html": [
                                      "html_var1",
                                      "html_var2",
                                      "html_var3" 
                                      .......
                                     ],
                             "text": [
                                      "text_var1",
                                      "text_var2" 
                                      .......
                                     ]
                            }

                  ,"viber" : {
                            "viber": [
                                    "viber_var1",
                                    "viber_var2" 
                                    .......
                                  ]
                           }

                  ,"sms" : {
                            "sms": [
                                    "sms_var1",
                                    "sms_var2" 
                                    .......
                                  ]
                           }
                  }

  }

}

Создание или изменение черновика

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

При изменении уже существующего черновика, изменяются только указанные в obj параметры. Не указанные - остаются как были.


{

  "action" : "issue.draft.set" 

  ,"obj" : {
            "alias" : "альтернативный идентификатор" -- не обязательно. не должен начинаться с цифры и не должен содержать пробелы
                                                     -- может использоваться во всех местах где требуется указать код черновика

            ,"name" : "название черновика" -- обязательно при создании

            ,"reltype" : ...

            ,"relref" : ...

            -- Параметры влияющие на выпуск не имеющие аналогов в issue.send

            ,"issue_exclude_filter" : "код_группы исключения из выпуска" -- участники указаной группы будут исключены
                                                                         -- из выпуска по данному черновику

            ,"issue_member_list" : "код группы-списка попавших в выпуск" -- в указанную группу-список накапливаются адреса
                                                                         -- попавшие в выпуск по данному черновику
                                                                         -- каждый адрес вносится только один первый раз

            ,"unsub_list" : "код группы-списка отписавшихся из выпуска"  -- В указанную группу-список накапливаются адреса
                                                                         -- нажавшие в выпуске "ссылку тематической отписки" 
                                                                         -- Каждый адрес вносится только один первый раз
                                                                         -- При последующих выпусках по этому черновику
                                                                         -- участники это группы-списка из него исключаются
                                                                         -- Ссылка тематической отписки оформляется как [% param.url_unsub_topic %]
                                                                         -- Так же, можно использовать ссылку "Отмены тематической отписки" 
                                                                         -- оформляемую как [% param.url_unsub_topic_cancel %]
                                                                         -- Подписчики, нажавшие её в каком либо имеющимся у них старом письме
                                                                         -- (новые они не получают из-за нахождения в группе-списке отписки)
                                                                         -- удаляются из группы отписки и со следующего выпуска по этому
                                                                         -- черновику могут опять получать письма (если нет других препятствий)

            ,"ignore_stoplist" : "0|1"                                   -- игнорировать пользовательский стоп-лист при выпуске
                                                                         -- доступно только при заключении отдельного соглашения

           -- Cодержимое письма и настройки выпуска (аналогично issus.send) 

           -- При выпуске приоритет имеют параметры указанные в самом выпуске, а при их отсутствии берутся из черновика если не указано иное (подробнее в issue.send)
           -- Для удаления параметра из черновика используйте значение null - для части параметров играет роль даже просто их наличие

           ,"letter" : { -- обязательно при создании
                        параметры содержимого письма как у issue.send кроме draft.id
                        для текстовых писем обязательны не пустые - адрес отправителя, тема и как минимум один текст
                        для sms сообщения обязательны не пустые - имя отправителя и текст
                        для viber сообщения обязательны не пустые - текст
                       } 

            ,"relink" : .....

            ,"relink.param" : .....

            ,"multiple" : ..... - игнорируется при issue.send.multi

            ,"tz_limit" : .....

            ,"tz_observance" : .....

            ,"users.slice" : ..... - игнорируется при issue.send.multi

            ,"only_unique" : ..... - игнорируется при issue.send.multi

            ,"link.sqid" : .....

            ,"campaign.id" : "код кампании" 

            ,"dkim.id" : " : .....

            ,"extra" : { ... } -- объединяется с extra выпуска, но имеет меньший приоритет

            ,"group" : "код группы подписчиков" -- игнорируется при выпуске
           }

 необязательные

  ,"id" : "код черновика или альтернативный идентификатор" -- если не указан, создается новый

  ,"return_fresh_obj": "" -- вернуть объект в формате issue.draft.get

}

ответ


{

 <общие поля>

 ,obj  { ... } -- объект в формате issue.draft.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление черновика


{

  "action" : "issue.draft.delete" 

-- одного

  ,"id" : "код черновика или альтернативный идентификатор" 

--- или нескольких

  ,"id" : ["код или альтернативный идентификатор черновика1", "код или альтернативный идентификатор черновика2", .. ]
}

ответ


{

 <общие поля>

}

Предпросмотр черновика

Вызов позволяет на основе черновика и указанного адреса получить персонализированный вариант сообщения что бы оценить вид письма.

Вторая схема использования - получить текстовую версию html-черновика что бы оценить корректность автоматического преобразования.


{

 "action" : "issue.draft.preview" 

 ,"email" : "адрес для параметеризации подстановок" -- не обязательно

 ,"autotext" : "1|ширина"  -- не обязательно
                           -- используется только совместно с html-версией
                           -- как результат возвращается html-версия преобразованная в текст
                           -- с сохранением всех команд персонализации
                           --
                           -- 1 - ширина строки 80 символов
                           -- ширина - ширина строки - как указано

-- одно из

 ,"id" : "номер черновика" 

-- или

 ,"obj" : { -- можно передавать объект из issue.draft.get - лишние поля будут проигнорированыются

            "letter" : {
                        параметры содержимого письма как у issue.send
                       }
           }

}

ответ


{

 <общие поля>,

 "text" : "текст выпуска" 

}

вверх

Архив выпусков

Список выпусков в архиве

В большинстве случаев вызов issue.list + несколько issue.get можно заменить одним более быстрым вызовом stat.uni для объекта issue.


 {

  "action" : "issue.list" 

  ,"from" : "YYYY-MM-DD" -- от даты (не обязательно)

  ,"upto" : "YYYY-MM-DD" -- до даты (не обязательно)

  ,"group" : [ -- фильтр по группам (не обязательно)

               код-группы-1

              ,код-группы-2

               ... 

             ] 

  ,"format" :  "email|sms|viber|html|text"  -- фильтр по формату (не обязательно).
                                      -- email это "html или text" 
 }

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор выпуска" 

            ,"gid" : "код группы" 

            ,"date" : "дата выпуска Ys" 
            }

            ...

           ]

}

Чтение выпуска в архиве

Если не нужен текст выпуска или филбтр выпуска, то вызов issue.get можно заменить более быстрым вызовом stat.uni для объекта issue.


 {

  "action" : "issue.get" 

  ,"id" : "уникальный идентификатор выпуска" 

  ,'source' => 0|1 -- вернуть текст как  пошёл в рассылку (0) или как было на входе (1), а так же исходный текст фильтра выпуска (полностью развёрнутый)
                   -- при source=1 текст выпуска для personal всё равно как при source=0 т.к. для pesonal не сохраняется хранится исходник
                   -- отсутствие параметра вообще - старый формат ответа. поддерживается до 01 октября 2017
 }

ответ


{

 <общие поля>

 "obj" : {

           "id" : "уникальный идентификатор выпуска",

          ,"date" : "дата выпуска",

          ,"group" : "уникальный идентификатор группы выпуска",

          ,"channel" : "email|sms|viber", -- канал выпуска

          ,"draft.id" : "уникальный идентификатор черновика, по которому сделан выпуск", (null)

          ,"draft.alias" : "альтернативный идентификатор черновика, по которому сделан выпуск", (null)

          ,"sequence.id" : "идентификатор последовательности, событие в которой вызвало выпуск", (null)

          ,"variant.id" : "варианта сплит-тестирования для которого сделан выпуск", (null)

          ,"multi.id" : "идентификатор мультивыпуска для которого сделан выпуск", (null)

  -- source = 0

          ,"name" : "название выпуска" 

          ,"issue" : { -- как элемент issue.send.multi
                     "letter" : {
                                  -- параметры содержимого выпуска
                                  -- но в тексте уже оформленное содержимое письма как пошло в рассылку
                                  -- при выпуске по черновику - его содержимое, а номер черновика выше в draft.id
                                 }
                      -- прочие параметры выпуска 
                     }

  -- source = 1

          ,"issue" : { -- как элемент issue.send.multi
                     "letter" : {
                                  -- параметры содержимого выпуска
                                  -- в тексте исходное содержимое
                                  -- при выпуске по черновику - его содержимое, а номер черновика выше в draft.id
                                 }
                      -- прочие параметры выпуска 
                     }

          ,"filter" : {
                        -- фильтр выпуска. полностью развёрнутый (подставлены условия вложенных фильтров)
                        -- не для personal и masssending
                      }

  -- для всех

          ,"attach" : [ -- названия прикрепленных файлов. содержимое можно получить через issue.attach.get

                         "aaa.doc" 

                        ,"bbb.doc" 

                        ...

                       ]

           ,"archive" : "ссылка на выпуск в архиве" 

           -- ссылки на изображения письма выпуска
           -- только для html выпусков, и только для групп отличных от personal
           -- в настоящий момент поддерживаются картинки 640x1100 и 150x180
           -- надо учитывать что ссылка может вести на отсутствующее изображение
           -- например, из-за сбоя при её формировании

           ,"thumbnail" : [
                          {
                           "url" : "ссылка на изображение" 
                          ,"width" : ширина в пикселах,
                          ,"height" : высота в пикселах
                          },

                         ...............

                         ]

         }

}

Получение файлов, приложенных к выпуску


 {

  "action" : "issue.get.attach" 

  ,"id" : "уникальный идентификатор выпуска" 

  ,"encoding" : "желаемая кодировка данных в ответе" -- пусто или не указана - обычная Unicode/UTF-8
                                                     -- base64 - содержимое будет возвращено в base64

  ,"attach" : [  -- имена файлов вложений, которые необходимо получить (если параметр не указан - все вложения)

                "aaa.doc" 

               ,"bbb.doc" 

                ...

              ] 

 }

ответ


{

 <общие поля>

 ,"list" : {

            "aaa.doc" : "содержимое файла|null - если файл не найден",

            "bbb.doc" : "содержимое файла|null - если файл не найден",

            ...

           }

}

DKIM-ключи

Список dkim-ключей

{

  "action" : "issue.dkim.list" 

}

ответ

{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"domain" : "домен" 

            ,"selector" : "селектор" 

            ,"onmoderation" : 0|1 -- 0 - dkim-ключ подтверждён, 1 - dkim-ключ ещё не подтверждён

            ,"create.date" : "дата и время создания" -- Ys, null

            ,"update.date" : "дата и время последнего изменения" -- Ys, null

            ,"domain.custom" : 0|1 - домен DKIM используется для кастомизации всех служебных доменов

            ,'domain.secure" : 0|1 - при этом используется http:// (0) или https:// (1)
            }

            ...

           ]

}

Чтение dkim-ключа

{

  "action" : "issue.dkim.get" 

-- или

  ,"id" : "идентификатор dkim-ключа" 

-- или

  ,"id" : {
           "domain" : "домен" -- при использовании интернациональных доменов записывайте их "как есть", без xn-- кодирования

          ,"selector" : "селектор" 

          }

}

ответ

{
  <общие поля>

  "obj" : {

      ,"id" : "идентификатор dkim-ключа" 

      ,"domain" : "домен" 

      ,"selector" : "селектор" 

      ,"public-key" : "публичный ключ" 

      ,"version" : "DKIM1" 

      ,"key-type" : "rsa" 

      ,"dns_record" : "<select>._domainkey.<domain> IN TXT  \"v=<version>; k=<key-type>; p=<public-key>\"" 

      ,"onmoderation" : 0|1

      ,"create.date" : "дата и время создания" -- Ys, null

      ,"update.date" : "дата и время последнего изменения" -- Ys, null

       -- следующие два поля настраиваются Службой Поддержки и выводятся просто для справки

      ,"domain.custom" : 0|1 - домен DKIM используется для кастомизации всех служебных доменов

      ,'domain.secure" : 0|1 - при этом используется http:// (0) или https:// (1)

          }

}

Создание dkim-ключа

{
  "action" : "issue.dkim.create" 

  ,"obj" : {

            "domain" : "домен" -- при использовании интернациональных доменов записывайте их "как есть", без xn-- кодирования

           }

-- необязательные

  ,"return_fresh_obj": "" -- вернуть объект в формате issue.dkim.get

}

ответ

{

 <общие поля>

 ,"obj"  { ... } -- объект в формате issue.dkim.get при наличии в запросе параметра "return_fresh_obj" 

 ,"id" : "идентификатор dkim-ключа" 

}

Удаление dkim-ключа

{

  "action" : "issue.dkim.delete" 

  ,"id" : "идентификатор dkim-ключа" 

}

ответ

{

 <общие поля>

}

Проверка настройки dkim-ключа

Производится проверка DNS-записи необходимой для использования dkim-ключа - запись <selector>._dkim.<domain> типа TXT должна иметь значение соответствующее образцу возвращаемому в issue.dkim.get

При успешной проверке onmoderation становится 0 и ключём можно пользоваться для выпуска рассылок. Иначе onmoderation становится 1 и ключ блокируется.

{

  "action" : "issue.dkim.check" 

  ,"id" : "идентификатор dkim-ключа" 

}

ответ

{

 <общие поля>

-- одно из

 "onmoderation" : 0 -- запись успешно проверена

-- одно из

 "onmoderation" : 1 -- запись проверить не удалось, использовать нельзя

,"warnings" : [ -- ошибки встреченные при проверке записей
              {
               "id" : "error/notfound"  -- не найдено ни одно записи вообще

              }
             ,{
               "id" : "error/value"  -- у найденой записи в указанном поле не то значение что ожидается

              ,"explain" : "имя поля" 

              ,"record" : "значение проверяемой DNS-записи" 
              }

              ......

             ]

}

Передача событий из системы клиенту (Callback, WebHook)

Клиент может получать от системы уведомления о события открытия письмо, перехода по ссылке, отписке.

События, на типы которых настроена передача, передаются клиенту по мере появления с интервалом порядко минуты.

Метод передачи POST multipart/form-data.

События передаются как элемент events в виде JSON-массива содержащего по одному элементу на событие.

Разные типы событий могут быть настроены для передачи по разным адресам. Если на один адрес настроены несколько типов событий, то внутри массива их порядок не определён.

Так же, формально, массив ни каким образом не упорядочен по дате и времени события, хотя, в целом, более ранние события обычно находятся в начале массива при первой попытке передачи.

Тайм-аут вызова - 15 секунд. Вы должны просто принять данные и сохранить их в свою очередь для дальнейшей обработки, а не пытаться производить её сразу по приходу вызова.

В случае тайм-аута или получения ответа с кодом не 200 передача событий будет повторяться до 10 раз с возрастающим интервалом между попытками.

В данный момент интервалы следующие

После попытки Пауза минут до следующей
1 5
2 5
3 5
4 10
5 15
6 25
7 45
8 60
9 60
10 90

Так как события внутри системы обрабатываются многопоточно, то и вызовов с передачей событий вам может быть несколько одновременно.

Для настройки передачи событий обратитесь в Службу Поддержки.

Передача событий в Google Big Data

Для передачи событий в Google Big Data настройте внешнюю авторизация для схемы gbd:// (описано в вызовах authext.*) и используйте ссылку вида

gbd://authext:AUTHEXT_ID@DATASET_ID/TABLE_ID

где

AUTHEXT_ID - код внешней авторизации

DATASET_ID - код набора данных

TABLE_ID - код (название) таблицы или view в первую колонку (она должна быть типа STRING) который будут вставлены json-закодированные строки с объектами событий. Одно событие - одна строка.

В дальнейшем с этими данными в таблице можно работать через функции работы с json в запросах Google Big Query.

Событые открытия письма

{
 "event"     : "read" 
,"event.dt"  : "дата-время события (Ys)" 
,"account"   : "код аккаунта" 
,"issue.id"  : "номер выпуска" 
,"issue.draft.id" : "номер черновика выпуска" (null)
,"letter.id" : "номер письма" 
,"letter.custid" : { -- значения пользовательских меток назначенных письму при выпуске
                     -- отсутствует если их не было или если не известны одновременно
                     -- issue.id и letter.id (так, например, бывает при отписке)
                    "метка1" : "значение1" 
                   ,"метка2" : "значение2" 
                   ........
                   }
,"email"     : "адрес получателя" 
,"email.id"  : "номер адреса" 

,"ip" : "ip-адрес" 
,"http.user-agent" : "браузер подписчика" 
}

Событие перехода по ссылке

{
 "event"     : "click" 
,"event.dt"  : "дата-время события (Ys)" 
,"account"   : "код аккаунта" 
,"issue.id"  : "номер выпуска" 
,"issue.draft.id" : "номер черновика выпуска" (null)
,"letter.id" : "номер письма" 
,"letter.custid" : { ... пользовательские метки ... }
,"email"     : "адрес получателя" 
,"email.id"  : "номер адреса" 

,"ip" : "ip-адрес" 
,"http.user-agent" : "браузер подписчика" 

,"url" : "ссылка по которой был направлен подписчик" 
}

Событие отписки

{
 "event"     : "unsub" 
,"event.dt"  : "дата-время события (Ys)" 
,"account"   : "код аккаунта" 
,"issue.id"  : "номер выпуска" 
,"issue.draft.id" : "номер черновика выпуска" (null)
,"letter.id" : "номер письма" 
,"letter.custid" : { ... пользовательские метки ... }
,"email"     : "адрес получателя" 
,"email.id"  : "номер адреса" 

,"ip" : "ip-адрес" 
,"http.user-agent" : "браузер подписчика" 

,"event.type" : "причина отписки: unsub - по ссылке отписки, topic - по ссылке тематической отписки, listunsub - нажато "Отписаться" в веб-почте, fbl - нажато "Это спам"  в веб-почте 
,"unsub.list" : "код группы для отписки при тематической отписке" 
}

Событие доставки

Некоторые почтовые системы присылают письма о недоставке в формате позволяющим понять только про какой аккаунт и почтовый адрес идёт речь, но не определить номер выпуска и номер письма.

В таком случае в событии передаётся номер выпуска и номер письма как 0.

{
 "event"     : "deliv" 
,"event.dt"  : "дата-время события (Ys)" 
,"account"   : "код аккаунта" 
,"issue.id"  : "номер выпуска" 
,"issue.draft.id" : "номер черновика выпуска" (null)
,"letter.id" : "номер письма" 
,"letter.custid" : { ... пользовательские метки ... }
,"email"     : "адрес получателя" 
,"email.id"  : "номер адреса" 

,"deliv.status" : "число-код результата (описано в stat.uni)" 
,"deliv.str"    : "строка ответа smtp-сервера
}

Сплит-тестирование

Описание

Сплит-тестирование (A/B-тестирование) позволяет выпустить по некоторой части выбранной аудитории несколько писем различающихся между собой чем-либо и по результатам (если охват не 100%) выбрать в ручную или автоматически "лучший" вариант и послать его оставшейся не охваченной части аудитории.

Традиционный вариант использования - A/B-тестирование - предусматривает только два тестовых варинта писем и выпуск победителя.

Но данная реализация позволяет иметь произвольное количество тестовых вариантов и при желании не выпускать победителя, а покрыть тестами сразу всю аудиторию.

Параметры тестирования

Параметры одного варианта письма

Правила работы

Тестирование начинается автоматически не ранее даты указанной для запуска самого раннего варианта и если вариантов к этому моменту не менее двух.

Дата запуска какого-либо тестового выпуска или выпуска-победителя может быть пропущена если тестирование в приостановленном состоянии или не хватает вариантов.

Пропущенные запуски будут выполнены как только тестирование будет переведено в активное состояние или добавлен недостающий вариант.

В любом случае запуск победителя возможен только после выпуска всех тестовых частей даже если это потребует пропуска назначенного времени.

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

На время формирования списка тестирование находится в состоянии "подготавливается к запуску" и прогресс формирования может быть отслежен через вызов track.get

Данные персонализации каждого участника берутся на момент выпуска варианта.

Каждому варианту писем достанется процент аудитории равный Процент тестового охвата / Количество вариантов. Варианту победителю при повторном выпуске достанется оставшаяся часть аудитории.

Если участников меньше количества вариантов (при охвате 100%) или меньше количества вариантов + 1 (при охвате не 100%), то тестирование при запуске сразу перейдёт в состояние "завершено". Другими словами - каждому варианту и победителю должен достаться хотя бы один получатель.

Если процент тестового охвата равен 100, то выпуска-победителя не предусматривается.

Если тестирование НЕ в состоянии "ожидает запуска" то менять процент и группу уже нельзя.

Если тестирование НЕ в состоянии "ожидает запуска" то добавлять или удалять варианты писем уже нельзя.

Если тестирование НЕ в состоянии "ожидает запуска", то его нельзя удалить.

Изменить вариант письма можно только если тестирование в состоянии "ожидает запуска" или дата запуска варианта письма ещё не прошла или и тестирование не активно и изменяемый вариант ещё не выпущен.

Пока тестирование не в состоянии "победитель выбран" или "завершено" можно поменять способ выбора победителя.

Если тестирование в состоянии "завершено", то можно изменить только его название.

Список сплит-тестирований

{
 "action" : "issue.split.list" 
}

ответ

{
 "list" : [
            {
             "id" : "номер тестирования" 

            ,"name" : "название" 

            ,"group" : код группы

            ,"group.name" : название группы

            ,"active" : "активность" 

            ,"state" : "состояние" 
            }
          ]
}

Создать сплит-тестрование

{
 "action" : "issue.split.create" 

 ,"name" : "название" 

 ,"group" : "код группы" -- группа для получения аудитории. только группы c типом адресов email. не masssending и не personal.

 ,"percent" : процент аудитории для тестового охвата. от 1 до 100. целое число.

 ,"winner.by" : manual | read | click | unsub  -- способ выбора победителя
                                               -- manual - в ручную
                                               -- read   - больше чтений
                                               -- click  - больше кликов
                                               -- unsub  - меньше отписок
                                               -- игнорируется, если процент равен 100

 ,"winner.after" : "DD hh" -- момент запуска победителя - через сколько дней (DD) и часов (hh) после выпуска последнего
                           -- теста автоматически выбрать победителя
                           -- игнорируется, если процент равен 100 или способ выбора победителя manual

 ,"active" : 0|1 -- активность. 0 - приостановлено. 1 - активно

 ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 - по умолчанию)" 
}

ответ

{
   <общие поля>

 ,"id" : номер тестирования
}

если "return_fresh_obj" : "1" 

ответ -- как на запрос "issue.split.get" соответствующего тестирования

Прочитать сплит-тестрование

{
 "action" : "issue.split.get" 

 ,"id" : номер тестирования
}

ответ

{
   <общие поля>

 ,"id" : номер тестирования

 ,"name" : "название" 

 ,"group" : код группы для получения аудитории

 ,"group.name" : название группы

 ,"percent" : процент аудитории для тестирования

 ,"winner.after" : "DD hh" -- момент выбора победителя.

 ,"winner.by" : "способ выбора победителя" 

 ,"active" : "активность" 

 ,"state" : "состояние тестирования" -- назначается автоматически
             -- 0 - ожидает запуска
             -- 1 - подготавливается к запуску
             -- 2 - запущено
             -- 3 - ожидает выбора победителя
             -- 5 - победитель выбран (да именно 5, а не 4)
             -- 4 - завершено
             -- -1 - ошибки

 ,"parts" : [ -- список вариантов тестовых писем
             {
              "id" : "номер варианта" 

             ,"start.at" : "время запуска" 

             ,"issue.at" : "номер выпуска" -- если вариант уже выпущен для теста

             ,"issue.winner" : "номер выпуска" -- если вариант уже выпущен как победитель

             ,"is_winner" : 0|1 - вариант выбран как победитель
             }

            ,{
              ......
             }

             .......
            ]
}

Изменить сплит-тестрование

{
 "action" : "issue.split.set" 

 ,"id" : номер тестирования

-- не обязательные поля. не указанное поле сохранит прежнее значение

 ,"name" : название

 ,"group" : группа для получения аудитории. не masssending и не personal
            -- изменение игнорируется если состояние тестирования не "ожидает запуска" 

 ,"percent" : процент аудитории для тестового охвата. 
            -- изменение игнорируется если состояние тестирования не "ожидает запуска" 
            -- при установке в 100 очищаются поля winner.by и winner.after

 ,"winner.by" : способ выбора победителя
            -- изменение игнорируется, если процент равен 100
            -- изменение игнорируется, если состояние тестирования "победитель выбран" или "завершено".
            -- при установке в manual очищается поле winner.after

 ,"winner.after" : момент запуска победителя
            -- изменение игнорируется, если процент равен 100 или способ выбора победителя manual
            -- изменение игнорируется, если состояние тестирования "победитель выбран" или "завершено".

 ,"active" : активность
             -- изменение игнорируется, если состояние тестирования "победитель выбран" или "завершено".

 ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 - по умолчанию)" 
}

ответ

{
   <общие поля>
}

если "return_fresh_obj" : "1" 

ответ -- как на запрос "issue.split.get" соответствующего тестирования

Удалить сплит-тестирование

Нельзя удалять тестирование в состоянии "запущено" и выше, так как при таком состоянии уже был как минимум один выпуск.

{
 "action" : "issue.split.delete" 

 ,"id" : номер тестирования
}

ответ

{
   <общие поля>
}

Выпустить победителя

Указывает какой вариант считать победителем и сразу выпускает его.

Допустимо при ручном способе выбора победителя если уже вышли все варианты, но победитель ещё не выбран.

Допустимо при автоматическом способе выбора победителя если уже вышли все варианты и не прошло время автоматического выбора.

Не допустимо если процент тестирования 100.

{
 "action" : "issue.split.winner" 

 ,"variant.id" : номер варианта
}

ответ

{
   <общие поля>
}

Список вариантов писем сплит-тестирования

{
 "action" : "issue.split.variant.list" 

,"split.id" : номер тестирования
}

ответ

{
   <общие поля>

 ,"list" : [
             {
               "id" : номер варианта

               ,"start.at" : время запуска

               ,"issue.at" : "номер выпуска" 

               ,"issue.winner" : "номер выпуска" 

               ,"is_winner" : 0|1 - вариант выбран как победитель
             }
            ,........
           ]
}

Создать вариант

Нельзя создать новый вариант если тестирование не в состоянии "ожидает выпуска"

{
 "action" : "issue.split.variant.create" 

 ,"split.id" : номер тестирования

-- параметры варианта

 ,"start.at" : "YYYY-MM-DD hh:mm" -- время запуска. дата и время с точностью до минуты

 ,"letter" : { -- высылаемое письмо

      ,"format" : "html|sms|text|viber" -- формат письма. в данный момент поддерживается только html

      ,"from" : "email отправителя", -- обязательно

      ,"sender" : "имя отправителя",

      ,"reply.email" : "обратный адрес для ответа" 

      ,"reply.name" : "имя для обратного адреса для ответа" 

      ,"to.name" : "имя получателя" 

      ,"subject" : "тема письма",

      ,"text" : "текст письма в соответствующем формате" 

      ,"link.qsid" : "параметр отслеживания переходов" -- аналогичен параметру issue,send

           }

 ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 - по умолчанию)" 
}

ответ

{
   <общие поля>

 ,"id" : номер варианта тестирования
}

если "return_fresh_obj" : "1" 

ответ -- как на запрос "issue.split.variant.get" соответствующего варианта

Прочитать вариант

{
 "action" : "issue.split.variant.get" 

 ,"id" : номер варианта

}

ответ

{
   <общие поля>

 ,"id" : номер варианта

 ,"split.id" : номер тестирования куда входит этот вариант

 ,"start.at" : "YYYY-MM-DD hh:mm" -- время запуска. дата и время с точностью до минуты

 ,"letter" : { -- высылаемое письмо

      ,"format" : "формат письма" 

      ,"from" : "email отправителя",

      ,"sender" : "имя отправителя",

      ,"reply.email" : "обратный адрес для ответа" 

      ,"reply.name" : "имя для обратного адреса для ответа" 

      ,"to.name" : "имя получателя" 

      ,"subject" : "тема письма",

      ,"text" : "текст письма в соответствующем формате" 

      ,"link.qsid" : "параметр отслеживания переходов" 

           }

 ,"issue.at" : "номер выпуска" -- если вариант уже выпущен для теста

 ,"issue.winner" : "номер выпуска" -- если вариант уже выпущен как победитель

 ,"is_winner" : 0|1 - вариант выбран как победитель

}

Изменить вариант

Изменить вариант письма можно только если тестирование в состоянии "ожидает запуска" или дата запуска варианта письма ещё не прошла или и тестирование не активно и изменяемый вариант ещё не выпущен.

{
 "action" : "issue.split.variant.set" 

 ,"id" : номер варианта

-- параметры варианта, изменятся только явно указанные. параметр letter заменяется полностью

 ,"start.at" : "YYYY-MM-DD hh:mm" -- время запуска. дата и время с точностью до минуты

 ,"letter" : { -- высылаемое письмо

      ,"format" : "формат письма" 

      ,"from" : "email отправителя" -- обязательно

      ,"sender" : "имя отправителя" 

      ,"reply.email" : "обратный адрес для ответа" 

      ,"reply.name" : "имя для обратного адреса для ответа" 

      ,"to.name" : "имя получателя" 

      ,"subject" : "тема письма" 

      ,"text" : "текст письма в соответствующем формате" 

      ,"link.qsid" : "параметр отслеживания переходов" 

           }

 ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 - по умолчанию)" 
}

ответ

{
   <общие поля>

 ,"id" : номер варианта тестирования
}

если "return_fresh_obj" : "1" 

ответ -- как на запрос "issue.split.variant.get" соответствующего варианта

Удалить вариант

Нельзя удалять вариант если тестирование не в состоянии "ожидает выпуска"

{
 "action" : "issue.split.variant.delete" 

 ,"id" : номер варианта
}

ответ

{
   <общие поля>
}

вверх

Целевые страницы

Сервис "Целевые страницы" предназначен для отслеживания страниц вашего сайта по которым прошёл подписчик после перехода из письма рассылки и определения дошёл ли он до куда вы планировали. При этом вы можете дополнительно добавлять/изменять хранимые в анкетах данные подписчика.

Сервис "Целевые страницы" не имеет своих специальных вызовов API, но используя вызовы issue.send, stat.uni и sys.settings.* вы получите всю интересующую вас информацию.

Использовать "Целевые страницы" очень просто.

  1. Получите с помощью вызова sys.settings.get значание настройки target.script - это код необходимый для отслеживание пути подписчика.
  2. Разместите полученный код на тех страницах сайта, за посещением которых при переходе из писем рассылок вы хотите следить.
  3. Выпускайте рассылки с помощью issue.send обязательно с использование пребразования ссылок.
  4. Все страницы на которых вы разместили код отслеживания и до которых дошёл из выпусков хоть один ваш подписчик будут автоматически созданы в базе с relref=-2.
  5. Альтернативно, при настроке передачи email, в базу будут вноситься данные не только о тех кто посетил вам сайт из рассылки, но и о тех кто просто посетил ваш сайт если вам известны их адреса (описано ниже)
  6. О каждом посещении такой страницы в базе будет автоматически создана запись о "клике" с запоминанием кто, из какой рассылки, когда и с какого адреса это произошло.
  7. Так же будет сгенерировано событие "Целевая страница достагнута"
  8. Используя вызов Универсальной Статистики делайте выборки по кликам (click.*) с типом ссылки -2 (сlick.link.reltype = -2) вы получите полную информацию о том кто, когда, из какого выпуска и что посетил на вашем сайте.

Если вы хотите отслеживать не только пользователей пришедших из рассылки, но прочих ваших посетителей для которых вы знаете их email, вам необходимо при получении настройки target.script указать дополнительный параметр email = 1, а при генерации страницы сайте заменять в полученом коде метку подстановки адреса на реальный адрес.

Возможно вы захотите дополнительно настроить параметр "Время отслеживания после перехода" (sys.settings.get/set, параметр target.cookie.ttl) - он хранит значение в минутах как долго после перехода из письма система будет отслеживать посещение страниц вашего сайта.

В данный момент значение этого параметра по умолчанию равно 360 - 6 часов.

Дополнительно, вы можете оформить включение пользователя достигшего целевой страницы в ту или иную группу-список - для этого задайте параметр group.id при получении значения настройки target.script. Это можно использовать для автоматической группировки посетителей целевых страниц сайта, скажем, по их пренадлежности к тому или иному разделу сайта. (Обратите внимание, что в логе кликов и логе устройств у записи номер выпуска и номер письма в выпуске будут равны 0).

Так же вы можете оформить включение создаваемой ссылки сразу в ту или иную группу ссылок - для этого задайте параметр link.group.id при получении значения настройки target.script. Это можно использовать для автоматической группировки целевых страниц сайта, скажем, по их пренадлежности к тому или иному разделу сайта. Настоятельно рекомендуется при создании таких групп указывать им reltype = -2.

Так же вы можете указать как дополнить/изменить хранимые в анкетах данные подписчика.

Прочие дополнительные параметры для target.script описаны в вызове sys.settings.get

Мы используем Cookiе для обеспечения работы данного сервиса при учёте посетителей из рассылок. Пользователи не принимающие их не смогут быть отслежены. Пользователи у которых наша отметка будет по какой-либо причине удалена раньше отведённого времени не могут быть отслежены всё запланированное время.

При отслеживании посетителей на основе предоставления адресов Cookie не используются.

Статистика

География и устройство подписчика

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

Эта информация доступна через объекты вызова Универсальной Статистики: issue.geo.*, issue.gadget.*, deliv.member.geo.*, deliv.member.gadget.*, click.member.geo.*, click.member.gadget.*, read.member.geo.*, read.member.gadget.*

Устройство подписчика

Для устройства подписчика определяются:

- Тип устройства - Desktop, Mobile, Tablet или Robot

- Операционную систему - Linux, Android, Windows, iOS, Mac, Blackberry или Other

- Семейство браузера - Firefox, Chrome, Opera, Safari, IE, Blackberry или Other

- Старшую часть версии браузера

Если устройство определено как Robot, то его операционная система всегда Other, браузер - Other, версия - 0.

Используйте issue.gadget.*, deliv.member.gadget.*, click.member.gadget.*, read.member.gadget.* вызова Универсальной Статистики.

География подписчика

География определяется с точностью до зарубежной страны и с точностью до субъекта федерации для России.

География хранится как одно число совмещающее в себе номер страны и номер региона в виде СССRR000

Для нумерации стран (CCC) используется ISO 3166-1.

Для нумерации субъектов федерации России (RR) - первый уровень классификатора ОКАТО/ОКТМО.

Таким образом код зарубежной страны это код по ISO * 100000, а код для России это 64300000 + код региона * 1000

Не определившаяся страна имеет код 0. Не определившийся регион России имеет код региона 0.

Имеются дополнительные функции по получению из кода географии буквеных кодов стран и регионов и рускоязычных названий.

Используйте issue.geo.*, deliv.member.geo.*, click.member.geo.*, read.member.geo.* вызова Универсальной Статистики.

Ссылки с полезной информацией и таблицами кодов:

http://www.iso.org/iso/ru/country_codes
https://www.iso.org/obp/ui/#search
http://ru.wikipedia.org/wiki/ISO_3166-1
http://ru.wikipedia.org/wiki/Субъекты_Российской_Федерации
http://ru.wikipedia.org/wiki/ОКАТО
http://ru.wikipedia.org/wiki/Общероссийский_классификатор_территорий_муниципальных_образований

вверх

Статистика активности подписчиков


{

    "action" : "stat.activity" 

   ,"gid" : "id группы" 

   ,"pagesize": "строк на странице (по умолчанию 20)" 

   ,"page": "текущая страница (по умолчанию 1)" 

   ,"sort": "сортировать: "date" - по дате, "email" - по адресу (по умолчанию по дате)" 

   ,"desc": "1  - сортировать в обратном порядке" 

   ,"from": "событие произошло начиная с даты (включительно, в формате ГГГГ-ММ-ДД)" 

   ,"upto": "событие произошло не позже даты (включительно, в формате ГГГГ-ММ-ДД)" 

   ,"issue.from": "событие произошло из выпуска вышедшего начиная с даты (включительно, в формате ГГГГ-ММ-ДД)" 

   ,"issue.to": "событие произошло из выпуска вышедшего не позже даты (включительно, в формате ГГГГ-ММ-ДД)" 

-- из следующих параметров можно указать только один (исключение - можно совместить with_deliver и with_errs)

   ,"with_deliver": "1" - если нужно включать тех, кому доставлено

   ,"with_errs": "1" - если нужно включать тех, у кого ошибки

   ,"with_links": "1" - если нужно включать тех, кто переходил по ссылкам

   ,"with_remove": "1"  - если нужно включать тех, кто отписался

   ,"with_read": "1"  -- если нужно включать тех, кто открыл выпуск

   ,"result" : [ способ возврата результата. смотрите общее описание ]

}

ответ


{

    <общие поля>

-- для result != response

   ,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для result = response

   ,"obj" : [

              [ 

               "адрес подписчика",

               "дата (время) события",

               "тип события",  -- типы событий:

                               -- l - переход по ссылке

                               -- d - доставка

                               -- r - открытие выпуска

                               -- u - отписка

               "id группы",

               "подробности", -- содержимое зависит от типа события (url ссылки, статус доставки, причина отписки)

            ],

    },

}

вверх

Статистика выпусков

Похожую и более обширную статистику по выпускам можно получить с помощью вызова "Универсальная Статистика"


{

 "action" : "stat.issue",

 "issue.from" : "дата выпуска от (включительно, в формате ГГГГ-ММ-ДД)",

 "issue.upto" : "дата выпуска по (включительно, в формате ГГГГ-ММ-ДД)",

 "group" : [ "код группы", ... ], -- кода групп. Если пусто - по всем

 "groupby" : "способ группировки по времени" -- по умолчанию - YM,

 -- Варианты значения groupby:
 --             Total   только итог по группе
 --              YY      с шагом 1 год
 --              YM      с шагом 1 месяц
 --              YD      с шагом 1 день
 --              Yh      с шагом 1 час
 --              Ym      с шагом 1 минута
 --              Ys      с шагом 1 секунда
 --
 --              MM      по интервалу Месяц - Месяц
 --              MD      по интервалу Месяц - День
 --              Mh      по интервалу Месяц - Час
 --              Mm      по интервалу Месяц - Минута
 --              Ms      по интервалу Месяц - Секунда
 --
 --              DD      по интервалу День - День
 --              Dh      по интервалу День - Час
 --              Dm      по интервалу День - Минута
 --              Ds      по интервалу День - Секунда
 --
 --              hh      по интервалу Час - Час
 --              hm      по интервалу Час - Минута
 --              hs      по интервалу Час - Секунда
 --
 --              mm      по интервалу Минута - Минута
 --              ms      по интервалу Минута - Секунда
 --
 --              ss      по интервалу Секунда - Секунда
 -- 

 "total" : "none | yes | only", -- итог по всем записям: не нужен | нужен | только он и нужен

 "withempty" : "0 | 1", -- Не отображать статистику по тем группам подписчиков, по которым не было отправлено ни одного выпуска

 "result" : [ способ возврата результата. смотрите общее описание ]
}

ответ


{

  <общие поля>

-- для result != response

 ,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для result = response

 ,"list" : {

         "код группы" -- если пусто ("") - это итог по всем записям; ANY - если было выбрано по всем 

          : {

             "дата" -- если пусто ("") - это итог по группе

              : {

                 "html.issue" : "выпусков в формате HTML",

                ,"html.receiver" : "получателей в формате HTML",

                ,"text.issue" : "выпусков в текстовом формате",

                ,"text.receiver" : "получателей в текстовом формате",

                ,"sms.issue" : "выпусков смс",

                ,"sms.receiver" : "получателей сообщений",

                ,"sms.sms" : "всего выпущено смс" 

                ,"sms.cost" : "цена выпущенных смс в единицах тарификации" - в данный момент это копейки

                ,"total.issue" : "Итого выпусков",

                ,"total.receiver" : "Итого получателей",

                }

              .............

            }

         ...........

        }

} 

вверх

Портрет аудитории

Этот вызов не поддерживает КД, так как используемый им "формат данных" не поддерживает КД


{

 "action" : "stat.group.portrait",

 "group" : "код группы", -- если пусто - по всем

 "empty_resp" : "0|1", -- включить в статистику ответы не выбранные ни кем

 "empty_quest" : "0|1", -- включить в статистику вопросы не отвеченные ни кем

 "format" : "код формата", -- задает по каким вопросам каких анкет требуется статистика. Если пусто - по всем

 "result" : [ способ возврата результата. смотрите общее описание ]
}

ответ


{

  <общие поля>

-- для result != response

  ,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для result = response

 ,"selected" : количество адресов участвовавших в выборке

 ,"list" : {

        "код анкеты" 

          : {

             "код вопроса" 

               : {

                  "код ответа" 

                    : {

                       "num" : "число ответов" 

                      ,"perc" : "процент от количества ответов на этот вопрос" 

                      }

                   .....

                 }

             .....

            }

        .....

       }

}

вверх

Общая статистика по группе


{

 "action" : "stat.group.common",

 "group" : [ ], -- коды групп. Если пусто - по всем

 "result" : [ способ возврата результата. смотрите общее описание ]
}

ответ


{

  <общие поля>

-- для result != response

 ,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для result = response

 ,"members" : { -- сумма locked.* может не равняться просто locked

               "total"                : "всего адресов" 

              ,"active"               : "активных" 

              ,"locked"               : "заблокированых" 

              ,"locked.unconfirmed"   : "заблокированых, так как не подтвердили регистрацию" 

              ,"locked.unsubscribed"  : "заблокированых, так как отписались" 

              ,"locked.blocked"       : "заблокированых, так как приостановили получение писем" 

            },

-- для email статистика по доменам

  ,"tld" : { -- количество адресов по доменам первого уровня

            "домен первого уровня" : "количество адресов" 

           ,"домен первого уровня" : "количество адресов" 

           ,"домен первого уровня" : "количество адресов" 

           ............

          },

  ,"sld" : {  -- количество адресов по доменам второго уровня

           "домен второго уровня" : "количество адресов" }

           ,"домен второго уровня" : "количество адресов" }

           ,"домен второго уровня" : "количество адресов" }

           ............

          }

-- для номеров мобильных телефонов статистика по операторам и тарифам
-- тариф указан за одну смс, а не за одно сообщение в рассылке !
-- сообщение, в зависимости от длинны, может состоять из нескольких смс !

  ,"operator" : {
                  "badphone" : { -- сюда попадут неверные номера (например городской вместо мобильного)
                                "n" : "количество номеров" 
                               }

                 ,"oper1" : { 
                             "n" : "количество номеров этого оператора
                            ,"tarif" : "цена в сотых долях копейки одной смс этому оператору" 
                            }

                 ,"oper2" : { 
                             "n" : "количество номеров этого оператора
                            ,"tarif" : "цена в сотых долях копейки одной смс этому оператору" 
                            }

                 ...........
                }

}

вверх

Универсальная статистика

Универсальная статистика позволяет получить информацию про переходы, открытия писем, тиражи выпусков и результаты доставки. Примеры использования вынесены в отдельный раздел.

При использовании кэширования запоминаются сами данные, а способ возврата результата задаваемый в "result" и "caption" применяется к ним позже.

При использовании кэширования в режиме "fetch" необходим только параметр "result" и связанные с ним дополнительные параметры так как при отсутствии данных в кэше вычисление результата производиться не будет.


{

 "action" : "stat.uni" 

,"cache" : { параметры кэширования }

---- Параметры расчёта

-- или Единичный запрос

,"skip" : число пропускаемых строк данных отчёта от начала. не обязательно. по умолчанию 0

,"first" : число выбираемых строк после пропуска skip. не обязательно. по умолчанию все

,"select" : [ -- обязательно
            --
            --     список полей для выборки
            -- или список полей и функций агрегирования
            -- или список полей и функция unique(*)
            --
            -- указывается или в виде строки
            --
            --  "поле или функция" 
            --
            -- или в виде объекта с картой преобразования для приведения полученых значений к "человеческому виду" 
            -- (например код недоставки можно преобразовать в его словесное описание)
            --
            -- {
            --  "field" : "поле или функция" 
            --
            --  карта преобразования значений одим из двух способов
            -- 
            -- ,"map" : { -- непосредственно в вызове
            --           "Y" : "Да" 
            --          ,"N" : "Нет" 
            --          ...........
            --          }
            --
            -- или 
            --
            -- ,"map" : "Название карты" -- название заранее созданной карты
            --
            -- и к обоим способам управление не найденным значением
            -- 
            -- ,"map.missing" : "Непонятно" -- не обязательно. чем заменить значение не имеющее соответствия в карте преобразований
            --                              -- если отсутствует, то значение пропускается как есть
            --  }
            --
            --
            -- поля типа дата могут содержать указания на точность в виде двух букв из набора (Y M D h m s)
            -- по умолчанию поля типа дата имеют точность Ys или YD - в зависимости от их назначения
            --
            -- например: dt:YM (дата от года до месяца), dt:Yh (дата от года до часа), dt:YY (год)
            -- не верно: dt:hY  - первый уточнитель (час) младше второго (год)
            --
            -- поля типа дата могут содержать указания на то что требуется получить
            --    номер дня в неделе  - dt:DOW
            --    номер дня в году    - dt:DOY
            --    номер недели в году - dt:WOY
            --    первый день недели  - dt:CW1D
            -- от значения даты
            --
            -- поле с постоянным значением задаётся как
            --
            -- сonst(-123.456)  -- число
            -- const("АБ ВГ Д") -- стока
            -- const.ID         -- идентификатор аккаунта
            -- const.сurrent    -- текущее время. описано ниже
            --
            -- допустимые функции агрегирования
            --    count(*)
            --    count(unique имяполя)
            --    max(имяполя)
            --    min(имяполя)
            --    avg(имяполя)
            --    sum(имяполя)
            --    range(имяполя)
            --    stdev(имяполя)
            --    variance(имяполя)
            --
            -- в функциях перед именем поля может использоваться префикс unique
            --
            -- поле не может быть и в списке выбора и при этом использоваться
            -- в функциях агрегирования (даже с разным заданием точности)
            -- 
            -- правильные примеры
            --   a,b,c
            --   a,b,avg(c),max(d)
            --   dt:YM,b,min(c),max(c)
            --   a,b,count(*),min(e)
            --   max(dt:YD),min(y)
            --   max(f),min(f)
            --   a,b,unique(*)
            --   a,b,count(unique c)
            --   const(23)
            --
            -- не правильные примеры
            --   
            --   a,b,avg(a)           -- поле а и в выборке и в агрегировании
            --   a,b,unique(*),avg(e) -- функция unique(*) не совместима ни с одной другой функцией
            --   unique(*)            -- unique(*) без списка полей
            --   const(ABC)           -- строка не в кавычках
            --   сonst(123E+10)       -- запись с экспонентой
          ]

,"filter" : [

           -- фильтр результатов, не обязательно
           --
           -- все элементы списка объеденяются через "И" 
           --

           -- элементы условий фильтрации

           -- если имя поля содержит указание точности даты, то
           -- значение для сравнения большей точности будут
           -- автоматически сокрашены. значия с меньшей точностью
           -- считаются ошибкой
           --
           -- пример: "2010-05-04 12:13:14" станет "2011-05" для точности YM
           -- не верно: "2010-05-04" не хватает точности для Ys
           --
           -- поля типа дата кроме констант могут так же сравниваться с текущим
           -- временем +/- сдвиг. это описано ниже.

           {

             "a"  : имя поля или функция агрегирования

            ,"op" : операции сравнения ( == != < <= > >= )

            ,"v"  : значение
           }

          ,{

             "a"  : имя поля

            ,"op" : операции (не-)вхождения в список (in !in)

            ,"v" : [ значение, значение, ...] список значений для проверки вхождения (in) или не вхождения (!шт)
           }

          ,{

             "a"  : имя поля

            ,"op" : операции (не-)определённости (is_null !is_null)
                    только для полей помеченных (null)
                    параметр "v" должен или отсутствовать или быть равен "" 
           }

           .........

           -- группировка условий ИЛИ

          ,{

            "op" : "OR" 

            "v" : [
                    массив описывающий элементы фильтра. "ИЛИ" между элементами
                  ]

           }

          ,{

            "op" : "!OR" 

            "v" : [
                    массив описывающий элементы фильтра. "ИЛИ" между элементами потом отрицание полученного результата
                  ]

           }

           -- группировка условий И

          ,{

            "op" : "AND" 

            "v" : [
                    массив описывающий элементы фильтра. "И" между элементами
                  ]

           }
          ,{

            "op" : "!AND" 

            "v" : [
                    массив описывающий элементы фильтра. "И" между элементами потом отрицание полученного результата
                  ]

           }

          ]

,"order" : [ 

           -- упорядочивание результата, не обязательно
           --
           -- список полей и функций агрегирования
         --
           -- префикc "-" задаёт сортировку по убыванию
           -- префикс "+" или его отсутвие - по возрастанию
           --
           -- a,-b
           -- +b,-avg(z)
           -- -a,+b,-c
           -- +dt:YM,-c

         ]

-- или Запрос с объединением

-- Результаты всех единичных запросов объединяются в одну строку ответа по уникальным ключам по порядку выполнения запроса.
-- Если в каком-либо запросе результатов для уникального ключа нет, то в строку добавляется соответствующее число значений null.
-- Если в результате строки с уникальным ключём встречается больше одного раза, то используется только первый результат, а остальные отбрасываются.
-- Дублирование уникального ключа в объединённой строке не происходит, так как он удаляется из результатов второго и далее запросов перед их добавкой в объединённую строку.
-- Смотрите пример запроса с объединением в разделе "Примеры запросов универсальной статистики" 

,"join" : [ -- объединяемы запросы. обязательно
           { Единичный запрос-1 }
          ,{ Единичный запрос-2 }
          ,{ Единичный запрос-3 }
          ....
          ]

,"groupby" : "число" -- обязательно. больше или равно 0
                     -- размер уникального ключа для объединения результатов
                     -- 0 - без ключа. объединяются все колонки. (например вы выбираете min() из нескольких объектов)
                     -- 1 - первая колонка каждого результата единичного запроса
                     -- 2 - первая и вторая
                     -- 3 - первая, вторая и третья
                     -- и так далее

,"order" : [        -- сортировка результата объединённого запроса. не обязательно
                    -- где "правило" это направление сортировки (- или +), номер колонки результата (с единицы),и способ сравнения (s - строчный, n - числовой)
                    -- например  "-1n","+2s","-4s","+5n" 
        "правило1",
        "правило2,
        .....
]

---- Прочие параметры

-- заголовки столбцов 
-- предназначены для создания заголовков в csv и xlxs, но появятся в ответе и при ответе "response" 
-- содержимое и количество элементов не анализируется, вы можете задать заголовков и меньше и больше количества столбцов данных

,"caption" : [ -- не обязательно
              "адын" 
             ,"дыва" 
             ,"тры
              ......
             ]

,"result" : [ способ возврата результата. смотрите общее описание ]
}

ответ


{
  <общие поля>

-- для result != response

 ,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для result = response

 ,"list" : [

          [строка1,данные в порядке заданом в select]

          [строка2,данные в порядке заданом в select]

          [строка3,данные в порядке заданом в select]

          ........

        ]

}

День недели, день года и неделя года, первый день недели

У полей типа дата можно указать что требуется не сама дата, а производные от неё величины:

Номер дня в неделе

Запись dt:DOW означает получение номера дня в неделе от значения даты.

Результатом является число от 1 до 7 - Пн -1 .... Сб - 6, Вс -7

В условиях понимает также 0 как воскресенье и английские полные названия дней, а так же их английские трёх и двух буквенные аббревиатуры (регистр не важен)

Номер дня в году

Запись dt:DOY означает получение номера дня в году от значения даты.

Результатом является число от 1 до 366.

Номер недели в году

Запись dt:WOY означает получение номера недели в году на которую приходится значения даты.

Результат возвращается как YYYY-Www, где YYYY - год, ww - номер недели от 01 до 53.

Номер недели определяется по ГОСТ ИСО 8601-2001: первой неделей года считается та, в который есть первый четверг года. (для григорианского календаря - та где 4 января).

Таким образом, начальные дни нового года могут быть в последней неделе прошлого года. И последние дни прошлого года - в первой неделе нового года.

Например:

2000-12-31 даст 2000-W52 - конец года в его последней неделе

2001-12-31 даст 2002-W01 - конец года в первой неделе следующего года

2012-01-01 даст 2001-W52 - первый день года в последней неделе предыдущего года

2012-01-02 даст 2002-W01 - а второй уже в первой неделе своего года

2014-05-04 даст 2014-W18 - просто дата на 18 неделе года

Первый день текущей недели

Запись dt:CW1D означает получение даты первого дня недели на которую приходится значения даты.

Результат возвращается как дата с точностью до дня YYYY-MM-DD

Начало недели определяется по ГОСТ ИСО 8601-2001: понедельник.

Константа текущее время

Синтаксис и поведение аналогично использованию current в сравнении.

const.сurrent:HL +Y year +M month +D day +h hour round +m minute round +s second

Точность по умолчанию всегда Ys если она не задана явно необязательным уточнением :HL

Где H - точность старшего компонента (Y M D h m s)
Где L - точность младшего компонента (Y M D h m s)

Например

const.current - 1 day - 1971-04-05 06:07:08

const.current:YM - 1 day - 1971-04-05

Сравнения с текущим временем

Для полей типа дата возможно сравнение с текущим временем +/- сдвиг.

Для этого значение v записывается в виде

"сurrent +Y year +M month +D day +h hour round +m minute round +s second"

Все поля задающие сдвиг не обязательны, но их порядок от года к секунде должен соблюдаться.

Знак сдвига обязателен и может быть "+" (как в примере) или "-".

К названию величины можно добавлять на конце "s"

Указатели округления часов и минут - round - не обязательны.

Результат вычисления current всегда неявно приводится к точности поля заданом в ключе "a"

Например, в условии

issue.dt:YM < current - 1 day

точность результата "current - 1 day" неявно преобразуется к YM.

Вычитание годов и месяцев

Особенность Григорианского календаря такова, что просто так отнимать/прибавлять от даты месяца и года нельзя, так как в месяце получившейся новой даты может не быть такого дня.

По этому вычитание годов или месяцев округляет результат.

Например, если сегодня 30е марта, то вычитание 1го месяца даст февраль, а 30е февраля было ранее только в 1712 году и следующий раз может быть, предположительно, в 3328 году.

Например, если сегодня 31 декабря, то вычитание 3х месяцев даст 31 сентября.

Например, если сегодня 29 февраля, то вычитание 1 года даст 29 февраля прошлого года, который явно не високосный.

Строго формально всё ещё хуже - бывает 61я секунда в минуте (не так уж и редко, последние разы 31 декабря 2008 23:59:60 и 30 июня 2012 23:59:60) и 23 или 25 часов в сутках (при переходе на и с летнего времени час или выпадает или добавляется, и без указания временной зоны один час может или пропасть или встретиться дважды). Но это системой не учитывается.

Вычитание дней

Вычитание/прибавление 1 дня можно записать как вычитание/прибавление 24 часов, по этому вычитание дней тоже округляет результат что иметь возможность установить время в 0:0:0.
А вычитание/прибавление часов - не округляет.

Округление часов

Округление часов указываемое словом round необходимо когда временной отрезок должен начинаться точно от начала часа.

Без округления результат сохранит текушие минуты и секунды дав время "на N часов назад/вперёд"

С округлением минуты и секунды будут установлены в 0 что даст время "начало часа N часов назад/вперёд"

Округление минут

Округление минут работает аналогично округления часов - секунды устанавливаютс в 0 и вы получаете точно начало минуты.

Правило вычисления current

точность даты понижается до года-месяца

к/из полученой даты прибавляются/вычитаются указанное количество годов и месяцев

результат преобразуется обратно к точности год-секунда установкой первого числа 0 часов 0 минут 0 секунд

точность даты понижается до года

к/из полученой даты прибавляются/вычитаются указанное количество лет

результат преобразуется обратно к точности год-секунда установкой первого месяца первого числа 0 часов 0 минут 0 секунд

точность даты понижается до года-месяца

к/из полученой даты прибавляются/вычитаются указанное количество месяцев

результат преобразуется обратно к точности год-секунда установкой первого числа 0 часов 0 минут 0 секунд

точность даты понижается до года-месяца-дня

к/из полученой даты прибавляются/вычитаются указанное количество дней

результат преобразуется обратно к точности год-секунда установкой 0 часов 0 минут 0 секунд

с округлением

точность даты понижается до года-месяца-дня-часа

к/из полученой даты прибавляются/вычитаются указанное количество часов

результат преобразуется обратно к точности год-секунда установкой 0 минут 0 секунд

без округлением

к/из полученой даты прибавляются/вычитаются указанное количество часов

с округлением

точность даты понижается до года-месяца-дня-часа-минуты

к/из полученой даты прибавляются/вычитаются указанное количество минут

результат преобразуется обратно к точности год-секунда установкой 0 секунд

без округлением

к/из полученой даты прибавляются/вычитаются указанное количество минут

Примеры

Примеры простые

сейчас

current

cейчас минус 1 час 3 минуты плюс 4 секунды

current -1 hour -3 minutes +4 second

сейчас минус 40 дней назад плюс 78 часов

current -40 days + 78 hours

что-то произошло за два последних часа

dt >= current - 2 hours

что-то произошло за два последних часа и только за них не считая текущий час

dt >= current - 2 hours round and dt < current - 0 hours round

выпуск состоялся в прошлом месяце (ниже есть пример считающийся быстрее)

issue.dt:YM == current - 1 month

выпуск состоялся в прошлом месяце и ранее (ниже есть пример считающийся быстрее)

issue.dt:YM <= current - 1 month

выпуск состоялся вчера

issue.dt:YM == current - 1 day

выпуск состоялся за последнии 24 часа

issue.dt:YM > current - 24 hours

выпуск состоялся начиная с 5 числа прошлого месяца и позже
обратите внимание, что вычитается 4 дня - так как вычитание месяца установит день в 1, то для получения 5го числа надо прибавить 4, а не 5

issue.dt >= current - 1 month + 4 days

выпуск состоялся начиная с 3 числа текушего месяца и позже
обратите внимание, что вычитание ноля месяцев использовано для сброса дня в 1 и уже потом коррекции его до 3

issue.dt >= current - 0 month + 2 days

Примеры, если вы хотите что бы считалось быстрее (но записывать зато сложнее):

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

выпуск состоялся в прошлом месяце и ранее
"current - 0 month" даст начало текущего месяца, а вычитание "1 second" даст последнюю секунду прошлого месяца

issue.dt <= current - 0 month - 1 second

выпуск состоялся ровно в прошлом месяце
"current - 1 month" даст начало прошлого месяца
"current - 0 month" даст начало текущего месяца, а вычитание "1 second" даст последнюю секунду прошлого месяца

issue.dt >= current - 1 month И  issue.dt <= current - 0 month - 1 second

Примеры быстрого сравнения для самостоятельного понимания:

выпуск состоялся с 4 мая до 20 августа этого года

issue.dt >= current - 0 year + 4 month + 3 days И  issue.dt < current - 0 years + 7 month + 19 days

выпуск состоялся с 4 мая по 20 августа этого года

issue.dt >= current - 0 year + 4 month + 3 days И  issue.dt <= current - 0 years + 7 month + 20 days - 1 second

Статистика с точностью до каждого письма/sms/viber

Каждое письмо/sms/viber имеют уникальный идентификатор состоящий из номера выпуска и номера письма в выпуске. Поэтому вы можете получить статистику с точностью до каждого письма/sms/viber каждого выпуска для каждого адресата.

До тех пор, пока у вас в каждом выпуске один адрес или телефон получателя встречается только один раз (а это может быть не так для Экспресс-Выпусков, Персональных писем и выпусков по группам с фильтрами со сложной группировкой), допустимо считать уникальным иденификатором письма для такого выпуска номер выпуска и адрес получателя.

Доступные для использования поля

Поля типа дата (помечены dt), временные константы и "текущее время" задаются временем московского часового пояса (с неявным подразумеванием зимнего или летнего времени).

Отметка dt:Ys обозначет, что поле имеет значение времени с точность от года (Y) до секунды (s).

Отметка (null) обозначает, что поле может иметь неопределённое значение.
Например если выпуск рассылки не использовал ни какой черновик, то значение поля issue.draft.id будет не опеределено.

Все логические операции с полем имеющим неопределённое значение всегда не выполняются.
Например, неопределённое значение номера черновика не попадёт под фильтр issue.draft.id > 10, но так же оно не попадёт и под фильтр issue.draft.id <= 10.

Запись xxx.yyy.* обозначает что доступны все поля объекта yyy.

Например, при наличии у объекта yyy поля zzz, можно его получить через xxx.yyy.zzz

Если какое-то поле является набором полей другого объекта, то доступны даже те поля другого объекта, что сами являются наборами полей.

Например, так как в объекте Клик доступны все поля объекта Выпуск (cliсk.issue.*), а в объекте Выпуск - объекта Группа (issue.group.*), то можно получить значение поля "Название группы из выпуска которой был этот клик" - сlick.issue.group.name

Информация о домене


domain.id - id домена

domain.name - назвение домена

Информация о подписчике


member.id - id подписчика

member.type - тип идентификатора подписчика
            - 0 - email
            - 1 - телефон
            - 2 - csid

member.email - значение идентификатора подписчика (email,телефон,csid)
             - при фильртрации по этому полю понимается не обязательный параметр addr_type
             - если он указан, то сравниваемые значения из v сначала нормализуются в соответствии с указаным типом
             - если нормализация удалась - используется нормализованное значние, если не удалась - оригинальное

member.haslock - блокировка подписчика (доступно так же и через обычный вызов member.get). может ли получать письма, sms или viber
                  0 - нет            - может
                  1 - отписался      - не может
                  2 - не подтверждён - не может
                  4 - имеет фатальные ошибки доставки - не может
                  прочие значения ( 3,5,6,7 ) - одновременное наличие нескольких указаных блокировок

member.create.time - дата время создания данных подписчика (Ys)
member.create.host - источник создания

member.update.time - дата время последнего изменения данных подписчика (Ys)
member.update.host - источник изменения

member.domain.* - информация о домене подписчика

-- информация об ошибках доставки. все поля null если последняя доставка была успешной.

member.error.error - количество ошибок доставки (null)

member.error.lock  - 0|1|null - адрес заблокирован (1) из-за ошибок (в зависимости от количества и типа ошибок) (null)
                              - заблокированный адрес не участвует в рассылке и попадает в статистику как hardbounced

member.error.dt    - дата последней ошибки доставки (YYYY-MM-DD hh:mm:ss) (null)

member.error.str   - текст последней ошибки доставки (null)

-- информация из данных подписчика

member.datakey.DATAKEY - значение ответа как при использовании member.get с таким же ключём данных
                       - *в данный момент допустимо только в select* 
                       - *в данный момент не может быть параметром функций агрегации*

-- информация из анкет подписчика

member.anketa.ANKETA.QUEST - значение ответа QUEST из анкеты ANKETA. 
                           - при отсутствии ответа на указаный вопрос результат null
                           - *в данный момент допустимо только в select* 
                           - *в данный момент не может быть параметром функций агрегации*

-- информация о заполнении форм

member.form.id         -- номер формы

member.form.name       -- название формы

member.form.dt_fill    -- дата последнего заполнения

member.form.dt_confirm -- дата подтверждения (null)

member.form.resp.dk    -- ключ данный ответа в анкетах подписчика

member.form.resp.name  -- название вопроса на который дан ответ

member.form.resp.type  -- тип вопроса на который дан ответ

member.form.resp.value -- значение ответа. для вопросов с выбором это название варианта ответа. для других - сам ответ.

member.form.resp.id    -- код ответа. для вопросов с выборов это код ответа. для других - null.

member.form.issue.id  - номер выпуска из которого заполнена форма

member.form.issue.draft.id - номер черновика использованного в выпуске (null)

member.form.issue.draft.name - название черновика использованного в выпуске (null)

member.form.issue.draft.alias - альтернативный идентификатор черновика использованного в выпуске (null)

member.form.letter.id - номер письма из которого заполнена форма

member.form.letter.dt - дата письма из которого заполнена форма

member.form.letter.custid(element_name) - значение элемента element_name пользовательской метки письма из которого заполнена форма (null)

Существует специальная особенность связанная с выборкой данных подписчика.

Пока вы используете в запросе поля id и email, вы получаете данные о всех подписчиках.

Как только в запросе появляется поле haslock, вы начинаете получать данные только о тех подписчиках, запись о которых до сих пор есть в вашей базе.

Это связано с тем, что информация о адресе подписчика и его номере хранится всегда, а информация о блокировке только при наличии записи о пользователе в вашей базе и она пропадает если вы его удаляете.

Пример когда проявится разница - запрос адресов участвовавших в выпуске ("select":["member.email"]) вернёт все адреса попавшие в выпуск, а такой же запрос с дополнительной выборкой текущего состояния блокировки ("select":["member.email", "member.haslock"]) вернёт только тех, кого вы ещё не удалили из своей базы.

Эту особенность можно использовать специально для отсечения тех кто из базы уже удалён. Фильтр member.haslock >= 0 выберет подписчиков с любым состоянием блокироваки, но приведёт к исключению из результатов тех кто уже удалён.

Информация o группе


group.id - id группы

group.gid - символический код группы

group.name - название группы

-- регулярно обновляемы кэш статистики по всем выпускам вышедшим по группе

-- для email-групп

group.stat.total.email.all.issues -- выпусков рассылок

group.stat.total.email.all.members -- получателей

group.stat.total.email.all.u_members -- уникальный получателей

group.stat.total.email.all.clicked -- кликов

group.stat.total.email.all.u_clicked -- уникальных кликов

group.stat.total.email.all.target -- достижений целевых страниц

group.stat.total.email.all.u_target -- уникальных достижений целевых страниц

group.stat.total.email.all.readed -- чтений

group.stat.total.email.all.u_readed -- уникальный чтений

group.stat.total.email.all.unsub -- отписок из писем выпусков

group.stat.total.email.all.u_unsub -- уникальных отписок из писем выпусков

-- для sms-групп

group.stat.total.sms.all.issues -- выпусков рассылок

group.stat.total.sms.all.members -- получателей

group.stat.total.sms.all.u_members -- уникальный получателей

group.stat.total.sms.all.size -- количество выпущенных смс

group.stat.total.sms.all.cost -- цена выпуск sms в единицах тарификации, в данный момент это копейки

Информация o выпуске

Поля с количеством отсеяных участников содержат актуальное значение для выпусков начиная с 26-07-2013.
Для более ранних выпусков поля содержат 0.

Поле issue.cost содержит актуальное значение для выпусков начиная с 01-11-2013.
Для более ранних выпусков поля содержат 0 так как старая система тарификации не пересчитываема в новую.


issue.id - id выпуска

issue.dt - дата выпуска (dt:Ys)

issue.name - название выпуска (совпадает с issue.subject если не было указано отдельно при выпуске)

issue.subject - тема выпуска (тема письма или имя отправителя sms)

issue.to_name - имя получателя (с 10.12.2015)

issue.from_name  - имя получателя (с 10.12.2015)
issue.from_email - адрес получателя (с 10.12.2015)

issue.reply_name  - имя для ответа (с 10.12.2015)
issue.reply_email - адрес для ответа (с 10.12.2015)

issue.members - число получателей выпуска

issue.format - формат выпуска. e - email, s - sms, v - viber

issue.size  - размер выпуска. Для email и viber - размер одного письма/сообщения, для sms - количество выпущенных смс.

issue.cost - цена выпуска. Для email и viber - 0, для sms - в единицах тарификации, в данный момент это копейки

issue.archive - ссылка на не персонализированную версию выпуска в веб-архиве
              - для Транзакционных Писем (personal) всегда null так не имеет смысла из-за того, что каждое транзакционное письмо индивидуально

issue.double - количество дублирующихся адресов в списке. имеет смысл только для Экспресс-Выпуска. заполняется с 11-12-2013

issue.onlyunique - режим подавления дублирующихся адресов при выпуске. имеет смысл только для Экспресс-Выпуска. заполняется с 11-12-2013
                 - 0 - высылать письма на адреса встреченные второй и более разы
                 - 1 - НЕ высылать письма на адреса встреченные второй и более разы

issue.wrongline - количество строк в списке (cvs или json) которые система не смогла разобрать и они были отброшены. имеет смысл только для Экспресс-Выпуска. заполняется с 11-12-2013

issue.hardbounce - количество участников группы не допущенных в выпуск из-за большого количества ошибок доставки

issue.stoplist - количество участников группы не допущенных в выпуск из-за нахождения их адреса в стоп-листе

issue.lockunsub - количество участников группы не допущенных в выпуск из-за того что их адрес отписан

issue.lockconfirm - количество участников группы не допущенных в выпуск из-за того то внесение в базу ими ещё не подтверждено

issue.excludefilter -- группа исключения использованная при выпуске (null)

issue.unsublist  -- группа тематической отписки использованная при выпуске (null)

issue.group.* - информация о группе по которой был выпуск

issue.campaign.* - информация о кампани указаной при задании выпуска

issue.draft.id - номер черновика использованного при выпуске (null)
issue.draft.alias - альтернативный идентификатор черновика использованного при выпуске (null)
issue.draft.name - название черновика использованного при выпуске (null)

issue.form.id - номер формы использованной при выпуске (null)
issue.form.name - название формы использованной при выпуске (null)

issue.dkim.id - номер DKIM-ключа использованного при выпуске (null) (до 6 октября 2016 года - null)
issue.dkim.by - источник DKIM-ключа использованного при выпуске (до 6 октября 2016 года - null)
                возможные значения: issue, draft, domain, conf, global

issue.sequence.id - номер последовательности вызвавшей выпуск (null)

issue.sequence.campaign.* - информация о кампаниях в которые входит последовательность выпустившая этот выпуск

-тестирование в рамках которого вышел этот выпуск

issue.variant.id - номер варианта сплит-тестирования для которого вышел выпуск (null)
issue.split.id  - номер АБ-тестирования в рамках которого вышел этот выпуск
issue.split.campaign.*  - информация о кампаниях в которые входит АБ

issue.thumbnail_640x1100 - ссылка на изображения письма выпуска 640x1100 (подробности в issue.get) (null)

issue.thumbnail_150x180 - ссылка на изображения письма выпуска 640x1100 (подробности в issue.get) (null)

-- суммарная статистика по выпуску. 
-- это кэш. первые 5 дней он обновляется раз в 15 минут. потом - раз в три часа

issue.deliv_ok - количество успешно доставленных писем

issue.deliv_bad - количество писем с ошибкой доставки

issue.clicked - количество кликов

issue.u_clicked - количество уникальных кликов

issue.readed - количество чтений

issue.u_readed - количество уникальных чтений

issue.unsubed   - количество отписок из выпуска

-- суммарная статистика по выпуску за час
-- это кэш. он обновляется раз в 20 минут

issue.hourly.dt - дата и час (YYYY-MM-DD hh) для которых сгруппирована статистиска 

issue.hourly.clicked - количество кликов

issue.hourly.u_clicked - количество уникальных кликов

issue.hourly.readed - количество чтений

issue.hourly.u_readed - количество уникальных чтений

issue.hourly.unsubed   - количество отписок из выпуска

-- суммарная статистика по выпуску/домену за день
-- это кэш. он обновляется раз в 20 минут

issue.daily.domain.id - id домена

issue.daily.domain.name - название домена

issue.daily.dt - дата (YYYY-MM-DD) для которой сгруппирована статистиска 

issue.daily.deliv    - общее количество писем. в первый день рассылки учитывает ok+bad+unk. в остальные - только ok+bad

issue.daily.deliv_ok - количество успешно доставленных писем

issue.daily.deliv_bad - количество писем с ошибкой доставки

issue.daily.clicked - количество кликов

issue.daily.u_clicked - количество уникальных кликов

issue.daily.readed - количество чтений

issue.daily.u_readed - количество уникальных чтений

issue.daily.unsubed   - количество отписок из выпуска

-- География кликнувших или прочитавших - список для каждого совершившего действие

issue.geo.id                - числовой код географии CCCRR000 - CCC числовой код страны по ISO 3166-1, RR - для России числовой код региона по ОКАТО/ОКТМО и 00 для прочих стран и не определившегося региона России

issue.geo.name              - название географии на русском языке "Страна. Регион" или "Страна" если регион не определён

issue.geo.country.id        - числовой код страны по ISO 3166-1

issue.geo.country.alpha2    - двухбуквенный код страны по ISO 3166-1

issue.geo.country.name      - название страны на русском языке

issue.geo.region.alpha5     - пятисимвольный код региона по ISO 3166-2 (имеет вид XX-XXX) для России и совпадает с alpha2 для прочих стран

issue.geo.region.name       - название региона для России на русском языке, пусто для прочих стран

-- Количество кликнувших или прочитавших по географии - кэш общего числа

issue.stat.geo.n                 - число кликов и чтений для данной географии

issue.stat.geo.id                - числовой код географии CCCRR000 - CCC числовой код страны по ISO 3166-1, RR - для России числовой код региона по ОКАТО/ОКТМО и 00 для прочих стран и не определившегося региона России

issue.stat.geo.name              - название географии на русском языке "Страна. Регион" или "Страна" если регион не определён

issue.stat.geo.country.id        - числовой код страны по ISO 3166-1

issue.stat.geo.country.alpha2    - двухбуквенный код страны по ISO 3166-1

issue.stat.geo.country.name      - название страны на русском языке

issue.stat.geo.region.alpha5     - пятисимвольный код региона по ISO 3166-2 (имеет вид XX-XXX) для России и совпадает с alpha2 для прочих стран

issue.stat.geo.region.name       - название региона для России на русском языке, пусто для прочих стран

-- Устройство подписчика

issue.gadget.gender     - Desktop, Mobile, Tablet, Robot

issue.gadget.os         - Linux, Android, Windows, iOS, Mac, Blackberry, Other

issue.gadget.browser    - Firefox, Chrome, Opera, Safari, IE, Blackberry, Other 

issue.gadget.browmajor  - старшая часть версии браузера

-- Вхождение в мультивыпуск

issue.multi.id          - номер мультивыпуска (null)

issue.multi.name        - название мультивыпуска (null)

Информация o кампании


campaign.id - id кампании

campaign.name - название кампании

---- суммарная статистика по входящим в кампанию выпускам и формам по всей кампании. это кэш.

-- количество кликнувших или прочитавших по географии - кэш общего числа

campaign.stat.geo.n                 - число кликнувших или прочитавших для данной географии

campaign.stat.geo.id                - числовой код географии CCCRR000 - CCC числовой код страны по ISO 3166-1, RR - для России числовой код региона по ОКАТО/ОКТМО и 00 для прочих стран и не определившегося региона России

campaign.stat.geo.name              - название географии на русском языке "Страна. Регион" или "Страна" если регион не определён

campaign.stat.geo.country.id        - числовой код страны по ISO 3166-1

campaign.stat.geo.country.alpha2    - двухбуквенный код страны по ISO 3166-1

campaign.stat.geo.country.name      - название страны на русском языке

campaign.stat.geo.region.alpha5     - пятисимвольный код региона по ISO 3166-2 (имеет вид XX-XXX) для России и совпадает с alpha2 для прочих стран

campaign.stat.geo.region.name       - название региона для России на русском языке, пусто для прочих стран

-- по всем email-рассылкам

campaign.stat.total.email.all.issues    - выпусков email-рассылок

campaign.stat.total.email.all.members   - получателей

campaign.stat.total.email.all.u_members - уникальный получателей

campaign.stat.total.email.all.clicked   - кликов

campaign.stat.total.email.all.u_clicked - уникальных кликов

campaign.stat.total.email.all.target    - достижений целевых страниц

campaign.stat.total.email.all.u_target  - уникальных достижений целевых страниц

campaign.stat.total.email.all.readed    - чтений

campaign.stat.total.email.all.u_readed  - уникальный чтений

campaign.stat.total.email.all.unsub     - отписок из писем выпусков

campaign.stat.total.email.all.u_unsub   - уникальных отписок из писем выпусков

-- только по транзакционным email-рассылкам

campaign.stat.total.email.personal.*    - аналогично campaign.stat.total.email.all, но только по выпускам транзакционных email-рассылок (рассылка personal)

-- только по макретинговым email-рассылкам

campaign.stat.total.email.marketing.*   - аналогично campaign.stat.total.email.all, но только по выпускам маркетинговых email-рассылок (все рассылки кроме personal)

-- по всем sms-рассылкам

campaign.stat.total.sms.all.issues      - выпусков sms-рассылок

campaign.stat.total.sms.all.members     - получателей

campaign.stat.total.sms.all.u_members   - уникальных получателей

campaign.stat.total.sms.all.size        - количество использованых sms

campaign.stat.total.sms.all.cost        - цена выпуска использованых sms в единицах тарификации, в данный момент это копейки

-- только по транзакционным smm-рассылкам

campaign.stat.total.sms.personal.*      - аналогично campaign.stat.total.sms.all, но только по выпускам транзакционных sms-рассылок (рассылка personal)

-- только по макретинговым sms-рассылкам

campaign.stat.total.sms.marketing.*     - аналогично campaign.stat.total.sms.all, но только по выпускам маркетинговых email-рассылок (все рассылки кроме personal)

-- по формам

campaign.stat.total.form.members        - заполнений форм

campaign.stat.total.form.u_members      - уникальных пользователей заполнивших формы

---- суммарная статистика по кампании по дням. это кэш.

campaign.stat.daily.dt   - день (YD) - YYYY-MM-DD
campaign.stat.daily.*    - аналогично campaign.stat.total

---- суммарная статистика по кампании по неделям это кэш.

campaign.stat.weekly.dt  - первый день недели (т.е. понедельник) (YD) - YYYY-MM-DD
campaign.stat.weekly.*   - аналогично campaign.stat.total

 -- то же самое по месяцам

campaign.stat.monthly.dt  - месяц (YM) - YYYY-MM
campaign.stat.monthly.*   - аналогично campaign.stat.total

Информация o ссылке


link.id - id ссылки

link.url - url ссылки

link.reltype - параметр классификации
                     -- отрицательные значения зарезервированы для системы
                     -- в данный момент:
                     --  -1 - ссылка из выпуска рассылки
                     --  -2 - ссылка - целевая страница достигнутая подписчиком

link.relref - параметр классификации

link.linkgroup.* - информация о группе в которую входит ссылка

Информация o группе ссылок


linkgroup.id - группы ссылок

linkgroup.name - name группы ссылок

linkgroup.reltype - параметр классификации

linkgroup.relref - параметр классификации

linkgroup.campaign.* - информация о кампаниях в которые входит группа ссылок

Информация о переходах

Не забывайте уточнять в запросе click.link.reltype !

Например, при подсчёте всех кликов из выпуска и без его указания вы получите информацию как по самим кликам из выпуска (reltype=1), так по посещения Целевых Страниц (reltype=-2). Уточните в фильтре не только номер выпуска, но и классификацию ссылки.


click.dt - дата и время клика (dt:Ys)

click.ip - ip кликнувшего

click.letter.* - информация о письме как в deliv.letter

click.member.* - информация о кликнувшем подписчике

click.member.geo.* - информация о географии подписчика (null)

click.member.gadget.* - информация об устройстве подписчика (null)

click.domain.* - информация о домене кликнувшего подписчика

click.issue.*  - информация о выпуске из которого был клик

сlick.link.id - id ссылки

click.link.url - url ссылки

click.link.reltype - параметр классификации

click.link.relref - параметр классификации

click.linkgroup.* - группы ссылок

click.linkgroup.campaign.* - информация о кампаниях в которые входит группа ссылок ссылки по которой был переход

Информация об открытии писем


read.dt - дата и время чтения (dt:Ys)

read.ip - ip прочитавшего (null до 01 декабря 2014 года)

read.letter.* - информация о письме как в deliv.letter

read.member.* - информация о прочитавшем (открывшем письмо) подписчике

read.member.geo.* - информация о географии подписчика (null)

read.member.gadget.* - информация об устройстве подписчика (null)

read.domain.* - информация о домене прочитавшего подписчика

read.issue.*  - информация о выпуске который был прочтён

Информация о доставке и прочих свойствах каждого сообщения


deliv.member.* - информация о подписчике-получателе

deliv.member.geo.* - информация о географии подписчика (null)

deliv.member.gadget.* - информация об устройстве подписчика (null)

deliv.domain.* - информация о домене подписчика-получателя

deliv.issue.*  - информация о выпуске который доставлялся

deliv.letter.id - номер письма в выпуске

deliv.letter.dt - для email и viber
                -   дата и время (dt:Ys) выпуска транзакционного письма. (null)
                -   (deliv.issue.dt не подходит из-за особенностей учёта выпуска транзакционных писем)
                -   только для выпусков personal
                -   для остальных - null
                -   до 11 марта 2015 года - null

deliv.letter.dt - для sms
                -   дата и время (dt:Ys) последнего изменения статуса sms. (null)
                -   до февраля 2015 года - null

deliv.letter.start_dt -- запланированная дата старта (dt:Ys) при растянутом выпуске (tz_limit/batch)
                      -- или при выпуске с учётом часового пояса (tz_observance)
                      -- до 18 мая 2016 года - null

deliv.letter.custid(element_name) - значение элемента element_name пользовательской метки письма

deliv.status - код статуса доставки
             - больше 0 - доставлено
             - равно  0 - в процессе доставки
             - мешьше 0 - ошибка доставки или отсеян из рассылки

             - Для писем кода ошибок обозначают
             -  -2 - все попытки доставки успеха не имели, но и не был получен ответ, что точно не примут
             -  -3 - письмо приняли за спам
             -  -4 - письмо отменено (например выпуск растянутый по времени решено вообще не выпускать дальше)
             -  -5xx - код ошибки SMTP-DSN 5xxx
             -  -15xx - код ошибки SMTP (5xx) сдвинутый на -1000

             - Для sms и viber  кода ошибок обозначают
             -     -4 - сообщение отменено
             -  -2000 - прочие ошибки
             -  -2003 - истекло время доставки
             -  -2004 - sms-центр принял сообщение, но потом не стал доставлять и просто удалил
             -  -2005 - не удалось доставить на телефон абонента
             -  -2006 - sms-центр сообщил, что потерял информацию о статусе доставки
             -  -2007 - неизвестная ошибка доставки
             -  -2008 - sms-центр сразу отверг сообщение
             -  -2009 - sms-провайдер не принял сообщение

deliv.result - результат доставки
             -  1  - доставлено
             -  0  - в процессе доставки
             - -1  - ошибка доставки

-- для sms-рассылок (до 04.12.2013 - null, для email-рассылок - null)

deliv.oper - оператор получателя (null)
           - other    - 0
           - megafon  - 1
           - skylink  - 2
           - mts      - 3
           - beeline  - 4
           - tele2    - 5

deliv.size - количество смс использованых для передачи сообщения (null)

deliv.cost - стоимость сообщения. сотых долях копейки (null)

-- информация о доставки ответов если активна настройка перехвата ответов на письма выпуска

deliv.replyed.to - адрес получателя ответа

deliv.replyed.dt - дата и время (Ys) доставки/недоставки ответа

deliv.replyed.status - код статуса доставки/недоставки ответа. аналогичен deliv.status 

deliv.replyed.errstr - строка ошибки доставки ответа (null)

Вместо префикса deliv можно использвать префиксы

Информация об отписке


unsub.dt     - дата получения информации об отписке (YYYY-MM-DD hh:mm:ss)

unsub.why    - способ отписки
             - 1 - нажав кнопку "Это спам" в своей почтовой системе/программе
             - 2 - по стандартной ссылке отписаться внизу письма
             - 3 - через жалобу в нашу службу поддержки
             - 4 - нажав кнопку "Отписаться" в своей почтовой системе/программе

unsub.letter.* - информация о письме как в deliv.letter

unsub.member.* - информация об отписавшемся подписчике

unsub.domain.* - информация о домене отписавшегося подписчика

unsub.issue.*  - информация о выпуске из которого отписались, если он известен (null)

Информация о ряде данных


datarow.id    - идентификатор ряда данных

datarow.name  - название ряда данных

datarow.prec  - точность даты ряда данных

datarow.campaign.* - информация о кампаниях в которые входит ряд данных

-- Одна запись ряда данных

datarow.record.dt     - дата записи
                      - ! дата всегда возваращается с точность Ys вне зависимости от datarow.prec

datarow.record.value  - значение записи

Cтоп-лист


stoplist.dt       - дата записи (Ys)

stoplist.source   - источник (null)

stoplist.type     - А - внесено владельцем аккаунта, М - владельцем адреса

stoplist.email    - адрес

Статистка заполнения форм опросов

При повторном заполнения подписчиком формы старые данные заменяются новыми

form.id - идентификатор формы

form.name - название формы

form.state - состояние формы

form.member.id - id заполнившего

form.member.email - адрес заполнившего

form.member.dt_fill - дата заполнения Ys (null)

form.member.dt_confirm - дата подтверждения заполнения Ys (null)

form.member.issue.id - номер выпуска из которого заполнена форма

form.member.issue.draft.id - номер черновика использованного в выпуске (null)

form.member.issue.draft.name - название черновика использованного в выпуске (null)

form.member.issue.draft.alias - альтернативный идентификатор черновика использованного в выпуске (null)

form.member.letter.id - номер письма из которого заполнена форма

form.member.letter.dt - дата письма из которого заполнена форма

form.member.letter.custid(element_name) - значение элемента element_name пользовательской метки письма из которого заполнена форма (null)

form.campaign.* - информация о кампаниях в которые входит форма

Общая информация об аккаунте


stat.common.dt     - дата (YYYY-MM-DD)

stat.common.tarif - код тарифа на эту дату

-- данные вызова member.list.count на эту дату. все поля null до 29.01.2014

stat.common.emails        - количество адресов в базе

stat.common.phones        - количество телефонов в базе

stat.common.active_emails - количество адресов способных участвовать в рассылках

stat.common.active_phones - количество телефонов способных участвовать в рассылках

stat.common.locked        - уникальное количество заблокированных - не могут быть в рассылке.

stat.common.stoplist      -    в том числе заблокированные так как находятся в стоп-листе

stat.common.lockconfirm   -    в том числе заблокированные так как не подтвердили внесение в базу

stat.common.hardbounce    -    в том числе заблокированные так как имеют фатальные ошибки доставки

stat.common.lockunsub     -    в том числе заблокированные так как отписались

Информация о последовательности (тригере)

sequence.id - id последовательности

sequence.name - название последовательности

-- информация о каждом прохождении последовательности каждым участником

sequence.progress.email - адрес участника

sequence.progress.step - шаг в последовательности

sequence.progress.state - cостояние
                            --  1 - активен - происходящие события учитываются
                            --  0 - пауза - происходящие события игнорируются
                            -- -1 - последовательность закончена
                            -- -2 - последовательность закончена из-за sequence.member.stop
                            -- -3 - последовательность закончена из-за parallel = 0 или -1
                            -- -4 - последовательность закончена из-за action = sequence.start, seqeunce.stop, sequence.goto
                            -- -5 - последовательность закончена из-за изменений по указанию
                            -- -6 - последовательность закончена из-за изменений так как участник оказался вне последовательности
                            -- -7 - последовательность закончена так как её прохождение отменено
                            -- -8 - последовательность закончена так как подписчик был удалён

sequence.progress.dt - дата-время попадания на текущий шаг или дата окончания последовательности (Ys)

Комбинирование в одном запросе

Возможно одновременное использование следующих сочетаний полей в одном запросе

Сводная статистика

Специальные названия полей используются для функций count() и sum() если в запросе участвуют несколько типов статистики.

Область вычисления функции задается аргументом.

В фильтре можно использовать поля из конкретной области статистики.


*.dt - дата события

*.issue.id - id выпуска

*.issue.name - тема выпуска

*.issue.group.id - id группы выпуска

*.issue.group.gid - код группы выпуска

*.issue.group.name - name группы выпуска

*.issue.dt - дата выпуска

*.issue.members - число получателей выпуска

*.issue.format - формат выпуска

*.issue.deliv_ok - количество успешно доставленных писем

*.issue.deliv_bad - количество писем с постоянной ошибкой доставки

*.issue.clicked - количество кликов

*.issue.u_clicked - количество уникальных кликов

*.issue.readed - количество чтений

*.issue.u_readed - количество уникальных чтений

*.issue.unsubed - количество отписок из выпуска

*.issue.draft.id - номер черновика использованного при выпуске (null)
*.issue.draft.name - название черновика использованного при выпуске (null)

*.issue.form.id - номер формы использованной при выпуске (null)
*.issue.form.name - название формы использованной при выпуске (null)

*.issue.sequence.id - номер последовательности вызвавшей выпуск (null)

*.issue.variant.id - номер варианта сплит-тестирования для которого вышел выпуск (null)

*.member.id - id подписчика

*.member.email - значение email или телефона подписчика

*.member.haslock - блокировка подписчика

вверх

Стоп-лист

Существует два стоп-листа: владельца (A) - вы им полностью управляете и подписчика (M) - вносит в него себя подписчик сам и вы не можете повлиять на имеющиеся там записи.

Чтение записей в стоп-листе владельца и подписчиков


{

  "action" : "stoplist.get" 

-- или  (для выборки всех лучше использовать stat.uni - быстее и можно сохранить результат в файл или прислать по почте)

 ,"list" : [ -- отсутсвие параметра - дать всех

             "email-1" -- есть в А и М

            ,"email-2" -- нет ни в одном

            ,"email-3" -- есть в А

             .....

           ]

-- или (устаревший вариант ! будет удалён ! используйте stat.uni)

 ,"type" : "тип листа" (A - внесенные владельцем, M - внесенные подписчиками, пусть - любой)

}

ответ

 <общие поля>

 list : {

          "email-1" : {

                        "A": 1

                       ,"M": 1

                       }

         ,"email-3" : {

                        "A": 1

                       }

         }

}

Внести в стоп-лист владельца

Внесение в стоп-лист владельца означает только то, что указанный адрес/телефон будет автоматически исключаться из всех рассылок в которых он мог бы участвовать.

Но вся информация связанная с адресом/телефоном остаётся в базе.

Запрос с указанием списка по умолчанию синхронный.

Запрос с указанием группы по умолчанию асинхронный. Используйте sync = 1 если вам реально нужен ответ с описанием какие адреса как были обработаны.

Асинхронные запросы возвращают номер трекера для отслеживания.


{

  "action" : "stoplist.add" 

-- указание подписчиков одним из способов

 ,"email" : "адрес или телефон" -- телефон в полном формате +7XXXYYYYYYY

или

 ,"list" : [

            "адрес/телефон" 

           ,"адрес/телефон" 

            ........

           ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

или

 ,"group" : код группы участники которой будут внесены в стоп-лист

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"group.filter" : [
                    фильтр отбора как у group.filter.set
                   ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"url" : ссылка на файл со списком адресов. по одному на строке. возможно сжатие zip.

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

                "filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный
}

ответ


{

 <общие поля>

-- для асинхронного запроса

,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для синхронного запроса

,"list" : {
           "адрес-1" : 0|1|кодошибки -- результат внесения - 1 - удалён, 0 - нет, кодошибки - синтаксическая ошибка в адресе/телефоне

          ,"адрес-2" : 0|1|кодошибки

           .................
          }

}

Удаление из стоп-листа владельца

Удаление из стоп-лист владельца означает только то, что указанный адрес/телефон будет удалён из стоп-листа владельца. Он перестанет автоматически исключаться из всех рассылок если не будет других причин это делать (стоп-лист пользователя, ошибки доставки и прочее)

Запрос с указанием списка по умолчанию синхронный.

Запрос с указанием группы по умолчанию асинхронный. Используйте sync = 1 если вам реально нужен ответ с описанием какие адреса как были обработаны.

Асинхронные запросы возвращают номер трекера для отслеживания.


{

  "action" : "stoplist.delete" 

 ,"from" : "client|member|all" -- из какого стоп-листа идёт удаление.
                               -- по умолчанию - сlient
                               -- all и member - доступны только если при заключении отдельного соглашения

-- указание подписчиков одним из способов

 ,"email" : "адрес или телефон" -- телефон в полном формате +7XXXYYYYYYY

или

 ,"list" : [

            "адрес/телефон" 

           ,"адрес/телефон" 

            ........

           ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 1 - синхронный

или

 ,"group" : код группы участники которой будут удалены из стоп-листа

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"group.filter" : [
                    фильтр отбора как у group.filter.set
                   ]

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"url" : ссылка на файл со списком адресов. по одному на строке. возможно сжатие zip.

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный

или

 ,"stat.uni" : { -- адреса для обработки получаются из запроса Универсальной Cтатистики 

                 -- подразумевается unique = 1

                "filter" : [ условие выборки как у запроса в вызове stat.uni ]

               ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

               ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.email" ]

               }

 ,"sync" : 0|1 -- изменение синхронности запроса. по умолчанию 0 - асинхронный
}

ответ


{

 <общие поля>

-- для асинхронного запроса

,"track.id" : номер -- номер асинхронного запроса для отслеживания с помощью track.*

-- для синхронного запроса

,"list" : {
           "адрес-1" : 0|1|кодошибки -- результат удаления - 1 - удалён, 0 - нет, кодошибки - синтаксическая ошибка в адресе/телефоне

          ,"адрес-2" : 0|1|кодошибки

           .................
          }

}

Очистить стоп-лист владельца

Удаляются все записи из стоп-листа владельца


{

  "action" : "stoplist.erase" 

}

ответ


{

    <общие поля>

}

вверх

Действия по расписанию

Действия по расписанию позволяют запланировать исполнение api-запроса (и даже нескольких при использовании batch) по гибкому расписанию.

При наступлении указанного времени действие запускается от имени пользователя указанного в sublogin. Если пользователь заблокирован или удалён, то действие выполнено не будет.

Для облегчения создания заданий, во всех вызовах ссылки указывающие на внешние данные могут содержать специальные метки для подстановки компонентов текущей даты и времени. Подробности - в разделе "Общие замечания".

Так же во всех вызовах, ссылки указывающие на внешние данные могут содержать указания на количество попыток получить данные и интервал между ними. Это полезно в случае когда точное время появления у вас данных не известно - вы создаёте всего одно отложенное действие на самое раннее время и указываете сколько раз пытаться обнаружить данные (в обычном случае первая же неудачная попытка получения данных завершает работу вывозова). Подробности - в разделе "Общие замечания".

Не активное действие по расписанию можно использовать как хранилище сложного api-запроса и выполнять по мере надобности через cron.runonce

При планировании запросов issue.send параметр sendwhen допустим только в значениях now и test.

Cписок действий по расписанию

{

 "action" : "cron.list" 

,"filter" : { -- не обязательно

              "reltype"  : число  -- не обязательно
                                  -- при наличии ограничивает список только действиями с указанным reltype

             ,"relref"   : число  -- не обязательно
                                  -- при наличии ограничивает список только действиями с указанным relref
             }
}

ответ

{

  <общие поля>

 ,"list" : [

             {

                "id"   : "идентификатор действия по расписанию" 

               ,"name" : "название действия по расписанию" 

               ,"active" : "активность действия по расписанию" 

               ,"lastran" : "дата и время последнего запуска действия по расписанию" 

               ,"sublogin" : "владелец" 

               ,"dt.from" : "дата начала действия" 

               ,"dt.upto" : "дата окончания действия" 

               ,"reltype" : ...

               ,"relref" : ...
             }

             ...

           ]

}

Чтение действия по расписанию

{

 "action" : "cron.get" 

 ,"id" : "идентификатор действия по расписанию" 

}

ответ

{

  <общие поля>

 ,"obj" : {
           "id"   : "идентификатор действия по расписанию" 

           ,"sublogin" : "владелец" 

           ,"name" : "название действия по расписанию" 

           ,"active" : "активность действия по расписанию" 

           ,"dt.from" : "дата начала действия" 

           ,"dt.upto" : "дата окончания действия" 

           ,"dailyrate" : "максимальное число запусков в день" 

           ,"totalrate" : "максимальное число запусков вcего" 

           ,"lastran" : "дата и время последнего запуска действия по расписанию" -- с точностью Ys. этот параметр доступен только на чтение

           ,"dailyruns" : "количество запусков за день" -- не текущий день, а за тот в который в lastran. этот параметр доступен только на чтение

           ,"totalruns" : "количество запусков всего" 

           ,"do" : {
                     -- выполняемое действие
                   }

           -- все списки возвращаются в полном виде. Если что-то при создании было указано как "ALL", то при чтении это будет список всех возможных значений.

           ,"minute" : [ список минут ]

           ,"hour" : [ список часов ]

           ,"day" : [ список дней месяца ]

           ,"weekday" : [ список дней недели ]

           ,"workday" : [ список признаков рабочести дня ]

           ,"reltype" : ...

           ,"relref" : ...
           }
}

Создание действия по расписанию

{

 "action" : "cron.create" 

 ,"name" : "название действия по расписанию

 ,"sublogin" : "владелец" -- только при работе основным логином
                          -- возможность явно указать владельца. при отсутствии - текущий логин
                          -- во всех других случаях устанавливается в текущий логин

 ,"active" : "активность действия по расписанию" -- 0 - не выполнять, 1 - выполнять при наступлении времени

 ,"dt.from" : "дата начала действия" -- не обязательно. дата (Ys) начала проверок на возможность запуска действия
                                     -- по умолчания - без ограничения

 ,"dt.upto" : "дата окончания действия" -- не обязательно. дата (Ys) начиная с которой проверки на возможность запуска действия не делаются
                                        -- по умолчания - без ограничения

 ,"dailyrate" : "" | 1+ -- максимальное число запусков в день. "день" это календарные сутки с 00:00 до 23:59. не обязательно
                        -- "" - не ограничено (по умолчанию)
                        -- число от 1 и больше - величина ограничения

 ,"totalrate" : "" | 1+ -- максимальное число запусков вcего. не обязательно
                        -- "" - не ограничено (по умолчанию)
                        -- число от 1 и больше - величина ограничения
 ,"do" : {
           -- выполняемое действие
           --
           -- вызов API без указания параметров авторизации
           --
           -- будет выполняться по указанному расписанию от имени пользователя указанного в sublogin
           --
           -- если такой пользователь на момент выполнения будет удалён или заблокирован, то выполнения не произойдёт
         }

  ,"minute" : [ список минут ] -- по которым срабатывает действие. от 0 до 55 с шагом 5

-- не обязательно. отсутствие трактуется как "ALL" 
-- вместо списка можно указать "ALL" что будет обозначать "все доступные значения".

  ,"hour" : [ список часов ] -- по которым срабатывает действие. от 0 до 23

  ,"day" : [ список дней месяца ] -- по которым срабатывает действие. от 1 до 31

  ,"weekday" : [ список дней недели ] -- по которым срабатывает действие. от 1 (Пн) до 7 (Вс)

  ,"workday" : [ список признаков рабочести дня ] -- по которым срабатывает действие. 0 - срабатывает в не рабочий день, 1 - срабатывает в рабочий день

  ,"reltype" : ...

  ,"relref" : ...
}

ответ

{

  <общие поля>

 ,"id" : "идентификатор созданного действия по расписанию" 

}

Изменение действия по расписанию

Если работа ведётся не основным пользователем, то вы можете менять только свои задания.

Обновляются только указанные в запросе поля.

{

 "action" : "cron.set" 

 ,"id"   : "идентификатор действия по расписанию" 

 ,"obj" : {

           "name" : "название действия по расписанию" 

          ,"sublogin" : "владелец" -- только при работе основным логином
                                   -- возможность сменить владельца

          ,"active" : "активность действия по расписанию" 

          ,"dt.from" : "дата начала действия" 

          ,"dt.upto" : "дата окончания действия" 

          ,"dailyrate" : "максимальное число запусков в день" 

          ,"totalrate" : "максимальное число запусков вcего" 

          ,"do" : {
                   -- выполняемое действие
                  }

          ,"minute" : [ список минут ] или "ALL" 

          ,"hour" : [ список часов ] или "ALL" 

          ,"day" : [ список дней месяца ] или "ALL" 

          ,"weekday" : [ список дней недели ] или "ALL" 

          ,"workday" : [ список признаков рабочести дня ] или "ALL" 

          ,"reltype" : ...

          ,"relref" : ...
          }

 ,"return_fresh_obj" : 0|1
}

ответ

{

  <общие поля>

}

Удаление действия по расписанию

Если работа ведётся не основным пользователем, то вы можете менять удалять только свои задания.

{

 "action" : "cron.delete" 

 ,"id" : "идентификатор действия по расписанию" 
}

ответ

{

  <общие поля>

}

Разовый запуск действия по расписанию

Активность, даты начала и окончания и назначенное время, количество запусков в день игнорируются.

Параметр lastran не обновляется.

Запускается от имени текущего логина.

{

 "action" : "cron.runonce" 

 ,"id" : "идентификатор действия по расписанию" 

}

ответ

{

  <общие поля>

,"result" : {
             результат выполнения
            }

}

вверх

Cобытийные действия / Триггерные рассылки

Описание

Введение

Cобытийные действия предназначены для автоматизации реакции системы в ответ на те или иные события происходящие с пользователем или вызванные его действиями.

Для задания автоматической реакции системы на те или иные действия/бездействия пользователя создайте "Последовательность" описывающую что ("Действия") в ответ на какие события ("Cобытия") в каком порядке ("Шаги","Варианты") должно происходить.

На каждом Шаге последовательности можно задать несколько сценариев развития событий создав несколько Вариантов описав какие События активируют какой вариант (один вариант могут активировать несколько разных действий) и задать какие Действия (одно или несколько) должны быть выполнены в каждом из вариантов.

Как простой частный случай с помощью событийных действий можно реализовать триггерные рассылки (автоответчики) - задайте на первом шаге условие начала последовательности и как действие - первое высылаемое письмо.
На следующий шагах задавайте интервал времени до высылки следующего письма и его высылку в действиях.

Для придания автоответчику интелектуальности можно, например, сочетать события "Клик" и "Прошло время" для выбора какое из писем высылать далее, действия "Изменить данные" что бы учитывать на какое из высланых писем пользователь среагировал, действия "Завершить последовательность" что бы прекратить высылать подписчику письма при отсутствии инетереса, действие "Перейти на другую последовательность" что бы сменить тематику в зависимости от его реакции.

В более сложных случаях можно организовать автоматические напоминания о забытых корзинах в интернет-магазинах, автоматическое изменение статуса покупателя в зависимости от числа покупок и даже квест-игры по электронной почте.

Обработка событий

Каждое событие произошедшее в системе асинхронно проверяется не подходит ли оно в какой-либо последовательности к её первому шагу или к текущему шагу уже находящегося на последовательности пользователя.

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

Подходящее событие может быть проигнорировано если пользователь или последовательно находятся в состоянии паузы.

Подходящее событие может не вызвать начало прождения пользователем последовательности если этому мешают настройки разовости и закрытости последовательности.

Удаление идентификатора подписчика из системы приведёт к прекращению его обработки во всех последовательностях. Однако не сразу, а при очередной обработке событий связанных с удалённым идентификатором.

Свойства последовательности

Однократность

Можно ли ещё раз начать прохождение последовательности, если она уже была пройдена или проходится в данный момент.

Параллельность

В данный момент не реализовано !

Можно ли одновременно проходить последовательность несколько раз и если можно, то при запуске нового прохождения прервать ли текущие

Закрытость

Новые участники не могут начать последовательность при наступлении подходящих событий.

Однако новый участник может быть добавлен вызовом sequence.member.start

Это полезно для тестирования. Отметьте последовательность закрытой на время её создания и тестирования, что бы ни кто из обычных пользователей не мог случайно начать её прохождение. А тестовых пользователей вы можете добавить через sequence.member.start

Ни как не влияет на прождение последовательности уже имеющимися участниками.

Пауза

Cобытия не учитываются и значит ни кто не продвигается по последовательности и не начинает её. Пропущенные события теряются.

Возобновляемость

Возобновление прохождения при увеличении количества шагов.

Участники, завершившие ранее прохождение последовательности путём дохождения до её последнего шага, автоматически возобновят её прохождение с шага, следующего за тем на котором они закончили.

Для одного участника возобновляется только одно, имеющее максимальный номер шага-окончиния, прохождение.

Не возобновляется участник и так проходящий последовательность в момент её удлиннения не смотря на параметр parallel.

вверх

События

Новая регистрация без подтверждения

Наступает когда пользователь первый раз вносится в базу и на это от него не требуется подтверждения регистрации.

Обратите внимание, что это событие должно быть в последовательности только первым и в другой позиции просто не сработает никогда по логике.

{

 "type" : "member.new.no-confirm" 

}

Новая регистрация c подтверждением

Наступает когда пользователь первый раз вносится в базу и от него требуется подтверждение регистрации.

Обратите внимание, что это событие должно быть в последовательности только первым и в другой позиции просто не сработает никогда по логике.

{

 "type" : "member.new.confirm" 

}

Подтверждение регистрации

Наступает когда пользователь подтверждает регистрацию.

{

 "type" : "member.confirm" 

}

Самостоятельное удаление регистрации

Наступает когда пользователь отписывается (т.е. запрещает дальнейшую высылку ему чего-либо)

{

 "type" : "member.unsubscribe" 

}

Прошло временя

Не может быть в списке у вариантов первого действия последовательности.

Наступает когда с момента попадания на данных шаг прошёл указаный интервал времени равный указанному количеству дней, часов и минут.

{

 "type" : "time.elapsed" 

,"interval" : "DDDDD hh:mm" или "hh:mm" или "mm" 
             -- DDDDD - до пяти цифр количества дней
             -- hh    - количество часов
             -- mm    - количество минут

}

Точное время

Не может быть в списке у вариантов первого действия последовательности.

Наступает когда текущее время совпало с заданными ожидаемыми.

Зачем ? Например удобно накопить желающих начать какой либо курс и разом начать его в удобный день высылкой первого письма.

Можно указать:

  1. месяц, день и время - сработает раз в год
  2. день и время - сработает раз в месяц если в нём есть такой день
  3. только время - сработает раз в день
  4. день недели - сработает раз в неделю в 0 часов 0 минут
  5. день недели и время - сработает раз в неделю в указанный момент
{

 "type" : "time.happened" 

,"weekday" : "номер дня недели" -- не обязятельно при наличии time
                     -- 1-6 - Пн-Сб, 7 - Вс
                     -- вызов sequence.steps.set дополнительно принимает 0 как Вс, но при записи преобразует в 7

,"time" : "дата и время" -- не обязательно при наличии weekday
                     -- YYYY - год
                     -- ММ - месяц
                     -- DD - день
                     -- hh - час
                     -- mm - минута

-- Допустимые сочетания дня недели, даты и времени:
-- "YYYY-MM-DD hh:mm" 
--         "DD hh:mm" 
--            "hh:mm" 
--  weekday
--  weekday + "hh:mm" 
}

Клик по ссылке

Наступает когда пользователь нажимает на ссылку в высылаемых ему письмах.

Рассылки должны выпускаться с преобразванием ссылок для учёта переходов, иначе клик системой замечен не будет. При высылки письма по действию "Выслать письмо" такое преобразование включается автоматически.

Клик по определённой ссылке в письме обычной рассылки ("свободный клик") приведёт к наступления события "Клик" во всех последовательностях где такое событие ожидается на первом шаге для этой ссылки и где состояние настроек последовательности позволяет ему начать её прохождение.

В результате свободного клика для кликнувшего пользователя в каждой начатой им последовательности будут выполнены предписанные там действия и он станет ждать событий на шаге 2.

Клик по определённой сслыки из письма высланого пользователю при исполнении действия "Выслать письмо" ("связаный клик") приведёт к событию "Клик" только в той последовательности из которой было выслано письмо и только если на текущем шаге пользователя ожидается такой клик. Состояние настроек последовательности или пользователя может повлиять на учёт клика.

В результате связаного клика для кликнувшего пользователя будут выполнены предписаные на его текущем шаге действия и он продвинется на следующий шаг.

{

 "type" : "click.link" 

,"url" : "ссылка" 

}

Изменение данных

Наступает когда при изменении данных пользователя удовлетворяются (или не удовлетворяются - в зависимости от параметра *.not) условия обоих фильтров если заданы оба и едниственного фильтра если задан один.

Фильтр БЫЛО проверяет данные до изменения. Фильтр СТАЛО - после изменений. Должен быть указан одни любой или оба фильтра сразу.

Если указан только фильтр БЫЛО, то пользователь удовлетворяет условия события, если его старые данные (до изменения) прошли проверку успешно, а новые (после изменения) - НЕ успешно. Т.е. данные перестали попадать под условие.

Если указан только фильтр СТАЛО, то пользователь удовлетворяет условия события, если его страные данные (до изменения) прошли проверку НЕ успешно, а новые (после изменения) - успешно. Т.е. данные стали попадать под условие.

Если указаны ОБА фильтра, то пользователь удовлетворяет условия события, если его старые данные (до изменения) успешно прошли проверку по фильтру БЫЛО, а новые (после изменения) успешно прошли проверку по фильтру СТАЛО. Т.е. данные перестали попадать под условие БЫЛО и стали попадать под условие СТАЛО.

Структура описания условий was.cond и new.cond аналогична структуре одного элемента условий фильтра у групп-фильтров.
Но не принимаются условия с типами AND, OR, group, stat.uni и PRF.

{

 "type" : "member.change" 

---------
-- фильтр "БЫЛО. может отсутствовать при наличии фильтра "СТАЛО" 

,"was.not" : 0|1 -- 0 - фильтр соблюдён если пользователь удовлетворяет условию
                 -- 1 - фильтр соблюдён если пользователь НЕ удовлетворяет условию

-- и одно из:

,"was.group" : "код группы" 

,"was.group.name" : "название группы" -- только при вызове get

-- или

,"was.cond" : { условие. при вызове get дополнительно aid.name и qid.name }

---------
-- фильтр "СТАЛО". может отсутствовать при наличии фильтра "БЫЛО" 

,"new.not" : 0|1 -- 0 - фильтр соблюдён если пользователь удовлетворяет условию
                 -- 1 - фильтр соблюдён если пользователь НЕ удовлетворяет условию

-- и одно из:

,"new.group" : "код группы" 

,"new.group.name" : "название группы" -- только при вызове get

-- или

,"new.cond" : { условие. при вызове get дополнительно aid.name и qid.name }

}

Второй вариант. Наступает когда данные изменились, но не важно как - просто предыдущее значение не равно новому.

По умолчанию производится строчное сравнение значений.

{

 "type" : "member.change" 

,"as_number" : 0|1 -- не обязательно. сравнивать значения как числа.

-- и одно из:

,"field" : {
            "aid" : "код анкеты" 
           ,"qid" : "код вопроса" 

            -- при вызове get дополнительно aid.name и qid.name
           }
--- или

,"field" : {
            "datakey" : "ключи данных" 
           }

}

Совпадение данных

Наступает когда пользователь переходит на данных шаг и его текущие данные попадают (или при match.not = 1 - не попадают) под описаное условие.

Применение - условные ветвления.

На шаге должен быть предусмотрены другие варианты действий иначе, если совпадения не будет, пользователь застрянет на этом шаге навечно.
Или в варианте с событием "Совпадение данных" должны быть альтернативные события которые могут продвинуть пользователя дальше.

Структура описания условий has.cond аналогична структуре одного элемента условий фильтра у групп-фильтров.
Но не принимаются условия с типами AND, OR, group, stat.uni и PRF.

{

 "type" : "member.match" 

,"match.not" : 0|1 -- 0 - совпадение если пользователь попал под условие
                   -- 1 - совпадение если пользователь НЕ попал под условие
-- одно из:

,"match.group" : "код группы" 

,"match.group.name" : "название группы" -- только при вызове get

или

,"match.cond" : { условие. при вызове get дополнительно aid.name и qid.name }
}

Форма заполнена

Наступает когда заполняется форма опросов.


 "type" : "form.filled" 

,"form.id" : код формы

,"form.name" : название формы -- только при вызове get

Форма подтверждена

Наступает когда пользователь подтверждает заполнение формы опросов.


 "type" : "form.completed" 

,"form.id" : код формы

,"form.name" : название формы -- только при вызове get

Целевая страница достигнута

Наступает когда пользователь достиг указанно целевой страницы


 "type" : "target.achieved" 

,"url" : "ссылка или маска ссылки" -- "*" заменяет любое количество символов, "?" - один символ

вверх

Действия

Ничего не делать

{

 "type" : "noop" 

}

Выслать письмо

Пользователю высылается персонализованное письмо на основе черновика выпуска рассылки.

Если указан черновик для sms или viber, то будет выслано sms или отправлено сообщение по номеру телефону связанному с email.

Если связанного номера телефона нет, то не произойдёт ничего.

Для привязки номера телефона заранее используйте member.head.attach

Обратите внимание, что от высланой sms не будет ни каких ответных действий (например кликов) и для продолжения прохождения последовательности на данном шаге необходимо иметь ещё какое-то действие которое может породить события или на следующем шаге среди ожидаемых событий должно быть какое-то безуловное (например "прошло столько-то времени")

{

 "type" : "send.letter" 

,"draft" : "код черновика рассылки" -- обязательно
                                    -- черновик должен иметь заполненный адрес отправителя

,"draft.name" : "название черновика" -- только при вызове get
}

Вызвать внешнюю ссылку

Указанная ссылка вызывается методом GET. Результат вызова игнорируется.

Вхождение символов EMAIL заменяется на адрес пользователя.

{

 "type" : "http.get" 

,"url"  : "http/https ссылка" 

}

Изменить данные

Данные пользователя изменяются указанным в формате образом или изменяется один ответ одной анкеты указанные прямо в описании.

В целом аналогично вызову member.update

{

 "type" : "member.update" 

 ,"fire_event" : 0 | 1 -- при выполнении записать событие Изменение Данных (member.change)
                       -- по умолчанию 0 - не записывать

 ,"if_has" : что делать, если изменяемый пункт анкеты уже заполнен

             -- "overwrite" - заменить старое значение новым из формата

             -- "merge"     - объединить старое и новое значения

             -- "skip"      - оставить старое значение

 ,"if_hasnt" : что делать, если изменяемый пункт анкеты ещё не заполнен

             -- "set"  - заполнить указанным в формате значением

             -- "skip" - оставить пункт незаполненным

-- одно из

,"format" : "код формата" 

,"format.name" : "название формата" -- только при вызове get

или

,"field" : {

             "aid" : "код анкеты" 

            ,"qid" : "код ответа" 

            ,"answer" : "значение ответа используемое при заполнении" 

            ,"aid.name" : "название анкеты" -- только при вызове get

            ,"qid.name" : "название анкеты" -- только при вызове get
            }

}

Внести в группу-список

Вносит пользователя в указанную группу-список если его там ещё нет.

{

 "type" : "group.in" 

,"group" : "код группы" 

,"group.name" : "название группы" -- только при вызове get
}

Удалить из группы списка

Удаляет пользователя из указанной группы-списока если он там есть.

{

 "type" : "group.out" 

,"group" : "код группы" 

,"group.name" : "название группы" -- только при вызове get
}

Запустить параллельно ещё последовательность

Параллельно с текущей запускается указанная последовательность с участием этого пользователя.

Запуску погут помешать настройки запускаемой последовательности, но в текущей последовательности это ни на что не повлияет.

{

 "type" : "sequence.start" 

,"sequence : "код последовательности" -- при отсутвии запускает саму себя ещё раз

,"sequence.name" : "название последовательности" -- только при вызове get
}

Остановить последовательность

Все участия пользователя в указанной последовательности (если имеются) принудительно завершаются.

{

 "type" : "sequence.stop" 

,"sequence : "код последовательности" -- при отсутвии останавливает саму себя

,"sequence.name" : "название последовательности" -- только при вызове get
}

Уйти в другую последовательность

Текущая последовательность принудительно завершается и запускается указанная новая последовательность с участием этого пользователя.

Запуску погут помешать настройки запускаемой последовательности, но в текущей последовательности это ни на что не повлияет.

{

 "type" : "sequence.goto" 

,"sequence : "код последовательности" -- при отсутвии перезапускает саму себя

,"sequence.name" : "название последовательности" -- только при вызове get
}

вверх

Список последовательноcтей

{

 "action" : "sequence.list" 

}

ответ

{

 <общие поля>

,"list" : [

            {

             "id" : "код последовательности" 

            ,"name" : "название" 

            }

            ...

           ]

}

вверх

Cоздать последовательность

{

  "action" : "sequence.create" 

 ,"name" : "название последовательности" -- обязательно

 ,"onlyonce" : 0 | 1 -- последовательность однократна. по умолчанию 0
                     -- 0 - нет
                     -- 1 - да

 ,"parallel" : -1 | 0 | +1 -- параллельность последовательности. по умолчанию 0.
                           -- *В данный момент не реализовано и всегда 0 !*
                           -- 0 - нет
                           -- +1 - да
                           -- -1 - да, с прерыванием текуших

 ,"closed" : 0 | 1 -- закрытость для новых участников. по умолчанию 0
                   -- 0 - нет
                   -- 1 - да

 ,"resume_on_growing" : 0 | 1 -- возобновить прохождение при увеличении количества шагов. по умолчанию 0
                              -- 0 - нет
                              -- 1 - да

 ,"pause" :  0 | 1 -- отстановка последовательности. по умолчанию 0
                   -- 0 - нет
                   -- 1 - да

}

ответ

{

 <общие поля>

 , "id" : "код созданой последовательности" 

}

вверх

Прочитать последовательность

{

  "action" : "sequence.get" 

 , "id" : "код последовательности" 

}

ответ

{

 <общие поля>

 ,"obj" : {

             ,"id" : "код последовательности" 

             ,"name" : "название последовательности" 

            ,"onlyonce" : "однократность последовательности" 

            ,"parallel" : "параллельность последовательности" 

            ,"closed" : "недоступность для новых участников" 

            ,"resume_on_growing" : "возобновить прохождение при увеличении количества шагов" 

            ,"pause" : "отстановка последовательности" 
           }
}

вверх

Изменить последовательность

Изменяются только явно заданые в запросе поля

Не заданые - остаются как были

{

  "action" : "sequence.set" 

 , "id" : "код последовательности" 

 ,"name" : "название последовательности" -  необязательный

 ,"onlyonce" : "однократность последовательности -  необязательный

 ,"parallel" : "параллельность последовательности" -  необязательный

 ,"closed" : "недоступность для новых участников" -  необязательный

 ,"resume_on_growing" : "возобновить прохождение при увеличении количества шагов" 

 ,"pause" : "отстановка последовательности" -  необязательный

 ,"return_fresh_obj" : "нужно вернуть данные объекта -- да, нет ( 1 | 0 )" 

  по умолчанию, считается "return_fresh_obj" : "0" 

}

ответ

{

 <общие поля>

}

если "return_fresh_obj" : "1", то ответ -- как на запрос чтения sequence.get

вверх

Удалить последовательность

{

  "action" : "sequence.delete" 

 , "id" : "код последовательности" 

}

ответ

{

 <общие поля>

}

вверх

Получить список шагов

Параметр "номер шага" в данный момент введён для поддержки возможных будущих расширений (например возможности изменения шагов по одному)

В данный момент, при сохранении нового списка шагов они упорядочиваются по возрастранию номера шага как он указан в sequence.list.set, но внутренне все шаги перенумеровываются начиная с 1 и в sequence.list.get уже фигурируют под этими номерами.

{

  "action" : "sequence.steps.get" 

 , "id" : "код последовательности" 

}

ответ

{

 <общие поля>

 ,"list" : { -- описание шагов последовательности

             "номер шага" : [ -- список вариантов шага

                               { -- вариант
                                "event" : [ -- список ожидаемых событий
                                            { описание события }
                                           ,{ описание события }
                                           .....
                                          ]

                                ,"action" : [ -- список действий выполняемых при наступлении любого из событий
                                             { описание действия }
                                            ,{ описание действия }
                                            .....
                                            ]
                               }

                             ,{ вариант }

                             ,{ вариант }

                             .....

                             ]

            ,"номер шага" : [
                              ....
                            ]

            ......

           ]

}

вверх

Установить список шагов

Изменение списка шагов полностью заменяет текущий список на новый

Если новый содержит меньше шагов, то пользователи оказавшиеся из-за их текущего положения вне списка автоматически заканчивают проходнение.

Остальные пользователи сохраняют номер шага на котором находятся, но "смысл шага" (события, действия, варианты) может стать полностью новым в соответствии с устанавливаемым списком.

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

Параметр "номер шага" - целое число - в данный момент ввёдён для поддержки возможных будущих расширений (например возможности изменения шагов по одному)

В данный момент, при сохранении нового списка шагов они упорядочиваются по возрастранию номера шага как он указан в sequence.list.set, но внутренне все шаги перенумеровываются начиная с 1 и в sequence.list.get уже фигурируют под этими номерами.

{

  "action" : "sequence.steps.set" 

 , "id" : "код последовательности" 

 , "list" : {

              описание шагов последовательности
              как в sequence.steps.get

            }

}

ответ

{

 <общие поля>

}

вверх

Начать прохождение последовательности

Вызов асинхронный !

Указанные пользователи начинают прохождение указанной последовательности как-будто произошло одно из событий её первого шага.

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

Настройки последовательности могут влиять на возможность того или иного участника начать её прохождение.


{

  "action" : "sequence.member.start" 

 , "id" : "код последовательности" 

-- обязателен один из параметров:

 ,"email" : "адрес подписчика" 

или

 ,"list" : [

            "адрес подписчика" 

           ,"адрес подписчика" 

            ........

           ]

или

 ,"group" : код группы для получения списка

}

ответ


 <общие поля>

,"track.id" : "номер трекера" 

вверх

Приостановить прохождение последовательности

Вызов асинхронный !

Указанные пользователи приостанавливаю прохождение указанной последовательности и возобновят его при вызове sequence.member.resume


{

  "action" : "sequence.member.pause" 

 , "id" : "код последовательности" 

-- обязателен один из параметров:

 ,"email" : "адрес подписчика" 

или

 ,"list" : [

            "адрес подписчика" 

           ,"адрес подписчика" 

            ........

           ]

или

 ,"group" : код группы для получения списка

}

ответ


 <общие поля>

,"track.id" : "номер трекера" 

вверх

Возобновить прохождение последовательности

Вызов асинхронный !

Указанные пользователи возобновляют прохождение указанной последовательности


{

  "action" : "sequence.member.resume" 

 , "id" : "код последовательности" 

-- обязателен один из параметров:

 ,"email" : "адрес подписчика" 

или

 ,"list" : [

            "адрес подписчика" 

           ,"адрес подписчика" 

            ........

           ]

или

 ,"group" : код группы для получения списка

}

ответ


 <общие поля>

,"track.id" : "номер трекера" 

вверх

Прервать прохождение последовательности

Вызов асинхронный !

Указанные пользователи завершают прохождение указанной последовательности на каком бы её шаге не находились

{

  "action" : "sequence.member.stop" 

 , "id" : "код последовательности" 

-- обязателен один из параметров:

 ,"email" : "адрес подписчика" 

или

 ,"list" : [

            "адрес подписчика" 

           ,"адрес подписчика" 

            ........

           ]

или

 ,"group" : код группы для получения списка

}

ответ


 <общие поля>

,"track.id" : "номер трекера" 

вверх

Участие пользователя в последовательностях

{

  "action" : "sequence.member.membership" 

 , "id" : "код последовательности" -- не обязательно

 , "email" : "адрес пользователя" 

}

ответ

{

 <общие поля>

 ,"list" : {
            "код последовательности" : [ -- список описаний шагов
                                          -- структура одно элемента описана в вызове sequence.member.list
                                          { ... }
                                         ,{ ... }
                                         ....
                                        ]

           ,"код последовательности" : [ -- список описаний шагов
                                          { ... }
                                         ,{ ... }
                                         ....
                                        ]
            .....
           }

вверх

Формы опросов

Формы опросов предназначены для получения информации от пользователей c сохранением её в анкеты.

Предусмотрен механизм верфикации заполнения формы при котором данные попадают сначала в первичную анкету (анкету-формы) и только после подтверждения в основные анкеты (анкеты-хранилища).

Данные анкеты-формы и анкет-хранилищ всегда доступны для просмотра и использования в рассылках. В зависимости от того, на сколько вам важна верификая заполнения вы можете пользоваться в работе как сразу первичными данными из анкеты-формы, так и только подтверждёнными даными из анекет-хранилищ.

Форма размещается клиентом у себя или созданой на сервисе входной странице landing-page.

После заполнения пользователем формы введённые данные сохраняются в указанную начальную анкету-формы, ему высылается fill-letter и он попадает на welcome-page (или welcome-redirect).

После подверждения заполнения формы (как описано в высланом ранее fill-letter), данные переносятся (если запланировано) из анкеты-формы по описаным в ней правилам в анкеты-хранилища, пользователю высылается welcome-letter и он попадает на fill-page (или fill-reedirect).

Заполнение и подтверждение формы так же пораждают тригерные события для расширения логики обработки. При переносе данных в анкеты-хранилища так же пораждается событие "Данные пользователя изменились".

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

Через вызов stat.uni можно получить статистику заполнения форм.

На страницах отображаемых пользоветеля, в письмах высылаемых пользователя, в ссылках перенаправления поддреживается персонализация на основе данных пользователя и данных заполненой им формы. Синтаксис аналогичен выпуску рассылки.

Не обязательно использовать код формы получаемый от нас. Заполнять формы адаптировав их под свои нужны и свой сайт можно с помощью API описанного по адресу https://sendsay.ru/api/

Список форм


{

  "action" : "form.list" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"name" : "название" 

            ,"state : "состояние" 

            ,"create.date" : "дата и время создания" -- Ys

            ,"update.date" : "дата и время последнего изменения" -- Ys
            }

            ...

           ]

}

Чтение формы


{

  "action" : "form.get" 

 ,"id" : "идентификатор формы" 

}

ответ


{

  <общие поля>

  "obj" : {

      "id" : "идентификатор формы" 

     ,"name" : "название" 

     ,"create.date" : "дата и время создания" -- Ys

     ,"update.date" : "дата и время последнего изменения" -- Ys

     ,"state" : 0|1 -- состояние: 0 - форма отключена, 1 - активна

     ,"preview" : 0|1 -- при возврате подписчика к форме из-за ошибки заполнения заполнять её ранее введёнными данными

     ,"only_once" : 0|1 -- форма заполняется подписчиком только один раз

     ,"anketa" : анкета-форма в которую собираются данные

     ,"group" : группа-список в которую попадают заполнившие форму. не обязательно

     ,"landing" : {

                   "webpage" : id веб-страницы служащей шаблоном для отрисовки landing-page. не обязательно

                  }

     ,"fill" : {
                "draft" :id черновика высылаемого как fill-letter. не обязательно

                -- или одно из или вообще ничего:

               ,"webpage" : id веб-страницы служащей шаблоном для отрисовки fill-page

               ,"link" : id ссылки для fill-redirect

               }

     ,"welcome" : {
                "draft" :id черновика высылаемого как welcome-letter. не обязательно

                -- или одно из или вообще ничего:

                "webpage" : id веб-страницы служащая шаблоном для отрисовки welcome-page

               ,"link" : id ссылки для welcome-redirect

               }

          }

    ,"notify" : {
                  "email" : [ список адресов ]

                 ,"draft" : id черновика
                 }

     ,"reltype" : ...

     ,"relref" : ...
    }
}

Создание или изменение формы

При изменении существующей формы изменяются только те поля, которые явно указаны в запросе.

Для удаление не обязательного параметра используйте пустое значение.


{

  "action" : "form.set" 

  ,"obj" : {

     ,"name" : "название" 

     ,"state" : 0|1 -- состояние: 0 - форма отключена, 1 - активна

     ,"anketa" : анкета-форма в которую собираются данные

     ,"preview" : 0|1 -- при возврате подписчика к форме заполнять её ранее введёнными данными
                      -- 0 - нет (по умолчанию)
                      -- 1 - да

     ,"only_once" : 0|1 -- форма заполняется подписчиком только один раз (0 - нет, 1 - да)

     ,"group" : группа-список в которую попадают заполнившие форму

     ,"landing" : {

                   "webpage" : id веб-страницы служащей шаблоном для отрисовки landing-page. не обязательно

                  }

     ,"fill" : {
                "draft" :id черновика высылаемого как fill-letter. не обязательно

                -- ответ пользователю после заполнения. одно из:

                  -- или отрисовка страницы (параметр link стирается)

               ,"webpage" : id веб-страницы служащей шаблоном для отрисовки fill-page

                  -- или перенаправление (параметр webpage стирается)

               ,"link" : id ссылки для fill-redirect. в ссылке поддерживается такая же персонализация к как и на веб-странице

                  -- или станица с текстом по умолчанию (оба параметра стираются)

               ,"webpage" : "" 

               ,"link" : "" 

               }

     ,"welcome" : {

                "draft" :id черновика высылаемого как welcome-letter. не обязательно

                -- ответ пользователю после подтверждения заполнения формы. одно из:

                  -- или отрисовка страницы (параметр link стирается)

               ,"webpage" : id веб-страницы служащей шаблоном для отрисовки fill-page

                  -- или перенаправление (параметр webpage стирается)

               ,"link" : id ссылки для fill-redirect. в ссылке поддерживается такая же персонализация к как и на веб-странице

                  -- или станица с текстом по умолчанию (оба параметра стираются)

               ,"webpage" : "" 

               ,"link" : "" 

               }

           }

     -- извещать о заполнении/подтверждении формы указаные адреса высылкой письма
     -- или полностью отсутствует или указан как минимум один адрес и номер черновика
     -- для удаления ранее настроенного извещения укажите "notify" : {}

     ,"notify" : {
                  "email" : [ список адресов ]

                 ,"draft" : id черновика
                 }

     ,"reltype" : ...

     ,"relref" : ...
    }

 -- необязательные

  ,"id" : "идентификатор формы" -- если не указан, создается новая

  ,"return_fresh_obj": "" -- вернуть объект в формате form.get

}

ответ


{

 <общие поля>

 ,obj  { ... } -- объект в формате form.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление формы


{

  "action" : "form.delete" 

  ,"id" : "идентификатор формы" 

}

ответ


{

 <общие поля>

}

Получение кода формы для установки на сайте


{

  "action" : "form.source" 

  ,"id" : "идентификатор формы" 

  ,"js" : 0|1 -- генерировать код с учётом использования java-script
              -- не всегда возможно разместить код с java-script на не своём сайте
              -- в большестве случаев java-script не будет работать в письмах
              --
              -- при использовании java-script код будет пытаться максимально оставлять
              -- пользователя на странице где он заполняет форму выводя все сообщения
              -- по возможности во всплывающих окнах
 ,"channel" : "site|email" -- где будет размещена форма, сайт или письмо. по умолчанию - site
}

ответ


{

 "html" : "код формы" 

,"url" : "полный адрес входной страницы формы" -- #6025 [BASE]/
,"url.fill" : "полный адрес страницы, отображаемой при окончании заполнения" 
,"url.welcome" : "полный адрес страницы, отображаемой при подтверждении заполнения" 
}

Перенос значений из анкеты-формы в анкеты-хранилища

Этот вызов переносит ответ пользователя из анкеты-формы в анкеты-хранилища не дожидаясь, пока он сам подтвердит заполнение,

Этот вызов делает ровно то что написано, он не подтвердит регистрацию нового пользователя за него и не породит событие "Форма заполнена".


{

  "action" : "form.transfer" 

  ,"id" : "идентификатор формы" 

  ,"email" : "адрес, данные которого перенести" 

}

ответ


{

 <общие поля>

}

вверх

Хранилище файлов

Хранилище имеющееся в вашем распоряжении позволяет:
- хранить в нём изображения и файлы на которые вы ссылаетесь из писем или прикрепляете к ним (хранилище картинок)
- получать от системы отчёты которые вы заказывали с параметром "сохранить на сервере" (хранилище отчётов)
- загружать файлы данных для импорта и Экспресс-Выпуска в недоступное публично место (хранилище загрузок)

Доступ к Хранилищу осуществляется по данному api, по обычному ftp, по ftps, по sftp, по ссылкам со схемой rfs://

Для доступа по api используйте описанные ниже методы.

Доступ по ftp осуществляется через ftp.sendsay.ru. При этом ftp-логин это или просто ваш общий логин или общий логин и личный логин записанные через "/", а ftp-пароль - ваш пароль. Так как ftp работает по не шифрованному протоколу, то рекомендуется создавать для работы с ним отдельного пользователя с максимально ограниченными правами.

Доступ по ftps/sftp осуществляется через ftps.sendsay.ru. Данные для доступа необходимо получить в Службе Поддержки.

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

К хранилищу отчётов и хранилищу загрузок публичного доступа по http/ftp/ftps/sftp нет.

К хранилищу загрузок есть дополнительно доступ по rfs://upload/путь-до-файла, что бы на загруженные файлы можно было использовать при импорте списков и рассылке Экспресс-выпуска

Параметр domain указывает на используемую область - хранилище картинок (image), хранилище отчётов (report), хранилище загрузок (upload).

Параметр path используемый в вызовах это абсолютный путь по хранилищу начинающийся со слэша.

Cписок файлов

Если path указывает на файл, то возвращается информация только о нём - можно использовать для проверки существования этого файла.

Если path указывает на каталог, то возвращается список его файлов и подкаталогов.


{

 "action" : "rfs.list" 

-- или список файлов

 ,"domain" : "image|report|upload" 

 ,"path" : "полный путь - каталог или файл" 

-- или список доступных хранилищ

 ,"domain" : "" 

 ,"path" : "/" 

}

ответ


{

 <общие поля>

 ,"type" : null | "dir" | "file" -- тип того, на что показывал path
                                 -- null   - нет такого каталога или файла
                                 -- "dir"  - это каталог
                                 -- "file" - это файл

 -- для type = null - отсутствует
 -- для type = dir  - cписок файлов в каталоге
 -- для type = file - список из одной записи описывающий запрошенный файл

 ,"file" : [

            {
              "name" : название

             ,"path" : полный путь c названием

             ,"size" : размер

             ,"date" : дата изменения (в формате yyyy-mm-dd hh:mm:ss)

             ,"url" : публичная веб-ссылка для доступа. только если domain=image
            }

            ......... 

           ]

 -- только для type = dir -- список подкаталогов

 ,"dir" : [

           {
             "name" : название

            ,"path" : полный путь с названием

            ,"date" : дата изменения (в формате yyyy-mm-dd hh:mm:ss)

            ,"url" : публичная веб-ссылка для доступа. только если domain=image
           }

           ......... 

          ]

} 

вверх

Переименовать файл или каталог

Вызов позволяет переименовать файл или каталог.

Путь path.to должен отсутствовать.

{

 "action" : "rfs.rename" 

 ,"domain" : "image|upload" 

 ,"path" : "полный путь который переименовывается" 

 ,"path.to" : "полный путь в который переименовывается" 

}

ответ
{

 <общие поля>

} 

Получить файл


{

 "action" : "rfs.file.get" 

 ,"domain" : "image|report|upload" 

 ,"path" : "полный путь с названием файла" 

 ,"encoding" : "желаемая кодировка данных в ответе" -- пусто или не указана - обычная Unicode/UTF-8
                                                    -- base64 - содержимое будет возвращено в base64
}

ответ


{

 <общие поля>

 ,"data" : "содержимое файла" -- предполагается что файл двоичный и :
                              -- без заказа кодировки значение октетов 0x00 - 0xFF отображено как U+0000-U+00FF
                              -- при заказе base64 данные будет возвращены в base64

} 

вверх

Записать файл

Только для domain = image и upload


{

 "action" : "rfs.file.put" 

 ,"domain" : "image|upload" 

 ,"path" : "полный путь с названием файла (отсутствующие попутные подкаталоги НЕ создаются автоматически)" 

 ,"data" : "содержимое файла" -- предполагается что файл двоичный и:
                              -- без указания кодировки значение октетов 0x00 - 0xFF заданы как U+0000-U+00FF
                              -- при указании base64 данные считаются закодированы в base64

 ,"encoding" : "кодировка данных" -- пусто или не указана - обычная Unicode/UTF-8
                                  -- base64 - содержимое data закодировано в base64
}

ответ


{

 <общие поля>

 ,"url" : публичная веб-ссылка для доступа. только если domain=image

} 

вверх

Удалить файл


{

 "action" : "rfs.file.delete" 

 ,"domain" : "image|report|upload" 

 ,"path" : "полный путь с названием файла" 

}

ответ


{

 <общие поля>

} 

вверх

Cоздать каталог

Только для domain = image и upload


{

 "action" : "rfs.dir.make" 

 ,"domain" : "image|upload" 

 ,"path" : полный путь с названием каталога (отсутствующие попутные подкаталоги создаются автоматически)" 

}

ответ


{

 <общие поля>

 ,"url" : публичная веб-ссылка для доступа. только если domain=image

} 

вверх

Удалить каталог

Только пустой каталог.


{

 "action" : "rfs.dir.delete" 

 ,"domain" : "image|report|upload" 

 ,"path" : "полный путь с названием каталога" 

}

ответ


{

 <общие поля>

} 

вверх

Кампании

Кампании позволяют объединять разные объекты для удобства и для упрощения получения сводной статистики.

Cписок кампаний

{

 "action" : "campaign.list" 

}

ответ

{

  <общие поля>

 ,"list" : [

             {

                "id"   : "идентификатор кампании" 

               ,"name" : "название кампании" 

             }

             ...

           ]

}

Чтение кампании

{

 "action" : "campaign.get" 

 ,"id" : "идентификатор кампании" 

}

ответ

{

  <общие поля>

 ,"obj" : {
           "id"   : "идентификатор кампании" 

           ,"name" : "название кампании" 

           ,"descr" : "описание кампании" 

           }
}

Создание/Изменение кампании

При изменении существующей кампании обновляются только указанные в запросе поля.

{

 "action" : "campaign.set" 

 ,"id"   : "идентификатор кампании" -- при отсутствии создаётся новая кампания

 ,"obj" : {

           "name" : "название кампании" -- при создании обязательно

          ,"descr" : "описание кампании" 

          }

 ,"return_fresh_obj" : 0|1
}

ответ

{

  <общие поля>

 ,"id"   : "идентификатор кампании" -- при cоздании

}

Удаление кампании

{

 "action" : "campaign.delete" 

 ,"id" : "идентификатор кампании" 
}

ответ

{

  <общие поля>

}

Cписок объектов кампании

{

 "action" : "campaign.member.list" 

,"id" : "идентификатор кампании" 

}

ответ

{

  <общие поля>

,"list" : [

           {

            "type" : "тип объекта" 

           ,"id"   : "идентификатор объекта" 

           }

          ..............
          ]
}

Добавить объект в кампанию

Объект может входить в состав нескольких кампаний.

{

 "action" : "campaign.member.add" 

,"id" : "идентификатор кампании" 

,"obj" : {
          "type" : "issue|cron|split|form|sequence|linkgroup|datarow" -- тип объекта

         ,"id"   : "идентификатор объекта" 
         }
}

ответ

{

  <общие поля>

}

Удалить объект из кампании

{

 "action" : "campaign.member.delete" 

,"id" : "идентификатор кампании" 

,"obj" : {
          "type" : "тип объекта" 

         ,"id"   : "идентификатор объекта" 
         }
}

ответ

{

  <общие поля>

}

вверх

Ряды данных

Ряд Данных предназначен для хранения статистики из внешних источников.

Ряд Данных позволяет хранит наборы пар (дата,число)

Дата - от YY до Yh, уникальна в Ряде Данных.

Число - целое

Пополнять Ряд Данных можно задавать непосредственно из данных вызова, из csv- или xlsx-файлов с указание по какой ссылке их забирать, из статистических данных Google Analitics.

Использование Рядов Данных в вызове Универсальной Статистики в сочетании с выборкой данных по выпуску рассылки (клики, чтения, доставки) позволит оценить, например, изменение посещаемости после рассылки.

Список рядов данных

{ 

 "action" : "datarow.list" 

}

ответ

{

  <общие поля>

 ,"list" : [

             { 

                "id"   : "идентификатор рядя данных" 

               ,"name" : "название ряда данных" 

               ,"prec" : "точность даты ряда данных" 

               ,"ga.bind" : "привязка к GA" 

               ,"reltype" : ...

               ,"relref" : ...
             }

             ...

           ]

}

Чтение ряда данных

{ 

 "action" : "datarow.get" 

 ,"id" : "идентификатор ряда данных" 

}

ответ

ответ

{

  <общие поля>

 ,"obj" : { 
           "id"   : "идентификатор рядя данных" 

           ,"name" : "название ряда данных" 

           ,"prec" : "точность даты ряда данных" 

           ,"ga.bind" : "привязка к GA" 

           ,"ga.request" :  {
                             параметры запроса к Google Analictics
                            }

            ,"reltype" : ...

            ,"relref" : ...
           }
}

Создание ряда данных

{ 

 "action" : "datarow.create" 

 ,"name" : "название ряда данных 

 ,"prec" : "точность даты ряда данных" -- "YY" - год, "YM" - год-месяц, "YD" - год-день, "Yh" - год-час
                                       -- точность даты указывается при создании и в дальнейшем изменена быть не может

 -- не обязательная привязка к Google Analitics

 ,"ga.bind" : "id авторизации" -- ссылка на объект authext хранящий авторизацию

 ,"ga.request" :  { -- игнорируется при пустом или отсутсвующем ga.bind

                    -- параметры запроса к Google Analictics для пополнения ряда данных через Сore Reporting API
                    -- https://developers.google.com/analytics/devguides/reporting/core/v3/reference
                    --
                    -- запрос должен возвращать в первой колонке дату, во второй - значение
                    -- обратите внимание, что GA возвращает дату с точностью YD и, следовательно, может использовать
                    -- только если точность ряда данных YD и меньше
                    --
                    -- автоматические запросы к GA вы можете настроить на удобное вам время с удобной частотой через Действия по расписанию cron.*

                   "ids" : "...." 
                  ,"metrics" : "...." 
                  ,"dimensions" : "...." 
                  ,"segment" : "...." 
                  ,"filters" : "...." 
                  }

 ,"reltype" : ...

 ,"relref" : ...
}

ответ

{

  <общие поля>

 ,"id" : "идентификатор созданного ряда данных" 

}

Изменение ряда данных

Обновляются только указанные в запросе поля

Точность ряда данных изменить нельзя

{ 

 "action" : "datarow.set" 

 ,"id" : "идентификатор ряда данных" 

 ,"name" : "новое название ряда данных 

 ,"ga.bind" : "новая авторизация к GA авторизации" -- укажите пусто что бы удалить привязку к GA

 ,"ga.request" : { новый запрос к GA } -- игнорируется при пустом или отсутсвующем ga.bind

 ,"reltype" : ...

 ,"relref" : ...
}

ответ

{

  <общие поля>

}

Удаление ряда данных

{ 

 "action" : "datarow.delete" 

 ,"id" : "идентификатор ряда данных" 
}

ответ

{

  <общие поля>

}

Внесение записей в ряд данных

Вызов позволяет внести, изменить или удалить записи.

Одна запись состоит из пары "дата" (идентифицирует запись в данном ряду данных) и соответствующего дате "числа".

Дата может задавать в любой точностью не меньшей чем указана при создании ряда данных.

Формат даты - YYYY-MM-DD hh:mm:ss или YYYYMMDDhhmmss

Лишние компоненты даты отбрасываются.

При наличии в данных нескольких записей для одной даты, результатом будет обработка последней.

Число - положительное или отрицательное число или ноль или пусто. Дробная часть, при наличии, отбрасывается.

Запись для отсутствующей ещё даты создаётся автоматически.

Запись для уже существующей даты изменяет значение на новое, если число не пусто, или удаляется, если задано пустое значение.

При указании файла или ссылки на файл поддерживается содержимое сжатое zip.

Если файл - таблица xlsx, то данные берутся с первого листа. Одна строка - одна запись. По умолчанию первая колонка интерпретируется как дата, вторая как число.

Иначе файл считается текстовым (csv) с попыткой автоматически определить разделитель из возможных (запятая, точка с запятой, вертикальная черта, табуляция). Одна строка - одна запись. Первая колонка - дата, вторая число.

Если Ряд Данных связан с Google Analitics, то он пополняется автоматически. Но можно пополнить его и по своему желанию использовав данный вызов в варианте с ga.

Положение колонки с датой и числом для любого способа задания данных можно указать явно через dt.index и value.index, если они отличаются от 0 и 1. Нумерация с нуля.

{ 

 "action" : "datarow.load" 

,"id" : "идентификатор ряда данных" 

,"dt.index" : "индекс колонки с датой" --  по умолчанию 0

,"value.index" : "индекс колонки с числом" -- по умолчанию 1

-- или получение данных из Google Analitics (требуется заранее заполнить ga.bind и ga.request

  "ga" : { -- указанные тут параметры сильнее параметров сохранённых в ga.request
          "start-date" : "YYYY-MM-DD | today | yesterday | NdaysAgo | minimum" 

         ,"end-date" : "YYYY-MM-DD | today | yesterday | NdaysAgo" 

         ,прочие параметры которые может понять Google Analitics
         }

-- или получения данных из внешнего файла

  "url" : "ссылка на файл" 

-- или явное задание структуры данных

  "list" : [
            [ "дата-1", "число-1"]
           ,[ "дата-2", "число-2"]
           ,[ "дата-3", "число-3"]
            ......
           ]

-- или задание данных содержимым файла

  "list" : "содержимое файла" 

}

ответ

{

  <общие поля>

,"count" : число добавленных, изменённых или удалённых записей

,"warnings" : [ описание ошибочных строк проигнорированных при обработке - причина и номер строки ]
}

Массовое удаление записей ряда данных

Альтернатива удалению записей по одной через datarow.load.

Удаляются все записи попадающие в указанный диапазон дат и значений.

В целях безопасности пустой фильтр игнорируется (формально он означал бы удаление всего).

Для удаления всех записей ряда данных используйте filter со специальным значением "ALL".

{ 

 "action" : "datarow.clean" 

,"id" : "идентификатор ряда данных" 

-- одно из

,"filter" : [
             фильтр как для stat.uni по полям datarow.record.dt и datarow.record.value
            ]

-- или

,"filter" : "ALL" 

}

ответ

{

  <общие поля>

,"count" : "количество удалённых записей" 
}

вверх

Ленты Новостей

Список лент

{
 "action" : "lenta.list" 
}

ответ

{
 "list" : [
            {
             "id" : номер ленты
            ,"name" : название ленты

  -- если лента привязана к подписчику

            ,"member" : "получатель" 
            }

           ...........

          ]
}

Прочитать ленту

{
 "action" : "lenta.get" 

."id" : номер ленты
}

ответ

{
 "obj" : {
          "id" : номер ленты

         ,"name" : название ленты

         ,"source" : [ -- информация об RSS/Atom каналах входящих в ленту

                       {
                        "url" : "адрес канала" 
                       ,"dt.fetched" : "YYYY-MM-DD hh:mm:ss" -- дата последнего чтения канала
                       ,"dt.updated" : "YYYY-MM-DD hh:mm:ss" -- дата последнего пополнения новостей из канала
                       ,"error" : {
                                   "count": количество ошибок работы с каналом
                                  ,"dt"   : "YYYY-MM-DD hh:mm:ss" - дата последней ошибки
                                  ,"str"  : описание послденей ошибки
                                  }

                       }
                       ...
                     ]          

    -- если лента привязана к подписчику

         ,"member" : "адрес получателя" 

         ,"decor"  : "идентификатор" -- идентификатор информационного письма или черновика выпуска который
                                     -- будет использоваться при выпуске накопившихся новостей

         -- расписание выпусков
         ,"schedule" : {
                        -- часы по которым высылать письма
                        "hour" : [ час, час, ... ]

                        -- номера дней недели по которым высылать письма
                        -- от 1 (Пн) до 7 (Вс)
                       ,"weekday" : [ номер, номер, ... ]
                       }
         }
}

Создать/изменить ленту

Создает ленту (при отсутствии id) или изменяет некоторые её параметры.

{
 "action" : "lenta.set" 

 ,"obj" : {

       "name" : "название ленты" 

      ,"member" : "адрес получателя" -- только при создании. измененить нельзя

-- для лент привязаных к получателю

      ,"decor"  : "идентификатор информационного письма или черновика выпуска" 

      -- расписание выпусков
      -- если параметр полностью отсутствует, то с 8 по 18 с понедельника по пятницу
      ,"schedule" : {
                     -- часы по которым высылать письма. если не указано, то с 8 по 18
                     "hour" : [ час, час, ... ]

                     -- номера дней недели по которым высылать письма, если не указано, то с Пн по Пт
                     -- от 1 (Пн) до 7 (Вс)
                    ,"weekday" : [ номер, номер, ... ]
                    }

   }

-- необязательные

 ,"id" : номер ленты

 ,"return_fresh_obj": "" -- вернуть объект в формате lenta.get

}

ответ

{

 <общие поля>

 ,"id" : номер ленты -- при отсутствии в запросе параметра "return_fresh_obj" 

 ,obj  { ... } -- объект в формате lenta.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление ленты

{

  "action" : "lenta.delete" 

  ,"id" : номер ленты

}

ответ

{

 <общие поля>

}

Добавление источника в ленту

{

  "action" : "lenta.source.add" 

  ,"lenta.id" : номер ленты

  ,"url" : "адрес источника добавляемого в ленту" 

}

ответ

{

 <общие поля>

}

Удаление источника из ленты

{

  "action" : "lenta.source.delete" 

  ,"lenta.id" : номер ленты

  ,"url" : "адрес источника удаляемого из ленты" 

}

ответ

{

 <общие поля>

}

Обновление источника

Указаный источник опрашивается вне расписания в поиске свежих новостей.

Если с момента прошлой провеки (плановой и этим вызовом) прошло меньше 15 минут, то опрос произведён не будет и количество свежих новостей будет возвращено как -1.

{

  "action" : "lenta.source.refresh" 

 ,"url" : "адрес обновляемого источника" 

}

ответ

{

 <общие поля>

,"news" : "количество найденех свежих новостей" 

}

Тестовый выпуск ленты

Вызов отправляет тестовое письмо с указанным количеством последних новостей ленты на указанный адрес (а не на адрес для которого создана лента).

В отличии от реального выпуска, отосланные новости не отмечаются как использованные и потом будут в итоге использованы при реальной отсылке.

Если количество последних новостей указано, то отметка их использования в предыдущих выпусках игнорируется.

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

Если в итоге новостей нет, то письмо не отправляется

Для тестирования выпуска рассылки с лентой есть альтернативные вариант - тестовый выпуск через issue.send

{
 "action" : "lenta.send" 

."id" : номер ленты

,"email" : адрес для отсылки тестового письма

,"n" : количество последних новостей для теста
       -- если не указано, то используются все ещё не отосланные новости (их может не быть)

,"decor" : "черновик" -- если лента привязана к подписчику, то можно не указывать - будет взят черновик указанный в ленте
                      -- если лента не привязана к подписчику, то черновик указать необходимо
}

ответ

{
 "news" : "количество новостей попавших в письмо" 

 < параметры ответа issue.send если письмо было выслано >
}

Шаблоны информационных писем

Эти вызовы поменяли формат принимаемых и возвращаемых данных на совместимый с issue.send

Поля старого формата ответов будут ещё некоторое время возвращаться для обратной совместимости.

Старый формат изменения данных будет ешё некоторое время приниматься для обратной совместимости.

Список шаблонов информационных писем


{

  "action" : "infolett.list" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"format" : "viber|sms|html|text" -- формат (для писем с несколькими текстами указывается только один)

            ,"name" : "название" 

            ,"onmoderation" : 0|1 -- 0 - шаблон одобрен для использования, 1 - шаблон на модерации

            }

            ...

           ]

}

Чтение шаблона информационного письма


{

  "action" : "infolett.get" 

 ,"id" : "идентификатор шаблона" 

}

ответ


{

  <общие поля>

  "obj" : {

           "id" : "идентификатор шаблона" 

          ,"name" : "название" 

          ,"onmoderation" : 0|1 -- 0 - шаблон одобрен для использования, 1 - шаблон на модерации

          ,"letter" : {
                       параметры содержимого письма как у issue.send

                       -- и дополнительно

                      ,"link.sqid" : "..." 
                      ,"campaign.id" : "код кампании" 
                      } 
          }

}

Создание или изменение шаблона информационного письма

При создании или изменении шаблона он автоматически попадает на модерацию и использовать его при высылке писем нельзя. Но его можно повтороно менять и удалять.

По результатам модерации вам придёт уведомление.


{

  "action" : "infolett.set" 

  ,"obj" : {

            "name" : "название" 

            ,"letter" : {
                         параметры содержимого письма как у issue.send
                         для текстовых писем обязательны не пустые - адрес отправителя, тема и как минимум один текст
                         для sms сообщения обязательны не пустые - имя отправителя и текст

                         -- дополнительно для email. не обязательно

                        ,"link.sqid" : "..." 
                        } 

           }

 -- необязательные

  ,"id" : "идентификатор шаблона" -- если не указан, создается новый

  ,"return_fresh_obj": "" -- вернуть объект в формате infolett.get

}

ответ


{

 <общие поля>

 ,obj  { ... } -- объект в формате infolett.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление шаблона информационного письма


{

  "action" : "infolett.delete" 

-- одного

  ,"id" : "код шаблона" 

--- или нескольких

  ,"id" : ["код шаблона1", "код шаблона2", .. ]

}

ответ


{

 <общие поля>

}

Предпросмотр информационного письма


{

  "action" : "infolett.preview" 

 ,"email" : "адрес для параметеризации подстановок" -- не обязательно

-- одно из

 ,"id" : "номер письма" 

-- или

,"obj" : { -- можно передавать объект из infolett.get - лишние поля будут проигнорированыются

           "letter" : {
                        параметры содержимого письма как у issue.send
                      }
        }
}

ответ


{

 <общие поля>,

 "text" : "текст письма" 

}

вверх

Форматы просмотра и шаблоны заполнения

Список форматов/шаблонов


{

  "action" : "format.list" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"name" : "название" 

            ,"type" : "uni|fill|view" -- соответственно тип: универсальный | шаблон заполнения | формат просмотра

            }

            ...

          ]

}

Чтение формата/шаблона


{

  "action" : "format.get" 

  "id" : "уникальный идентификатор" 

}

ответ


{

 <общие поля>

,"obj" : {

             "id" : "уникальный идентификатор" 

            ,"name" : "название" 

            ,"type" : "uni|fill|view" -- соответственно тип: универсальный | шаблон заполнения | формат просмотра

            ,"fields" : [ -- список полей ответов анкет в том порядке, в котором они будут использоваться при отображении

                              {

                                 "aid" : "код анкеты" 

                                ,"qid" : "код ответа" 

                                -- поля для шаблона заполнения данных

                                ,"unused" : "1|0" - использовать это поле или нет при заполнении

                                ,"answer" : "значение ответа используемое при заполнении" 

                              }

                         ...

                        ]

            }

}

Создание или изменение формата/шаблона


{

  "action" : "format.set" 

  ,"obj" : {

            ,"name" : "название" 

            ,"type" : "uni|fill|view" -- соответственно тип: универсальный | шаблон заполнения | формат просмотра

            ,"fields" : [ -- список полей ответов анкет в том порядке, в котором они будут использоваться при отображении

                          {

                            "aid" : "код анкеты" 

                           ,"qid" : "код ответа" 

                            -- поля для шаблона заполнения данных

                            "unused" : "1|0" -- использовать это поле или нет при заполнении

                            "answer" : "значение ответа используемое при заполнении. для вопросов с выбором - это объект !" 

                           }

                         ...

                        ]

            }

 необязательные

  "id" : "уникальный идентификатор" -- если не указан, создается новый

  ,"return_fresh_obj": "1" -- вернуть объект в формате format.get

}

ответ


{

 <общие поля>

 ,obj : { ... } -- объект в формате format.get, если в запросе "return_fresh_obj" : "1" 

}

Удаление формата/шаблона


{

 "action" : "format.delete" 

,"id" : "уникальный идентификатор" 

}

ответ


{

 <общие поля>

}

вверх

Ссылки

Новые ссылки и группы ссылок появляются с системе автомитически c каждым новым выпуском issue.send в зависимости от настроек relink и relink.param.

Если в выпуске есть ссылки для отслеживания переходов, то, при необходимости, создаются соответствующие им объекты "ссылка".

Поддерживаются пользовательские метки reltype/relref (смотрите описание в Общих Замечаниях)

Список ссылок


{ 

 ,"action" : "link.list" 

 ,"filter" : { -- не обязательно

              "group"  : "идентификатор группы" -- не обязательно
                                                -- при наличии ограничивает список только ссылками указанной группы

             ,"group.reltype"  : число          -- не обязательно
                                                -- при наличии ограничивает список только ссылками групп с указанным reltype

             ,"group.relref"   : число          -- не обязательно
                                                -- при наличии ограничивает список только ссылками групп с указанным relref

             ,"reltype"  : число  -- не обязательно
                                  -- при наличии ограничивает список только ccылками с указанным reltype

             ,"relref"   : число  -- не обязательно
                                  -- при наличии ограничивает список только ссылками с указанным relref
             }
}

ответ

{

  <общие поля>

 ,"list" : [

             { 

                "id"       : "идентификатор ссылки" 

               ,"url"      : "url ссылки" 

               ,"group"    : [ 

                               {

                                 "id"   : "идентификатор группы ссылок" 

                                ,"name" : "название группы ссылок" 

                               }

                               ...

                             ]

             }

             ...

           ]

  ,"group" : { -- присутствует в ответе, только если в запросе был указан идентификатор группы

               "id"   : "идентификатор группы" 

               ,"name" : "название группы" 

               ,"reltype" : ....

               ,"relref" : ....
             }

}

Прочитать ссылку


{

  ,"action" : "link.get" 

  ,"id"     : "идентификатор ссылки" 
}

ответ

{

   <общие поля>

  "obj" : {

     "id" : "идентификатор ссылки" 

    ,"url"   : "url ссылки" 

    ,"reltype" : ......

    ,"relref" : ......
  }

}

Создание ссылки

Ссылки не уникальны. С одним и тем же url может быть несколько ссылок с разным id.

{
   "action" : "link.create" 

  ,"url"    : "url ссылки" 

  ,"test"   : "0|1"  -- проверять запросом доступность ссылки

  ,"reltype" : число -- не обязательно, по умолчанию 0
                     -- отрицательные значения зарезервированы для системы
                     -- в данный момент:
                     --  -1 - ссылка из выпуска рассылки
                     --  -2 - ссылка - целевая страница достигнутая подписчиком

  ,"relref" : число  -- не обязательно, по умолчанию 0
}

ответ

{

   <общие поля>

 "id" : "идентификатор ссылки" 

}

Изменение ссылок

При изменении нескольких ссылок все изменения будут проигнорированы если проверка (GET) хоть одной ссылки для которой указано 1 вернёт HTTP код ответа не 2xxx и не 301, 302, 303, 401


{

   "action" : "link.set" 

  -- не указанные поля остаются не изменёнными. test учитывается только при наличии url

или одна ссылка

  ,"id"     : "идентификатор ссылки" 

  ,"url"    : "новое значение" 

  ,"test"   : "1/0 - проверять доступность ссылки" 

  ,"name"   : "новое название группы" 

  ,"reltype" : "новое значение" 

  ,"relref" : "новое значение" 

или несколько ссылок

  ,"list"   : [ 

               {

                  "id"     : ....

                 ,"url"    : ....

                 ,"test"   : ....

                 ,"reltype"  : ....

                 ,"relref"   : ....

                }

                ..

              ]

}

ответ

{

   <общие поля>

}

Удаление ссылки

{
   "action" : "link.delete" 

  ,"id"     : "идентификатор ссылки" 
}

ответ

{

   <общие поля>

}

Изменить участие ссылки в группах ссылок


{

   "action" : "link.set.group" 

  ,"id"     : "идентификатор ссылки" 

  ,"group"  : { -- участие в не указанных группах не изменяется

                 "id1" : "0 - удалить из группы. 1 -добавть в группу" 

                ,"id2" : "0|1" 

                ,"id3" : "0|1" 

                ...

               }

}

ответ

{

   <общие поля>

}

Группы ссылок

Группы ссылок позволяют объединять несколько ссылок в одно целое. Например для более простого получения по ним суммарной статистики.

Поддерживаются пользовательские метки reltype/relref (смотрите описание в Общих Замечаниях)

Получить список групп ссылок


{ 

 "action" : "link.group.list" 

 ,"filter" : { -- не обязательно

              "reltype"  : число  -- не обязательно
                                  -- при наличии ограничивает список только группами с указанным reltype

             ,"relref"   : число  -- не обязательно
                                  -- при наличии ограничивает список только группами с указанным relref
             }
}

ответ

{

  <общие поля>

 ,"list" : [

             { 

                "id"   : "идентификатор группы ссылок" 

               ,"name" : "название группы ссылок" 

               ,"reltype" : ....

               ,"relref" : ....

             }

             ...

           ]

}

Прочитать группу ссылок


{

  ,"action" : "link.group.get" 

  ,"id"     : "идентификатор группы" 
}

ответ

{

   <общие поля>

  "obj" : {

     "id" : "идентификатор группы" 

    ,"name"   : "название группы" 

    ,"reltype" : ......

    ,"relref" : ......
  }

}

Создать группу ссылок / Изменить группу ссылок


{

  ,"action" : "link.group.set" 

-- для создания

  ,"name"   : "название группы" -- обязательно

  ,"reltype" : число - не обязательно, по умолчанию 0

  ,"reltype" : число -- не обязательно, по умолчанию 0
                     -- отрицательные значения зарезервированы для системы
                     -- в данный момент:
                     --  -2 - ссылка - целевая страниц

  ,"relref" : число  - не обязательно, по умолчанию 0

-- для изменения

  ,"id"     : "идентификатор группы" 

  -- не указанные поля остаются не изменёнными

  ,"name"   : "новое название группы" 

  ,"reltype" : "новое значение" 

  ,"relref" : "новое значение" 
}

ответ

{

   <общие поля>

  ,"id" : "идентификатор группы" 

}

Удалить группу ссылок

Удаляется именно группа ссылок как объект обеспечивающий группировку ссылок.

Входящие в нёё ссылки не удаляются.


{

  ,"action" : "link.group.delete" 

  ,"id"     : "идентификатор группы" 

}

ответ

{

   <общие поля>

} 

Шаблоны веб-страниц

В данный момент Шаблоны веб-страниц используются только Формами опросов и не имеют ни какого отдельного самостоятельного назначения.

Список шаблонов веб-страниц


{

  "action" : "webpage.list" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"name" : "название" 

            ,"create.date" : "дата и время создания" -- Ys, null

            ,"update.date" : "дата и время последнего изменения" -- Ys, null
            }

            ...

           ]

}

Чтение шаблона веб-страницы


{

  "action" : "webpage.get" 

 ,"id" : "идентификатор шаблона" 

}

ответ


{

  <общие поля>

  "obj" : {

      "id" : "идентификатор шаблона" 

     ,"name" : "название" 

     ,"page" : "html текст страницы" 

     ,"create.date" : "дата и время создания" -- Ys, null

     ,"update.date" : "дата и время последнего изменения" -- Ys, null

          }

}

Создание или изменение шаблона веб-страницы


{

  "action" : "webpage.set" 

  ,"obj" : {

      ,"name" : "название" 

      ,"page" : "html текст страницы" 

           }

 -- необязательные

  ,"id" : "идентификатор шаблона" 

  ,"return_fresh_obj": "" -- вернуть объект в формате webpage.get

}

ответ


{

 <общие поля>

 ,"id" : номер -- объект в формате webpage.get при наличии в запросе параметра "return_fresh_obj" 

 ,"obj" :  { ... } -- объект в формате webpage.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление шаблона веб-страницы


{

  "action" : "webpage.delete" 

  ,"id" : "идентификатор шаблона" 

--- или несколько

  ,"id" : [ "id1", "id2", .. ]

}

ответ


{

 <общие поля>

}

вверх

Внешние авторизации

Хранение параметров внешних авторизаций в данный момент поддерживает

Список внешних авторизаций

{

 "action" : "authext.list" 

}

ответ

{

  <общие поля>

 ,"list" : [

             {

                "id"   : "идентификатор внешней авторизации" 

               ,"type" : "тип внешней авторизации" 

               ,"login" : "логин внешней авторизации" 

               ,"reltype" : ...

               ,"relref" : ...
             }

             ...

           ]

}

Чтение внешней авторизации

Поля хранящие собственно данные для авторизации прочесть нельзя.

{

 "action" : "authext.get" 

 ,"id" : "идентификатор внешней авторизации" 
}

ответ

{

  <общие поля>

 ,"obj" : {
           "id"   : "идентификатор внешней авторизации" 

           ,"type" : "тип внешней авторизации" 

           ,"login" : "логин внешней авторизации" 

           ,"reltype" : ...

           ,"relref" : ...
           }
}

Создание внешней авторизации

{

 "action" : "authext.create" 

 ,"type" : "тип внешней авторизации" 
            -- в данный момент поддерживается:
            --  0 - пара логин-пароль
            --  8 - Google Analitics
            -- 10 - Google Big Data / Google Big Query

 ,"login" : "логин внешней авторизации" -- просто некая уникальная метка

 ,"reltype" : ...
 ,"relref" : ...

-- для Google Big Data / Google Big Query

  -- Зарегистрируйтесь в Google, создайте проект в Google Cloud Platform и создайте сервисный аккаунт со ограниченными правами специально для sendsay
  -- Сервисный акканут - специальный гугловый аккаунт для работы с конкретным проектом в рамках заданных прав. Подробнее о нём читайте в https://cloud.google.com/iam/docs/service-accounts?hl=en_US

  -- После этого нужно выгрузить приватный ключ сервисного аккаунта в формате json. Все данные будут в одноименных полях - client_email, private_key, project_id - для использования в данном вызове.

 ,"client_email": "XXXXXXXXX@developer.gserviceaccount.com" -- почтовый адрес сервисного аккаунта

 ,"project_id": "XXXX" -- код проекта

 ,"private_key": "-----BEGIN PRIVATE KEY-----XXXXXXX-----END PRIVATE KEY-----\n" -- приватный ключ сервисного аккаунта

 ,"token": "..." - что угодно, скажем, ещё раз адрес сервисного аккаунта

-- для Google Analitics

 ,"token" : "refresh token" 

-- для пары логин-пароль

 ,"token" : "пароль:логин" 

}

ответ

{
  <общие поля>

 ,"id" : "идентификатор созданной внешней авторизации" 

}

Изменение внешней авторизации

Обновляются только указанные в запросе поля

{
 "action" : "authext.set" 

 ,"id" : "идентификатор внешней авторизации" 

 ,"type" : "тип внешней авторизации" 

 ,"login" : "логин внешней авторизации" 

 ,"token" : "токен внешней авторизации" 

 ... прочие поля специфические для используемого типа авторизации ...

 ,"reltype" : ...

 ,"relref" : ...
}

ответ

{

  <общие поля>

}

Удаление внешней авторизации

{
 "action" : "authext.delete" 

 ,"id" : "идентификатор внешней авторизации" 
}

ответ

{

  <общие поля>

}

Информация об авторизации в Google Analitics

{ 

 "action" : "authext.ga.props" 

,"id" : "идентификатор внешней авторизации" 

}

ответ

{

  <общие поля>

 "list" : {
           "логин-1" : [
                         {
                          "id" : "id сайта" 
                         ,"name" : "название сайта" 
                         }

                        ,....
                       ]

          ,....
          }
}

вверх

Система

Отправить пожелание Деду Морозу

Использовать только перед новым годом


{

 "action" : "sys.dedmoroz" 

 "email" :  "email для ответа" 

,"message" : "пожелание" 

}

ответ


{

 <общие поля>

}

вверх

Получить настройки


{

 "action" : "sys.settings.get",

 "list" : [ ], -- коды настроек. Если пусто - все настройки кроме отмеченных (НД)

 "params" : { -- не обязательно

             "код настройки" : {
                                "параметр" : "значение",

                                "параметр" : "значение",

                                ....
                               },

             "код настройки" : [
                                 ...
                               ]

            }

}

ответ


{

 <общие поля>,

 "list" : {

           "код настройки" : "значение в формате зависящим от настройки" 

           ..........

          }

}

Поменять настройки


{

 "action" : "sys.settings.set",

 "list" : { -- только те что надо изменить

            "код настройки" : "значение в формате зависящим от настройки" 

            ...........

          }

}

ответ


{

 <общие поля>

}

Список настроек которые можно только прочесть


 "about.id"    : "код клиента" 

,"about.name"  : "Название" 

,"about.tarif" : "код тарифа" 

,"about.user"  : "текущий пользователь (sublogin)" 

,"trial"       : 0|1 - тестовый режим. действуют некоторые ограничения

,"trial.issue.limit" : лимит на тираж писем. null - ограничения нет. (НД)

,"trial.issue.rest"  : доступный остаток тиража писем. null - ограничения нет. (НД)

,"allow.email" : 0|1 - доступна работа с email

,"allow.sms"   : 0|1 - доступна работа с sms

,"sms.unlimited" : 0|1 - тираж sms не ограничен купленым количеством

,"sms.used" : "использовано единиц тарификации sms начиная с 01-11-2013" - в настоящий момент это копейки. (НД)

,"sms.byed" : "куплено единиц тарификации sms начиная с 01-11-2013" - в настоящий момент это копейки. (НД)

,"member.tarif.limit" : лимит на количество email-адресов в базе используемый при расчёте оплаты. null - ограничения нет. (НД)

,"member.hard.limit" : лимит на количество email-адресов в базе который не превысить. null - ограничения нет. (НД)

,"member.hard.rest" : доступный отаток на внесенеие email-адреcов. null - ограничения нет. (НД)

,"member.noconfirm.limit" : лимит на количество адрес которые можно внести без подтверждения. null - ограничения нет. (НД)

,"member.noconfirm.rest" : доступный остаток на внесение без подтверждения. null - ограничения нет. (НД)

,"issue.email.moderation" : включена (1) или нет (0) модерация выпусков email-рассылок (НД)

,"issue.email.sender.moderation" : включено (1) или нет (0) подтверждение адресов отправителей писем

,"issue.sms.moderation" : включена (1) или нет (0) модерация выпусков sms-рассылок (НД)

,"issue.sms.sender.moderation" : включена (1) или нет (0) модерация названий отправителей sms

,"issue.month.limit" : лимит месячного тиража email-писем. null - ограничения нет. (НД)

,"issue.month.rest" : доступный остаток месячного тиража email-писем. null - ограничения нет. (НД)

,"issue.month.limit.excess : превышение лимита месячного тиража email-писем. (НД)
                               -- allowed - разрешено
                               -- once    - разрешено один раз - первый превышающий выпуск выйдет в не превышающей части, а дальнейшие - полностью не выйдут
                               -- denied  - запрещено - даже частично превышающий выпуск полностью не выйдет

,"issue.month.personal.limit : лимит месячного тиража транзакционных email-писем. null - ограничения нет. (НД)

,"issue.month.personal.rest : доступный остаток месячного тиража транзакционных email-писем. null - ограничения нет. (НД)

,"sec.allowip" : [ -- список ip-адресов или сidr-адресов только с которых разрешёна работа
                   -- пустой список - нет ограничений
                  "адрес" 
                 ,"адрес" 
                  ...
                 ]

,"sec.login.denysuper" : 0|1 -- вход основным логином запрещён,

,"sec.login.lock.error" : лимит числа неудачных попыток входа после которого логин блокируется,

,"sec.login.lock.inactive" : количество дней неактивности после которых логин блокируется,

,"sec.password.history" : глубина истории паролей каждого логина. нельзя сменить пароль на имеющийся в истории,

,"sec.password.check" : 0|1 -- проверять что новый пароль соответствует каждому из требований
                            -- * 8 символов и длиннее
                            -- * содержит буквы
                            -- * содержит цифры

,"sec.password.expire.day" : количество дней после смены пароля после которых заставлять менять пароль опять

,"target.script" :  {
                      -- код для сервиса "Целевые страницы" предназначеный для размещения на сайте
                      -- поддерживаются дополнительные параметры (все не обязательны)

                     "group.id : номер -- автоматически включать посетившего целевую страницу в указанную группу-список

                    ,"link.group.id" : номер -- автоматически включать новые целевые страницы в указанную группу ссылок

                    ,"noqs" : 1 -- удалить из адреса целевой страницы все параметры (например они всегда разные для разных посетителей)

                    ,"url" : "ccылка" -- вместо адреса целевой страницы использовать указаный

                    ,"noevent" : 1 -- не создавать событие "Цель достигнута" 

                    ,"email" : 1 -- включить в код метку для подстановки email посетителя
                                 -- при выдаче страницы каждый раз заменяйте метку на реальный адрес в кодировке url-encoded

                    ,"js" : 1 -- получить код в формате java-script. это полезно в случае сложных, частично обновляемых по мере действий
                              -- пользователя страниц сайта. например при использовании подхода single-page-application

                    ,"datakey": [
                                 -- правила изменения пользовательских данных
                                 -- формат совпадает с аналогичным параметром вызова member.set

                                 -- данная возможность позволяет создавать сложные сценарии учёта достижения страниц вашего сайта
                                 -- например "Забытая корзина" или "Запоминание просмотренных товаров" 
                                 -- Служба Поддержки может проконсультировать вас подробнее по данному вопросу.
                                ]
                    }
            ]

,"spec_attach.pdf" : 0|1 -- доступность для использования в рассылке индивидуальных pdf-файлов
,"spec_attach.xlsx" : 0|1 -- доступность для использования в рассылке индивидуальных excel-файлов
,"spec_attach.url" : 0|1 -- доступность для использования в рассылке персональных прикрепляемых файлов

,"dt.now" : "YYYY-MM-DDThh:mm:ss+HH:MM -- текущие дата, время и временная зона систему. В формате ISO-8601.
                                          например "2014-11-11T15:36:09+03:00" 
                                          именно в этой временной зоне считаются все даты и время используемые в API

,"tz.delta" -- проверка знания системой часового пояса в вашем способе записи. воспользуйтесь перед первыми выпусками рассылок с учётом часовго пояса
            -- Если не распознаётся - обратитесь в Службу Поддержки за расширением нашего списка городов и стран.
            --
            -- список проверяемых строк берётся из одноимённого ключа в params
            --
            -- как результат возвращается объект где ключами являются проверяемые строки, а их значениями - null если строка не распознана,
            -- или её часовой пояс в виде смещения относительно UTC в виде +hhmm или -hhmm

Список настроек которые можно и прочесть и поменять


 "about.owner.email" : [ "email", "email", ... ],  -- email-адрес/адреса ответственного лица.
                             -- Используется как получатель для всех писем системы когда не указан другой адрес

 "support.access" : "0|1", -- службе поддержки разрешён вход для помощи

 "email.utf8" : "0|1", -- поддержка национальных почтовых адресов
                       -- выключена для старых аккаунтов для сохранения совместимости
                       -- включена для новых аккаунтов
                       --
                       -- изменить настройку можно только в одну сторону - с выключено (0) на включено (1)
                       -- для старых аккаунтов есть необходимость начать использовать национальные адреса
                       --
                       -- для новых аккаунтов всегда включено и не выключается

 -- Использование шаблонов информационных писем

 "infolett.confirm.need" : "код шаблона", -- Высылается когда адрес регистрируется или при использовании вызова
                                          -- "member.sendconfirm" без указания желаемого черновика.
                                          -- Пусто - стандартный системный текст

 "infolett.confirm.done" : "код шаблона", -- Высылается сразу после подтверждения регистрации.
                                          -- Пусто - не высылается ничего

 "infolett.info"         : "код шаблона", -- Высылается когда требуется сообщить подписчику его регистрационные
                                          -- данные и он уже подтвердил регистрации.
                                          -- Пусто - стандартный сиситемный тест
 --

 "msisdn.country" : "число", -- код страны (без +) для нормализации регистрируемых не полных номеров телефонов

 "msisdn.zone"    : "число", -- код зоны для нормализации регистрируемых не полных номеров телефонов

 --

 "issue.paused" : 0||1 -- глобально остановить-возобновить запуск новых рассылок

                       -- останавливает (1) запуск новых заданий на рассылку - они сразу переходят в состояние "Приостановлено" 

                       -- возобновляете (0) запуск новых заданий на рассылку - обычный режим работы

                       -- глобальная остановка НЕ останавливает уже формирующиеся выпуски - для этого есть issue.running.pause

                        -- глобальное возобновление НЕ запускает приостановленные выпуски - для этого есть issue.running.resume

 "issue.dkim.id" : "идентификатор dkim-ключа или пусто" -- будет использоваться если у выпуска на задан свой
                                                        -- пусто - будет использоваться системный

 "issue.link.qid" : "строка", -- добавка к ссылкам в письмах

                              -- переходы из писем рассылок плохо видны в статистике именно как переходы из рассылки
                              -- и часто попадают в общую кучу "прямые переходы" (typed in).

                              -- Вы можете указать параметр (например source=pro) который будет добавляться ко всем
                              -- ссылкам в вашем выпуске (например, было http://site.tld/,а станет http://site.tld/?source=pro)
                              -- и, соответственно, позволит однозначно определить при анализе посещаемости сайта
                              -- что посетитель пришёл именно из выпуска рассылки. 

 "issue.dontsend.550" : 0|1, -- автоматически исключать из выпусков рассылок адреса у которых последняя ошибка smtp = 550 - неизвестный получатель

 --

 "target.сookie.ttl", : положительное-число -- "Время отслеживания после перехода" для сервиса "Целевые страницы". Задаётся в минутах. По умолчанию 360 - 6 часов

 -- Уведомления о действиях с адресами производимыми пользователями

 -- !!! Эти параметры существуют для совместимости со старым интерфейсом и старыми формами сбора данных. Они не работают для текущей версии системы.

 -- Значением является массив в котором могут быть или один и более почтовый адрес или одна и только одна ссылка http/https

 -- Указанные почтовые адреса проверяются на синтаксическую верность и при ошибке вызов завершится так же с ошибкой

 -- В ссылке последовательность EMAIL при вызове заменяется на адрес подписчика с которым произведено действие

 "notify.member.join"    : [ .. ], -- Уведомление о регистрации нового адреса (через внешние формы)

 "notify.member.confirm" : [ .. ], -- Уведомление о подтверждении регистрации адреса

 "notify.member.modify"  : [ .. ], -- Уведомление об изменении данных регистрации (самим владельцем адреса)

 "notify.member.remove"  : [ .. ], -- Уведомление об удалениии регистрации (самим владельцем адреса)

 -- Перенаправления пользователя после совершения действий

 "redirect.member.join"        : "url", -- После регистрации через внешнюю форму
                                        -- когда пользователь зарегистрирован как новый
                                        -- Может быть переопределено или отменено в конкретной
                                        -- форме на сайте параметром redirect_to.

 "redirect.member.join.exists" : "url", -- После регистрации через внешнюю форму
                                        -- когда пользователь уже существует
                                        -- Может быть переопределено или отменено в конкретной
                                        -- форме на сайте параметром redirect_exists_to

 "redirect.member.confirm"     : "url", -- После подтверждения регистрации

 "redirect.member.remove"      : "url", -- После удаления регистрации

Список пользователей


{

 "action": "user.list" 

}

ответ


{

 <общие поля>

 ,"list" : [

           {

             "sublogin" : "саблогин пользователя",

             "status" : "-1 - ожидает смены пароля |0 - активен | 1 - заблокирован",

            },

           ..

         ]

}

вверх

Создание пользователя

Созданный пользователь получает состояние "Требуется сменить пароль"


{

 "action" : "user.create",

 "sublogin" : "саблогин",

 "password" : "пароль",

 "email" : "email для отправки пароля" -- необязательно

}

ответ


{

  <общие поля>

}

вверх

Удаление пользователя


{

 "action" : "user.delete",

 "sublogin" : "саблогин" 

}

ответ


{

  <общие поля>

}

вверх

Изменение пароля и статуса любого пользователя

Настройки основному пользователю (sublogin пусто при авторизации) может менять только он сам.


{

 "action" : "user.set",

 "sublogin" : "логин пользователя", -- обязательно. 

 "status" : "-1 - заставить сменить пароль | 0 - активировать | 1 - заблокировать" -- необязательно
            -- если текущий статус -1 то изменить его возможно только сменой пароля

 "password.new" : "новый пароль", -- необязательно

 "password.old" : "старый пароль", -- обязательно, если указан password.new

 "email" : "адрес для высылки письма с новым паролем" -- не обязательное поле
}

ответ


{

 <общие поля>

}

вверх

Изменение пароля себе

Меняет пароль текущему пользователю


{

 "action" : "sys.password.set",

 "password.new" : "новый пароль",

 "password.old" : "старый пароль",
}

ответ


{

 <общие поля>

}

вверх

Обращение в саппорт


{

 "action" : "sys.message" 

 "email" :  "email для связи" 

,"message" : "текст сообщения" 

,"attach" : "cодержимое прикрепляемого файла" -- не обязательно
}

ответ


{

 <общие поля>

}

вверх

Журнал работы


{

 "action" : "sys.log",

 "from"   : "YYYY-MM-DD hh:mm:ss" -- дата события от, не обязательно

 "upto"   : "YYYY-MM-DD hh:mm:ss" -- дата события по, не обязательно

}

ответ


{

 <общие поля>,

 "list" : [ -- по возрастанию времени

           {

             "dt" : "дата события",

             "ip" : "ip адрес инициатора",

             "login" : "логин инициатора",

             "object" : "объект",

             "action" : "действие",

             "title" : "заголовок",

             "value.old" : "старое значение (если применимо)",

             "value.new" : "новое значение (если применимо)" 

           }

           ..........

          ]

}

вверх

Права доступа

Чтение прав


{

"action": "rights.get",

"user" : "логин пользователя",

"list" : [  -- список запрашиваемых проверяемых вызовов, если пусто - то все 

           "action1",

           "action2",

           ...

         ]

}

ответ


{

 <общие поля>,

 "list" : {  -- список вызовов: 1 - доступно, 0 - недоступно

            "action1" : " 1 | 0 " 

            "action2" : " 1 | 0 ",

            ..

          }

}

Установка прав


{

 "action": "rights.set",

 "user" : "логин пользователя",

 "list" : {  -- список устанавливаемых прав: 1 - доступно, 0 - недоступно.
             -- Изменяются только значения перечисленных в списке прав. 

            "action1" : " 1 | 0 ",

            "action2" : " 1 | 0 ",

            ...

          }

}

ответ


{

 <общие поля>

}

вверх

Хранимые данные

Абстрактное хранилище структурированных данных.

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

Это экспериментальная возможность. Она может быть изменена или отменена.

Список хранимых данных


{

  "action" : "sys.storage.list" 

}

ответ


{

 <общие поля>

,"list" : [
           "идентификатор хранимых данных-1" 

           "идентификатор хранимых данных-2",

           ...

           ]

}

Чтение хранимых данных

При указании в id шаблона (символ "*" - любое количество симоволов) будут возвращены данные о записях идентификаторы которых под него подходят.

Например: "key1:*" или "key1:*part2*"


{

  "action" : "sys.storage.get" 

 ,"id" : "идентификатор хранимых данных или шаблон идентификаторов" 

 ,"datakey" : [ "dk1","dk2",....] - вернуть только части объекта указанные ключами данных. не обязательно
}

ответ


{

  <общие поля>

-- ответ при запросе без шаблона

  "obj" : {

            "id" : "идентификатор хранимых данных" 

           ,"data" : .... -- данные

           ,"create.date" : "дата и время создания" -- Ys, null

           ,"update.date" : "дата и время последнего изменения" -- Ys, null
          }

-- ответ при запросе с шаблоном

  "list" : [
            { структура как obj }
           ,{ структура как obj }
           ,{ структура как obj }
           .........
           ]

}

Создание или изменение хранимых данных

{

  "action" : "sys.storage.set" 

  ,"id" : "идентификатор хранимых данных" -- строка до 120 символов
                                          -- при отсутствии данных с таким идентификатором запись создаётся

  ,"obj" : {

-- одно из

            "data" : {
                      -- данные
                     }
-- или

            "data" : [
                      -- данные
                     ]

           }

 -- необязательные

  ,"return_fresh_obj": "" -- вернуть данные в формате sys.storage.get

}

ответ

{

 <общие поля>

 ,"obj" :  { ... } -- данные в формате sys.storage.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление хранимых данных


{

  "action" : "sys.storage.delete" 

  ,"id" : "идентификатор хранимых данных" 

--- или несколько

  ,"id" : [ "id1", "id2", .. ]

}

ответ


{

 <общие поля>

}

вверх

Ключи данных

"Ключ Данных" это строка описывающая путь к данным хранимым для данного адреса.

Этот новый формат доступы к данным разработан для замены форматы АВО и поддержки хранения сложных структур данных.

На самом деле вы уже знакомы в ключами данных если:

- впускаете Экспресс-Выпуск с данными в формате JSON-массив

- используете внешние данные для персонализации писем

- даже просто пользуетесь персонализацией выпуска (запись [% anketa.person.name %] это доступ по ключу данных из трёх элементов :)

Синтаксис

Ключ данных описывает путь по структуре данных который надо пройти для получения нужного значения.

Путь состоит из отдельных элементов (минимум из одного) разделяемых точками:

Ключ из трёх элементов - "aaa","bbb","ccc"

aaa.bbb.ccc

Набор символов элемента пути технически не ограничен, однако стоит проявить разумную сдержанность:

Ключ с на китайском

關鍵.數據

Точка внутри сегмента экранируется обратным слэшем aaa.b@b\.b.ccc

Ключ по прежнему из трёх элементов - "aaa","b@b.b","ccc"

aaa.b@b\.b.ccc

Для доступа к элементу объекта его ключ в объекте записывается через точку после пути до объекта.

В элементе "aaa" взять "bbb", а от него "ccc"

aaa.bbb.ccc

Для доступа к элементу массива его индекс записывается в квадратных скобках сразу после пути до массива.

aaa.bbb[5].ccc

Есть специальные ключи данных - "*", "$" и "@" - они описаны в вызовах которые их поддерживают.

Преимущества

- Данные формата АВО полностью доступны через КД

- Нет ограничения на набор хранимых данных

- Нет ограничений на сложность иерархии хранения данных и их сочетание (например "адреса подписчика" могут представлять собой массив из объектов хранящих структуру одного адреса по компонентам (страна, гоород, улица, дом, квартира, координаты), а компоненты адреса в свою очередь там же быть объектами - "координаты" это "широта" и "долгота").

- Нет ограничений на формат данных

- У разных подписчиков по одному и тому же ключу данных можно хранить разные стуктуры и типы данных

- Доступ к данным в выпуске рассылки осуществляется как и прежде путём прямой записи [% anketa.ключ.данных ] или через функцию datakey() - [ datakey(anketa,keyvar) %], где keyvar это переменная значением которой является ключ данных (для доступа по динамически формируемым ключам или в случае когда ключ содержит символы не подходящие для прямой записи anketa.ключ.данных)

Совместимость

Все данные старого формата АВО доступны через КД без каких-либо ухищерений.

Код "Анекты" становится первым элементом пути в ключе данных, код вопроса - вторым: "aid.qid"

Большинство вызовов уже поддерживает КД. Не поддерживают КД только вызовы form.*, format.*. Это будет сделано в ближайшее время.

Есть только два момента, которые необходимо учесть:

- значением для того, что в АВО называется "вопрос с выбором" будет объект, а не массив. Ключами объекта будут кода ответов, значениями - null.

- внутренний формат даты в модели АВО это "YYYYMMDDhhmmss", а в модели КД это "YYYY-MM-DD hh:mm:ss".

Это учитывается автоматически при использовании вызова member.get, но при персонализации выпуска рассылки эта разница может быть заметна.

Если вы проверили формат КД для решения своих задач и убедились что он вам подходит, то мы можем помочь вам с автоматизированным переводом формата хранения даты в новый вид - обратитесь в службу поддержки. (формально вы и сами можете провести такое преобразование, но мы сделаем это для вас проще и быстрее).

Данные формата КД ограниченно доступны при использовании доступа по моделе АВО так как только самые простые варианты структур данных доступны через АВО.

Возможные изменения

Длина одного компонента пути ключа данных и всего ключа данных в данный момент не зафиксирована окончательно что бы изучить потребности клиентов. Скорее всего общая длина будет ограничена 255 символами.

Длина строки данных в данный момент не зафиксирована окончательно что бы изучить потребности клиентов. Скорее всего она длина будет ограничена 255 символами, но с предоставлением альтернативы "больших строк".

Для численных операций, минимальное и максимальное целое число в данный момент не определены, но ориентируйтесь на возможности предоставляемые 64-битными вычислениями.

Для численных операций, точность числа с плавающей точкой в данный момент не определена, но ориентируйтесь на возможности предоставляемые 64-битными вычислениями.

По одному и тому же ключу данных у разных подписчиков могут хранится данные разной структуры. Это эксперементальная возможность. Скорее всего она будет отменена с введением типизации и словаря данных.

По одному и тому же ключу данных у разных подписчиков могут хранится данные разного типа. Это эксперементальная возможность. Скорее всего она будет отменена с введением типизации и словаря данных.

В данный момент не проводится проверки данных вносимых через вызовы member.set и member.import на соответствие тому или иному типу данных (кроме проверки дат и времени). С введением типизации и словаря данных такие проверки станут как минимум возможны по вашему желанию, а в некторых случах проводится автоматически.

Форматы данных

Все данных поддерживают набор символов Unicode.

Для хранения и доступа с использованием Ключей Данных доступны следующие типы величин:

Cкаляр

"Просто значение" представляющее собой число или строку.

1
"бла-бла
"ещё одна строка" 

null

Специальный вариант скаляра обозначающий "неопределённое значение".

Записывается просто как

null

Без кавычек !

Массив (список)

Упорядоченный набор других величин (любых).

Каждая величина имеет свой "индекс" (начиная в 0) в массиве описывающих её позицию.

Например, массив из трёх величин, последняя из которых - другой массив

 [ 1, "хихи", [ 4, 99 ] ]

Объект (словарь)

Набор пар "ключ - значение", где "ключ" - скаляр, а "значение" - любая величина.

Каждый ключ объекта уникален и имеет только одну соответствующую ему величину.

Например

{
 "один" : "111" 

,"зима" : [ декабрь", "январь", "февраль"]

,"килограмм" : {
                "грамм" : 1000
               ,"унция" : 36.81
               }

,"關鍵" : { 數據" : "關鍵數據是最好的事情" }
}

Пример структуры данных

Пример структуры данных описывающей несколько адресов у одого из подписчиков:

{
 "member" : { ............}

,"contact" : [

             {
              "title" : "Центральный офис" 

             ,"phone:" : [ "+78216550011" ,"88003336963" ]

             ,"is_office" : "1" 

             ,"address" : {
                           "country" : "Россия" 

                          ,"index" : "197022" 

                          ,"city" : ", Санкт-Петербург" 

                          ,"street" : "ул. Профессора Попова " 

                          ,"building" : "д.23 лит.Д" 

                          ,"apartment" : "пом. 27Н (офис 413)" 

                          ,"alias" : "БЦ «Гайот»" 

                          ,"location" : {
                                         "latitude" : "59.972256" 
                                        ,"longitude" : "30.311468" 
                                        }
                         }
              }

            ,{
              "title" : "Московский офис" 

             ,"phone:" : [ "+74952695015" ,"88003336963" ]

             ,"is_office" : "1" 

             ,"address" : {
                           "country" : "Россия" 

                          ,"index" : "121087" 

                          ,"city" : "Москва" 

                          ,"street" : "Улица Барклая" 

                          ,"building" : "д. 6 стр. 5" 

                          ,"apartment" : "офис № 217" 

                          ,"alias" : "бизнес-центр «Барклай Плаза»" 

                          ,"location" : {
                                         "latitude" : "55.752678" 
                                        ,"longitude" : "37.702110" 
                                        }
                         }
              }

            ]
}

Второй номер телефона основного офиса - "сontact[0].phone[1]"

Город московского офиса - "сontact[1].address.city"

Координаты основного офиса - "contact[0].location"

Широта основного офиса - "contact[0].location.latitude"

Хранение данных о подписчике

Данные об одном подписчике представляются как корневой объект имеющий как минимум ключ "member" по которому во вложеном объекте хранятся системные данных об адресе, а остальные ключи корневого объекта вы используете по своему усмотрению.

{
 "member" : {
             "id" : ....
            ,"email" : ....
            ,"domain" : ....
            ,....
            }

,"ваш ключ-1" : ............

,"ваш ключ-2" : ............

,"ваш ключ-3" : ............

}

Использвание в персонализации писем

Доступ к данным подписчика в выпуске рассылки осуществляется как и прежде - через объект anketa:

- путём прямой записи [% anketa.ключ.данных %] 
- через функцию datakey().

Функция datakey() полезна когда ключ данных содержит символы, не допустимые напрямую в командах персонализатора (это временно), или когда ключ данных хранится в другой переменной (например в результатах фильтра с has/save).

[% datakey(anketa,keyvar) %], где keyvar это переменная значением которой является ключ данных.

[% datakey(anketa,"ключ.данных") %], где "ключ.данных" это строка непосредственно задающия ключ данных.

Формально datakey() предназначен для помощи в косвенной адресации и первым параметром функции может быть любая переменная персонализации, не обязательно только anketa.

вверх

Фильтр отбора в группе (ДК)

Этот фильтр работает только по данным доступным по формату ДК.

Фильтр отбора состоит из списка элементов задающих условия отбора или условия объединения других условий.

Пустой фильтр (т.е. не содержащий ни одного элемента) ни когда не совпадает ни с чем и результат его использования всегда пустой.

По своей структуре фильтр аналогичен условию отбора Универсальной Статистики - у них общая структура одного условия фильтра, общие логически и группирущие уссловия и многие операции.

Фильтр представляет из себя список элементарных условий объеденяемых через "И".

Большинство условий позволяют сравнивать значение из основного ключа данных (задаётся в "a") как с константой (задаётся в "v") так и со значением из другого ключа данных (задаётся в "b").

вверх

Пример

Адрес имеет 2 и меньше ошибки доставки И регион проживания один из RU01,RU05,RU40 И день рождения завтра И ( входи в группу abc ИЛИ входит в группу xyz ) И регион проживания совпадает с регионом заказа


[

          {
            "a" : "member.error.error"      -- Адрес имеет 2 и меньше ошибки доставки

            "op" : "<=", 

            "v" : "2" 
          },

          {
            "a" : "personal.region",     -- регион проживания один из RU01,RU05,RU40

            "op" : "in",

            "in" : [ "RU01" ,"RU13" ,"RU40" ]
          },

          {
            "a" : "personal.birthday:YD",     -- день рождения завтра

            "op" : "==",

            "v" : "current +1 day",
          },

          [
             {
                "op" : "in_group", -- входит в группу abc

                "v" : "abc",
              },

              {
                "op" : "in_group", -- входит в группу xyz

                "v" : "xyz",
              },
          ],

          {
            "a" : "personal.region",     -- регион проживания

            "op" : "in",

            "b" : "order.region" 
          }
]

вверх

Метки условий

Метки условий позволяют узнать каким условиям сработал фильтр.

Это можно использовать для тонкой персонализации писем рассылки и красивого оформления сложных отчётов из member.list (например колонка в отчёте в которую выводится значение метки будет содержать или не содержать какое-то слово в завасимости от того сработало условие или нет).

Меткой может быть дополнено любое условии кроме "ИЛИ" (формат не позволяет, но метками можно снабдить внутренние условия этого "ИЛИ").

{
 <прочие условия фильтра>

 "label" : "lalala" -- имя метки фильтра, обязательно если хотите отметить это условие

,"label.yes" : "yyy" -- не обязательно, значение метки при выполнении условия

,"label.no" : "nnn" -- не обязательно, значение метки при не выполнении условия

}

Если указано только label, то при выполнении условия значением метки становится её же имя (lalala=lalala).

Если указано не пустое label.yes, то при выполнении условия значением метки становится значение label.yes (lalala=yyy)

Если указано не пустое label.no, то при не выполнении условия значением метки становится значение label.no (lalala=nnn)

Если значение присваивается метке, которая уже имеет значение, то старое значение заменяется новым.

Набор меток по результатам фильтрации доступен:

- для каждого адреса рассылки при персонализации текста письма - через объект member.LABEL.*

- в вызове member.list в ответе - через параметр label

- в вызове member.list в формате - через элемент { "label" : "lalala" }

Условия объединения

Для обеспечения приоритета условий используйте скобки, а не предположения какой у чего приоритет на основании "в таком-то языке так-то".

Условия вычисляются только до момента достаточного для принятия решения об их исходе.

Список условий ИЛИ прекращает вычисляться после первого же условия завершившегося положительно.

Список условий И прекращает вычислять после первого же условия завершившегося отрицательно.

Условие И


 {

  "op" : "AND" 

 ,"v" : [
          массив описывающий элементы фильтра. "И" между элементами
        ]

 }


 {

  "op" : "!AND" 

 ,"v" : [
          массив описывающий элементы фильтра. "И" между элементами потом отрицание полученного результата
        ]

 }

Условие ИЛИ

Действует между элементами прочих списков условий кроме основного списка фильтра и списка группировки.


 {

  "op" : "OR" 

 ,"v" : [
          массив описывающий элементы фильтра. "ИЛИ" между элементами
        ]

 }


 {

  "op" : "!OR" 

 ,"v" : [
          массив описывающий элементы фильтра. "ИЛИ" между элементами потом отрицание полученного результата
        ]

 }

вверх

Вхождение в состав группы


 {

  "op" : "in_group" или "!in_group" 

 ,"v"  : "код группы" 

 }

вверх

Попадание в выборку Универсальной Статистики


 {

  "op" : "stat.uni" или "!stat.uni" 

-- параметры stat.uni

  -- подразумевается unique = 1

 ,"v" : [ условие выборки как у запроса в вызове stat.uni ]

 ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

 ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.id" ]

 }

вверх

Целочисленое сравнение

Скалярное значение по ключу данных "a" как число не равно (!=), равно (==), меньше (<), меньше или равно (<=), больше или равно (>=), больше (>) чем число в "v" или величина по ключу данных "b"


 {

 ,"a"  : "ключ данных" 

 ,"op" : "!= | == | < | <= | => | >" 

-- или

 ,"v" : "целое число" 

-- или

 ,"b" : "ключ данных" 

 }

вверх

Cравнение "между"

Скалярное значение по ключу данных "a" больше или равно величина-1 и меньше или равно величина-2.

Сравнение целочисленное. Если у "a" указана точность даты, то сравниваются даты и величины-1/-2 трактуются как даты соответствующей точности.

!between работает как отрицание результата between.


 {

 ,"a"  : "ключ данных" 

 ,"op" : "between" или "!between" 

 ,"v" : [ величина-1, величина-2 ]
 }

вверх

Строчное сравнение

В большинстве случаев его использования можно избежать, если изначально спланировать структуру данных.

Скалярное значение по ключу данных "a" как строка не равно (ne), равно (eq), меньше (lt), меньше или равно (le), больше или равно (ge), больше (gt) чем строка в "v" или величина по ключу данных "b"


 {

 ,"a"  : "ключ данных" 

 ,"op" : "ne | eq | lt | le | ge | gt" 

-- или

 ,"v" : "строка" 

-- или

 ,"b" : "ключ данных" 
 }

вверх

Совпадение подстроки

В большинстве случаев его использования можно избежать, если изначально спланировать структуру данных.

Это относительно медленная операция и в большинстве случаев может быть заменена проверкой на вхождение элемента или его существование.

begins / !begins - строка по ключу данных "a" начинается/не начинается со строки "v" или строки по ключу данных "b"

сontains / !contains - строка по ключу данных "a" содержит/не содержит строки "v" или строки по ключу данных "b"

ends / !ends - строка по ключу данных "a" оканчивается/не оканчивается на строку "v" или строку по ключу данных "b"


 {

 ,"a"  : "ключ данных" 

 ,"op" : "begins | !begins | contains | !contains | ends | !ends" 

-- или

 ,"v" : "строка" 

-- или

 ,"b" : "ключ данных" 
 }

вверх

Величина null

Скалярное значение по ключу есть (is_null) или не есть (!is_null) неопределённое значение null

 {

 ,"a"  : "ключ данных" 

 ,"op" : "is_null" или "!is_null" 

 }

вверх

- in/!in (есть в stat.uni) - скаляр в списке / не в списке (строчное сравнение)

Вхождение в список

Скалярное значение по ключу входит (in) или не входит (!in) в список. Сравнение строчное.

Если данные по ключу не существуют, то in не совпадёт ни когда, а !in совпадёт всегда.

Если данные по ключу не скаляр, то in не совпадёт ни когда, а !in совпадёт всегда.

Если данные по ключу это null, то он совпадёт с "" в списке.

 {

 ,"a"  : "ключ данных" 

 ,"op" : "in" или "!in" 

 ,"v"  : [ "строка-1", "cтрока-2" ... ]
 }

вверх

Поиск по ключам объекта

has any of - cреди ключей объекта "a" есть хоть один из списка "v" (строчное сравнение). если "a" не объект то не совпадёт

has each of - cреди ключей объекта "a" есть каждый из списка "v" (строчное сравнение). если "a" не объект то не совпадёт

!has any of - отрицание has any of. если "a" не объект то не совпадёт

!has each of - отрицание has each of. если "a" не объект то не совпадёт

 {

 ,"a"  : "ключ данных" 

 ,"op" : "has any of"  | "has each of" | "!has any of"  | "!has each of" 

 ,"v"  : [ "строка-1", "cтрока-2" ... ]
 }

Итератор

Итератор предназначен для перебора всех значений списка или объекта и применения к ним суб-фильтра.

Итератор "has" хоть один элемент подходит под условие суб-филтра.

 {

 ,"a"  : "базовый ключ данных" 

 ,"op" : "has" 

 ,"v"  : [
          суб-фильтр группы. подразумевается "И" 
         ]

 ,"save" : "id-сохранения" -- не обязательно
 }

Итератор "!has" ни один элемент не подходит под условие суб-фильтр.

 {

 ,"a"  : "базовый ключ данных" 

 ,"op" : "!has" 

 ,"v"  : [
          суб-фильтр группы. подразумевается "И" 
         ]

 }

Если базовый ключ:
- не существует или null - результат итератора "не правда"
- если скаляр - фильтр вызывается один раз
- если массив - фильтр вызывается для каждого элемента, по порядку начиная с 0
- если объект - фильтр вызывается для каждого элемента, порядок не определён

При переборе существуют специальные ключи данных для доступа к перебираемым данных:

- "@"   - текущий элемент элемент индекса или массива или скаляря
- "@id" - величина индекса массива или ключа объекта текущего элемента или null для скаляра
- "$" - весь пользователь

При вложенных переборах
- "@[ddd]" - элемент индекса или массива или скаляря от внешнего has на ddd уровней выше
- "@id[ddd]" - величина индекса массива или ключа объекта текущего элемента или null для скаляра от внешнего has на ddd уровней выше

Множественные совпадения итератора has

Существуют ситуации, когда небходимо не просто выяснить результат итератора "совпал-не совпал", а что именно вызвало совпадение.

Например, у подписчика хранится список купленных им лицензий с указанием, среди прочего, даты окончания лицензии.

И надо выпустить напоминание тем, кто ещё не продлил лицензии оканчивающиеся затра.

{
 "a" : "product.license" -- ключ списка лицензий

 ,"op" : "has" 

 ,"v" : [
          { -- лицензия истекает завтра
           "a" : "@.expired:YD" 

          ,"op" : "==" 

          ,"v" : "current + 1 day" 
          }

         ,{ -- и ещё не продлена
           "a" : "@.prolongation" 

          ,"op" : "==" 

          ,"v" : "0" 
          }

         ,{ -- и адрес не в домене test.ru
            -- (формально эту проверку надо делать вне итератора, так как её итог не зависит от данных
            -- в элементе "license" и тут она приведена только для примера специального ключа "$")
           "a" : "$.member.domain" 

          ,"op" : "ne" 

          ,"v"  : "test.ru" 
          }

        ]
}

Простое применение "has" даст только список подписчиков у которых есть лицензии истекающие в ближайшее время.

А как определиить какие именно лицензии вызвали совпадение и что делать если таких лицензий несколько, а писем должно быть по одному на лицензию ?

Для этого предназначен параметр сохранения "save" - каждое совпадение вызывает сохранение ключа данных (а не самих данных) тестирование которого дало совпадение.

{
  "op" : "has" 

 .......

 ,"save" : "lic" 
}

Если у подписчика совпало, допустим, три лицензии, то их ключи данных будут записаны в специальный ключ данных в системной анкете member.FILTER.<величина-из-save>


{
 "member" : {
             ...

            ,"FILTER" : {
                         "lic" : [
                                  "product.license[2]" 
                                 ,"product.license[5]" 
                                 ,"product.license[6]" 
                                 ]
                        }

            ...
            }

}

При выпуске рассылки, в зависимости от параметра выпуска "multiple", сохранённые данные будут доступны как:

- multiple = 0 - как описано выше. member.FILTER хранит все совпадения всех итераторов группы-фильтра. подписчик получит одно письмо

- multiple = 1 - в группе фильтре должен сработать только один итератор c save. подписчик получит несколько писем в данных персонализации которых ключ сохранения будет скаляром последовательно принимающим все совпавшие значения.

В примере с лицезиями, это будет письмо с member.FILTER.lic = "product.license[2]", потом письмо с member.FILTER.lic = "product.license[5]" и ещё письмо с member.FILTER.lic = "product.license[6]".

Для доступа к данным совпавшей лицензии используйте комманду [% datakey(anketa,member.FILTER.lic) %]

Так же, можно просто получить список совпадения воспользовавшись вызовом member.list - в ответе в параметре "save" будет структура со всеми совпадениями аналогичная member.FILTER

вверх

Работа с датой и временем

Дата и время хранятся как обычные строки.

Однако при их записи в базу (member.set, member.import) рекомендуется указвать тип данных, что бы при записи происходила верификация и нормализация величины.

Без этого, строка представляющая дату-время может не верно проходить проверку с величиной current, так как такие операции требуют строки строго в формате "YYYY-ММ-DD hh:mm:ss" - т.е. год из четырёх цифр, а остальные из двух (с ведущим 0, если надо).

Для работы с датой-временем требуется указать квалификатор точности в ключе данных параметра "a" (или, при наличии, в ключе данных "b"). Это автоматически включит режим работы с датой-временем.

Он задаётся аналогично stat.uni - две буквы точности ("от" и "до") после двоеточия. Например "a.b[0].c[7]:Ym"

Указание квалификатора позволяет понять, что вы хотите использовать значение указанного ключа данных как дату-время, а не как строку или число.

При указании квалификатора, величина "v":

- при задании даты-времени константой - проверяется на соответствие указанной точности. иначе - ошибка

- при задании относительного времени (current) - его результат вычисляется с указанной точностью

- при задании квалификатора у "а", величина "b" без квалификатора считается указанной с таким же квалификатором.

- при задании квалификатора у "b", величина "a" без квалификатора считается указанной с таким же квалификатором.

- при задании квалификаторов и у "а" и у "b" они должны совпадать

При задании квалификатора, точность значения "a" ("b") приводится к указаной точности путём её уменьшения (или сохранние если они совпадают).

Если "a" ("b") меньшей точность чем указано в квалификаторе, то значение останется как есть. В любом случае дата-время в "a" ("b") должны быть в нормализованом формате "YYYY-MM-DD hh:mm:ss" (пример для точности Ys) и с ведущими нулями для компонентов меньших 10 (т.е не "1971-5-4 3:2:1", а "1971-05-04 03:02:01"). Для этого используйте параметр "type" когда вносите данные через member.set и member.import

Относительное время задаётся аналогично stat.uni - использованием слова "сurrent" и добавлением/вычитанием необходимых компонентов (лет,месяцев,дней,часов..).

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

Если значение по ключу данных не дата-время в верном формате, null, массив или объект, то результат не предсказуем и зависит как от значения, там и от используемого "op".

Допустимые операции c датой-временем

- численные и строчные сравнения (в данном случае они ведут себя одинаково так как явно указан тип данных)

- in и !in

- begins/!begins/contains/!contains/ends/!ends

- between и !between

Если вы уверены, что никогда не ошибётесь в значении "v" при внесении в базу и задании даты-времени константой и не используете относительное время, то квалификатор точности можно не использовать - операции строчного сравнения, in/!in, begins/!begins/contains/!contains/ends/!ends сработают как и ожидается.

вверх

Функция size()

Обычный ключ

К величине на которую указывает ключ данных в параметре "a" можно применить функцию размера size().

{

 "a" : "size(member.email)" 

 .....

}

Если величина null, то результат - null.

Если величина скаляр, то результат - его длина в символах.

Если величина массив, то результат - количество элементов в массиве.

Если величина объект, то результат - количество пар "ключ-значение" в объекте.

По результатам has/save

Применение size() к результатам сохранёным в итераторе has даст количество сохранённых элементов для данного id-сохранения

{

 "a" : "save_size(id-cохранения)" 

 .....

}

Функции агрегации

Доступны функции суммы величин sum(), минимальной величины min(), максимальной величины max(), среднего значения - avg().

В примерах используется agr как имя любой их этих функций

Обычный ключ

Если ключ показывает на скаляр, то результат - значение скаляра.

Если ключ показывает на массив, то результат - сумма/минимальный/максимальный/cреднее из элементов массива которые должный быть скалярами. Элемент null пропускается. Строки преобразуются в числа. Не скалярный элемент вызовет ошибку фильтра.

{
 "a" : "agr(КД)" 
}

Если ключ показывает на объект, то результат - сумма/минимальный/максимальный/cреднее из элементов (которые должный быть скалярами) взятых взятых от каждой пары объекта по относительному ключу. Элемент null пропускается. Строки преобразуются в числа. Не скалярный элемент вызовет ошибку фильтра.

{
 "a" : "agr(КД-ОБЪЕКТ,@.КД-ОТНОСИТЕЛЬНO-ОБЪЕКТА)" 
}

По результатам has/save

Применение к результатам сохранёным в итераторе has даст соответствующую функции величину из элементов взятых по относительному ключу от каждого ключа из сохранённых в id-сохранения. Элемент null пропускается. Строки преобразуются в числа. Не скалярный элемент вызовет ошибку фильтра.

{

 "a" : "save_agr(id-cохранения,@.КД-ОТНОСИТЕЛЬНЫЙ)" 

 .....

}

вверх

Фильтр отбора в группе (АВО)

Этот фильтр работает только по данным доступным по формату АВО и его признаком является наличие "which" в первом элементе.

Без этого фильтр считатся заданным в формате ДК, описаном выше.

Фильтр отбора состоит из списка элементов задающих условия отбора или условия объединения других условий.

Пустой фильтр (т.е. не содержащий ни одного элемента) ни когда не совпадает ни с чем и результат его использования всегда пустой.

Каждый элемент содержит поле "which" задающее его тип и возможные дополнительные поля в зависимости от типа:

При получении описания фильтра вставляются дополнительные поля aid.name, qid.name и pid.name содержашие названия соответсвующие кодам.

И значения ключей хэша resp так же становятся названиями соответствующих ответов.

Это позволяет сократить количесво запросов к API для визуализации фильтра на стороне клиента.

При установке значения фильтра эти дополнительные поля и значения ключей хэша resp игнорируются.

вверх

Формальное описание


[

  {

   "which" : "тип элемента 1" 

   <возможно дополнительные поля>

  }

 ,{

   "which" : "тип элемента 2" 

   <возможно дополнительные поля>

  }

 ...

 ,{

   "which" : "тип элемента N" 

   <возможно дополнительные поля>

  }

]

вверх

Пример

Адрес имеет 2 и меньше ошибки доставки И регион проживания один из RU01,RU05,RU40 И день рождения завтра И ( входи в группу abc ИЛИ входит в группу xyz )

При записи фильтра


[

          {

            "which" : "<=",      -- Адрес имеет 2 и меньше ошибки доставки

            "aid" : "member",

            "qid" : "error",

            "resp" : "2" 

          },

          {

            "which" : "AND" 

          },

          {

            "which" : "any",     -- регион проживания один из RU01,RU05,RU40

            "aid" : "personal",

            "qid" : "region",

            "resp" : {

                        "RU01" : null

                       ,"RU13" : null

                       ,"RU40" : null

                     }

          },

          {

            "which" : "AND" 

          },

          {

            "which" : "dtnowmd",     -- день рождения завтра

            "aid" : "personal",

            "qid" : "birthday",

            "resp" : "+1" 

          },

          {

            "which" : "AND" 

          },

          {

            "which" : "group",

            "group" : [

                         {

                           "which" : "PRF", -- входи в группу abc

                           "resp" : 1,

                           "pid" : "abc" 

                         },

                         {

                           "which" : "OR" 

                         },

                         { 

                           "which" : "PRF", -- входи в группу xyz

                           "resp" : 1,

                           "pid" : "xyz" 

                         }

          }

При получении фильтра


[

          {

            "which" : "<=",      -- Адрес имеет 2 и меньше ошибки доставки

            "aid" : "member",

            "qid" : "error",

            "resp" : "2",

            "aid.name" : "Cистемная анкета",

            "qid.name" : "Количество ошибок доставки" 

          },

          {

            "which" : "AND" 

          },

          {

            "which" : "any",     -- регион проживания один из RU01,RU05,RU40

            "aid" : "personal",

            "qid" : "region",

            "aid.name" : "Персональные данные",

            "qid.name" : "Регион" 

            "resp" : {

                        "RU01" : "Адыгея" 

                       ,"RU13" : "Мордовия" 

                       ,"RU40" : "Калуга" 

                     }

          },

          {

            "which" : "AND" 

          },

          {

            "which" : "dtnowmd",     -- день рождения завтра

            "aid" : "personal",

            "qid" : "birthday",

            "resp" : "+1" 

            "aid.name" : "Персональны данные",

            "qid.name" : "День рождения" 

          },

          {

            "which" : "AND" 

          },

          {

            "which" : "group",

            "group" : [

                         {

                           "which" : "PRF", -- входи в группу abc

                           "resp" : 1,

                           "pid" : "abc" 

                           "pid.name" : "Группа знающих буквы A-B-C" 

                         },

                         {

                           "which" : "OR" 

                         },

                         { 

                           "which" : "PRF", -- входи в группу xyz

                           "resp" : 1,

                           "pid" : "xyz" 

                           "pid.name" : "Группы знающих буквы X-Y-Z" 

                         }

          }

вверх

Условия объединения

Для обеспечения приоритета условий используйте скобки, а не предположения какой у чего приоритет на основании "в таком-то языке так-то".

Условие И


 {

  "which" : "AND" 

 }

Условие ИЛИ


 {

  "which" : "OR" 

 }

Группа условий (скобки)


 {

  "which" : "group" 

  "group" : [

              массив описывающий фильтр

            ]

 }

вверх

Вхождение в состав группы


 {

  "which" : "PRF" 

 ,"resp"  : "0 - не входит в группу, 1 - входит в группу" 

 ,"pid"   : "код проверяемой группы" 

 }

вверх

Попадание в выборку Универсальной Статистики


 {

  "which" : "stat.uni" 

 ,"resp"  : "0 - не попадает, 1 - попадает" 

-- параметры stat.uni

  -- подразумевается unique = 1

 ,"filter" : [ условие выборки как у запроса в вызове stat.uni ]

 ,"cache"  : [ настройки кэширования как у запроса в вызове stat.uni ]

 ,"select" : [ ... ] -- используйте только если не подходит значение по умолчанию [ "member.id" ]

 }

вверх

Любой из списка ответов

Хотя бы одни ответ из списка есть в ответах подписчика


 {

  "which" : "any" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"resp"   : {

              "код ответа 1" : null

             ,"код ответа 2" : null

              .....

             }

 }

вверх

Каждый из списка ответов

Каждый ответ из списка есть в ответах подписчика


 {

  "which" : "each" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"resp"   : {

              "код ответа 1" : null

             ,"код ответа 2" : null

              .....

             }

 }

вверх

Есть хоть один ответ

Выбран хотя бы один ответ


 {

  "which" : "atleast" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Нет ни одного ответа

На вопрос не выбран ни один ответ


 {

  "which" : "empty" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Целочисленое сравнение

Ответ на вопрос как число не равен (!=), равен (==), меньше(<), меньше или равен (<=), больше или равен (>=), больше (>) чем число в "resp"


 {

  "which" : "!= | == | < | <= | => | >" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"resp" : "целое число" 

 }

вверх

Числено равно сегодня

Ответ на вопрос числено равен текущему году (nowy), месяцу (nowm) или дню (nowd)


 {

  "which" : "nowy | nowm | nowd" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Дата равна сегодня

Компонента "дата" в ответе равна (dtnow), не равна (dtnowne), ранее (dtnowlt), ранее или равна (dtnowle), позже или равна (dtnowge), позже (dtnowgl) чем "дата сегодня +/- сдвиг"


 {

  "which" : " dtnow | dtnowne | dtnowlt | dtnowle | dtnowge | dtnowgt " 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"resp" : "сдвиг в днях от сегодня" -- не обязательно. например: -2 - позавчера, +1 - завтра

 }

вверх

Месяц и день равны сегодня

Компонента "месяц и день" в ответе равны месяцу и дню в "дате сегодня +/- сдвиг"


 {

  "which" : "dtnowmd" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"resp" : "сдвиг в днях от сегодня" -- не обязательно. например: -2 - позавчера, +1 - завтра

 }

вверх

Год равен текущему

Компонен "год" в ответе равен текущему году


 {

  "which" : "dtnowy" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Месяц равен текущему

Компонен "месяц" в ответе равен текущему месяцу


 {

  "which" : "dtnowm" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

День равен текущему

Компонен "день" в ответе равен текущему дню +/- сдвиг


 {

  "which" : "dtnowd" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"resp" : "сдвиг в днях от сегодня" -- не обязательно. например: -2 - позавчера, +1 - завтра

 }

вверх

Строка пуста

Ответ пустой


 {

  "which" : "emptyS" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Строка не пуста


 {

  "which" : "dirtyS" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Cтрочное сравнение

Это медленый фильтр.

В будущем он может быть удалён.

В большинстве случаев его использования можно избежать, если изначально делать вопрос не полем ввода, а списком выбора

Строка ответа на вопрос не равна (ne), равна (eq), меньше(lt), меньше или равна (le), больше или равна (ge), больше (gt) чем строка в "resp"

Для вопросов типа дата-время сравнение производится с учётом точности resp.

Например, при содержимом ответа на вопрос "2015-03-26 18:36:25" (от года до секунды) сравнение с "2015-03-30" (от года до дня) вызовет
сначала снижение точности ответа до аналогичной - "2015-03-26" и только потом будет проведено сравнение.

Если точность resp выше точности ответа на вопрос, то точности уравниваться не будут и произойдёт обычное стравнение строк.


 {

  "which" : "ne | eq | lt | le | ge | gt" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"resp" : "строка для сравнения" 

 }

вверх

Наличие подстроки в строке

Это медленый фильтр.

В будущем он может быть удалён.

В большинстве случаев его использования можно избежать, если изначально делать вопрос не полем ввода, а списком выбора

Строка ответа начинается (beg) или не начинается (nbeg) со строки "resp"; содержит (has) или не содержит (nhas) строку "resp"; заканчивает (end) или не заканчивается (nend) строкой "resp"


 {

  "which" : "has | nhas | beg | nbeg | end | nend" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"resp" : "строка" 

 }

вверх

Примеры запросов универсальной статистики

Переходы по ссылкам

Суммарное количество переходов по ссылкам


{

  "select" : ["issue.name" ,"issue.group.name" ,"issue.dt" ,"issue.clicked" ,"issue.members"]

 ,"order"  : ["issue.dt"] 

}


{

  "select" : ["issue.name" ,"issue.group.name" ,"issue.dt" ,"issue.clicked" ,"issue.members"]

 ,"filter" : [{ "a" : "issue.group.gid", "op" : "==" , "v" : "gdfhdfh" }]

 ,"order"  : ["issue.dt"] 

}


{

  "select" : ["issue.name" ,"issue.group.name" ,"issue.dt" ,"issue.clicked" ,"issue.members"]

 ,"filter" : [ {"a" : "issue.group.gid", "op" : "==", "v" : "gdfhdfh" }
             , {"a" : "issue.dt", "op" : ">=", "v" : "2011-03-17 11:30:27"}] 

}


{

  "select" : ["issue.name" ,"issue.group.name" ,"issue.dt" ,"issue.clicked" ,"issue.members"]

 ,"filter" : [{"a" : "issue.id", "op" : "==", "v" : 15 }]

 ,"order"  : ["issue.dt"] 

}


{

  "select" : ["issue.name" ,"issue.group.name" ,"issue.dt" ,"issue.clicked" ,"issue.members"]

 ,"filter" : [ {"a" : "issue.group.gid", "op" : "==", "v" : "gdfhdfh" }
             , {"a" : "issue.dt", "op" : "==", "v" : "2011-03-17 11:30:27" }]

 ,"order"  : ["issue.dt"] 

}

Переходы по ссылкам за период с разбивкой по времени


{

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"order"  : ["click.dt:YD"]

}


{

  "select" : ["click.dt:YM" ,"count(*)"]

 ,"order"  : ["click.dt:YM"]

}


{

  "select" : ["click.dt:YY" ,"count(*)"]

 ,"order"  : ["click.dt:YY"]

}


{ 

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"filter" : [ { "a" : "click.dt:YM", "op" : ">=", "v" : "2011-05" }
             , { "a" : "click.dt:YM", "op" : "<=", "v" : "2012-05" } ]

 ,"order"  : ["click.dt:YD"] 

}


{

  "select" : ["click.dt:YM" ,"count(*)"]

 ,"filter" : [ { "a" : "click.dt:YY", "v" : "2011", "op" : ">=" }
             , { "a" : "click.dt:YY", "op" : "<=", "v" : "2010" } ]

 ,"order"  : ["click.dt:YM"] 

}


{

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"filter" : [ { "a" : "issue.id", "op":"==", "v" : 15}
             , { "a" : "click.dt:YM", "op" : "<=", "v" : "2011-05" }]

 ,"order"  : ["click.dt:YD"] 

}


{

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"filter" : [{"a" : "issue.dt:YY", "op":">", "v" : "2011"} ]

 ,"order"  : ["click.dt:YD"] 

}


{

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"filter" : [ { "a" : "issue.dt:YY", "op":">", "v" : "2011"}
             , { "a" : "issue.group.gid", "op" : "==", "v" : "gdfhdfh" }]

 ,"order"  : ["click.dt:YD"] 

}

Переходы по каждой ссылке в выпуске


{

  "select" : ["click.link.url" ,"count(*)"]

 ,"order"  : ["click.link.url"] 

}


{ 

  "select" : ["click.link.url" ,"count(*)"]

 ,"order"  : ["click.link.url"]

 ,"filter" : [ { "a" : "click.dt:YY", "op" : "==", "v" : "2010" } ] 

}


{

  "select" : ["click.link.url" ,"count(*)"]

 ,"filter" : [ { "a" : "click.dt:YY", "op" : "==", "v" : "2011" }
             , { "a" : "issue.id", "op" : "==", "v" : "16" } ] 

 ,"order"  : ["click.link.url"]

}


{

  "select" : ["click.link.url" ,"count(*)"]

 ,"order" : ["click.link.url"]

 ,"filter" : [ { "a" : "click.dt:YY", "op" : ">=", "v" : "2011" }
             , { "a":"issue.group.gid", "op":"==", "v":"all"}
             , { "a" : "issue.dt", "op" :  "==", "v" : "2010-01-26 16:51:26" } ] 

}

Переходы по определенной ссылке


{

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"filter" : [ {"a" : "click.link.url", "op" : "==", "v" : "http://www.disney.com/" }] 

}


{

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"filter" : [ {"a" : "click.link.url", "op" : "==", "v" : "http://www.disney.com/" }
             , { "a" : "click.dt", "op" : ">=", "v" : "2011-05-11 14:25:54" }] 

}


{

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"filter" : [ {"a" : "click.linkgroup.id", "op" : "==", "v" : "16" }
             , { "a" : "click.dt", "op" : ">=", "v" : "2011-05-11 14:25:54" }] 

}


{

  "select" : ["click.dt:YD" ,"count(*)"]

 ,"filter" : [ {"a" : "click.linkgroup.name", "op" : "==", "v" : "Профиль all" }
             , {"a" : "click.dt", "op" : ">=", "v" : "2011-05-11 14:25:54" }] 

}

Переходы определенного подписчика по ссылкам


{

  "select" : ["click.dt:YD" ,"click.link.url" ,"count(*)"]

 ,"filter" : [{"a" : "member.email", "op" : "==", "v" : "vadim\@iprojects.ru" }]

}


{

  "select" : ["click.dt:YD","click.link.url ","count(*)"]

 ,"filter" : [ {"a" : "member.email", "op" : "==", "v" : "ask\@subscribe.ru" }
             , { "a" : "click.dt:Ym", "op" : ">=", "v" : "2011-08-10 13:30" } ] 

}

Чтения

Все чтения выпуска


{

  "select" : ["issue.name", "issue.group.name" ,"issue.dt" ,"issue.readed" ,"issue.members"]

}


{

  "select" : ["issue.name", "issue.group.name" ,"issue.dt" ,"issue.readed" ,"issue.members"]

 ,"filter" : [ {"a" : "issue.group.gid", "op" : "==", "v" : "import20110627175254" }
             , { "a" : "issue.dt", "op" : "==", "v" : "2011-06-28 13:54:48" } ] 

}

Чтения выпуска за период с разбивкой по времени (по дням, месяцам, годам)


{

  "select" : ["read.dt:YY" ,"count(*)"]

}


{

  "select" : ["read.dt:YM" ,"count(*)"]

}


{

  "select" : ["read.dt:YD" ,"count(*)"]

}


{

  "select" : ["read.dt:YD" ,"count(*)"]

 ,"filter" : [ {"a" : "issue.group.gid", "op" : "==", "v" : "import20100720185533" }
             , { "a" : "issue.dt", "op" : "==", "v" : "2010-10-18 17:17:17" }] 

}


{ 

  "select" : ["read.dt:YD" ,"count(*)"]

 ,"filter" : [{"a" : "issue.dt:Ym", "op" : ">", "v" : "2010-10-18 17:30" }] 

}

Зафиксированные чтения выпусков подписчиками


{

 "select" : ["read.dt" ,"member.email"]

} 


{ 

  "select" : ["read.dt" ,"member.email"]

 ,"filter" : [ {"a":"read.dt:YD", "op" : "<=", "v" : "2010-10-20" }]

 ,"order"  : [ "read.dt"] 

}


{

  "select" : ["read.dt" ,"member.email"]

 ,"filter" : [ { "a" : "read.dt:YD", "op" : "<=", "v" : "2010-10-20" }
             , { "a" : "issue.group.gid", "op" : "==", "v" : "import20100720185533" }
             , { "a" : "issue.dt", "op" : "==", "v" : "2010-10-18 17:17:17" }
             ]

 ,"order"  : [ "read.dt"] 

}

Доставка выпусков

Успешная доставка


{

  "select" : ["issue.name" ,"issue.group.name" ,"issue.dt" ,"issue.deliv_ok" ,"issue.members"]

 ,"filter" : [ { "a" : "deliv.status", "op" : ">", "v" : "0" } ]

}


{

  "select" : ["issue.name","issue.group.name","issue.dt","issue.deliv_ok","issue.members"]

 ,"filter" : [ { "a" : "deliv.status", "op" : ">", "v" : "0" }
             , { "a" : "issue.group.gid", "op" : "==", "v" : "masssending" } ] 

}


{

  "select" : ["issue.name","issue.group.name","issue.dt","issue.deliv_ok","issue.members"]

 ,"filter" : [ { "a" : "deliv.status", "op" : ">", "v" : "0" }
             , { "a" : "issue.group.gid", "op" : "==", "v" : "masssending" }
             , { "a" : "issue.dt", "op" : "==", "v" : "2010-05-31 16:23:47" } ] 

}

Ошибки при доставке выпусков


{

  "select" : ["issue.name","issue.group.name","issue.dt","member.email","deliv.status"]

 ,"filter" : [ { "a" : "deliv.status", "op" : "<", "v" : "0" } ] 

}


{

  "select" : ["member.email","deliv.status"]

 ,"filter" : [ { "a" : "deliv.status", "op" : "<", "v" : "0" }
             , { "a" : "issue.group.gid", "op" : "==", "v" : "p212" }
             , { "a" : "issue.dt", "op" : "==", "v" : "2010-12-31 23:02:29" } ] 

}

Доставка выпусков определенному подписчику


{

  "select" : ["issue.name","issue.group.name","issue.dt","deliv.status"]

 ,"filter" : [ { "a" : "member.email", "op" : "==", "v" : "test@test.ru" } ] 

}


{

  "select" : ["issue.name","issue.group.name","issue.dt","deliv.status"]

 ,"filter" : [ { "a" : "member.email", "op" : "==", "v" : "test@test.ru"}
             , { "a" : "issue.dt:YM", "op" : ">=", "v" : "2010-12" } ] 

}

Замена вызова stat.issue.delivering


{

  "select" : ["issue.id","issue.group.gid","issue.dt","member.email","deliv.status"]

 ,"filter" : [ {"a" : "issue.dt:YM", "op" : ">=", "v" : "2011-08" }
             , {"a":"deliv.status", "op":"<","v":"0"} ]

 ,"order"  : ["issue.id","issue.dt","member.email"] 

}


{

  "select" : ["member.email","count(deliv.*)"]

 ,"filter" : [ {"a" : "issue.dt:YM", "op" : ">=", "v" : "2011-08" }
             , { "a":"deliv.status", "op":"<","v":"0"} ]

 ,"order"  : ["member.email"] 

}


{

  "select" : ["member.email","count(deliv.*)"]

 ,"filter" : [{"a" : "issue.dt:YM", "op" : ">=", "v" : "2011-08" }]

 ,"order"  : ["member.email"] 

}

Сводные отчеты

Суммарное число чтений, кликов по дням


{

  "select" : ["*.dt:YD", "count(click)", "count(read)" ]

 ,"order"  : ["-*.dt:YD"]

}

Суммарное число чтений, кликов по месяцам с 2011 года


{ 

  "select" : ["*.dt:YM", "count(click)", "count(read)" ]

 ,"filter" : [{"a":"*.dt:YY", "op" : ">=", "v" : "2011" }] 

 ,"order"  : ["-*.dt:YM"]

}

Суммарное число много чего по дате события

Указание *.dt:YM ограничивает дату события - т.е. дату клика, чтения, выпуска. Даты событий не взаимосвязаны и клики и чтения берутся все случившиеся

в указанный интервал. Сравните со следующим отчётом.


{

  "select" : ["*.dt:YM","sum(issue.members)","count(deliv)","count(read)","count(unique read.member.id)","count(click)", "count(unique click.member.id)"]

 ,"filter" : [ {"a" : "*.dt:YM", "op" : ">=", "v" : "2011-06" }
             , {"a" : "deliv.status", "op" : ">", "v" : "0" }] 

 ,"order"  : ["-*.dt:YM"]

}

Суммарное число много чего для даты выпуска

Указание *.issue.dt:YM ограничивает дату выпуска - т.е. клики и чтения считаются только от выпусков указанного интервала


{

  "select" : ["issue.dt:YM","sum(issue.members)","sum(issue.deliv_ok)","sum(issue.clicked)","sum(issue.readed)"]

 ,"filter" : [ {"a" : "issue.dt:YM", "op" : ">=", "v" : "2011-06" } ]

 ,"order"  : ["issue.dt:YM"]

}

Статистика активности по выпускам

Быстрый вызов по заранее подготовленным данным с небольшим (5-10 минут) отставанием от реального времени


{

  "select" : [ "issue.name", "issue.dt", "issue.group.name", "issue.members",
             , "issue.deliv_ok", "issue.deliv_bad", "issue.clicked", "issue,u_clicked",
             , "issue.readed", "issue.u_readed", "issue.unsubed" 
             ]

 ,"filter" : [ {"a" : "issue.dt", "op" : ">=", "v" : "2011-06-01 00:00:00" } ] 

 ,"order"  : [ "issue.dt" ]

}

Аналогичный, но заметно более медленный на больших списках, вызов по данным в реальном времени

{

  "select" : [ "*.issue.name", "*.issue.dt", "*.issue.group.name", "*.issue.members" 
             , "count(deliv_ok)", "count(deliv_bad)", "count(click)", "count(unique click.member.id)" 
             , "count(read)", "count(unique read.member.id)" 
             ]

 ,"filter" : [ {"a" : "*.issue.dt", "op" : ">=", "v" : "2011-06-01 00:00:00" } ] 

 ,"order"  : ["*.issue.dt"]

}

Запрос с объединением двух единичных запросов

Для каждого участника выпуска номер 50 выбирается его адрес, дата внесения в базу,дата выпуска рассылки, статус доставки, дата первого клика в письме рассылки, общее число кликов в письме

Уникальный ключ составлен из номера получателя и номера выпуска. В данном случае это избыточно (номер выпуска у всех один), но если запрос подразумевает охват нескольких выпусков, то это необходимо.
Вместо номера получателя можно было бы использовать его адрес (он так есть в выборке), но тогда будет выполняться дольше, так как числовое поле (номер) в уникальном ключе работает быстрее чем строчное (адрес).

{
 "action":"stat.uni" 

,"groupby" : 2 -- размер ключа

,"join" : [

   {  -- Первый запрос. На основе списков доставки выбираются участники выпуска 50 и их данные
    "select":[
             -- уникальный ключ
              "deliv.member.id" 
             ,"deliv.issue.id" 

             -- данные об адресе и доставке
             ,"deliv.member.email" 
             ,"deliv.member.anketa.member.create.time" 
             ,"deliv.issue.dt" 
             ,"deliv.status" 
             ]

   ,"filter":[
              {"a":"deliv.issue.id","op":"==","v":"50"}
             ]
  }
,

  {   -- Второй вопрос. Данные о кликах выпуска 50
   "select":[
            -- уникальный ключ
             "click.member.id" 
            ,"click.issue.id" 

            -- данные о первом клике и их количество
            ,"min(click.dt)" 
            ,"count(*)" 
            ]

  ,"filter":[
             {"a":"click.issue.id","op":"==","v":"50"}
            ]
  }
       ]

,"caption":[
           -- Один раз колонки уникального ключа

            "Номер получателя" 
           ,"Номер рассылки" 

           -- Колонки первого запроса

           ,"Адрес получателя" 
           ,"Дата регистрации" 
           ,"Дата рассылки" 
           ,"Статус доставки" 

           -- Колонки второго запроса

           ,"Дата первого клика" 
           ,"Число кликов" 
           ]
}

В результате получится примерно следующее.

Пользователь второй из строки не кликал, что отражается как null в соответствующий колонках.

Номер получателя Номер рассылки Адрес получателя Дата регистрации Дата рассылки Статус доставки Дата первого клика Число кликов
34 50 2013-08-27 13:49:14 2013-10-04 16:26:03 1 2013-10-04 16:45:28 1
21 50 2013-08-27 10:07:17 2013-10-04 16:26:03 1 null null
78 50 2013-08-27 10:36:32 2013-10-04 16:26:03 1 2013-10-04 16:45:28 3

Форматы данных для импортирования и Экспресс-Выпуска

Указание дат и времени для модели АВО

В зависимости от точности заданной в анкете для вопроса типа "дата", необходимо использовать один из следующих форматов записи даты или даты-времени в данных импорта:

точность yd - "YYYY-MM-DD" 
точность yh - "YYYY-MM-DD hh"
точность ym - "YYYY-MM-DD hh:mm"
точность ys - "YYYY-MM-DD hh:mm:ss"

Указание дат и времени для модели КД

Рекомендации для указания даты и времени теже что и описанные в вызове member.set

Присоединение или удаление дополнительных идентификаторов для модели АВО

При внесении пользователей возможно указание его дополнительных идентификаторов для присоединения или удаления.

Специальные колонки называются member.head.attach и member.head.detach и ведут себя аналогично одноимённым вызовам.

В отличии от обычных колонок данных эти специальные колонки могут быть указаны не по одному разу.

Допустимое значение может быть

Если идентификаторы в колонке имеют одни и тот же тип который не определяется автоматически, то тип можно указать в названии колонки. Например "member.head.attach:csid".

Указание типа в названии колонки полезно и при внесении автоматически определяемых типов для строго контроля. Например при добавлении номеров телефонов и без
строгой типизации случайно попавший в данные email будет обработан. А при строгой типизации - нет.

Без строгой типизации возможно указание типа перед идентификатором через двоеточие если не срабатывает автоопределение типа. Например "csid:Lalalala".

Вся запись пользователя вносится атомарно - проблема с присоединением или удалением отменит все действия (изменение прочих данных, внесение в группу импорта, создание подписчика).

Пример:

email,a265.q827,member.head.attach,a265.q300,member.head.attach
30_m-1@test.ru,30-M1-265+,30_m-1-A@test.ru,30-M1-300,30_m-1-B@test.ru
30_m-2@test.ru,30-M2-265+,,30-M1-300,30_m-2-B@test.ru
30_m-3@test.ru,30-M3-265+,30_m-3-A@test.ru,30-M3-300,30_m-3-B@test.ru|30_m-3-C@test.ru
30_m-4@test.ru,30-M2-265+,csid:xxxx,

Присоединение или удаление дополнительных идентификаторов для модели КД

При внесении пользователей возможно указание его дополнительных идентификаторов для присоединения или удаления.

Специальные ключи данных называются member.head.attach и member.head.detach и ведут себя аналогично одноимённым вызовам.

{
 "datakey" : "member.head.attach|detach" 
,"addr_type" : "тип адреса" -- не обязательно. строгая типизация типа идентификатора.
}

Допустимое значение может быть

Если идентификаторы в имеют одни и тот же тип который не определяется автоматически, то тип можно указать в addr_type.

Указание типа полезно и при внесении автоматически определяемых типов для строго контроля. Например при добавлении номеров телефонов и без
строгой типизации случайно попавший в данные email будет обработан. А при строгой типизации - нет.

Без строгой типизации возможно указание типа перед идентификатором через двоеточие если не срабатывает автоопределение типа. Например "csid:Lalalala".

Вся запись пользователя вносится атомарно - проблема с присоединением или удалением отменит все действия (изменение прочих данных, внесение в группу импорта, создание подписчика).

Описание

Для импортирования данных или выпуска рассылки через Экспресс-Выпуск доступны четыре формата описания адресов и их данных.

Данные задаваемые непосредственно в вызове указываются в параметре users.list

Внешние данные указываются ссылкой в параметре users.url

Вместо настоящего идентификатора подписчика вы можете указывать номер подпиcчика в системе - id:DDDD - где DDD номер подписчика доступный через member.id
(только для уже существующих подписчиков)

При использовании в модели ABO значение для вопроса с выбором может быть как кодом ответа, так и названием ответа.

Если значение совпало с имеющимся кодом - значит это код.

Иначе значение сопоставляется с названиями ответов и при нахождении точного соответствия будет использовать код ответа найденного названия.

Если два ответа имеют одинаковое название - выбирается первый по порядку строчной сортировки их кодов.

JSON-массив

Непосредственно задание массива содержащего объекты каждый из которых описывает один адрес и его данных любой структуры. (только Экспресс-Выпуск)

Данные трактуются по моделе КД.

При выпуске почтовой рассылки адрес получателя задаётся ключём member.email

При выпуске sms или viber рассылки номер получателя задаётся ключём member.cellphone

Остальные данные не обязательны и могут быть любой структуры, что в сочетании с возможностью шаблонизатора организовывать циклы, получать списки ключей и значений объектов позволяет создавать очень сложные шаблоны.

JSON-объект-АВО

Непосредственно задание объекта содержащего массив caption для описания соответствия столбцов данных анкетам и массив rows для самих данных.

При выпуске почтовой рассылки адрес получателя находится в колонке member.email.

При выпуске sms или viber рассылки номер получателя находится в колонке member.cellphone.

Остальные данные не обязательны, но при их наличии каждая колонка содержит только по одному значению для каждого адреса (не может быть массивом или объектом) и должна быть описана в caption.

JSON-объект-КД

Непосредственно задание объекта содержащего массив caption для описания соответствия столбцов ключам данных и массив rows для самих данных. (только импорт)

XLSX

Представление содержимого файла XLSX (Excel 2007) в виде строки.

Данные трактуются по моделе АВО

Первый лист должен содержать в первой строке описание какой анкете и ответу соответствует данная колонка (в формате коданкеты.кодвопроса).

При импорте возможно задание этого соответствия в параметре caption вместо первой строки первого листа.

Одна ячейка может содержать только одно значение для каждого адреса (не может быть массивом или объектом).

Понимаются дzанных сжатые архиватором zip, хотя пользы от этого ни какой - xslx и так является zip-архивом и повторное сжатие нечего не даст.

CSV

Представление содержимого файла CSV в виде строки.

Данные трактуются по моделе АВО

Разделитель колонок - запятая или точка с запятой.

Cимвол экранирования - двойная кавычка.

Содержимое ячейки может быть заключено в кавычки.

Если надо внести сразу несколько ответов на вопрос с множественным выбром, то ячейка должна содержать кода необходимых ответов разделенной символом вертикальной черты "|".

Первая может содержать "#charset=кошка" для однозначного определения кодировки.

Тогда следующая строка должна содержать описание какой анкете и ответу соответствует данная колонка (в формате коданкеты.кодвопроса).

При импорте возможно задание этого соответствия в параметре caption вместо строки.

Одна ячейка может содержать только одно значение для каждого адреса (не может быть массивом или объектом).

Понимаются данных сжатые архиватором zip.

Если в колонке как часть его значения встречается символ разделитель колонок, то значение колоноки должно быть взято в кавычки.
Если в колонке как часть его значения встречается символ экранирования, то значение колоноки должно быть взято в кавычки, а символ экранирования удвоен.

Верно:

test@test.ru,"ЗАО ""Рога и копыта""",+70000000000
test@test.ru,"Рога, копыта и хвосты",+70000000000

НЕ верно:

test@test.ru,ЗАО "Рога и копыта",+70000000000
test@test.ru,Рога, копыта и хвосты,+70000000000

Порядок определения формата при использовании users.list

1) Если значение массив, то данные считаются заданными в формате JSON-массив

2) Если значение объект, то данные считаются заданными в формате JSON-объект

3) Если значение строка представляющая zip-архив и он содержит файл workbook.xml, то считается что это XLSX (технически XLSX это zip-архив со специальным набором файлов)

4) Если значение строка представляющая zip-архив и он не содержит файл workbook.xml, берётся первый по порядку файл архива и cчитается, что это данные в формате CSV

5) Если значение строка не-zip-архив, то считается, что это данные в формате CSV

Порядок определения формата при использовании users.url

1) Если content-type ответа application/json, то данные должны описывать массив или объект и формат выбирается как описано выше в пунктах 1 и 2.

2) При другом значении content-type выбор происходит как описано выше в пунктах 3, 4 и 5.

Пример задания JSON-массив

Доступ к этим данным при Экспресс-Выпуске осуществляется с использованием префикса "anketa".

Например: [% anketa.member.email ] [ anketa.hash.DEF ] [ anketa.peronal.something0.aaa %]

"users.list" : [

{
 "member" : { "email" : "test@test.ru" },

-- простые данные

 "string" : "строка текста какой-то длинные",

 "hash" : {
           "DEF" : "значение ключа DEF" 
          ,"ABC" : "значение ключа ABC" 
          },

 "array" : [ "первый элемент" ,"второй элемент" ,"третий элемент" ....],

-- структура с вложенными данными

 "personal" : {

      "fio" : {
               "fam" : "Фамилиё" 
              ,"name" : "Ымя" 
              }

      "homephone" : "112",

      "gender" : "есть",

      "something" : [ { "aaa" : "bbb", "ccc" : "ddd" }, "eeeee", [ "ffff", "ggg", "hhh"] ]
 }

-- ещё структура

"kredit-payment-by-month" : [
                              {
                               "month" : "2012-12" 
                              ,"summa" : "1234" 
                              },
                              {
                               "month" : "2013-01" 
                              ,"summa" : "4567" 
                              },
                              {
                               "month" : "2013-02" 
                              ,"summa" : "8901" 
                              }
                            ]
},

{
 "member" : { "email" : "test2@test.ru" },

 .........

},

.....

]

Пример задания JSON-объект-АВО

"users.list" : {

  "caption" : [
      {
       "anketa" : "member" 
      ,"quest" : "cellphone" 
      },
      {
       "anketa" : "info" 
      ,"quest" : "firstname" 
      },
      {
       "anketa" : "info" 
      ,"quest" : "title" 
      },
      {
       "anketa" : "info" 
      ,"quest" : "middlename" 
      },
  ],

 "rows" : [
     [ "+70000000000","Павел","Иванович","Уважаемый" ],
     [ "+70000000000","Алексей","Алексеевич","Глубоко уважаемый" ],
     ......
 ]

}

Пример задания JSON-объект-КД

Параметр caption может содержать

- ignore - со значение 1 для игнорирования столбца
- описание ключа данных datakey, режима mode и, не обязательно, тип данных "type" - в ячейке данных любая структура
- указание на динамический ключ dynamic - в ячейке данных структура аналогичная member.set или null (ячейка игнорируется)

Изменение данных происходит по тем же правилам, что и при вызове member.set

Можно думать о импорте JSON-объект-КД как о массовом member.set с индивидульными данными для каждого.

"users.list" : {

  "caption":[
             {
               "datakey" : "ключ данных 1" 
              ,"mode"    : "режим 1" 
              ,"type"    : "тип данных-1" 
              }
             ,       
              {
               "ignore" : 1
              }
             ,
              {
               "datakey" : "ключ данных 2" 
              ,"mode"    : "режим 2" 
              ,"type"    : "тип данных-2" 
              }
             ,       
              {
               "dynamic" : 1
              }
             .......
            ]

 "rows" : [ -- в клонках данные любой струтуры 
     [ "+70000000000","Павел","Иванович",[1,2,3] ],
     [ "+70000000000","Алексей","Алексеевич",{ "a" : "B", "c" : "D" } ],
     ......
 ]

}

Пример задания XLSX

Все ячейки должны иметь тип "строка", не содержать ни какого форматирования или оформления.

В виду двоичного содержимого файла, он не может быть приведён в документации и доступен по ссылке

https://sendsay.ru/api/sample.xlsx

Пример задания CSV

"users.list" : "member.cellphone,info.firstname,info.title,info.meddlename\n+70000000000,Павел,Иванович,Уважаемый\n+70000000000,Алексей,Алексеевич,Глубоко уважаемый\n......" 

вверх

Общие замечания

Ошибки, опечатки, не соответствия

В данном документе могут быть ошибки и опечатки. Реальное поведение API может иногда отличаться от описанного.

При доработке и изменении API мы оставляем обратную совместимость (на время или на всегда), но при исправлении ошибок вида "поведение API не соответствует документации" такого не происходит, так как исправляется явная ошибка.

Если реальное поведение API отличается от описанного здесь, то настоятельно рекомендуется не ориентироваться на "то что есть", а связаться с нами написав на ask@sendsay.ru сообщение о найденной проблеме.

Кастомизированные ссылки

Все ссылки указываемые в вызовых и предназначенные для получения по ним данных (списков, файлов, картинок..) могут содержать метки для подстановки элементов текущих даты и +/- сдвиг и для подстановки случайного числа.

Кастомизация случайном числом записывается в виде

{RND}

Результат - число из 19 цифр. Несколько RND в одном урле датут один и тот же результат.

Кастомизация временем записывается в виде

{DT.format +D day +h hour round +m minute round +s second}

Все поля задающие сдвиг не обязательны, но их порядок от дня к секунде должен соблюдаться.

Знак сдвига обязателен и может быть "+" (как в примере) или "-".

К названию величины можно добавлять на конце "s"

Формат вывода даты-времени задаваемый в format не должен содержать пробельных символов (запишите две подстановки {DT}), а следующие последовательности заменяются в нём на компоненты даты текущей даты в момент использования ссылки для получения данных +/- сдвиг.

YYYY - год (4 цифры)

MM -  месяц (2 цифры)
DD -  день (2 цифры)
hh -  час (2 цифры)
mm -  минута (2 цифры)
ss -  секунда (2 цифры)

M -  месяц (1 или 2 цифры)
D -  день (1 или 2 цифры)
h -  час (1 или 2 цифры)
m -  минута (1 или 2 цифры)
s -  секунда (1 или 2 цифры)

Основное назначение этой особенности - облегчение создания действий по расписанию. Например, вам требуется ежедневно импортировать данные, но файл с ними имеет в названии текущий месяц и день. Тогда параметр со ссылкой на данные будет выглядеть как "http://test.ru/a/b/file{DT.MMDD}.csv".

Примеры

{DT.YYYY-MM-DD_hh:mm:ss + 1 day} - завтрашняя дата с точностью до секунды. "2015-03-24_18:00:43"

{DT.YYYY-MM-DD + 1 day} {DT.YYYY-MM-DD + 1 day} - завтрашняя дата с точностью до секунды через пробел. "2015-03-24 18:00:43"

{DT.DDMMYYYY - 2 days} - позавчерашняя дата с точностью до дня записанная слитно."22032015"

Указание на количество попыток получения данных

При использовании действий по расписанию возможна ситуация когда точное время появления у вас данных не известно.

Создавать действие с несколькими запусками в этом случае не верно - после появления данных запланированное действие выполнится несколько раз, а не один.

Для выхода из такой ситуации, предусмотрена возмножность вместе с адресом данных указать сколько раз пытаться их по ссылке получить и какую паузу делать между попытками.

Это работает во всех случаях когда в api-вызове требуется указать адрес для получения данных.

Система будет пытаться получить данные пока не наступит одно из событий

- Данные будут получены (ответ http с кодом 200)

- Закончатся все попытки

- Закончится день в который было сделан api-вызов

Если ссылка содержит кастомизацию временем с помощью {DT} или случайным числом {RND}, то кастомизация будет вычисляться каждый раз заново при каждой новой попытке.

Для того что бы воспользоваться такой возможностью небходимо ссылку на данные записать не строкой, а объектом вида

{
 "uri" : "cсылка на данные" 

,"retrys" : "количество попыток" -- число от 2 до 288 

,"retrys.delay" : "секунд между попытками" -- число от 60 до 3600

}

Например для импорта

{
 "action" : "member.import" 

.......

 ,"users.url" : {
                 "uri" : "http://test.ru/data{DT.YYYYMMDD}.csv" 

                ,"retrys" : 100

                ,"retrys.delay" : 300
                }

.......

}

JSON и числа

Обратите внимание, что если указывать числа (целые и дробные) именно как числа (а не как стоки - в кавычках), то надо помнить о строгости JSON в этом вопросе

Пример сборки запроса к API

При любом способе передачи данных значение параметра request содержащее строчное представление JSON описывающего структуру данных для запроса должно быть сначала правильно закодировано с точки зрения JSON.

Действующий стандарт RFC 7159 http://tools.ietf.org/html/rfc7159 однозначно указывает, что все символы вне диапазона кодов %x20-21 / %x23-5B / %x5D-10FFFF должны быть закодированы, а не передаваться как есть. Даже такие "простые и привычные" как двойные кавычки, переводы строк и табуляции. Используйте готовые функции для вашего языка программирования, а не собирайте "по быстрому" json-строку руками.

При вызове с использованием application/x-www-form-urlencoded данные для передачи в POST-запросе должны собираться по описанному ниже алгоритму.

Алгоритм приблизительный и не учитывает особенностей тех или иных языков программирования.

Предполагается что все исходны данные уже в кодировке utf-8.

var_str = "apiversion=100&json=1&" 

var_str = var_str + "request.id=" + urlencode( var_request_id ) + "&"  # только если вы используете свою нумерацию запросов

var_str = var_str + "request=" + urlencode( struct_to_json_str( var_json_data ) )

где

Настоятельно рекомендуется пользоваться готовыми функциями вашего языка программирования для urlencoding и перевода структуры данные в json-строку.

Обширная практика службы поддержки показывает, что даже самостоятельная реализация этих действий (особенно получения json-строки) может привести значительной переоценке своих навыков программирования в строну уменьшения и последующей депрессии на этой почве.

Пользовательские метки объектов

+Часть типов объектов (в перспективе все типы) позволяют присвоить каждому их экземпляру пару чисел для отметки их в пользовательских целях.

Такие объекты имеют параметры reltype (предполагается что это значение хранит некий обобщённый тип класификации) и relref (указатель на что-то внутри классификации).

По умолчанию при создании оба поля получают значения равные 0.

У обоих полей все отрицательные значения зарезервированы для системных нужд. Используйте для себя положительные значения во избежания конфликтов.

Тестирование с локальными адресами

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

К таким адресам относятся:

     10.0.0.0        -   10.255.255.255  (10/8 prefix)
     127.0.0.0       -   127.255.255.255 (127/8 prefix)
     172.16.0.0      -   172.31.255.255  (172.16/12 prefix)
     192.168.0.0     -   192.168.255.255 (192.168/16 prefix)

С полным списком можно ознакомиться в RFC 5735 http://tools.ietf.org/html/rfc5735

вверх

История изменений

0.153     2017-07-17      * !! С 17 июля 2017 года для member.list вступает в действие ограничение размера выдачи для result=response
                          * Новый вызов email.get - информация об адресе
                          * Новый формат ответа вызова issue.get (старый формат поддерживается)
                          * Webhook/Callback - новый параметр issue.draft.id
                          * Выпуск рассылки
                            - Viber: правильное название параметра button.label это button.text
                            - Возможность указать mime-type/charset для не персонализированных атачей
                            - Ссылки для external_extra/external_anketa можно указать в issue.send->extra, а не в тексте выпуска
                            - Ссылки со схемой skype:// участвуют в учёте переходов и всегда без проверок на существование
                          * Вызов issue.late.list - новый параметр в ответе: name - название выпуска
                          * Вызов track.get: параметр filter.action - может быть массивом

0.152     2017-06-17      * !! С 17 июля 2017 года для member.list - ограничение размера выдачи для result=response
                          * Новый вызов member.find
                          * Фильтр по ключам данных - новые условия between / !between
                          * Универсальная статистика - условия группировки  AND/!AND/OR/!OR на замену прежним вариантам
                            (прежние варианты остаются работоспособными)
                          * Улучшение получения внешних анкетных данных external_anketa - новый параметр dk
                          * Тригерные последовательности
                             - Удаление пользователя из базы прекращает его прохождение последовательностей
                             - Новые состояния -7 и -8 прохождения последовательности

0.151     2017-05-17      * НОВОЕ ! Интеграция c Google Big Data / Google Big Query
                            - заносите данных о событиях в Google Big Data
                            - используйте в импорте данные Google Big Query
                            - используйте в выпуске рассылок данные Google Big Query
                          * Ссылки со схемой viber:// и fb-messenger:// участвуют в учёте переходов и всегда без проверок на существование
                          * Ссылки в с доменом  telegram.me по умолчанию не проверяются на существование

0.150     2017-04-17      * Новый вызов group.filter.match
                          * Работа с данными по sftp

0.149     2017-03-17      * Черновики issue.draft.* для совпадения по структуре с issue.send: 
                             - поля link.qsid, dkim.id, campaign.id перенесены из letter в obj (совместимость сохранена)
                             - новые поля в obj: only_unique, group, relink, relink.param, multiple, tz_limit, tz_observance, users.slice ,extra
                          * Передача событий из системы клиенту callback: добавлена передача пользовательских идентификаторов письма letter.custid
                          * Новый вызов email.cleanerror - очистить ошибки доставки
                          * Во всех урлах можно исползьзовать объект authext что бы не засвечивать постоянно данные аутентификации
                          * Подстановка внешних даннных [% external_xxx %] - новые настройки ignore_error, timeout
                          * Выпуск рассылки issue.send: уточнена совместная работа tz_observance и tz_limit
                          * Импорт подписчиков member.import: новый дополнительный отчёт в csv со всеми ошибками
                          * Ключи данных: как экранировать точку в сегменте ключа данных если она значима, а не разделитель сегментов
                          * Исправлен пример вставки штрихкода [% barcode %]

0.148     2017-02-17      * Фильтры по ключам данных - условия группировки  AND/!AND/OR/!OR на замену прежним вариантам
                            (прежние варианты остаются работоспособными)
                          * Универсальная статистика
                              - stoplist.* - доступ к стоп-листу
                              - новые поля member.type, member.create|update.time|host
                          * sys.storage.get - новый параметр datakey для выборочного получения данных
                          * Список не проверяемых по умолчанию ссылок дополнен fb.com, а так же вариантами c www
                            для всех числящихся там сайтов

0.147     2017-01-17      * Фильтры по ключам данных
                              - большинство условий теперь позволяют сопоставить значения одного ключа
                                данных с другим ключом данных, а не только с константой
                              - новые условия "has each/any of" 
                          * Универсальная статистика - дополнительны параметр addr_type при фильтрации по member.email,
                            так как клиентские идентификаторы (csid) могут посимвольно быть похожы на адреса или телефоны

0.146     2016-12-17      * Контроль request.id для вызовов issue.send*  и member.import*
                          * Передача событий из системы клиенту - события доставки/недоставки
                          * Расширение правил побора DKIM по домену отправителя
                          * Параметр mute вызова issue.send упразнён

0.145     2016-11-17      * Передача событий из системы клиенту (Callback, WebHook)
                          * Экспресс-Выпуск с накоплением - issue.send: accumulate
                          * В событии sequence member.change можно указывать Ключ Данных
                          * Доступны дополнительные параметры из <param> при использовании YML в выпуске
                          * sys.storage.get - получение записей по шаблону

0.144     2016-10-17    
                          * Управление альтернативными идентификаторами подписчика
                             - добавление при member.set - параметр head.attach
                             - новый вызов "Замена альтернативного идентификатора" - member.head.replace 
                          * Расширение использования DKIM при выпуске
                             - dkim по домену отправителя
                             - dkim.id = 0
                          * Универсальная статистика
                             - источник и номер DKIM использованные в выпуске
                             - новый синтаксис получения значение элемента пользовательской метки - custid(element_name) 

0.143     2016-09-17      * Возможность ведения статистики по ответам получателей на письма рассылок - "Перехват ответов на письма выпуска" и stat.uni: deliv.replyed.*
                       * При импорте в форматах Ключей Данных возможно присоединени-отсоединени альтернативных идентификаторов подписчика
                          * Константы на базе текущего времени в Универсальной Статистике - stat.uni: const.current
                          * Формы: дата создания и изменения
                          * Исправление issue.emailsender.*: параметр называется не email, а name

0.142     2016-08-17      * НОВОЕ ! Полная поддержка национальны доменов и имён получателей на национальных алфавитах.
                            Такие адреса могут использовать "как-есть", например

                                 проверка@тест.рф
                                 企业@企业.企业

                            без всяких ухищрений типа xn--кодирования.

                          * НОВОЕ ! API-форм для полного управления формами на стороне клиента
                          * НОВОЕ ! Мультиканальные триггеры - с шага триггера можно выслать не только email, но и sms и viber
                          * Универсальная статистика - issue.archive бессмысленен для personal и теперь возвращает null

0.141     2016-07-17      * Переработаный вызов email.test - не только проверка, но и внесение в группу-список и блокировка
                          * Ссылки в с доменом odnoklassniki.ru и ok.ru по умолчанию не проверяюся на существование
                          * Описан лимит на размер запроса POST и наличие у доменного имени нескольких ip-адресов
                          * Учточнение бесполезности параметра name вызова issue.send при Транзакционных Письмах

0.140     2016-06-17      * НОВОЕ ! Приостановка/возобновление формирующихся выпусков рассылки
                             - Вызовы issue.running.*
                             - Парамер issue.paused для sys.settings.*
                             - Новое состояние трекера -7 - "Приостановлено" 
                          * Целевые странцы - новый варант скрипта и внесение данные в подписчика - target.script: js and datakey
                          * Новые форматы сжатия для отчётов - gzip и bzip2
                          * Формы - Настройка only_once - "Заполняется один раз" 
                          * member.where: новый параметр group
                          * rfs.rename: правильное название парамера path, а не path.from

0.139     2016-05-17      * НОВОЕ ! Выпуски через Viber
                          * НОВОЕ ! Выпуск рассылки - выпуск только части тиража (users.slice)
                          * Выпуск рассылки - параметр "Название выпуска" 
                          * Универсальная статистика - тема выпуска теперь в issue.subject, а issue.name - название выпуска
                          * Универсальная статистика - deliv.letter.start_dt - запланированная дата доставки письма растянутого тиража
                          * Возможность кастомизация ссылки или имени файла отчёта случайным числом - метка {RND}
                          * Событие "Целевая страница достигнута" для триггерных действий
                          * Новые настройки DMARC mail.ru не позволяют больше использовать их адреса как адрес отправителя выпуска

0.138     2016-04-17      * НОВОЕ ! csid - клиентский идетификатор подписчика
                          * НОВОЕ ! stat.uni: order при использовании join
                          * Параметры reltype/relref для форм
                          * Вызовы sequence.member.start/pause/resume/stop - теперь асинхронны
                          * Ссылки в с доменом linked.in по умолчанию не проверяюся на существование
                          * member.exists выдаёт null для синтаксически неверного идентификатора
                          * stat.uni: уточнение описания groupby
                          * issue.send: уточнение описания tz_observance

0.137     2016-03-17      * НОВОЕ ! Мультиканальные выпуски
                             - issue.send.multi
                             - issue.multi.*
                             - stat.uni::issue.multi.*
                             - issue.get:multi,channel
                          * НОВОЕ ! Выпуск с учётом временной зоны
                             - issue.send: tz_observance
                             - sys.settings.get: tz.delta
                          * Улучшение форм
                              - form.source: получение версии формы для письма - параметр channel
                              - Отслеживание письма из которого заполнено - stat.uni: member.form.issue.* member.form.letter.* form.issue.* form.letter.*
                              - Доступ как ответам через Универсальную Статистику - stat.uni: member.form.*
                          * Автоматическая генерация текстовой версии рассылки - параметр autotext в issue.send и issue.draft.preview
                          * Последовательности - новый вариант срабатывания события member.change - вариант "просто изменились данные" 
                          * Список подпиcчиков: быстрая сортировка по member.create.time и member.update.time
                          * Параметры reltype/relref для track.*
                          * Параметр вызова jsonp отменён

0.136     2016-02-17      * Сохранение отчётов теперь доступно в формате json
                          * Универсальная статистика - *.member.geo.* и *.member.geo.gender.* могут быть null
                          * Группы - новые поля create.time, update.time, reltype, relref
                          * issue.get - новое поле draft.alias

0.135     2016-01-17      * Черновики выпуска
                             - Тематическая отписка - unsub_list
                             - Список участвоваших в выпуске - issue_member_list
                             - Фильтр исключения из выпуска - issue_exclude_filter
                             - Список используемых переменных персонализации - variables
                             - Альтернативный буквенный идентификатор - alias
                          * Универсальная статистика - выборка константы через сonst()
                          * Отложенные выпуски - новое поле track.id что бы простым образом узнать номер трекера выпуска
                          * Выпуск рассылок: ссылки на домен vkontakte.ru тоже по умолчанию без проверки

0.134     2015-12-17      * Сохранение отчётов теперь доступно в формате html
                          * Черновики выпусков: новые поля - dkim.id, reltype и relref
                          * Универсальная статистика: новые параметры для выпуска - issue.to/from/reply_email/name
                          * Тригеры: в cобытии time.happened формат времени изменён на "YYYY-MM-DD hh:mm" 
                          * Новый вызов member.exists - проверка сущестовования подписчика
                          * Выпуск рассылок: уточнение, что имена прикрепляемых файлов начинающиеся с _ зарезервированы
                          * Тригеры: уточнение описания работы события "Изменения данных" 
                          * Выпуск рассылок: ссылки на домены rutube.com и youtube.com тоже по умолчанию без проверки

0.133     2015-11-17      * Статус 6 у track.get - "антиспам проверка" 
                          * Возможность при импорте прибавлять или вычитать значение столбца

0.132     2015-10-17      * Мультиканальность. Присоеднение/Отсоединение идентификаторов при импорте
                          * Возможность кодирования в base64 при указании данных импорта прямо в вызове
                          * Отменены "права доступа" к архиву выпуска" так как все ссылки на него и так индивидуальны

0.131     2015-09-17      * Мультиканальность - один подписчик - несколько идентификаторов
                              - новые вызовы member.head.*
                              - поддержка в member.* и issue.send
                              - улучшен member.delete
                          * При подготовке данных для импорта для вопросов с выбором можно теперь указать название вопроса вместо его кода
                          * Часть доменов по умолчанию не проверяется при проверке существования ссылок (issue.send)
                          * Возвращаемые значения
                              - новые параметры compress и separator
                              - срок хранения отчётов - 90 дней

0.130     2015-08-17      * Новая схема сохранения результатов статистики - возможность загрузить на http/ftp, получить по почте, получить уведомление по sms
                          * Возможность заново потребовать подтверждения регистрации адреса - member.set или member.update с установкой member.lockconfirm в "1" 
                          * Доступна дата изменения статуса доставки sms - stat.uni: deliv.letter.dt
                          * Дата создания и последнего изменения объектов - create.time и update.time в вызовах *.list и *.get для issue.draft, issue.dkim, issue.smssender, 
                            issue.emailsender, webpage, sys.storage
                          * Возможность получить атач выпуска в base64 - issue.get.attach: encoding

0.129     2015-07-17      * Установка своего DKIM-ключа по умолчанию. sys.settings.*: issue.dkim.id
                          * Указание DKIM-ключа для конкретного выпуска. issue.send: dkim.id

0.128     2015-06-17      * Улучшение Целевых страниц - внесение в группу-список, внесение любого адреса
                          * Расширение Универсальной статистики - group.stat.* - статистика группы по её рассылкам
                          * Новые вызовы для настройки DKIM - issue.dkim.*
                          * Новый вызов переименования файла в хранилище - rfs.rename
                          * Новый параметр notify при работе с Формами form.set/get
                          * Новые состояния трекера -5 и-6 
                          * Новые системные настройки issue.email.moderation, issue.sms.moderation
                          * Импорт подписчиков: параметр newbie.letter.confirm больше не поддерживается
                          * Список доступных хранилищ - rfs.list с параметром domain="" 

0.127     2015-05-17      * Новый урл вызова и набор протоколов шифрования
                          * member.get возвращает member.haslock для согласованности с stat.uni
                          * stat.uni: новый объект sequence.* для замены отдельных вызовов sequence.stat и sequnce.member.list
                          * Устарели и будут удалены 01.09.2015 вызовы sequence.stat и sequnce.member.list

0.126     2015-04-17      * Клиентские метки(данные) для каждого письма рассылки deliv.letter.custid.*
                          * Выпуск рассылки с исключением по одной или нескольким группам-спискам
                          * Расширенная кастомизация ссылок датой - шаблон {DT}
                          * Возможность указание количества и интервала попыток получения данных по ссылке
                          * Включение выпуска рассылки сразу в несколько кампаний - campaign.id может быть массивом
                          * Уточнение как сравниваются данные типа дата-время в АВО-фильтре
                          * Универсальная статистика: поля letter и letter_dt переменованы и теперь доступны в других объектах
                          * Новая причина отписки - 4 - по кнопке "Отписаться" 
                          * При использовании кэша в режиме refresh подсказка "cache" так же возвращается

0.125     2015-03-17      * !!! C 15 мая 2015 года новый урл вызова и набор протоколов шифрования
                          * Интеграция с Яндекс.Маркет - используйте в персонализации писем данные о товарах
                          * Поддержка клиентского уникального идентификатора подписчика - сustomerid:
                          * Выпуск рассылки - возможность указать желаемый mime-type и charset для прикрепляемого файла
                          * Точная дата-время транзакционного письма - stat.uni: deliv.letter_dt
                          * Модерация адреса отправителя писем - issue.emailsender.* и sys.settings.get: issue.email.sender.moderation, issue.sms.sender.moderation
                          * Кэш гео-данных для выпуска и кампании - stat.uni: issue.stat.geo.*, campaign.stat.geo.*
                          * Статус доставки -4 - "отменено" 
                          * Хранение "прочих данных" - sys.storage.*

0.124     2015-02-17      * Получение данных для импорта и выпуска рассылки через SOAP
                          * Интеграция c Siebel
                          * Универсальная статистика
                             - конвертация результата через map/map.missing
                             - большой набор статистики для кампаний campaign.stat.*
                             - определение был ли выпуск рассылки связан с формой *.issue.form.*
                          * Список отложенных выпусков - фильтр по черновику
                          * Отслеживание действий
                             - параметр economed в результатах member.import
                             - поддержка вызова member/activate
                          * Получение настроек
                             - issue.month.*
                             - member.hard.rest
                             - часть настроек теперь не выдаётся по умолчанию
                          * В выпусках personal проверка ссылок теперь не производится по умолчанию.
                            Её необходимость для personal надо задавать явно.

0.123     2015-01-17      * Выпуск рассылки issue.send
                             - расширено описание уникального идентификатора каждого письма
                             - параметр users.list может быть в base64
                             - минимальный batch.size для email - 2000
                             - отложеный выпуск той же рассылки (кроме masssending и personal) на тоже время теперь ошибка
                             - атрибуты x-do-link-relink и x-do-link-test тегов A и IMG для индивидуального
                               управления перелинковкой и проверкой ссылок
                          * Список отложенных выпусков issue.later.list
                             - sendwhen не выдаётся - есть status
                             - старые параметры больше не выдаются
                          * Получить отложенный выпуск issue.later.get
                             - выдача оригинального api-запроса вместо его некой "очень похожей" версии; 
                             - старые параметры больше не выдаются
                          * Высылка приглашений member.sendconfirm при использовании group, group.filter, url или stat.uni
                            оформляется выпуском рассылки personal и возвращается его issue.id - можно отследить статистику

0.122     2014-12-17      * Трекинг каждого Транзакционного Письма
                             - issue.send - параметры ответа track.id и letter
                             - stat.uni  - параметр letter для read,link,deliv,unsub
                           * Новый раздел Хранилища Файлов "Загрузки" для приватных данных рассылок и импорта
                          * Метод доступа rfs:// для указания файлов из "Загрузок" при рассылке и импорте
                          * Действия по расписанию - ограничение общего количества выполнений - totalrate/totalruns
                          * Универсальная статистика - ip-адрес открытия письма - read.ip
                          * Новый статус доставки sms -2009

0.121     2014-11-17      * Новый функционал "Персональные прикрепляемые файлы" 
                          * Новый функционал ftp доступ к хранилищу файлов и отчётов
                          * Новые настройки sys.settings.get
                              - dt.now - текущее время системы
                              - spec_att.* - доступность специальных приложений

0.120     2014-10-17      * Новый функционал "Рекламные кампании" 
                              - вызовы campaign.*
                              - поддержка в вызовах stat.uni, issue.send, issue.draft.*, infolett.*
                          * Новый функционал "Персонализированные PDF-документы" 
                          * Новый функционал "Персонализированные Excel-документы" 
                          * Новый функционал "Вставка штрих-кодов" 
                          * Новая глава "Персонализация выпусков" 
                          * Новый функционал "метки в условиях" для фильтров групп по ключам-данных
                          * Улучшена работа с хранилищем файлов
                              - rfs.list - параметр type
                              - rfs.list - параметры date и url для каталогов тоже
                              - rfs.dir.delete - работает и в домене report тоже
                          * Улучшеные отчёты через member.list
                              - формат вывода можно указать на ходу прямо в вызове 
                              - формат вывода поддерживает ключи данных
                              - расширен параметр caption - свои названия для каждой колонки
                          * Вызов lenta.* работает теперь и с инфописьмами и с шаблонами оформления
                          * В объекте Действие по расписанию появились параметры reltype/relref
                          * Вызов datarow.list теперь возвращает и ga.bind

0.119     2014-09-17      * Новый формат данных для вызовов issue.draft.* / infolett.* - расширение возможностей
                            и совместимость с issue.send. Старый формат продолжает поддерживаться.
                          * Функции агрегации в фильтрах на основе Ключей Данных - min, max, sum, avg
                          * Новые поля в Универсальной статистике
                            - issue.draft.name - название черновика
                            - read/click/deliv.member.geo.* - информация о географии 
                            - read/click/deliv.member.gadget.* - информация об устройстве
                           * Уточнения описания
                             - Фильтр групп  при указании не существующего КД, условие in не совпадёт ни когда
                             - Автоматическая загрузка рядов данных привязанных к GA должна производиться через cron
                             - Идентификатор отслеживания выпуска не выдаётся для рассылок personal
                             - Уточнение правил выпуска с черновиком нового формата

0.118     2014-08-17      * Новый функционал "Ключи Данных" - способо хранения и использования
                            данных любой сложности
                          * Условие ИЛИ и скобки в stat.uni
                          * issue.send: personal можно выпускать для телефонов указывая его номер в email
                          * Ссылки могут быть кастомизированные датой
                          * member.list поддерживает group.filter - фильтр задаваемый прямо в запросе
                          * Уточнение описания stoplist.add/delete
                          * stat.activity: правильное название параметра не "to" а "upto" 
                          * Действия по расписанию
                            - новые параметры dt.from и dt.upto
                            - правильное название параметра lastrun, а lastran
                            - уточнение что действие запускается от имени sublogin

0.117     2014-07-17      * Новый функционал "Действия по расписанию" - вызов cron.*
                          * Выпуск рассылки: содержимое прикрепляемого файла можно
                            передать в base64
                          * Поддержка вызова с JSONP

0.116     2014-06-17      * Новый функционал "Формы опросов" 
                            - реализуется через вызовы form.*
                            - вспомогательны объекты "Шаблоны веб-страниц" webpage.*
                            - новые события тригеров form.filled и form.completed
                            - статистка доступна через stat.uni: form.*
                            - набор свойств form у вопроса анкеты для поддержки трансляции данных
                            - вызов decor.siteform удалён как устаревший

                          * Уточнение формата даты для datarow.load

                          * Новый параметре attach вызова sys.message

                          * Вызов sequence.member.membership: правильное название параметра с адресом это email

                          * Забытое описание вызова link.delete

                          * Расширенные кода недоставки
                            - сбор по умолчанию отключён
                            - доступны через stat.uni deliv_elim
                            - изменены номера статусов -1x на -10001x

0.115     2014-05-17      * Логические операции с группами (объединиие, пересечение и прочие) и получение группы-списка из группы фильтра с помощью вызовов group.snapshot и group.clean

                          * Расширенные кода недоставки -1x

                          * Список адресов из разового фильтра с помошью group.filter для member.update/delete/sendconfirm и stoplist.add/delete

0.114     2014-04-17      * Определение географии и устройства подписчика

                          * Ряды данных - сбор данных о конверсии. Как напрямую от вас, так и импорт из Google Analitics

                          * Несколько запросов за один вызов

                          * sequence.member.start теперь игнорирует закрытость последовательности. полезно при тестировании

                          * target.script - новые параметры noqs и url

                          * authext - вспомогательный объект для рядов данных

0.113     2014-03-17      * Целевые страницы. Проследите пусть подписчика по сайту после перехода из письма !

                          * Для удаления блокировки из-за ошибок доставки следует использовать member.set или member.update
                            явно указывая установку поля member.error в "0" 

                          * Новое состояние трекера запросов: -4 - отложенное на будущее действие

                          * Выпуск транзакционной рассылки с указанием получателя в параметре email сразу закончится с ошибкой,
                            если адрес синтаксически не верен или не возможен для рассылки (отписан, в стоп-листе, ошибки доставки)

                          * Адрес отправителя обязателен в черновике или информационном письме для использования их в других вызовах

                          * Пользовательские метки объектов. Первые кто поддреживает link и linkgroup
                            (+ соответствующие изменения в stat.uni,link.*, link.group.*)

                          * sys.settings.get - возможность задания параметров для настроек

0.112     2014-02-17      * Внешние данные для персонализации (external_anketa(),external_extra())
                            позволяют творить чудеса (раздача личных кодов скидок малая их часть)

                          * Выпуск рассылки issue.send: 
                               новый параметр reply.name - имя для обратного адреса для ответа
                               from.email - теперь обязателен для email
                               from.name  - теперь обязателен для sms

                          * Универсальная статистика
                               member.error.* - информация об ошибках доставки адреса
                               stat.common.* - общая статистика по дням
                               dt:CW1D - модификатор получения первого дня недели
                               dt:DOW  - модификатор получения номера дня недели
                               dt:DOY  - модификатор получения номера дня в году
                               dt:WOY  - модификатор получения номера недели в году
                               unsub.dt - описано забытое ранее поле

                          * Для member.update/delete/sendconfirm и stoplist.add/delete источником адресов может служить Универсальная статистика

                          * Удаление нескольких черновиков за раз в issue.draft.delete

0.111     2013-12-17      * Специальный вызов для отправки пожеланий Деду Морозу sys.dedmoroz

                          * Универсальная статистка stat.uni
                               issue.hourly.* - Кэш быстрого получения значений параметров выпуска (клики,чтения,отписки) с группировкой по каждому часу
                               issue.daily.* - Кэш быстрого получения значений параметров выпуска (доставки,клики,чтения,отписки) с группировкой по каждому дню для каждого домена
                               deliv.oper/size/cost - Детальная статистика каждого получателя sms-рассылки - оператор, количество sms, цена за эти sms
                               issue.double, issue.wrongline, issue.onlyunique - полезная инфомация о статистике Экспресс-выпуска
                               deliv.result - Исход доставки. Вспомогательное значение для облегчения некоторых выборок
                               вычитание day из current округляет время до 0:0:0
                               указание round позволяет округлить часы и минуты
                               при использовании join можно указать groupby равным 0 - это тоже имеет смысл
                               забытое описание member.haslock = 4

                          * Общая статистика группы stat.group.common - новая информация об операторах

                          * Учточнение правил экранирования для списков в CSV

                          * Список зарезервированных имён для приложений к письмам

                          * Тест ленты lenta.send ещё удобнее с параметром decor

                          * Изменение ленты lenta.set теперь возвращает id

0.110     2013-10-30      * Выпуск рассылки issue.send
                               новый параметр to.name - имя отправителя
                               новый параметр reply.email - обратный адрес для ответа
                               поддержка выпуска с растягиванием по времени для email - tz_limit
                               параметры отвечающие за содержимое выпуска перенесены в letter (совместимость сохранена)
                               параметр grp переименован в group (совместимость сохранена)
                               уточнение значений по умолчанию для relink.param

                          * Вызовы issue.later.list/get - расширение и совместимость с issue.send (совместимость сохранена)
                            Результат issue.later.get теперь можно использовать для issue.send

                          * Поддержка to.name и reply.email в issue.split.variant.create/set/get, infolett.set/get. issue.draft.set/get

                          * Новая тарификация sms
                               новое значение в stat.issue - sms.cost
                               новая переменная в stat.uni - issue.cost
                               значения sys.settings.get(sms.byed,sms.used) теперь в копейках

0.109     2013-10-17
                          * Новые вызовы stoplist.add и stoplist.delete с поддержкой трекинга и большими возможностями для замены вызова stoplist.set

                          * Новый вызов member.where - список групп фильтров в которых состоит адрес

                          * Универсальная статистика stat.uni
                                объединение результатов нескольких запросов в один - join
                                новый параметр caption для красивого оформления результатов
                                новые переменные issue.access.* - получения прав доступа к выпуску
                                исправление описание переменной unsub.why - 1 и 2 были перепутаны местами

                          * Работа с файловым хранилищем rfs.file.put/get
                                новый параметр encoding для указания что данные не двоичные, а base64
                                замечание о кодировке двоичных данных для rfs.file.get/put

                          * anketa.quest.delete - возможность удалить несколько вопросов за раз

                          * anketa.create - теперь всегда возвращает id новой анкеты

                          * Учтонение что wget() пока понимает только константы

0.108     2013-09-20
                          * Новые возможности динамического контента из RSS и социальных сетей - lenta.*
                          * Выпуск sms c ограничением частоты, времени дня и учётом временной зоны получателя - параметр tz_limit вызова issue.send
                          * Параметры шаблонизатора общие для всех - параметр extra вызова issue.send
                          * Забытое описание параметра email в issue.draft.preview и infolett.preview
                          * Окончание совместимости со старым способом возврата ошибок
                          * Окончание совместимости со старым способом указания уведомлений

0.107     2013-08-01      
                          * Получение анкетных данных из Универсальной статистки через member.anketa.*
                          * Новые переменные в Универсальной статистике
                                issue.archive - адрес архива выпуска
                                issue.thumbnail_* - адреса изображений предпросмотра выпуска 
                                issue.hardbounce, issue.stoplist, issue.lockunsub, issue.lockconfirm - количество адресов не допущенных в выпуск с разбивкой по причинам
                                member.haslock = 4 - информация о новой блокировке адреса - из-за фатальных ошибок доставки
                          * Отчёты со статистикой и об ошибках в результатах отслеживания асинхронного вызовы issue.send при рассылке "Экспресс-Выпуск" 
                          * Новые статистические данные в member.list.count: locked.stoplist и locked.hardbounced
                          * Указание списка для member.update/delete/sendconfirm с помощью ссылки
                          * Получение адреса изображений предпросмотра выпуска рассылки в issue.get
                          * track.list - в фильтре по статусу можно указать список статусов, а не только один
                          * issue.get ссылка на архив теперь возвращается в параметре archive вместо url
                          * Отменены ни где не используемые параметры:
                                Анкета - member_fill
                                Вопрос анкеты - listsubtype, mustselect, onetime, defval
                                Параметр subtype - только для вопросов dt
                                Параметр width - только для вопросов free
                          * Окончание поддержки старого вызова issue.running "Список выпусков формируемых прямо сейчас" 

0.106     2013-07-04      * Модерация имени отправителя sms - вызовы issue.smssender.*
                          * Дополнительный параметр "report_file.json" в результате отслеживания асинхронного вызова member.import
                          * Новый параметр в ответе member.set - newbie
                          * Новые состояния асинхронного запроса - "Отменено" и "Генерация отчёта" 
                          * Новые события тригерных действий - "Регистрация с подтверждением" и "Регистрация без подтверждения" 
                          * Новый способ выбора победителя сплит-тестирования - "меньше отписок" 
                          * Новое состояние сплит-тестирования - "Победитель выбран" 
                          * Новый параметр в свойствах варианта сплит-тестирования - is_winner
                          * Новые настройки sms.used, sms.byed и sms.unlimited  в вызове sys.setting.get
                          * Окончание совместимости со старыми параметрами вызовов member.import/member.import.probe/anketa.quest.add/anketa.quest.set
                          * !!! Изменился способ указания высылаемых уведомлений в вызовах member.import, member.set, member.sendconfirm
                            !!! ПОДРОБНОСТИ В РАЗДЕЛЕ "Общие замечания" 
                            !!! ПЕРЕЙДИТЕ НА НОВЫЙ СПОСОБ ДО 15 СЕНТЯБРЯ 2013 ГОДА

0.105     2013-06-07      * Новая возможность - Отслеживания состояния и хода асинхронных вызовов - track.*
                          * Измененения в вызовах issue.send, member.* и stat.* для поддержки отслеживания
                          * Новое статистическое поле "отписок" в объекте Выпуск (issue) вызова Универсально Статистики stat.uni
                          * Новая настройка about.user в вызове sys.setting.get
                          * Уточнение формата параметров sys.log
                          * Уточнение описания параметра newbie.confirm и названия вызова member.set
                          * Уточнение описания параметра users.list для member.import
                          * Правильное написание параметра clean_group для member.import
                          * !!! ВЫЗОВ issue.running "Список выпусков формируемых прямо сейчас" УСТАРЕЛ.
                            !!! ИСПОЛЬЗУЙТЕ ЗАМЕНУ "Список асинхронных вызовов" 
                            !!! ПЕРЕЙДИТЕ НА НОВЫЙ СПОСОБ ДО 01 АВГУСТА 2013 ГОДА

0.104     2013-05-07      * Уточнение поведения issue.send c отложеным выпуском

0.103     2013-04-29      * Универсальная статистика stat.uni
                               резко повышенное быстродействие при использовании новых полей
                               новые статистические поля доставки/кликов/чтений в объекте Выпуск (issue) вызова stat.uni и примеры с их использованием
                               новый объект domain - домен подписчика
                          * Новый движок импорта подписчиков. Из видимых изменений
                               у вызовов member.import(.probe) изменился способ возврата ошибок и предупреждений
                               новый параметр clean_group - очистить группу-список перед импортом
                               JSON-объект можно использовать как источник данных 
                          * Новая настройка "Не высылать на отсутствующие адреса" issue.dontsend.550 в вызовах sys.settings.get/set
                          * Модерация информационных писем и поле onmoderation в вызовах infolett.*
                          * Работа с анкетами
                               anketa.quest.add/anketa.quest.set -- описание параметра id в ответе
                               anketa.quest.set - изменение нескольких вопросов сразу. новый способ передачи параметров
                               anketa.quest.add - создание нескольких вопросов за раз. новый способ передачи параметров
                          * Вызовы rfs.*
                               новый параметр url
                               уточнение описания
                          * Замечание про тестирование с локальным адресам
                          * Замечание про последовательность обработки

0.102     2013-03-15      * !!! ИЗМЕНИЛСЯ СПОСОБ ВОЗВРАТА ОПИСАНИЯ ОШИБОК !!!
                            !!! ПЕРЕЙДИТЕ НА НОВУЮ СХЕМУ ДО 15 СЕНТЯБРЯ 2013 ГОДА !!!
                          * Новый вызов member.list.count - Количество участников в группе
                          * Список подписчиков member.list: новый параметр member.haslock
                          * Список групп group.list: вставлено забытое описание параметров type и add_type
                          * Прочитать группу group.get
                               новый параметр with_filter
                               параметр id позволяет задавать список групп
                          * Универсальная статистика stat.uni
                               новый параметр "cache" - подсказки как в итоге использовался кэш
                               уточнения про временную зону, 61ю секунда, 23 и 25 часов
                               исправление в примерах
                          * кэширование ответов - уточнение описания режима cache : "fetch" 

0.101     2013-01-25      * система кэширования результатов вызовов
                          * Универсальная статистика stat.uni
                               поддержка кэширования и специальный ответ result = "none" 
                               поддержка вычитания годов и месяцев из текущей даты и полезны к этому примеры
                               информация об отписках
                               полностью переработаное и расширеное описание всех доступных данных
                          * кэширование условий stat.uni в условиях отбора группы
                          * уточнения описания полей с массивами аресов в sys.settings.*
                          * уточнение описания параметра sort вызова member.list

0.100     2012-12-18      * issue.send: возможность задания данных произвольной сложной структуры для "Экспресс-Выпуска" 
                          * новый вызов issue.later.get
                          * новый вызов sys.password.set
                          * новые специальные ответы "Перенаправление" и "Смена пароля" 
                          * member.import/member.import.probe: новые поля в ответе rows и queue_position
                          * sys.settings.*: новые параметры redirect.member.join.exists и sec.*
                          * decor,siteform: новый параметр redirect_exists_to
                          * issue.send: возможность указать время отложенного выпуска с точностью до минуты
                          * issue.later.send: возможность указать новое время отложенного выпуска с точностью до минуты
                          * issue.later.list: новое поле draft.id
                          * email.test: новые поля ip и ptr
                          * уточнение описания как работать с сессией авторизации

0.99      2012-11-13      * Описаны лимиты внесения без подтверждения для импорта адресов списком
                          (member.import) и внесения по одному адресу (member.set)
                          * Новый параметр sequence.event в member.import
                          * issue.send: исправлено неверное название параметра draft на верное draft.id
                          * Отслеживание переходов в сплит-тестирования: link.qsid для issue.split.variant.*
                          * Вызовы decor.issue.* (Общее оформление) удалены
                          * issue.later.list - изменения и новые поля в ответе
                               format, group, status.reason, issue.date, status
                          * sys.settings.get - новые значения
                               trial, trial.issue.limit, trial.issue.rest
                               allow.email, allow.sms
                               member.tarif.limit, member.hard.limit,
                               member.noconfirm.limit, member.noconfirm.limit

0.98      2012-10-02      * Группы по результатам статистических запросов (group.filter.set/get)
                          * Прочитать выпуск (issue.get):
                               параметр draft переименован в draft.id
                               новые параметры sequence.id и variant.id
                          * Универсальная статистика (stat.uni)
                               новые поля - issue.draft.id, issue.sequence.id, issue.variant.id, member.haslock
                               новые операции в фильтре - is_null и !is_not
                               сравнение значения поля с текущим временем

0.97      2012-09-06      Сплит-тестирование / А-B тестирование

0.96      2012-08-06      Cобытийные действия / Триггерные рассылки 

0.95      2012-07-23      * Новый вызов issue.running
                          * Новый параметр  format в вызове issue.list
                          * Новые параметры from,upto,group,format в вызове issue.later.list

0.94      2012-06-26      Транзакционные выпуски. Описание различия между четырмя способами выпуска.

0.93      2012-04-19      * Добавление поддержки списка email или одного email в вызове member.update
                          * Добавлен параметр addr_type для уточнения типа адреса в вызове member.set.
                          * Вызов issue.draft.set - division стало необязательно.
                          * В вызовах issue.draft.get,issue.draft.set,issue.draft.list для использования
                              предустановленных черновиков добавлены параметры "template", "template.thumbnail".
                          * Форматирование, уточнения (group.create, issue.draft.get).

0.92      2012-02-15      * Формат данных подписчиков для импортирования и экспресс выпуска (users.list) изменен
                          * Поддерживается формат XLSX или стандартный CSV
                          (разделитель колонок - запятая, допускается заключение текста ячейки в кавычки)

0.91      2012-01-31      * Добавлена поддержка параметра "result", для вызовов stat.issue и stat.uni.
                          * Во всех запросах использующих параметр "result" = (response|email|save) добавлен
                            дополнительный параметр "result.format" для возможности выбора формата XLSX,
                            если "result" равен "save" или "email".
                          * Сам параметр "result" стал необязательным, по умолчанию -"response".  

0.90      2012-01-20      Поддержка авторизация с помощью биометрических карт AGSES

0.89      2011-11-11      * Исправления stat.issue (описание, добавлен итог по выпускам и получателям),
                          * stat.activity (добавлена возможность сохранения и высылки,обратная сортировка,
                             изменения в описании),
                          * stat.uni (добавлены типы статистики deliv_ok deliv_bad deliv_unk и пример их
                             использования в сводной статистике) 

вверх