APISTREAM, 1.10 2023-04-24

Назначение

Потоковое API позволяет отправлять массовые рассылки, передавая по одному письму в одном API-запросе.

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

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

Статистика по выпускам, отправленным через Потоковое API доступна через стандартные способы получения статистики Sendsay – раздел "Статистика" в веб-интерфейсе или метод stat.uni обычного API.

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

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

Письма группируются в выпуски на основе меток или номера черновика (как описано ниже). При любом варианте, дополнительно, учитываются - день, саблогин, номер dkim, наличие AMP-версии, номера lbac.

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

Вызов

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

URL вызова указан в его описании

Слово ACCOUNT в нём - код вашего аккаунта.

Метод вызова

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

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

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

Настоятельно рекомендуется использовать Keep-Alive для получения лучшей пропускной способности.

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

Метод вызова - POST - RFC 2616 section 5 https://tools.ietf.org/html/rfc2616#section-9.5

Максимальный размер запроса - в настоящий момент 25 Мбайт.

Content-Type: запроса - application/json

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

Вы можете получить HTTP код ответа 502 или 504 при запросах длительность обработки которых превышает лимит.

При любом исходе запроса, если он обработан, то код ответа HTTP - 200. Ошибки в запросе сообщаются в ответе через errors. А не кодами HTTP - это не REST.

HTTP только транспорт, он имеет свои кода ответов и их надо обрабатывать в соответствии со спецификацией HTTP.

Запрос

Содержимое запроса - JSON-строка в кодировке UTF-8.

{
 "action" : "код вызываемого действия" -- обязательно

 прочие параметры, если нужны
}

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

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

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

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

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

Авторизация

Авторизация по ключу api

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

Для использования ключа api укажите его

или в самом запросе:

{
 "apikey" : "ключ api" 

 данные для вызова
}

или в HTTP заголовке Authorization

Authorization: sendsay apikey=url-кодированный-ключ-api

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

Создайте заранее саблогин от имени которого будут идти вызовы. Настройте использование JWT через Службу Поддержки.

Требования к токену - описаны в основном API по адресу https://sendsay.ru/api/api.html

Для использования jwt-токена укажате его

или в самом запросе:

Передаётся в параметре apikey с приставкой "jwt:'

{
 "apikey" : "jwt:токен" 

 данные для вызова
}

или в HTTP заголовке Authorization

Authorization: sendsay apikey=jwt:url-кодированный-токен

Ответ

JSON-строка в кодировке UTF-8

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


{

 <общие поля>

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

}

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

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

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

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

{

 <общие поля>

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

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

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

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

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

              ......

             ]
}

Отправить письмо

Принимается единичное письмо.

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

Адрес вызова

https://trapi.sendsay.ru/tranz/api/v2/ACCOUNT/json/issue.send/personal

Запрос

{
   "apikey" : "..........."  -- ключ апи. выдаётся в основном api
                             -- или в заголовке Authorization

  ,"action" : "issue.send" 

  ,"group"  : "personal" 

  ,"uuid" : "уникальны идентификатор письма"  -- строка до 240 символов. А-Za-z0-9=_-  если не указан или пустой, то будет назначен автоматически
                                              -- клиентский (уникальный) идентификатор для письма
                                              -- используется для идентификации конкретного письма при получении статистики в основном api
                                              -- в callback передаётся в параметре gate.uniq
                                              -- хранится 30 дней, после этого идентифицировать письмо в статистике можно дате и получателю
                                              -- при автоматическом назначении мы очень стараемся обеспечивать уникальность, но лучше не ленитесь сами

  ,"label" : "метка" или [ "метка1", "метка2", "метка3" ] -- набор произвольных меток выпуска позволяющих выпуск фильтровать/классифицировать в основном api
                                                  -- от 0 до 3 строк не длиннее 32 байт
                                                  -- необязательно
                                                  -- обратите внимание, что это метки именно выпуска, а не конкретного письма - при отображении статистики
                                                  -- в интерфейсы письма будет сгруппированы в выпуски на основе данных меток и dkim.id
                                                  -- это позволяет иметь обобщённую статистику по разным "кампаниям" выпускающимся одновременно
                                                  -- так же, метки объеденённые через пробел используются как название выпуска вместо стандартного "NONE" 
                                                  -- но при использовании черновика правила другие и описаны ниже

  ,"lbac.id" : номер политики из основного api для назначения доступа по LBAC 

-- указание содержимого письма, одно из

  ,"letter" : {

                "subject" : "Тема письма" -- обязательно не пусто

               ,"from.name" : "Имя отправителя" 

               ,"from.email" : "Адрес отправителя (email)" -- обязательно не пусто

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

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

               ,"to.name" : "Имя получателя" 

               ,"message" : { -- одна или обе не пустые версии письма

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

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

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

                * прикреплённые файлы письма

                -- по умолчанию, mime-тип файла определяется по расширению

                -- по умолчанию, кодировкой текстовых файлов text/* считается utf-8

                ,"attaches" : [ 

                             {  -- для двоичных файлов
                              "name" : "имя файла",

                             ,"content": "содержимое файла закодированное base64",

                             ,"encoding" : "base64",

                             ,"mime-type" : "тип атача", -- не обязательно, заменяет тип установленный по расширению имени атача

                             ,"charset" : "набор символов атача", -- не обязательно, заменяет используемое по умолчанию utf-8
                             }

                             {  -- для текстовых файлов
                              "name" : "имя файла",

                             ,"content": "содержимое файла utf-8",

                             ,"mime-type" : "тип атача", -- не обязательно, заменяет тип установленный по расширению имени атача

                             ,"charset" : "набор символов атача", -- не обязательно, заменяет используемое по умолчанию utf-8
                             }

                             ......
                          ]
               }

-- или

  ,"letter" : {

               "draft.id" : "номер или алиас черновика" 
                            -- при использовании черновика письма группируются в выпуск по его номеру и dkim.id
                            -- название черновика становится названием выпуска
                            -- а правила группировки по меткам не используются

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

              }

   ,"email" : "адрес получателя" -- обязательно, не пусто

   ,"email.fake" : "адрес для учёта" -- если не пусто, то именно этот адрес будет занесён в систему и будет фигурировать в отчётах
                                     -- таким образом вы можете дополнительно обезопасить себя - в системе не будут храниться реальный адрес
                                     -- данный адрес так же как и указанный в email проверяется на нахождение в стоп-листе и отписки

   ,"ttl" : "положительное число" -- сколько времени делать попытоки доставить письмо
                                  -- по умолчанию 4 дня если иное не указано в черновике или в аккаунте
                                  -- указывается в секундах  

   ,"dkim.id" : "идентификатор DKIM для подписи письма" 

   ,"relink" :  0|1 -- Преобразовывать ссылки автоматически. По умолчанию - 0, НЕ преобразовывать, что отлично от обычного api
                    -- 1 - да, 0 - нет

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

    -- данные персонализации
    -- персонализируются все версии текста письма и тема письма
    -- для совместимости с полным API индивидуальные данные получателя должны содержать в ключе anketa
    -- по сравнению с полным API не работают функции: countup, countdown, draw_graph, lenta_get, geturl

    ,"extra" : {
                "anketa" : {
                            "val" : 5        -- [% anketa.val %]
                           ,"arr" : [ 8 ,9 ] -- [% anketa.arr[1] %]
                           ,"hash" : { "qwerty" : "йцукен"} -- [% anketa.hash.asdf %]
                           }
                .............
               }

   ,"customer.id" : "клиентский идентификатор" -- строка до 255 байт. если не указан или пустой, то не используется
                                               -- используйте, напрмер, для идентификации получателя (ваш код вашего
                                               -- клиента)
                                               -- если у вас свой уникальный идентификатор письма, то это не сюда, а в uuid
                                               -- доступен при получении статистики в основном api
                                               -- в callback передаётся в параметре сustomer.id
                                               -- хранится 30 дней

   -- слабый черновик
   -- по умолчанию черновик имеет преимущество на параметрами содержимого subject/from.email/from.name/reply.email/reply.name/to.name/message.*
   -- и указываемые там значение (если вообще будут указаны) при наличи draft.id игнорируются
   -- указание в параметре 1 делает черновик "слабым" и перечисленные выше параметры содержимого, если указаны в вызове, заменяют собой аналогичные из черновика

   ,"weak_draft" : 0|1 -- не обязательно. по умолчанию 0 - черновик имеет преимущество

   -- забота о переменных
   -- указанные в списке переменные ProScript проверяются для каждого письмо на пусто-непусто
   -- если пусто, то письмо отменяется с причиной "care for var VAR" 
   -- проверяются только переменные название которых состоих из символов @a-zA-Z.[_]@
   -- cписок из выпуска имеют приоритет над черновиком
   -- cписок ни как не проверяется на использование или не использование его переменных в их тексте
   -- или наличие в данных персонализации
   -- проверка реализуется через специальный ProScript вставляемый без перевода строки перед началом
    -- текста сообщения. при успешной провеке он не оставляется после себя ни какого текста.

   ,"care_vars" : [ список ключей данных ] -- не обязательно
}

Ответ

{
 "uuid" : "идентификатор письма" -- копия входного значения или автоматически назначеный
}

Возможные ошибки

Ошибки отмеченные звёздочкой возворащаются сразу при приёме запроса.

Не отмеченные - как результат обработки через статистику.
Трёхзначный код - код ошибки ESMTP, возвращается в status как отрицательное число состоящее из указаных цифр
Например 6.7.8 как -678

cant_decode - не удалось разобрать json или он не объект (*)

wrong_letter - letter не объект (*)

wrong_uuid - клиентский uuid длиннее 255 или содержит не допустимые символы (*)

wrong_apikey - пустой в apikey (*)

wrong_credentials - ошибка в ззголоовке Authorization (*)

internal_write - внутренние проблемы (*)

6.7.8 unknown_draft - указан неизвестный черновик

6.7.8 draft_isnt_filled_completely - в черновике заполнены не все поля обязательные для выпуска

6.7.8 issue_vs_draft_channel_missmatch - черновик не предназначен для канала email

6.6.5 wrong_action - ошибка в action

6.6.5 wrong_extra - extra не объект

6.6.5 wrong_letter - letter не объект

6.6.5 wrong_letter.from.email - ошибка в адресе отправителя

6.6.5 wrong_letter.reply.email - ошибка в адресе для ответов

6.6.5 wrong_letter.message - message не объект

6.6.5 wrong_letter.attaches - attaches не массив

6.6.5 wrong_letter.attaches.N - запись N не объект (N - целое число)

6.6.5 wrong_letter.attaches.N.name - ошибка с имени записи N не объект (N - целое число)

6.6.5 wrong_letter.attaches.N.encoding - ошибка в encoding записи N не объект (N - целое число)

6.6.5 wrong_email - ошибка в адресе получателя

6.6.5 wrong_email.fake - ошибка в адресе для учёта

6.6.5 wrong_group - ошибка в group

6.6.5 wrong_lbac.id - ошибка в lbac.id

6.6.5 wrong_too_many_labels - меток выпуска больше трёх

6.6.5 wrong_label_toolong_0 - слишком длинная метка 0

6.6.5 wrong_label_toolong_1 - слишком длинная метка 1

6.6.5 wrong_label_toolong_2 - слишком длинная метка 2

6.6.5 wrong_customer.id_toolong - слишком длинный customer.id

Специфические кода ошибок доставки

Все фатальны. Используются в статистике результатов доставки (основное api)

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

6.6.5 - проблема формирования письма, подробности в тексте ошибки

6.7.0 - месячный лимит писем исчерпан

6.7.8 gate is disabled - у аккаунта не подключёно транзакционное апи

6.7.8 authorization failed - api-ключ не валиден

6.7.8 error/account/blocked - акаунт заблокирован

6.7.8 account_not_activated - не выполнена активация акаунта

6.7.8 account_phone_not_confirmed - не подтверждён номер телефона акаунта

6.7.8 error/sender/onmoderation - адрес отправителя на модерации

6.7.8 error/sender/unknown - адрес отправителя не известен

6.7.8 error/sender/prohibited - адрес отравителя не допустим

6.7.8 error/issue/blocked - выпуски заблокированы

6.7.8 error/issue/blocked_personal - транзакционные выпуски заблокированы

6.7.8 error/issue/blocked_spamtraff - выпуски заблокированы из-за спам-трафика

Отправить отложенное письмо

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

Указание времени в в endpoint

Адрес вызова

POST https://trapi.sendsay.ru/tranz/api/v2/ACCOUNT/json/issue.send/personal/later/времявыпуска

{
 как обычное письмо
}

Указание времени в письме

POST https://trapi.sendsay.ru/tranz/api/v2/ACCOUNT/json/issue.send/personal/later/

{
 как обычное письмо

 "later.time" : "время выпуска" 
}

Ответ

Как и при "Отправить письмо"

{
 "uuid" : "идентификатор письма" -- копия входного значения или автоматически назначеный
}

Возможные ошибки

Как и при "Отправить письмо".

Дополнительный код ошибки

wrong_later.time - время с ошибкой, пустое, не совпадает в письме и endpoint (*)

Как работает

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

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

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

Следствия

Время - Московское или укажите на конце Z для UTC+0

Если времена указаны и в endpoint и в письме, то они должны совпадать как строки.

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

Форматы указания времени:

YYYY-MM-DDThh:mm

YYYY-MM-DD hh:mm

YYYY-MM-DDThh:mmZ

YYYY-MM-DD hh:mmZ

YYYY-MM-DDThh:mm:ss

YYYY-MM-DD hh:mm:ss

YYYY-MM-DDThh:mm:ssZ

YYYY-MM-DD hh:mm:ssZ

Дополнительные форматы - можно не указывать разделители, а так же они могут быть любым одним не цифровым символом

YYYYMMDDhhmm

YYYY/MM/DDhh,mm

YYYYMMDDhhmmZ

YYYY/MM/DDhh,mmZ

Отправить несколько писем

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

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

Разумное количество писем за один раз где-то до 50-100.

Для отправки разом практически любого тиража (миллион, два, три...) существует Экспресс-Выпуск в полном api.

Адрес вызова

https://trapi.sendsay.ru/tranz/api/v2/ACCOUNT/json.batch/issue.send/personal

Запрос

{
   "apikey" : "..........."  -- ключ апи. выдаётся в основном api
                             -- или в заголовке Authorization
                             -- если указываете в запросе, то именно тут, а не в каждом задании

  ,"uuid" : "идентификатор запроса" -- не обязательно, просто для облегчения возможно отладки
                                    -- НЕ заменяет собой индивидульный uuid каждого задания

  ,"action" : "batch" 

  ,"do" : [  -- ошибка одного задания ни как не влияет на другие задания - они продолжают обрабатываться
             -- не более 1024 заданий

           { одно задание как при выхзове issue.send }

          ,{ другое задание как при выхзове issue.send }

          ....
          ]

}

Ответ

{
 "result" : [
             { ответ (возможно с ошибкой) на одно задание }
            ,{ ответ (возможно с ошибкой) на другое задание }
            ............
            ]
}

Возможные ошибки

специфические ошибки всего вызова

wrong_do - параметр do не массив (*)

too_many_tasks - количество заданий в do больше максимально допустимого

специфические ошибки конкретной задачи

wrong_task - задание не объект (*)

Общие замечания

Ошибки, опечатки, не соответствия

В данном документе могут быть ошибки и опечатки. Реальное поведение API может иногда отличаться от описанного.

При доработке и изменении API мы оставляем обратную совместимость (на время или на всегда), но при исправлении ошибок вида "поведение API не соответствует документации" такого не происходит, так как исправляется явная ошибка.

Если реальное поведение API отличается от описанного здесь, то настоятельно рекомендуется не ориентироваться на "то что есть", а связаться с нами написав на ask@sendsay.ru сообщение о найденной проблеме.

Ближайшие не совместимые изменения

Нет.

История изменений

1.10    2023-04-24      * Поддержка care_vars для защиты от не верных значений данных персонализации
                        * Группировка писем по черновику, если использутся
                        * Общее описание правил группировки
                        * Новая ошибка wrong_too_many_labels

1.9     2022-12-20      * Содержимое атача не обязательно кодировать base64 если он текстовый
                        * Параметр ignore_stoplist
                        * Ошибка wrong_letter теперь может выдаваться и сразу при приёме

1.8     2022-08-22      * Уточнение что количество заданий в batch ограничено 1024
                        * Уточнение что jwt-токен отложеных писем должен быть валиден на момент выпуска

1.7     2022-08-07      * Новые кода ошибок account_not_activated и account_phone_not_confirmed
                        * Уточнение кода ESMTP для адрес "имеет постоянные ошибки доставки или в стоп-листе" 
                        * Уточнение что API отдельно, а HTTP - отдельно

1.6     2022-06-06      * Новые кода ошибок и учтонённые описания прежних
                        * weak_draft должен находиться в корне вызова

1.5     2022-02-18      * Длинна uuid до 240 символов

1.4     2021-11-18      * Авторизация с помощью JWT
                        * Параметр weak_draft
                        * Заголовок Authorization
                        * Новые ошибки с кодом 6.7.8

1.3     2021-07-01      * Параметр customer.id для дополнительной идентификации письма
                        * Третья метка дял группировки писем в кампании
                        * Новые кода ошибок и уточнение описания старых

1.2     2021-03-26      * Возможность отправки писем, отложенных на определённое время

1.1     2020-12-14      * Уточнён срок храниния клиентских идентификаторов писем

1.0     2020-07-17      * Первоначальное описание