API, Betta 0.123 2015-01-17

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

Вызов

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

https://pro.subscribe.ru/api

Метод вызова

Транспорт - HTTP over TLS (https) - RFC 2616 http://tools.ietf.org/html/rfc2616, RFC 2818 http://tools.ietf.org/html/rfc2818

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

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

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

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

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

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

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

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

вверх

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

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

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

Например, два вызова 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" будет добавляться параметр cache с информацией о результате использования кэша.

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

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

Вызов:

{
 "action: "xxx" 

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

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

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

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

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

{
 ....

 "cache" : {
            "hit" : 0
           }

 ....
}

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

{
 ....

 "cache" : {
            "hit" : 1

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

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

 ....
}

вверх

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

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

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

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

{
 "action" : "batch" 

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

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

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

              ....
             ]
}

ответ

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

,"result" : [

               { один ответ }

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

              ....
             ]
}

вверх

Проверка работоспособности

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


{

 "action" : "ping" 

}

ответ


{

 <общие поля>

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

}

вверх

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


{

 "action" : "pong" 

}

ответ


{

 <общие поля>

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

}

вверх

Авторизация

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

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

Полученный в ответ номер сессии должен передаваться во всех запросах в json-параметре "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" 

}

ответ


{

 <общие поля>

}

вверх

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

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

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

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

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

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

{

 "action" : "track.list" 

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

             "action" : тип запроса

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

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

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

ответ

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

 "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" 
                    "stat.activity" 
                    "stat.issue" 
                    "stat.group.portrait" 
                    "stat.group.common" 
                    "issue.split.create" 
                    "stoplist.add" 
                    "stoplist.delete" 

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

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

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

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

      ,"param" : {

   -- === issue.send ===

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

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

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

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

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

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

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

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

                   ,"eta" : {

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

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

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

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

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

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

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

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

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

   -- === stat.uni ===

                    "report_file" : "название файла" -- пусто до окончания запроса
                                                     -- после окончания - название файла с отчётом для вызова rfs.list.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 с отчётом в хранилище отчётов если были ошибки.
                                                          -- все ошибки в машиноразбираемом виде

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

   -- === member.list.count  ===
   -- === member.sendconfirm ===
   -- === member.update ===
   -- === member.delete ===
   -- === stoplist.add ===
   -- === stoplist.delete ===
   -- === group.snapshot ===
   -- === group.clean ===

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

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

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

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

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

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

                  }
       }
}

вверх

Подписчики

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


{
  "action" : "member.set" 

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

 ,"addr_type": "тип адреса подписчика (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.*" и "-group.*" в данный момент игнорируются, но могут стать ошибками в будущем. лучше не делайте их.

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

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

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

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

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

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

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

 ,"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 как в АВО.


{

  "action" : "member.get" 

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

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

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

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

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

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

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

 ,"datakey" : "*" 

}

ответ

{

    <общие поля>

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

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

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

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

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

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

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

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


{

  "action" : "member.set" 

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

 ,"addr_type": "тип адреса подписчика (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" : "адрес подписчика" 

}

ответ


{

    <общие поля>

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

  ,"obj" : { 

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

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

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

            "ank1" : {

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

                       "quest" : "val" 

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

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

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

                      ,"quest" : [ "val" ]

                      }

          ,"ank2" : {

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

                     }

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

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

              "id1" : 1

             ,"id2" : 1

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

             ,"idN": 1

            }

          }

}

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

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

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

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

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


{

  "action" : "member.delete" 

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

 ,"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.*

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

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

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

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

}

вверх

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

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

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

{
 "action" : "member.where" 

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

ответ

{

 <общие поля>

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

          -- или

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

          }

}

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

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

{

  "action" : "member.list" 

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

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

-- или

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

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

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

 ,"member.haslock" : "код" -- учёт состояния блокировки подписчика
                   -- пусто или отсутствует - не учитывать состояние блокировки
                   -- -1 - есть хоть какая-то блокировка       - не может получать письма или sms
                   --  0 - нет блокировки                      - может получать письма или sms
                   --  1 - заблокирован так как отписался      - не может получать письма или sms
                   --  2 - заблокирован так как не подтверждён - не может получать письма или sms
                   --  4 - заблокирован так как имеет фатальные ошибки доставки - не может получать письма или sms
                   --  прочие значения ( 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

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

  ,"result" : "response|email|save" 
               -- cпособ возврата результата: (по умолчанию - response) 
               -- response - сразу (в ответе), в этом случае вызов синхронен
               -- email    - выслать на почту, в этом случае вызов асинхронный и вместо результата возвращается track.id
               -- save     - сохранить на сервере, в этом случае вызов асинхронный и вместо результата возвращается track.id

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

  ,"caption" :"id|name" 

--или 

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

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

-- для response

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

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

-- email или save
  ,"result.format" : "csv|xlsx" -- формат файла с данными (необязательно, по умолчанию csv)

-- email

 ,"email" : [ e@mail1 ,e@mail2 ... ]   -- адреса получателей списка подписчиков

--

}

ответ

{

    <общие поля>

-- для result = email или save

  ,"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" : всего именно телефонов

           ,"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-объект.


{

  "action" : "member.import" 

или 

  "action" : "member.import.probe" 

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

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

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

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

или

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

или

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

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

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

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

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

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

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

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

                {

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

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

                  или 

                 "ignore": "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 не влияет (они всегда вносятся без подтверждения)

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

               "mailto:your@email.tld" 
               ,.....
              ]
}

ответ

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

-- только для 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": "ключ данных колонки",

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

                 "dynamic": "1",

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

                  "ignore" : 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" : "адрес подписчика" 

или

 ,"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" : "member.sendconfirm" 

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

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

 ,"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 - асинхронный
}

ответ


{

  <общие поля>

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

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

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

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

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

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

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

вверх

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


{

  "action" : "member.confirm" 

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

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

}

ответ


{

 <общие поля>

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

}

вверх

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

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

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

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

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

  resolver - определение MX

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

  connref  - не удалось соединиться с MX

  banner   - ошибка в первичном банере

  helo     - ошибка в ответ на HELO

  mailfrom - ошибка в ответ на MAIL FROM

  rcptto   - ошибка в ответ на RCPT TO

Для принимания как работает SMTP и что значат все эти странные слова полезно изучить RFC 5321.


{

  "action" : "email.test" 

  "smtp.test" : "проверять доступность по smtp - 0|1" -- не обязательное поле

 ,"smtp.timeout" : "таймаут в секундах" -- не обязательное поле. по умолчанию 15.

 ,"list" :  [ -- список адресов для проверки

             " missing@CityCat.ru" 

            ,"     PRO@subscribe.ru   " 

            ,"123@test@test.ru  " 

            ......

            ]

}

ответ


{

    <общие поля>

   "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" -- код ошибки

                                      }

         }

}

вверх

Группы адресов - Сегментирование аудитории

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

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

Группа-фильтр это динамический список адресов. Участие в такой группе описывается фильтром применяемым к персональных данным и статистике поведения. Участие адреса в такой группе каждый раз устанавливается в момент проверки в реальном времени. За счёт этого выпуск и подсчёт статистик по таким группам несколько медленние из-за фильтрации данных.

Фильтр группы-фильтра поддерживает оба формата хранения - и АВО и КД.

Cписок групп


{

 "action" : "group.list" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "код группы" 

            ,"name" : "название" 

            ,"type" : "тип группы" -- list или filter

            ,"addr_type" : "тип адресов" -- email или msisdn
            }

            ...

           ]

}

Создать группу

При создании группы типа filter она первоначально получит пустой набор правил отбора.

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


{

  "action" : "group.create" 

 ,"id" : "смысловой код группы. символы a-zA-Z0-9" 
         -- например "clients2011" 
         -- не обязателен. при отсутвии будет назначен автоматически

 ,"name" : "название группы" -- обязательно

 ,"issue.passwd" : "пароль выпуска по почте" -- не обязательно, используйте только если будете отсылать задания
                                             -- на выпуск рассылки через почтовый интерфейс

 ,"type" : "list | filter" -- обязательно. тип группы.

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

                           -- filter - группа является набором фильтров для отбора по всей базе

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

                                 -- email - адреса электронной почты

                                 -- msisdn - номера телефонов

}

ответ


{

 <общие поля>

 , "id" : "код созданой группы" 

}

вверх

Прочитать группу

Обратите внимание, что при вызове со списком групп, сообщения об ошибочных кодах групп будут возвращены не в errors (как это было бы при одиночном вызове), а в warnings, так как в целом считается что вызов закончился успешно.


{

  "action" : "group.get" 

 ,"with_filter" : 0 | 1 -- вернуть в ответе так же и фильтр группы

 ,"id" : "код группы" - для одной группы

-- или

 ,"id" : [ "код группы", "код группы", ... ]  - для списка групп

-- или

 ,"id" : [ "*" ] - для всех групп
}

ответ


{

 <общие поля>

-- для одной группы

 ,"obj" : {

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

             ,"name" : "название группы" 

             ,"type" : "тип группы" -- list или filter

             ,"addr_type" : "тип адресов" -- email или msisdn

             ,"issue.passwd" : "пароль выпуска по почте" 

             ,"filter" : [ -- если запрошено
                          фильтр группы как в вызове group.filter.get 
                         ]
          }

-- или для нескольких групп

 ,"list" : [
            {
             содержимое obj для одной группы
            }

           ,{
             содержимое obj для другой группы
            }

            .....
           ]

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

}

вверх

Изменить группу


{

  "action" : "group.set" 

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

 ,"name" : "название группы" - не обязательно

 ,"issue.passwd" : "пароль выпуска по почте" - не обязательно

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

 ,"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" : [

              массив описывающий фильтр

             ]

}

ответ


{

 <общие поля>

}

вверх

Снимок группы / Расширить группу-список

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

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

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

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

{
 "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) выпускаются через вызов issue.send

Есть четыре варианта его использования.

Группа-спискок Группа-фильтр Экспресс-Выпуск Транзакционное письмо
Адреса получателей Зарнее созданые в базе список Динамический поиск по базе адресов данные которых удовлетворяют фильтру Указываются при выпуске или забираются по ссылке Указывается при выпуске
Данные для персонализации Из базы Из базы Указываются при выпуске Из базы если не указаны при выпуске
Количество переменных Не ограничено Не ограничено Не ограничено Не ограничено
Шаблонизатор Продвинутый Продвинутый Продвинутый Продвинутый
Собственные функции-плагины к шаблонизатору Да Да Да Да
Сложные вложенные структуры данных Да, в модели ДК Да, в моделе ДК Да Да
Подтверждение от владельца адреса Требуется Требуется Требуется Не обязательно
Разбор жалоб на спам Согласно договора Согласно договора Согласно договора С особым пристрастием
Лимит в месяц Не ограничено Не ограничено Не ограничено Первоначально равен максимально допустимому количеству адресов в базе. Далее в зависимости от репутации

Отослать выпуск

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

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

Индивидуальные данные персонализации для каждого адреса берутся из сохранённых в базе (группа-список, группа-фильтр) или из указанных прямо при выпуске (Экспресс-Выпуск,Транзакционные письма) и доступны через префикс anketa шаблонизватора.

Данные формата АВО автоматически доступны как преобразованные в формат ДК.

Данные персонализации для каждого получателя могут быть дополнены внешними данными (формат ДК) в процессе выпуска. Подробнее в разделе "Внешние данные для персонализации"

Из списка участников группы и списка Экспресс-Выпуска система автоматически исключает адреса не подтвердившие внесение в базу, отписавшиеся, находящиеся в стоп-листе или имеющие постоянные ошибки доставки.
Вам не надо предпринимать ни каких дополнительных усилий для этого - всё происходит автоматически.

Для каждого сформированного письма система отслеживает статус доставки - доставлено/не доставлено (и с какой ошибкой), а для каждой sms - размер, предполагаемый оператор и стоимость доставки. Это доступно через объект deliv вызова stat.uni (из этого следует что из deliv можно так же получить просто список участников выпуска).

Для каждого успешно доставленного письма система отслеживает факты открытия и факты перехода по каждой ссылке (если учёт переходов не был отключён при выпуске). Время каждого открытия/перехода и ip-адрес доступны через объекты read и click вызова stat.uni.

Так же система отслеживает вид устройства, операционную систему, браузер и его версию если произошло чтение и переход из письма. Это доступно через объект gadget вызова stat.uni.

При установке на страницах сайта специального счётчика, возможно отслеживание пути по сайту и достижения целевых страниц после перехода из письма - подробност в разделе "Целевые Страницы".

Каждое письмо/sms имеют уникальный идентификатор состоящий из номера выпуска и номера письма в выпуске. Поэтому вы можете получить описанную выше статистику с точностью до каждого письма/sms каждого выпуска для каждого адресата.

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


{
    "action" : "issue.send",

* параметры содержимого выпуска. Для Транзакционных писем - только указание черновика и прикреплённых файлов.

    "letter" : {

                * содержимое письма

                или указание параметров

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

               ,"from.name" : "Имя отправителя" --SMS обязителен и имя отправителя должно быть в списке уже промодерированных имён (вызовы issue.smssender.*)

               ,"from.email" : "Адрес отправителя (email)" -- обязательно не пустой  для текстовых писем,  игнорируется для SMS

               ,"reply.email" : "Обратный адрес для ответа (email)" -- игнорируется для SMS

               ,"reply.name" : "Имя для обратного адреса для ответа" -- игнорируется для SMS

               ,"to.name" : "Имя получателя" -- в этом поле уместна персонализация для подстановки имени и фамилии получателя. игнорируется для SMS

               ,"message": {

                           -- для выпуска по email-адресам одна или обе не пустые версии письма

                           "html" : "html-версия письма" 

                          ,"text" : "текстовая версия письма" 

                          -- для выпуска по SMS

                           "sms"  : "sms cообщение" 

                          }

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

                "draft.id": "номер черновика содержимое которого даст выпуск", -- тип черновика определит вид и формат рассылки (email или sms)

                * прикреплённые файлы письма. тип файла определяется по расширению. (игнорируется для SMS)

                -- следующие имена файлов зарезервированы для внутренних нужд и не должны использоваться
                --  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" },

                             { "name" : "имя файла", "content": "содержимое файла" },

                             { "url"  : "url откуда заборать. Внешиение ссылки http / https / ftp или из хранилища загрузок rfs://upload/путь-до-файла " },

                             { "url"  : "персонализованный url откуда забирать - для каждого свой" }, -- подробнее в разделе "Персонализация выпуска" 

                             -- генерация персонального pdf-документа на ходу. подробнее в разделе "Персонализация выпуска" 

                             { "pdf"  : "номер черновика"}

                             -- генерация персонального excel-документа на ходу. подробнее в разделе "Персонализация выпуска" 

                             { "xlsx"  : "номер черновика"}

                             ...

                            ]
               }

* параметры способа выпуска. Тестовый выпуск не доступен для Экспресс-Выпуска, Транзакционных писем и SMS

    "group" : "id группы | masssending - если это Экспресс-Выпуск | personal - Транзакционное письмо",
              -- старый параметр gid переименован в group для совместимости с другими вызовами
              -- можно продолжать указывать gid - он будет использоваться при отсутствии group

              -- если фильтр группы описан в формате КД и в нём используется оператор has с параметром save,
              -- то один участник группы может попасть в рассылку несколько раз в зависимости от количества
              -- срабатываний has/save и параметра выпуска "multiple".
              -- В описании фильтра формата КД описано когда и чем это полезно.

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

    "sendwhen": "Когда выпустить (now - сейчас | later - отложенный | delay - задержаный | save - отложить на хранение | test - тестовый)",
                -- при использовании в "Действиях по расписанию" допустимы только now и test

    "campaign.id" : "код кампании" -- код кампании в которую будет включён выпуск 
                                   -- пусто или не указан - будет включён в кампанию, если она указана в черновике
                                   -- 0 - не будет включён ни в какую кампанию, даже указанную в черновику

                                   -- код кампании (указаный или полученый из черновика) игнорирует если выпуск выходит
                                   -- в рамках АБ-тестирования или тригерной рассылки

при отложеном выпуске (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" 

при выпуске теста (test) указываются адреса получателей. не более пяти.

    "mca": [

            "e@mail.1",

            "e@mail.2",

             ......

           ]

* параметры преобразования ссылок для учёта перехода по ним (игнорируются для SMS)

    "relink" : 0|1 -- Преобразовывать ссылки автоматически
                   -- 1 - да, 0 - нет

    "relink.param" : {
                      -- если relink = 1, а какой то из параметров relink.param не указан,
                      -- то считается что: link=1, image=0 и test=1 соответственно.

                      "link" : 0|1 -- преобразовывать ссылки тега <A>
                                   -- 1 - да, 0 - нет

                      "image": 0|1 -- преобразовывать ссылка на внешние картинки для тега <IMG>
                                   -- и фоновых изображений в <BODY> и <TABLE>
                                   -- 1 - да, 0 - нет

                      "test": 0|1 -- проверять существование адресов (только если адрес подлежит преобразованию)
                                  -- при включённой проверке выпуск не выйдет если ссылка
                                  -- не действительна (ответ не 200, не 301 и не 302)
                                  -- 1 - да, 0 - нет

                      -- Глобальные настройки из 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
                     }

    "link.qsid": "Параметр для отслеживания переходов по ссылкам" 
                  -- добавляется ко всем ссылкам через query-string.
                  -- значение должно быть уже url-encoded
                  -- при выпуске по черновику, если этот параметр пуст, то берётся link.qsid из черновика

* список получателей и данные персонализации для Экпресс-Выпуска
  -- подробнее в разделе "Форматы данных для импортирования и Экспресс-Выпуска" 
  -- данные персонализации могут быть дополнены внешними данными в процессе выпуска
  -- подробнее в разделе "Внешние данные для персонализации" 

    "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 или из хранилища загрузок rfs://upload/путь-до-файла 

* параметры списка получателей для Экпресс-Выпуска

    "only_unique": 0|1 -- Следить за уникальностью адресов в списке
                       -- 1 - да,  повторно встреченные адреса исключаются из выпуска
                       -- 0 - нет, повторно встреченные адреса участвуют в рассылке

* учёт множественных совпадений итератор в фильтре

    "multiple" : 0|1 -- что делать с множественными совпадениями итератора
                     -- 0 - по умолчанию, выпустить одно письмо
                     -- 1 - по одному письму на совпадение
                     -- В описании фильтра формата КД описано когда и чем это полезно.

* получатель для Транзакционных писем

    "email" : "адрес получателя или номер телефона получателя" 
                                 -- данные для персонализации берутся из базы
                                 --
                                 -- данные персонализации могут быть дополнены внешними данными в процессе выпуска
                                 -- подробнее в разделе "Внешние данные для персонализации" 
                                 --
                                 -- при этом способе указания получателя он проверяется на возможность отсылки ему письма
                                 -- (синтаксическая верность адреса, стоп-лист, ошибки доставки, отписался) и вы сразу получите
                                 -- в ответ описание ошибки если она есть
                                 -- при  других способах задания получателя, проверка на возможность выпуска происходит
                                 -- только при формировании рассылки

или

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

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

 ,"tz_limit" {

   "default" : {

      -- ограничение на частоту отправки сообщений
      -- за rate минут будет передано в доставку в не более number сообщений
      --
      -- если параметр отсутствует или пуст или обе величины равны 0,
      -- то ограничения нет

      "batch" : { -- или отсутствуют или указаны оба значения
                 "number" : "сколько cообщений" -- обязательно, >= 2000
                ,"rate"   : "за сколько минут"  -- обязательно, >= 0
                }

     }
  }

* Для 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|html|text"  -- фильтр по формату (не обязательно).
                                      -- email это "html или text" 
}

ответ


{

  <общие поля>

 ,"list" :  [

              {

                 "id" : "код выпуска" 

                ,"create.date" : "YYYY-MM-DD hh:mm:ss" -- дата постановки задания

                ,"status" : "статус задания" -- hold  - отложено на хранение
                                             -- later - отложено для выпуска
                                             -- error - ошибка выпуска

                -- если статус "отложено из-за ошибки" 

                ,"status.reason" : код ошибки

                -- если статус "отложено для выпуска" 

                ,"later.time" : "YYYY-MM-DD hh:mm" -- запланированная дата выпуска

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

                ,"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" -- дата постановки задания

                ,"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" : "код выпуска" 

}

ответ


{

  <общие поля>

}

Список имён sms-отправителей

{

  "action" : "issue.smssender.list" 

}

ответ

{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"name" : "отправитель" 

            ,"onmoderation" : 0|1 -- 0 - имя одобрено, 1 - имя ещё на модерации
            }

            ...

           ]

}

Чтение имени sms-отправителя

{

  "action" : "issue.smssender.get" 

  ,"id" : "идентификатор sms-отправителя" 

}

ответ

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

  "obj" : {

       "id" : "идентификатор sms-отправителя" 

      ,"name" : "отправитель" 

      ,"onmoderation" : 0|1

          }

}

Создание или изменение имени 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-отправителя" 

}

ответ

{

 <общие поля>

}

Персонализация выпуска

Данные для персонализации

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

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

Для Транзакционных Писем данные могут быть взяты или из хранимых в системе или указаны при выпуске.

Для прочих рассылок данные персонализации берутся из сохранённых в системе при импорте подписчиков (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="data:image/gif;base64,[% 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) можно дополнить данными из внешнего источника получаемыми динамически по ходу выпуска рассылки.

Это реализуется путём использования специальны функций шаблонизатора в тексте и/или шаблоне выпуска:

[% external_anketa("url") %]
[% external_extra("url") %]

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

Вызовы указанных адресов производятся методом POST с передачей cgi-параметра request содержащего json-utf8 данные о текущем выпуске.

Ответ так же ожидается в json-utf8.

При вызове передаются следующие данные позволяющие идентифицировать что это за выпуск:

 {
  "some.id" : "......." - некая строка до 32 символов однозначно идентифицирующая выпуск (это НЕ номер выпуска который он получит позже)

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

 ,"group.id" : "..." - код группы для которой формируется выпуск

 ,"track.id" : 123 - номер трекера для track.get присвоенный выпуску

 ,"draft.id" : 123 - номер черновика на основе которого формирутеся выпуск или null

 ,"seq.id" :  123 - номер последовательности вызвавшей этот выпуска или null

 ,"variant.id" : 123 - номер варианта А/Б-тестирования вызвавшего этот выпуска или null

 ,"extra":  { ... } -- данные параметра extra вызова issue.send или null

-- только для external_anketa()

 ,"users.list" : [
                  список адресов для которых ожидаются дополнительные анкетные данные
                 ]

 }

Дополнение анкетных данных

Не работает в текстовых копиях.

Анкетные данные пользователей дополняются результатами обработки external_anketa().

В выпуске может быть несколько использований external_anketa() - все они будет обработаны один раз перед началом формирования писем по порядку расположения в тексте. Место расположения в данный момент не важно. Дополненные параметры действуют с самого начала текста.

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

Однако ошибка при получении данных для первой группы фатальна и выпуск прекращается.

Для одного выпуска вызовов каждого external_anketa() может быть несколько и они могут поступать одновременно - в зависимости от количества получателей выпуск формируется в несколько потоков, а получатели обрабатываются порциями. В данный момент обычный размер порции составляет 1000 адресов.

В параметре users.list передаются адреса текущей группы получателей.

Ответ должен быть json-массивом содержащим json-объекты или null.

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

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

Элементом массива может быть 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" -- новые внешние данные
}

Дополнение общих параметров

Работает в текстовых копиях.

Общие параметры указываемые в ключе 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() - просто замена без попыток объединения ключей
          "х" : "Х" 
         }
}

вверх

Динамический контент

Описание

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

Во всех ссылках поддерживается авторизация HTTP-Basic - укажите её обычным образов в самой ссылке.

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

Готовые страницы сайта

"Готовая страница сайта" это доступная веб-страница любого сайта.

При использовании в выпуске рассылки команды шаблонизатора [% wget("url") %] указаная страница забирается с сайта и подставляется в текст письма. Это происходит в момент формирования текста выпуска рассылки и по этому адрес страницы может быть задан только как константа.

От страницы используется только содержимое между тегами <body></body>.

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

Такие части отмечаются в своём начале добавлением строки начинающейся с <!-- issueBegin --> и в конце добавлением строки начинающейся с <!-- issueEnd -->

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

Лента Новостей

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

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

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

В выпуске может быть использовано несколько лент.

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

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

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

Использование лент новостей в Индивидуальных письмах

Индивидуальные письма подписчикам формируются из новостей накопившихся в их личных лентах которые к подписчикам привязаны владельцем ПРО при их создании (lenta.set).

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

Если свежих новостей нет, то письмо не высылается.

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

Предоставляются простые базовые шаблоны которые можно модифицировать на свой вкус. Они доступны по адресу https://pro.subscribe.ru/resources/help/lenta/drafts.html

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

Для использовании в рассылке новостей одной или нескольких лент надо воспользоваться функциям шаблонизатора news() или lenta().

Функция news() не требует специальной обработки и результатом её работы становится содержимо полей "content" всех свежих новостей. Это упрощает работу с лентами если готового источника у вас ещё нет и вы можете легко формировать его как нравится. Или когда имеющийся источник выдаёт в "content" новости то, что надо для выпуска.

Функция lenta() только присваивает переменной lenta специальную структуру данных описывающую все свежие на данный момент новости и их источники. Для их отображение требуется использование в теле письма оператора [% FOREACH %] для обработки каждой новости. Это сложнее, но вы можете оформить вывод новостей самым гибким образом.

Обе функции могут работать как с заранее созданной лентой новостей (указывается её номер), так и напрямую с каналом новостей (указывается его url).

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

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

По умолчанию новости отсортированы в порядке убывания и даты публикации. Задать свой порядок и группировку по источникам можно с помощью щаблонизатора - рассмотрите пример "Новости по источникам" по адресу https://pro.subscribe.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" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор черновика" 

            ,"format" : "sms|html|text" -- формат черновика (для черновиков с несколькими текстами указывается только один)

            ,"name" : "название" 

            ,"template" : "0|1" -- признак шаблона (шаблон - заренее предустановленный черновик с оформлением)
            }

            ...

           ]

}

Чтение черновика


{

  "action" : "issue.draft.get",

  "id" : "код черновика" 

}

ответ


{

  <общие поля>

  "obj" : {

      "id" : "идентификатор черновика" 

      ,"name" : "название черновика" 

      ,"letter" : {
                   параметры содержимого письма как у issue.send

                   -- и дополнительно

                  ,"link.sqid" : "..." 
                  ,"campaign.id" : "код кампании" 
                  } 

      ,"template" : "0|1" -- признак шаблона (шаблон - заранее предустановленный черновик с оформлением)

-- У предустановленных черновиков (при template = 1)

      ,"template.thumbnail" : "http://.." -- расположение (URL) изображения шаблона

}

Создание или изменение черновика

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


{

  "action" : "issue.draft.set" 

  ,"obj" : {

            "name" : "название черновика" 

           ,"letter" : {
                        параметры содержимого письма как у issue.send кроме draft.id
                        для текстовых писем обязательны не пустые - адрес отправителя, тема и как минимум один текст
                        для sms сообщения обязательны не пустые - имя отправителя и текст

                        -- дополнительно

                       ,"link.qsid" : "параметр отслеживания переходов как у issue.send" 
                       } 
           }

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

  ,"id" : "идентификатор черновика" -- если не указан, создается новый

  ,"return_fresh_obj": "" -- вернуть объект в формате issue.draft.get

}

ответ


{

 <общие поля>

 ,obj  { ... } -- объект в формате issue.draft.get при наличии в запросе параметра "return_fresh_obj" 

}

Удаление черновика


{

  "action" : "issue.draft.delete" 

-- одного

  ,"id" : "код черновика" 

--- или нескольких

  ,"id" : ["код черновика1", "код черновика2", .. ]
}

ответ


{

 <общие поля>

}

Предпросмотр черновика


{

 "action" : "issue.draft.preview" 

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

-- одно из

 ,"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|html|text"  -- фильтр по формату (не обязательно).
                                      -- email это "html или text" 
 }

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор выпуска" 

            }

            ...

           ]

}

Чтение выпуска в архиве

Если не нужен текст выпуска, то вызов issue.get можно заменить более быстрым вызовом stat.uni для объекта issue.


 {

  "action" : "issue.get" 

  ,"id" : "уникальный идентификатор выпуска" 

 }

ответ


{

 <общие поля>

 "obj" : {

           "id" : "уникальный идентификатор выпуска",

          ,"date" : "дата выпуска",

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

          ,"format" : "html|text|sms", -- формат выпуска

          ,"title" : "тема письма" 

          ,"issue.access" : { "права доступа у выпуску. структура аналогичная параметру access вызова issue.set.access,
                               за исключением отсутствия варианта default" }

          ,"text" : "содержимое выпуска" 

          ,"draft.id" : "уникальный идентификатор черновика, по которому сделан выпуск",

          ,"sequence.id" : "уникальный идентификатор последовательности, событие в которой вызвало выпуск",

          ,"variant.id" : "уникальный варианта сплит-тестирования для которого сделан выпуск",

          ,"attach" : [ -- прикрепленные файлы

                         "aaa.doc" 

                        ,"bbb.doc" 

                        ...

                       ]

           ,"archive" : "ссылка на выпуск в архиве" 

           -- ссылки на изображения письма выпуска
           -- только для html выпусков, и только для групп отличных от personal
           -- в настоящий момент поддерживаются картинки 640x1100 и 150x180
           -- надо учитывать что ссылка может вести на отсутствующее изображение
           -- например, из-за сбоя при её формировании

           ,"thumbnail" : [
                          {
                           "url" : "ссылка на изображение" 
                          ,"width" : ширина в пикселах,
                          ,"height" : высота в пикселах
                          },

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

                         ]

         }

}

Получение файлов, приложенных к выпуску


 {

  "action" : "issue.get.attach" 

  ,"id" : "уникальный идентификатор выпуска" 

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

                "aaa.doc" 

               ,"bbb.doc" 

                ...

              ] 

 }

ответ


{

 <общие поля>

 ,"list" : [

            "aaa.doc" : "содержимое файла|null - если файл не найден",

            "bbb.doc" : "содержимое файла|null - если файл не найден",

            ...

           ]

}

Изменение параметров доступа к выпуску в архиве


 {

  "action" : "issue.set.acсess" 

  ,"id" : "уникальный идентификатор выпуска" 

  ,'access' : { -- доступ к выпуску на вебе разрешён 

                "who" : default  -- использовать значение из глобальной настройки "issue.access" вызова sys.settings.get

                        all      -- всем

                        member   -- внесённым в базу

                        issue    -- участникам группы по который был выпуск

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

                        none     -- никому

               ,"passwd" : "индивидуальный пароль для доступа к выпуску" -- дополнительно для доступа запрашивается этот пароль

                                                                         -- не применим если who = default или none

               ,"group" : код группы -- обязателен для who = group

               }

 }

ответ


{

 <общие поля>

}

Сплит-тестирование

Описание

Сплит-тестирование (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" -- формат письма. в данный момент поддерживается только 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. О каждом посещении такой страницы в базе будет автоматически создана запись о "клике" с запоминанием кто, из какой рассылки, когда и с какого адреса это произошло.
  6. Используя вызов Универсальной Статистики делайте выборки по кликам (click.*) с типом ссылки -2 (сlick.link.reltype = -2) вы получите полную информацию о том кто, когда, из какого выпуска и что посетил на вашем сайте.

Возможно вы захотите дополнительно настроить параметр "Время отслеживания после перехода" (sys.settings.get/set, параметр target.cookie.ttl) - он хранит значение в минутах как долго после перехода из письма система будет отслеживать посещение страниц вашего сайта.

В данный момент значение этого параметра по умолчанию равно 360 - 6 часов.

Дополнительно, вы можете оформить включение создаваемой ссылки сразу в ту или иную группу ссылок - для этого задайте параметр link.group.id при получении значения настройки target.script. Это можно использовать для автоматической группировки целевых страниц сайта, скажем, по их пренадлежности к тому или иному разделу сайта. Настоятельно рекомендуется при создании таких групп указывать им reltype = -2.

Прочие дополнительные параметры для target.script описаны в вызове sys.settings.get

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

Статистика

География и устройство подписчика

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

Эта информация доступна через объекты вызова Универсальной Статистики: 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.gadget.*, click.member.gadget.*, read.member.gadget.* вызова Универсальной Статистики.

Ссылки с полезной информацией и таблицами кодов:

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" : "response|email|save", -- cпособ возврата результата:

                                      --   response (по умолчанию) - сразу (в ответе), в этом случае вызов синхронен

                                      --   email    - выслать на почту, в этом случае вызов асинхронный и вместо результата возвращается track.id

                                      --   save     - сохранить на сервере, в этом случае вызов асинхронный и вместо результата возвращается track.id

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

    -- email

    email : [ e@mail1 ,e@mail2 ... ]   -- адреса получателей отчета

    -- email или save

    "result.format" : "csv|xlsx"       -- формат файла с данными (необязательно, по умолчанию csv)

}

ответ


{

    <общие поля>

-- для result = email или save

   ,"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" : "response|email|save", -- cпособ возврата результата:

                                   --   response (по умолчанию) - сразу (в ответе), в этом случае вызов синхронен

                                   --   email    - выслать на почту, в этом случае вызов асинхронный и вместо результата возвращается track.id

                                   --   save     - сохранить на сервере, в этом случае вызов асинхронный и вместо результата возвращается track.id

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

-- email

 ,email : [ e@mail1 ,e@mail2 ... ]   -- адреса получателей

-- email или save

 ,"result.format" : "csv|xlsx"       -- формат файла с данными (необязательно, по умолчанию csv)

}

ответ


{

  <общие поля>

-- для result = email или save

 ,"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" : "response|email|save", -- cпособ возврата результата:

                                   --   response (по умолчанию) - сразу (в ответе), в этом случае вызов синхронен

                                   --   email    - выслать на почту, в этом случае вызов асинхронный и вместо результата возвращается track.id

                                   --   save     - сохранить на сервере, в этом случае вызов асинхронный и вместо результата возвращается track.id

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

-- email

 ,email : [ e@mail1 ,e@mail2 ... ]   -- адреса получателей списка подписчиков

-- email или save

 ,"result.format" : "csv|xlsx"       -- формат файла с данными (необязательно, по умолчанию csv)

}

ответ


{

  <общие поля>

-- для result = email или save

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

-- для result = response

 ,"selected" : количество адресов участвовавших в выборке

 ,"list" : {

        "код анкеты" 

          : {

             "код вопроса" 

               : {

                  "код ответа" 

                    : {

                       "num" : "число ответов" 

                      ,"perc" : "процент от количества ответов на этот вопрос" 

                      }

                   .....

                 }

             .....

            }

        .....

       }

}

вверх

Общая статистика по группе


{

 "action" : "stat.group.common",

 "group" : [ ], -- коды групп. Если пусто - по всем

 ,"result" : "response|email|save" -- cпособ возврата результата:

                                   --   response (по умолчанию) - сразу (в ответе), в этом случае вызов синхронен

                                   --   email    - выслать на почту, в этом случае вызов асинхронный и вместо результата возвращается track.id

                                   --   save     - сохранить на сервере, в этом случае вызов асинхронный и вместо результата возвращается track.id

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

-- email

 ,"email" : [ e@mail1 ,e@mail2 ... ]   -- адреса получателей отчетов

-- email или save
  ,"result.format" : "csv|xlsx"       -- формат файла с данными (необязательно, по умолчанию csv)

}

ответ


{

  <общие поля>

-- для result = email или save

 ,"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" и связанные с ним дополнительные параметры так как при отсутствии данных в кэше вычисление результата производиться не будет.

При "result" равном "email" или "save" вызов асинхронный !


{

 "action" : "stat.uni" 

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

---- Параметры расчёта

-- или Единичный запрос

,"skip" : число пропускаемых строк данных отчёта от начала. не обязательно. по умолчанию 0

,"first" : число выбираемых строк после пропуска skip. не обязательно. по умолчанию все

,"select" : [ -- обязательно
            --     список полей для выборки
            -- или список полей и функций агрегирования
            -- или список полей и функция unique(*)
            --
            -- поля типа дата могут содержать указания на точность в виде двух букв из набора (Y M D h m s)
            -- по умолчанию поля типа дата имеют точность Ys или YD - в зависимости от их назначения
            --
            -- например: dt:YM (дата от года до месяца), dt:Yh (дата от года до часа), dt:YY (год)
            -- не верно: dt:hY  - первый уточнитель (час) младше второго (год)
            --
            -- поля типа дата могут содержать указания на то что требуется получить
            --    номер дня в неделе  - dt:DOW
            --    номер дня в году    - dt:DOY
            --    номер недели в году - dt:WOY
            --    первый день недели  - dt:CW1D
            -- от значения даты
            --
            -- допустимые функции агрегирования
            --    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)
            --
            -- не правильные примеры
            --   
            --   a,b,avg(a)           -- поле а и в выборке и в агрегировании
            --   a,b,unique(*),avg(e) -- функция unique(*) не совместима ни с одной другой функцией
            --   unique(*)            -- unique(*) без списка полей
          ]

,"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" : "()" или "!()" 

            "v" : [
                    массив описывающий фильтр (подразумевается "И" между элементами)
                  ]

           }

          ]

,"order" : [ 

           -- упорядочивание результата, не обязательно
           --
           -- список полей и функций агрегирования
         --
           -- префикc "-" задаёт сортировку по убыванию
           -- префикс "+" или его отсутвие - по возрастанию
           --
           -- a,-b
           -- +b,-avg(z)
           -- -a,+b,-c
           -- +dt:YM,-c

         ]

-- или Запрос с объединением

-- Результаты всех единичных запросов объединяются в одну строку ответа по уникальным ключам по порядку выполнения запроса.
-- Если в каком-либо запросе результатов для уникального ключа нет, то в строку добавляется соответствующее число значений null.
-- Если в результате строки с уникальным ключём встречается больше одного раза, то используется только первый результат, а остальные отбрасываются.
-- Дублирование уникального ключа в объединённой строке на происходит, так как он удаляется из результатов второго и далее запросов перед их добавкой в объединённую строку.
-- Смотрите пример запроса с объединением в разделе "Примеры запросов универсальной статистики" 

,"join" : [ -- объединяемы запросы. обязательно
           { Единичный запрос-1 }
          ,{ Единичный запрос-2 }
          ,{ Единичный запрос-3 }
          ....
          ]

,"groupby" : "число" -- обязательно. больше или равно 1
                     -- размер уникального ключа для объединения результатов
                     -- 0 - без ключа. объединяются все колонки. (например вы выбираете min() из нескольких объектов)
                     -- 1 - первая колонка каждого результата единичного запроса
                     -- 2 - первая и вторая
                     -- 3 - первая, вторая и третья
                     -- и так далее

---- Прочие параметры

-- заголовки столбцов 
-- предназначены для создания заголовков в csv и xlxs, но появятся в ответе и при ответе "response" 
-- содержимое и количество элементов не анализируется, вы можете задать заголовков и меньше и больше количества столбцов данных

,"caption" : [ -- не обязательно
              "адын" 
             ,"дыва" 
             ,"тры
              ......
             ]

,"result" : "response|email|save|none" -- cпособ возврата результата:

                                   --   response (по умолчанию) - сразу (в ответе), в этом случае вызов синхронен

                                   --   email    - выслать на почту, в этом случае вызов асинхронный и вместо результата возвращается track.id

                                   --   save     - сохранить на сервере, в этом случае вызов асинхронный и вместо результата возвращается track.id

                                   --   none     - ответ всегда пустой массив. этот вариант полезен только для использования
                                   --              c кэшированием в режиме refresh - кэш обновляется, а сами данные не высылаются
                                   --              для сокращения размера ответа

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

-- email

 ,"email" : [ e@mail1 ,e@mail2 ... ]   -- адреса получателей отчетов

-- email или save
  ,"result.format" : "csv|xlsx"       -- формат файла с данными (необязательно, по умолчанию csv)

}

ответ


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

-- для result = email или save

 ,"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: понедельник.

Сравнения с текущим временем

Для полей типа дата возможно сравнение с текущим временем +/- сдвиг.

Для этого значение 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

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

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

Доступные для использования поля

Поля типа дата (помечены 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.email - значение email или телефона подписчика

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

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* 
                           - *в данный момент не может быть параметром функций агрегации*

Существует специальная особенность связанная с выборкой данных подписчика.

Пока вы используете в запросе поля id и email, вы получаете данные о всех подписчиках.

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

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

Пример когда проявится разница - запрос адресов участвовавших в выпуске ("select":["member.email"]) вернёт все адреса попавшие в выпуск, а такой же запрос с дополнительной выборкой текущего состояния блокировки ("select":["member.email", "member.haslock"]) вернёт только тех, кого вы ещё не удалили из своей базы.

Эту особенность можно использовать специально для отсечения тех кто из базы уже удалён. Фильтр member.haslock >= 0 выберет подписчиков с любым состоянием блокироваки, но приведёт к исключению из результатов тех кто уже удалён.

Информация o группе


group.id - id группы

group.gid - символический код группы

group.name - название группы

Информация o кампании


campaign.id - id кампании

campaign.name - название кампании

Информация o выпуске

Поля с количеством отсеяных участников содержат актуальное значение для выпусков начиная с 26-07-2013.
Для более ранних выпусков поля содержат 0.

Поле issue.cost содержит актуальное значение для выпусков начиная с 01-11-2013.
Для более ранних выпусков поля содержат 0 так как старая система тарификации не пересчитываема в новую.


issue.id - id выпуска

issue.dt - дата выпуска (dt:Ys)

issue.name - тема выпуска

issue.members - число получателей выпуска

issue.format - формат выпуска

issue.size  - размер выпуска. Для email - размер одного письма, для sms - количество выпущенных смс.

issue.cost - цена выпуска. Для email - 0, для sms - в единицах тарификации, в данный момент это копейки

issue.archive - ссылка на копию выпуска в веб-архиве (доступ ограничивается - см. issue.set.access)

issue.access.type - тип доступа к выпуску в архиве - см issue.set.access. 
                    *в данный момент допустимо только в select* 
                    *в данный момент не может быть параметром функций агрегации*

issue.access.group- группа доступа к выпуску в архиве (null) - см issue.set.access. 
                    *в данный момент допустимо только в select* 
                    *в данный момент не может быть параметром функций агрегации*

issue.access.passwd пароль доступа к выпуску в архиве (null) - см issue.set.access. 
                    *в данный момент допустимо только в select* 
                    *в данный момент не может быть параметром функций агрегации*

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.group.* - информация о группе по которой был выпуск

issue.campaign.* - информация о кампани указаной при задании выпуска

issue.draft.id - номер черновика использованного при выпуске (null)
issue.draft.name - название черновика использованного при выпуске (null)

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.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  - старшая часть версии браузера

Информация 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 - номер письма в выпуске

click.member.* - информация о кликнувшем подписчике

click.member.geo.* - информация о географии подписчика

click.member.gadget.* - информация об устройстве подписчика

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 - номер письма в выпуске

read.member.* - информация о прочитавшем (открывшем письмо) подписчике

read.member.geo.* - информация о географии подписчика

read.member.gadget.* - информация об устройстве подписчика

read.domain.* - информация о домене прочитавшего подписчика

read.issue.*  - информация о выпуске который был прочтён

Информация о доставке и прочих свойствах получателя


deliv.letter - номер письма в выпуске

deliv.member.* - информация о подписчике-получателе

deliv.member.geo.* - информация о географии подписчика

deliv.member.gadget.* - информация об устройстве подписчика

deliv.domain.* - информация о домене подписчика-получателя

deliv.issue.*  - информация о выпуске который доставлялся

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

             - Для писем кода ошибок обозначают
             -  -2 - все попытки доставки успеха не имели, но и не был получен ответ, что точно не примут
             -  -3 - письмо приняли за спам
             -  -5xx - код ошибки SMTP-DSN 5xxx
             -  -15xx - код ошибки SMTP (5xx) сдвинутый на -1000

             - Для sms  кода ошибок обозначают
             -  -2000 - прочие ошибки
             -  -2003 - истекло время доставки
             -  -2004 - sms-центр принял сообщение, но потом не стал доставлять и просто удалил
             -  -2005 - не удалось доставить на телефон абонента
             -  -2006 - sms-центр сообщил, что потерял информацию о статусе доставки
             -  -2007 - неизвестная ошибка доставки
             -  -2008 - sms-центр сразу отверг сообщение
             -  -2009 - sms-провайдер не принял сообщение

             - Статусы описывающие адреса/телефоны которые потенциально могли быть
             - в рассылке, но были отсеяны из списка получателей
             - ещё на этапе формирования - т.е. письмо/смс вообще не формировалось.
             - По умолчанию сбор этих статусов не активен. Для его включения обратитесь в саппорт.
             -  -100010 - Адрес в стоп-листе
             -  -100011 - Адрес имеет постоянные ошибки доставки
             -  -100012 - Адрес не подтверждён
             -  -100013 - Адрес отписался
             -  -100014 - Оператор получателя вам недоступен (для 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 можно использвать префиксы

Информация об отписке


unsub.dt     - дата получения информации об отписке (YYYY-MM-DD hh:mm:ss)

unsub.why    - способ отписки
             - 1 - нажав кнопку "Это спам" в своей почтовой системе
             - 2 - по ссылке отписаться или через наш сайт
             - 3 - через жалобу в нашу службу поддержки

unsub.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  - значение записи

Статистка заполнения форм опросов

При повторном заполнения подписчиком формы старые данные заменяются новыми

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.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     -    в том числе заблокированные так как отписались

Комбинирование в одном запросе

Возможно одновременное использование следующих сочетаний полей в одном запросе

Сводная статистика

Специальные названия полей используются для функций 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.sequence.id - номер последовательности вызвавшей выпуск (null)

*.issue.variant.id - номер варианта сплит-тестирования для которого вышел выпуск (null)

*.member.id - id подписчика

*.member.email - значение email или телефона подписчика

*.member.haslock - блокировка подписчика

вверх

Стоп-лист

Существует два стоп-листа: владельца (A) - вы им полностью управляете и подписчика (M) - вносит в него себя подписчик сам и вы не можете повлиять на имеющиеся там записи.

Чтение записей в стоп-листе владельца и подписчиков


{

  "action" : "stoplist.get" 

 ,"type" : "тип листа" (A - внесенные владельцем, M - внесенные подписчиками, пусть - любой)

 ,"list" : [ -- отсутсвие параметра - дать всех

             "email-1" -- есть в А и М

            ,"email-2" -- нет ни в одном

            ,"email-3" -- есть в А

             .....

           ]

}

ответ

 <общие поля>

 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" 

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

 ,"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" 

}

ответ


{

    <общие поля>

}

Внести изменения в стоп-лист владельца

Вызов устарел и будет удалён 01-01-2014.

Используйте вызовы stoplist.add и stoplist.delete обладающие большей функциональностью.

вверх

Действия по расписанию

Действия по расписанию позволяют запланировать исполнение 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.сreate" 

 ,"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обытия не учитываются и значит ни кто не продвигается по последовательности и не начинает её. Пропущенные события теряются.

Возобновляемость

Возобновление прохождения при увеличении количества шагов.

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

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

Не возобновляется участник и так проходящий последовательность в момент её удлиннения не смотря на параметр parrallel.

вверх

События

Новая регистрация без подтверждения

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

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

{

 "type" : "member.new.no-confirm" 

}

Новая регистрация c подтверждением

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

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

{

 "type" : "member.new.confirm" 

}

Подтверждение регистрации

Наступает когда пользователь подтверждает регистрацию.

{

 "type" : "member.confrim" 

}

Самостоятельное удаление регистрации

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

{

 "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" : "дата и время" -- не обязательно при наличии wekday
                     -- ММ - месяц
                     -- DD - день
                     -- hh - час
                     -- mm - минута

-- Допустимые сочетания дня недели, даты и времени:
--    "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 }

}

Совпадение данных

Наступает когда пользователь переходит на данных шаг и его текущие данные попадают (или при 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" : "noop" 

}

Выслать письмо

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

{

 "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 - да

 ,"parrallel" : -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.stats" 

 , "id" : "код последовательности" 

}

ответ

{

 <общие поля>

 ,"list" : {
             "номер шага" : "количество пользователей на нём" 

            ,"номер шага" : "количество пользователей на нём" 

            ....

             "finished" : "количество пользователей закончивших последовательность (не важно по какой причине)" 
            }

}

вверх

Список участников последовательности

{

  "action" : "sequence.member.list" 

 , "id" : "код последовательности" 

 , "steps" : [ .... ] - список интересующих шагов. по умолчанию все.

 ,"groupby" : "member" | "step" - способ группировки. по умолчанию - без группировки
}

ответ

{

 <общие поля>

-- без группировки

 ,"list" : [ -- список описаний каждого участника на каждом шаге
            {
             "sequence" : "последовательность" 

            ,"step"     : "позиция в последовательности или finished" 

            ,"member"   : "адрес участника" 

            ,"state"    : "cостояние" 
                            -- 0 - пауза - происходящие события игнорируются
                            -- 1 - активен - происходящие события учитываются
                            -- -1 - последовательность закончена
                            -- -2 - последовательность закончена из-за sequence.member.stop
                            -- -3 - последовательность закончена из-за parallel = 0 или -1
                            -- -4 - последовательность закончена из-за action = 11,12,13
                            -- -5 - последовательность закончена из-за изменений по указанию
                            -- -6 - последовательность закончена из-за изменений так как участник оказался вне последовательности

            ,"dt"       : "дата-время попадания на текущий шаг или дата окончания последовательности" 

            }

            ,{ .... }

            .....
           ]

-- с группировкой по участникам

 ,"list" : {
            "адрес участника" : [ -- список для этого участника
                                   { ... }
                                  ,{ ... }
                                  ....
                                ]

           ,"адрес участника" : [ -- список для этого участника
                                   { ... }
                                  ,{ ... }
                                  ....
                                ]
            .....
           }

-- с группировкой по шагам

 ,"list" : {
            "шаг" : [ -- список для этого шага
                       { ... }
                      ,{ ... }
                      ....
                    ]

           ,"шаг" : [ -- список для этого шага
                       { ... }
                      ,{ ... }
                      ....
                    ]

            .....
           }

}

вверх

Начать прохождение последовательности

Указанные пользователи начинают прохождение указанной последовательности как-будто произошло одно из событий её первого шага.

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

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


{

  "action" : "sequence.member.start" 

 , "id" : "код последовательности" 

-- обязателен один из параметров:

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

или

 ,"list" : [

            "адрес подписчика" 

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

            ........

           ]

или

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

}

ответ


<общие поля>

вверх

Приостановить прохождение последовательности

Указанные пользователи приостанавливаю прохождение указанной последовательности и возобновят его при вызове sequence.member.resume


{

  "action" : "sequence.member.pause" 

 , "id" : "код последовательности" 

-- обязателен один из параметров:

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

или

 ,"list" : [

            "адрес подписчика" 

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

            ........

           ]

или

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

}

ответ


<общие поля>

вверх

Возобновить прохождение последовательности

Указанные пользователи возобновляют прохождение указанной последовательности


{

  "action" : "sequence.member.resume" 

 , "id" : "код последовательности" 

-- обязателен один из параметров:

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

или

 ,"list" : [

            "адрес подписчика" 

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

            ........

           ]

или

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

}

ответ


<общие поля>

вверх

Прервать прохождение последовательности

Указанные пользователи завершают прохождение указанной последовательности на каком бы её шаге не находились


{

  "action" : "sequence.member.stop" 

 , "id" : "код последовательности" 

-- обязателен один из параметров:

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

или

 ,"list" : [

            "адрес подписчика" 

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

            ........

           ]

или

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

}

ответ


<общие поля>

вверх

Участие пользователя в последовательностях

{

  "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 можно получить статистику заполнения форм.

Список форм


{

  "action" : "form.list" 

}

ответ


{

 <общие поля>

,"list" : [

            {

             "id" : "уникальный идентификатор" 

            ,"name" : "название" 

            ,"state : "состояние" 

            ,"dt"    : "дата создания" 

            }

            ...

           ]

}

Чтение формы


{

  "action" : "form.get" 

 ,"id" : "идентификатор формы" 

}

ответ


{

  <общие поля>

  "obj" : {

      "id" : "идентификатор формы" 

     ,"name" : "название" 

     ,"dt"   : "YYYY-MM-DD" -- дата создания

     ,"state" : 0|1 -- состояние: 0 - форма отключена, 1 - активна

     ,"preview" : 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

               }

          }

}

Создание или изменение формы

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

Для удаление не обязательного параметра используйте пустое значение.


{

  "action" : "form.set" 

  ,"obj" : {

     ,"name" : "название" 

     ,"state" : 0|1 -- состояние: 0 - форма отключена, 1 - активна

     ,"anketa" : анкета-форма в которую собираются данные

     ,"preview" : 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" : "" 

               }

           }

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

  ,"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 код будет пытаться максимально оставлять
              -- пользователя на странице где он заполняет форму выводя все сообщения
              -- по возможности во всплывающих окнах
}

ответ


{

 "html" : "код формы" 

,"url" : "полный адрес входной страницы формы" -- #6025 [BASE]/
,"url.fill" : "полный адрес страницы, отображаемой при окончании заполнения" 
,"url.welcome" : "полный адрес страницы, отображаемой при подтверждении заполнения" 
}

Перенос значений из анкеты-формы в анкеты-хранилища

Этот вызов переносит ответ пользователя из анкеты-формы в анкеты-хранилища не дожидаясь, пока он сам подтвердит заполнение,

Этот вызов делает ровно то что написано, он не подтвердит регистрацию нового пользователя за него и не породит событие "Форма заполнена".


{

  "action" : "form.transfer" 

  ,"id" : "идентификатор формы" 

  ,"email" : "адрес, данные которого перенести" 

}

ответ


{

 <общие поля>

}

вверх

Хранилище файлов

Хранилище имеющееся в вашем распоряжении позволяет:
- хранить в нём изображения и файлы на которые вы ссылаетесь из писем или прикрепляете к ним (хранилище картинок)
- получать от системы отчёты которые вы заказывали с параметром "сохранить на сервере" (хранилище отчётов)
- загружать файлы данных для импорта и Экспресс-Выпуска в недоступное публично место (хранилище загрузок)

Доступ к Хранилищу осуществляется по данному api, по обычному ftp, по ссылкам со схемой rfs://

Для доступа по api используйте описанные ниже методы.

Доступ по ftp осуществляется через ftp.sendsay.ru. При этом ftp-логин это или просто ваш общий логин или общий логин и личный логин записанные через "/", а ftp-пароль - ваш пароль. Так как ftp работает по не шифрованному протоколу, то рекомендуется создавать для работы с ним отдельного пользователя с максимально ограниченными правами.

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

К хранилищу отчётов и хранилищу загрузок публичного доступа по http/ftp нет.

К хранилищу загрузок есть дополнительно доступ по rfs://upload/путь-до-файла, что бы на загруженные файлы можно было использовать при импорте списков и рассылке Экспресс-выпуска

Параметр domain указывает на используемую область - хранилище картинок (image), хранилище отчётов (report), хранилище загрузок (upload).

Параметр path используемый в вызовах это абсолютный путь по хранилищу начинающийся со слэша.

Cписок файлов

Если path указывает на файл, то возвращается информация только о нём - можно использовать для проверки существования этого файла.

Если path указывает на каталог, то возвращается список его файлов и подкаталогов.


{

 "action" : "rfs.list" 

 ,"domain" : "image|report|upload" 

 ,"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
           }

           ......... 

          ]

} 

вверх

Получить файл


{

 "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" : "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 сообщения обязательны не пустые - имя отправителя и текст

                        ,"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" : "название" 

            }

            ...

           ]

}

Чтение шаблона веб-страницы


{

  "action" : "webpage.get" 

 ,"id" : "идентификатор шаблона" 

}

ответ


{

  <общие поля>

  "obj" : {

      "id" : "идентификатор шаблона" 

     ,"name" : "название" 

     ,"page" : "html текст страницы" 

          }

}

Создание или изменение шаблона веб-страницы


{

  "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", .. ]

}

ответ


{

 <общие поля>

}

вверх

Внешние авторизации

Хранение параметров внешних авторизаций в данный момент поддерживает только Google Analitics и предназначено для обеспечения автоматического пополнения Рядов Данных статистикой из Google Analitics.

Список внешних авторизаций

{

 "action" : "authext.list" 

}

ответ

{

  <общие поля>

 ,"list" : [

             {

                "id"   : "идентификатор внешней авторизации" 

               ,"type" : "тип внешней авторизации" 

               ,"login" : "логин внешней авторизации" 

               ,"reltype" : ...

               ,"relref" : ...
             }

             ...

           ]

}

Чтение внешней авторизации

Токен прочесть нельзя.

{

 "action" : "authext.get" 

 ,"id" : "идентификатор внешней авторизации" 
}

ответ

{

  <общие поля>

 ,"obj" : {
           "id"   : "идентификатор внешней авторизации" 

           ,"type" : "тип внешней авторизации" 

           ,"login" : "логин внешней авторизации" 

           ,"reltype" : ...

           ,"relref" : ...
           }
}

Создание внешней авторизации

{

 "action" : "authext.create" 

 ,"type" : "тип внешней авторизации" 
            -- в данный момент поддерживается:
            --  8 - Google Analitics

 ,"login" : "логин внешней авторизации" -- используется тольо как метка для уникальности в пределах одного типа

 ,"token" : "токен внешней авторизации" 
             -- для GA - refresh token

 ,"reltype" : ...

 ,"relref" : ...
}

ответ

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

 ,"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" : лимит на количество адресов в базе используемый при расчёте оплаты. null - ограничения нет

,"member.hard.limit" : лимит на количество адресов в базе который не превысить. null - ограничения нет

,"member.noconfirm.limit" : лимит на количество адрес которые можно внести без подтверждения. null - ограничения нет

,"member.noconfirm.rest" : доступный остаток на внесение без подтверждения. 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" : код для сервиса "Целевые страницы" предназначеный для размещения на сайте
                   поддерживаются дополнительные параметры

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

                   - noqs : 1 - удалить из адреса целевой страницы все параметры (например они всегда разные для разных посетителей)

                   - url : "ccылка" - вместо адреса целевой страницы использовать указаный

,"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

Список настроек которые можно и прочесть и поменять


 "about.owner.email" : [ "email", "email", ... ],  -- email-адрес/адреса ответственного лица.
                             -- Используется как получатель для всех писем системы когда не указан другой адрес

 "support.access" : "0|1", -- службе поддержки разрешён вход для помощи

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

 -- Значением является массив в котором могут быть или один и более почтовый адрес или одна и только одна ссылка 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", -- После удаления регистрации

 -- Использование шаблонов информационных писем

 "infolett.confirm.need" : "код шаблона", -- Высылается когда адрес регистрируется или при использовании вызова
                                          -- "member.sendconfirm" без указания желаемого черновика.
                                          -- Пусто - стандартный системный текст

 "infolett.confirm.done" : "код шаблона", -- Высылается сразу после подтверждения регистрации.
                                          -- Пусто - не высылается ничего

 "infolett.info"         : "код шаблона", -- Высылается когда требуется сообщить подписчику его регистрационные
                                          -- данные и он уже подтвердил регистрации.
                                          -- Пусто - стандартный сиситемный тест
 --

 "msisdn.country" : "число", -- код страны (без +) для нормализации регистрируемых не полных номеров телефонов

 "msisdn.zone"    : "число", -- код зоны для нормализации регистрируемых не полных номеров телефонов

 --

 "issue.link.qid" : "строка", -- добавка к ссылкам в письмах

                              -- переходы из писем рассылок плохо видны в статистике именно как переходы из рассылки
                              -- и часто попадают в общую кучу "прямые переходы" (typed in).

                              -- Вы можете указать параметр (например source=pro) который будет добавляться ко всем
                              -- ссылкам в вашем выпуске (например, было http://site.tld/,а станет http://site.tld/?source=pro)
                              -- и, соответственно, позволит однозначно определить при анализе посещаемости сайта
                              -- что посетитель пришёл именно из выпуска рассылки. 

 "issue.access" : { "права доступа у выпуску. структура аналогичная параметру access вызова issue.set.access,
                     за исключением отсутствия варианта default" }

 "issue.dontsend.550" : 0|1 -- автоматически исключать из выпусков рассылок адреса у которых последняя ошибка smtp = 550 - неизвестный получатель

,"target.сookie.ttl" : положительное-число -- "Время отслеживания после перехода" для сервиса "Целевые страницы". Задаётся в минутах. По умолчанию 360 - 6 часов

Список пользователей


{

 "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 ",

            ...

          }

}

ответ


{

 <общие поля>

}

вверх

Ключи данных

"Ключ Данных" это строка описывающая путь к данным хранимым для данного адреса.

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

На самом деле вы уже знакомы в ключами данных если:

- впускаете Экспресс-Выпуск с данными в формате JSON-массив

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

- даже просто пользуетесь персонализацией выпуска (запись [% anketa.person.name %] это доступ по ключу данных из трёх элементов :)

Преимущества

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

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

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

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

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

- Доступ к данным в выпуске рассылки осуществляется как и прежде путём прямой записи [% 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:" : [ "+74957830011" ,"88003336963" ]

             ,"is_office" : "1" 

             ,"address" : {
                           "country" : "Россия" 

                          ,"index" : "115280" 

                          ,"city" : "Москва" 

                          ,"street" : "Проезд Завода Серп и Молот" 

                          ,"building" : "д.10" 

                          ,"apartment" : "офис № 603" 

                          ,"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.

вверх

Фильтр отбора в группе (ДК)

Этот фильтр работает только по данным доступным по формату ДК.

Фильтр отбора состоит из списка элементов задающих условия отбора или условия объединения других условий.

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

По своей структуре фильтр аналогичен условию отбора Универсальной Статистики - у них общая структура одного условия фильтра, общие логически и группирущие уссловия и многие операции.

Фильтр представляет из себя список элементарных условий объеденяемых через "И".

вверх

Пример

Адрес имеет 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", -- входи в группу abc

                "v" : "xyz",
              },
          ]

]

вверх

Метки условий

Метки условий позволяют узнать каким условиям сработал фильтр.

Это можно использовать для тонкой персонализации писем рассылки и красивого оформления сложных отчётов из 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 : "()" ).

Условие ИЛИ

Действует между элементами прочих списков условий кроме основного списка фильтра и списка группировки.


 [

   { элемент }

   ,{ элемент }

   ,{ элемент }

 ]

Группа условий (скобки)


 {

  "op" : "()" или "!()" 

  "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' ]

 }

вверх

Целочисленое сравнение

Скалярное значение по ключу данных как число не равно (!=), равно (==), меньше (<), меньше или равно (<=), больше или равно (>=), больше (>) чем число в "v"


 {

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

 ,"op" : "!= | == | < | <= | => | >" 

 ,"v" : "целое число" 

 }

вверх

Строчное сравнение

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

Скалярное значение по ключу данных как строка не равно (ne), равно (eq), меньше (lt), меньше или равно (le), больше или равно (ge), больше (>) чем строка в "v"


 {

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

 ,"op" : "ne | eq | lt | le | ge | gt" 

 ,"v" : "строка" 

 }

вверх

Величина 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" хоть один элемент подходит под условие суб-филтра.

Итератор "!has" ни один элемент не подходит под условие суб-фильтр.

 {

 ,"a"  : "базовый ключ данных" 

 ,"op" : "has" или "!has" 

 ,"v"  : [
          суб-фильтр группы. подразумевается "И" 
         ]

 ,"save" : "id-сохранения" -- не обязательно
 }

Если базовый ключ:
- не существует или null - результат итератора "не правда"
- если скаляр - фильтр вызывается один раз
- если массив - фильтр вызывается для каждого элемента, по порядку начиная с 0
- если объект - фильтр вызывается для каждого элемента, порядок не определён

При переборе существуют специальные ключи данных для доступа к перебираемым данных:

- "@"   - текущий элемент элемент индекса или массива или скаляря
- "@id" - величина индекса массива или ключа объекта текущего элемента или null для скаляра
- "$" - весь пользователь

Множественные совпадения итератора

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

Например, у подписчика хранится список купленных им лицензий с указанием, среди прочего, даты окончания лицензии.

И надо выпустить напоминание тем, кто ещё не продлил лицензии оканчивающиеся затра.

{
 "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". Это автоматически включит режим работы с датой-временем.

Он задаётся аналогично stat.uni - две буквы точности ("от" и "до") после двоеточия. Например "a.b[0].c[7]:Ym"

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

При указании квалификатора, величина "v":

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

- при задании относительного времени (current) - его результат вычисляется с указанной точностью

При указании квалификатора, точность значения "a" приводится к указаной точности путём её уменьшения (или сохранние если они совпадают).

Если "a" меньшей точность чем указано в квалификаторе, то значение останется как есть. В любом случае дата-время в "a" должны быть в нормализованом формате "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

Если вы уверены, что никогда не ошибётесь в значении "v" при внесении в базу и задании даты-времени константой и не используете относительное время, то квалификатор точности можно не использовать - операции строчного сравнения и in/!in сработают как и ожидается.

вверх

Функция 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"  : "код вопроса" 

 ,"reps" : "сдвиг в днях от сегодня" -- не обязательно. например: -2 - позавчера, +1 - завтра

 }

вверх

Месяц и день равны сегодня

Компонента "месяц и день" в ответе равны месяцу и дню в "дате сегодня +/- сдвиг"


 {

  "which" : "dtnowmd" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"reps" : "сдвиг в днях от сегодня" -- не обязательно. например: -2 - позавчера, +1 - завтра

 }

вверх

Год равен текущему

Компонен "год" в ответе равен текущему году


 {

  "which" : "dtnowy" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Месяц равен текущему

Компонен "месяц" в ответе равен текущему месяцу


 {

  "which" : "dtnowm" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

День равен текущему

Компонен "день" в ответе равен текущему дню +/- сдвиг


 {

  "which" : "dtnowd" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 ,"reps" : "сдвиг в днях от сегодня" -- не обязательно. например: -2 - позавчера, +1 - завтра

 }

вверх

Строка пуста

Ответ пустой


 {

  "which" : "emptyS" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Строка не пуста


 {

  "which" : "dirtyS" 

 ,"aid"  : "код анкеты" 

 ,"qid"  : "код вопроса" 

 }

вверх

Cтрочное сравнение

Это медленый фильтр.

В будущем он может быть удалён.

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

Строка ответа на вопрос не равна (ne), равна (eq), меньше(lt), меньше или равна (le), больше или равна (ge), больше (gt) чем строка в '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":[
           -- Один раз колонки уникального ключа

            "Номер получателя" 
           ,"Номер рассылки" 

           -- Колонки первого запроса

           ,"Адрес получателя" 
           ,"Дата регистрации" 
           ,"Дата рассылки" 
           ,"Статус доставки" 

           -- Колонки второго запроса

           ,"Дата первого клика" 
           ,"Число кликов" 
           ]

,"result" : "save" 
,"result.format" : "xlsx" 
}

В результате получится примерно следующее.

Пользователь второй из строки не кликал, что отражается как 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

Описание

Для импортирования и рассылки через Экспресс-Выпуск доступны четыре формата описания адресов и их данных.

Данные задаваемые непосредственно в вызове указываются в параметре users.list

Внешние данные указываются ссылкой в параметре users.url

Данные трактуются по моделе КД.

При выпуске почтовой рассылки адрес получателя задаётся ключём member.email

При выпуске sms-рассылки номер получателя задаётся ключём member.cellphone

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

При выпуске почтовой рассылки адрес получателя находится в колонке member.email.

При выпуске sms-рассылки номер получателя находится в колонке member.cellphone.

Остальные данные не обязательны, но при их наличии каждая колонка содержит только по одному значению для каждого адреса (не может быть массивом или объектом) и должна быть описана в caption.

Данные трактуются по моделе АВО

Первый лист должен содержать в первой строке описание какой анкете и ответу соответствует данная колонка (в формате коданкеты.кодвопроса).

При импорте возможно задание этого соответствия в параметре caption вместо первой строки первого листа.

Одна ячейка может содержать только одно значение для каждого адреса (не может быть массивом или объектом).

Понимаются дzанных сжатые архиватором zip, хотя пользы от этого ни какой - xslx и так является zip-архивом и повторное сжатие нечего не даст.

Данные трактуются по моделе АВО

Разделитель колонок - запятая или точка с запятой.

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 сообщение о найденной проблеме.

Кастомизированные ссылки

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

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

{DT.YYYY} - год (4 цифры)

{DT.MM} -  месяц (2 цифры)
{DT.DD} -  день (2 цифры)
{DT.hh} -  час (2 цифры)
{DT.mm} -  минута (2 цифры)
{DT.ss} -  секунда (2 цифры)

{DT.M} -  месяц (1 или 2 цифры)
{DT.D} -  день (1 или 2 цифры)
{DT.h} -  час (1 или 2 цифры)
{DT.m} -  минута (1 или 2 цифры)
{DT.s} -  секунда (1 или 2 цифры)

Основное назначение этой особенности - облегчение создания действий по расписанию. Например, вам требуется ежедневно импортировать данные, но файл с ними имеет в названии текущий день. Тогда параметр со ссылкой на данные будет выглядеть как "http://test.ru/a/b/file{DT.DD}.csv".

Пример сборки запроса к API

При вызове с использованием application/x-www-form-urlencoded данные для передачи в POST-запросе должны собираться по описанному ниже алгоритму.

Алгоритм приблизительный и не учитывает особенностей тех или иных языков программирования.

Предполагается что все исходны данные уже в кодировке utf-8.

var_str = "apiversion=100&json=1&" 

var_str = var_str + "jsonp=" + urlencode( var_jsonp_callback ) + "&"  # только если вы используете JSONP

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.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 и пример их
                             использования в сводной статистике) 

0.88      2011-10-26      Возможность влиять на код группы-списка создваемой при внесении списка подписчиков

0.87      2011-10-20      Не совместимое изменение работы member.get и member.set с участием в группах-списках.

0.86      2011-10-18      Возможность разовой авторизации.

0.85      2011-10-17      Новые вызовы user.create, user.delete, user.set (вместо sys.password.set)

0.84      2011-10-13      Новые вызовы issue.later.*, rights.*, user.list, issue.draft.preview и infolett.preview.

0.83      2011-10-10      Новые вызовы link.* и sys.messaage. Вызов about.get удалён.

0.82      2011-09-27      Вызов "Универсальная статистика" 

0.81      2011-09-26      Новый вызов "Окончание работы". Уточнение что импорт может добавлять в любую группу-список

0.80      2011-09-22      Вызов создания группы group.create теперь полностью поддерживает создание группы-списка

0.79      2011-09-14      Только протокол https;  Вызов about.get будет удалён

0.78      2011-09-13      Добавлены разделы "Журнал работы", "Пароль", "Чтение настроек", "Изменение настроек",
                          "Общая статистика по группе", "Статистика выпусков", "Портрет аудитории", "Доставка выпусков" 

0.77      2011-08-11      Добавлены разделы "Пригласить подписчиков","Общее оформление выпусков",
                          "Массовое изменение данных подписчиков", "Форма подписки для сайта", "Файлы изображений и отчетов" 

0.76      2011-06-27      Добавлен раздел "Форматы просмотра и шаблоны заполнения" 

0.75      2011-06-23      Вызов member.delete может работать со списком и группой

0.74      2011-05-30      Уточнение описания member.list

0.73      2011-05-30      Форматирование

0.72      2011-05-30      Добавлен раздел "Список подписчиков" (вызов member.list), и "Архив выпусков" (вызов issue.list)

0.71      2011-05-11      Добавлен раздел "Черновики выпусков" 

0.70      2011-04-29      Расширение функций вызова stat.activity

0.69      2011-04-27      Вызов email.test

0.68      2011-04-26      Добавлен раздел "История изменений" 

0.67      2011-04-26      * Вынос вызовов <OBJ>.list к своим объектам. Описание синхронных и асинхронных вызовов.
                          * Уточнение про медленные фильтры. Форматирование.

0.66      2011-04-25      Вызовы issue.draft.*, и infofett.*

0.65      2011-04-25      Форматирование

0.64      2011-04-25      Уточнения в описании стоп-листа

0.63      2011-04-25      Форматирование

0.62      2011-04-25      Добавление вызовов stoplist.*  Удаление stoplist.list

0.61      2011-04-12      Описание параметра group в вызове member.set

0.60      2011-04-12      Описание параметра newbie.confirmв вызове member.set

0.59      2011-04-11      Уточнения типа _plain после quest пугали людей и они писали fio_plain

0.58      2011-04-11      Описан member.get

0.57      2011-03-17      Списковые ответы опять хэш.Разница между получением и установкой фильтра. Пример расширен работой с датой

0.56      2011-03-16      Форматирование

0.55      2011-03-16      Улучшено описание параметра session, вызов about.get отнесён в статистику

0.54      2011-03-16      Форматирование

0.53      2011-03-16      Описание фильтра отбора в группе

0.52      2011-03-15      Форматирование

0.51      2011-03-15      Форматирование

0.50      2011-03-15      Форматирование

0.49      2011-03-15      Форматирование

0.48      2011-03-14      member.set::newbie.notify иmember.set::newbie.notify.letter

0.47      2011-03-11      Более внятное название название метода

0.46      2011-03-10      member.set/confirm/delete

0.45      2011-03-10      anketa.save/anketa.quest.save ->anketa.set/anketa.quest.set

0.44      2011-03-01      Уточнение, что дополнятся при импорте может не любая группа

0.43      2011-02-17      Общее описание методов получение изамены фильтра в группе

0.42      2011-02-17      Описание общих методов работы сгруппами

0.41      2011-02-15      Форматирование

вверх