Вернуться к документации

Adal Outbound

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

Открыть как Markdown
На этой странице

Adal Outbound

Adal Outbound — отдельный контур для исходящей HTTP-доставки. Ваше приложение передаёт Adal адрес получателя, метод, заголовки и тело запроса, а Adal принимает сообщение в очередь, выполняет доставку, повторяет неудачные попытки и сохраняет их результаты.

Ваше приложение → Adal Outbound → URL получателя

Outbound работает независимо от входящего потока Adal. Для него не нужно создавать Server, Request или Destination: приложение работает с отдельным API и само указывает получателя каждого сообщения.

Beta: Adal Outbound развивается. Перед production-запуском проверьте интеграцию на тестовом получателе и предусмотрите обработку повторной доставки.

Когда использовать Outbound

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

Например:

  • SaaS-продукт отправляет события на webhook URL клиентов;

  • платформа уведомляет партнёров об изменениях заказов, платежей или других объектов;

  • внутренний сервис вызывает публичный API CRM, help desk или другой внешней системы;

  • команде нужна единая история исходящих доставок и данные для диагностики;

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

Один запрос к Outbound создаёт одно сообщение для одного получателя.

Как работает Outbound

Рабочий поток состоит из пяти этапов:

  1. Создайте refresh token в разделе Outbound → Tokens.

  2. Обменяйте refresh token через Control Plane на access token.

  3. Получите список регионов, выберите регион и сохраните его домен.

  4. Отправляйте сообщения непосредственно в выбранный регион.

  5. После истечения access token получите новый с помощью того же refresh token.

Outbound → Tokens │ ▼ Refresh token │ ▼ POST /auth/outbound │ ▼ Access token │ ├──► GET /outbound/servers │ └──► POST https://{region-domain}/api/send

Список регионов не нужно запрашивать при каждом обновлении access token. Получите его при первоначальной настройке, сохраните выбранный domain и обновляйте список отдельно, когда хотите проверить появление новых регионов или изменить географию отправки.

Adal не выбирает регион автоматически и не выполняет межрегиональный failover. Регион определяет ваше приложение.

Аутентификация

Outbound использует два типа токенов.

Токен Для чего нужен Где использовать
Refresh token Получение access token Только в Control Plane
Access token Получение списка регионов и отправка сообщений В Control Plane и выбранном регионе

Refresh token

Refresh token — долгоживущий секрет приложения. Создайте его в разделе Outbound → Tokens в дашборде.

Значение refresh token показывается только при создании. Сохраните его сразу: позднее посмотреть или восстановить это значение нельзя. Если токен потерян, создайте новый.

Неиспользуемый или скомпрометированный refresh token можно отозвать в том же разделе. После отзыва получить с его помощью новый access token невозможно.

Храните refresh token в секретном хранилище приложения. Не помещайте его в исходный код, клиентское приложение, обычные логи или сообщения об ошибках.

Получение access token

Перед работой с Outbound API обменяйте refresh token на короткоживущий access token:

POST https://cp.adal.cloud/auth/outbound Authorization: Bearer <refresh-token>

Успешный ответ содержит access token и сведения о сроке его действия:

{ "access_token": "<access-token>", "expires_at": 1786200278343 }

Access token используется для получения списка регионов и отправки сообщений.

После истечения access token повторите запрос к /auth/outbound с тем же действующим refresh token. Повторно получать список регионов только из-за обновления access token не требуется.

Не передавайте refresh token региональным API и не используйте его для отправки сообщений.

Выбор региона

Получите список доступных регионов через Control Plane:

GET https://cp.adal.cloud/outbound/servers Authorization: Bearer <access-token>

Пример элемента списка:

{ "key": "kz2", "name": "Almaty, Kazakhstan", "domain": "kz2.adal.cloud", "country": "Kazakhstan", "city": "Almaty" }
Поле Описание
key Идентификатор региона
name Отображаемое название
domain Домен регионального API
country Страна размещения
city Город размещения

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

Например:

https://kz2.adal.cloud

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

Отправка сообщения

Отправьте сообщение непосредственно в выбранный регион:

POST https://kz2.adal.cloud/api/send Authorization: Bearer <access-token> Content-Type: application/json

Пример тела запроса:

{ "destination": "https://example.com/webhooks", "method": "POST", "headers": { "Content-Type": ["application/json"], "X-Event-Id": ["evt_01K2..."] }, "body_base64": "eyJldmVudCI6Im9yZGVyLmNyZWF0ZWQifQ==", "max_attempts": 3, "add_idempotency": true }

Поля сообщения

Поле Обязательно Описание
destination да Публичный HTTP- или HTTPS-адрес получателя
method да HTTP-метод исходящего запроса
headers нет Заголовки исходящего запроса
body_base64 нет Тело исходящего запроса в Base64
max_attempts нет Максимальное количество попыток доставки для сообщения
add_idempotency нет Нужно ли Adal генерировать Idempotency-Key, если он не передан; значение по умолчанию — true

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

Заголовки

Каждому имени заголовка соответствует массив значений:

{ "X-Event-Id": ["evt_01K2..."], "X-Example": ["one", "two"] }

Такой формат позволяет передавать несколько значений одного HTTP-заголовка.

Некоторыми транспортными заголовками управляет Adal. Не передавайте вручную Host, Content-Length, Connection, Transfer-Encoding и другие hop-by-hop или прокси-заголовки.

Если API отклоняет заголовок как зарезервированный, удалите его из headers.

Можно передать собственный стабильный идентификатор события — например, X-Event-Id или Idempotency-Key. Adal сохраняет пользовательские заголовки между повторными попытками.

Автоматический Idempotency-Key

По умолчанию Adal добавляет автоматически сгенерированный Idempotency-Key в исходящий HTTP-запрос, если пользователь не передал собственный ключ. Для управления этим поведением используйте опциональное поле add_idempotency:

Поле Тип Обязательное Значение по умолчанию
add_idempotency boolean нет true

Значением должен быть JSON boolean true или false. Строки и числа, например "true", 1 и 0, не допускаются.

add_idempotency Пользовательский Idempotency-Key Результат
не передан отсутствует Adal генерирует ключ в формате UUIDv7
не передан присутствует Adal сохраняет пользовательский ключ
true отсутствует Adal генерирует ключ в формате UUIDv7
true присутствует Adal сохраняет пользовательский ключ
false отсутствует Заголовок не добавляется
false присутствует Adal сохраняет пользовательский ключ

Значение false отключает только автоматическую генерацию. Adal никогда не заменяет и не удаляет пользовательский Idempotency-Key.

Автоматически созданный ключ имеет формат UUIDv7:

Idempotency-Key: 01989d55-20df-7a72-87f9-6e50f3d78315

Adal создаёт ключ и сохраняет его вместе с сообщением до постановки доставки в очередь. Все попытки доставки одного сообщения, включая повторные, используют одно и то же значение. Новый самостоятельный вызов /send без пользовательского ключа получает новый UUIDv7.

Чтобы использовать собственный логический ключ, передайте его в headers:

{ "destination": "https://example.com/hook", "method": "POST", "headers": { "Content-Type": ["application/json"], "Idempotency-Key": ["order-create-12345"] }, "body_base64": "eyJvcmRlcl9pZCI6MTIzNDV9", "add_idempotency": true }

В исходящий запрос попадёт неизменённый Idempotency-Key: order-create-12345. Это полезно, если вызывающему приложению нужно использовать один логический ключ в нескольких вызовах /send. Тот же пользовательский ключ сохраняется, даже если add_idempotency имеет значение false.

Чтобы отключить автоматическую генерацию при отсутствии ключа:

{ "destination": "https://example.com/hook", "method": "POST", "add_idempotency": false }

В этом случае Adal не добавляет Idempotency-Key.

Тело запроса

body_base64 содержит байты тела исходящего HTTP-запроса, закодированные в Base64. Это позволяет передавать JSON, текстовые и бинарные данные в одном формате.

Например, тело:

{"event":"order.created"}

передаётся так:

{ "body_base64": "eyJldmVudCI6Im9yZGVyLmNyZWF0ZWQifQ==" }

Base64 не задаёт тип содержимого. При необходимости передайте Content-Type отдельно в headers.

Ответ на отправку

После успешного приёма сообщения Adal возвращает:

202 Accepted

Ответ содержит идентификатор сообщения, выбранный регион и начальный статус pending. Например:

{ "id": 123, "region": "kz2", "status": "pending" }

202 Accepted означает только то, что Adal принял сообщение и поставил его на доставку.

Это не означает, что:

  • получатель уже получил запрос;

  • получатель вернул успешный HTTP-ответ;

  • бизнес-операция внутри системы получателя завершилась успешно.

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

Статусы сообщения

В истории Outbound используются следующие основные состояния:

Статус Значение
pending Сообщение ожидает первой или следующей попытки
delivering Adal выполняет HTTP-запрос к получателю
success Получатель вернул HTTP-статус класса 2xx
failed Разрешённые попытки исчерпаны без ответа 2xx

Эти статусы относятся только к Outbound и не связаны со статусами доставки во входящем потоке Adal.

Доставка и повторные попытки

Доставка считается успешной, если получатель вернул HTTP-статус класса 2xx.

Ошибка соединения, DNS или TLS, тайм-аут либо любой другой HTTP-статус считаются неудачной попыткой. Пока лимит max_attempts не исчерпан, Adal планирует следующую попытку.

Задержка после неудачной попытки с номером N рассчитывается по формуле:

delay = N² minutes

Например:

Неудачная попытка Задержка до следующей попытки
1 1 минута
2 4 минуты
3 9 минут

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

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

Модель at least once и идемпотентность

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

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

Поэтому обработчик получателя должен:

  • быть идемпотентным там, где это возможно;

  • распознавать повторы по стабильному идентификатору события или idempotency key;

  • сохранять результат обработки этого идентификатора;

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

Adal сохраняет переданные заголовки и тело сообщения между попытками, поэтому идентификатор события остаётся тем же. Если Adal генерирует Idempotency-Key, одно и то же созданное значение также используется во всех попытках доставки сообщения. Это помогает получателю распознать повторную доставку, но для предотвращения повторного выполнения бизнес-операции получатель всё равно должен реализовать идемпотентную обработку или дедупликацию по ключу.

Ответ 2xx означает, что получатель успешно ответил на HTTP-запрос. Он не доказывает, что бизнес-операция внутри системы получателя завершилась успешно.

Безопасность адреса получателя

Outbound выполняет запросы по URL, который передаёт пользователь, поэтому Adal проверяет destination и защищает инфраструктуру от SSRF.

Поддерживаются только публично доступные HTTP- и HTTPS-адреса. Отклоняются:

  • localhost и loopback-адреса;

  • приватные и служебные диапазоны IP;

  • внутренние hostname;

  • URL со встроенным логином или паролем.

Адрес проверяется перед подключением и повторно при HTTP redirect. Поэтому Outbound нельзя использовать для прямой отправки на 127.0.0.1, private IP или внутренний hostname.

Примеры недопустимых адресов:

http://localhost/ http://127.0.0.1/ http://192.168.1.10/

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

История и диагностика

Текущее состояние сообщения и история попыток доступны в дашборде:

Outbound → Messages

Сначала выберите регион, через который было отправлено сообщение.

Для каждой попытки, в зависимости от результата, Adal показывает:

  • HTTP-код и заголовки ответа;

  • текст технической ошибки;

  • время DNS-разрешения;

  • время установления соединения и TLS;

  • TTFB и общее время запроса;

  • сведения о TLS-соединении;

  • цепочку HTTP redirects и итоговый URL.

Эти данные помогают отличить ошибку DNS от проблемы TLS, тайм-аута, медленного ответа приложения или неуспешного HTTP-статуса.

Тело HTTP-ответа получателя Adal не сохраняет и не показывает.

История сообщений и попыток доступна через дашборд, а не через публичный API.

Региональная обработка и хранение

Сообщение отправляется непосредственно через выбранный регион. Там же хранятся данные, необходимые для доставки и диагностики:

  • URL получателя;

  • HTTP-метод;

  • заголовки;

  • тело сообщения;

  • текущее состояние;

  • история попыток.

Control Plane используется для аутентификации и получения списка регионов, но не является централизованным хранилищем истории Outbound.

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

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

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

Кредиты

Кредиты списываются при успешном принятии сообщения Adal, а не при каждой попытке доставки.

Повторные попытки, которые Adal выполняет для уже принятого сообщения, не требуют дополнительных кредитов.

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

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

Если доступного баланса недостаточно, сообщение не принимается к доставке и ответ 202 Accepted не возвращается. Проверяйте ответ API до того, как считать сообщение поставленным в очередь.

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

Ошибки и устранение неполадок

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

Основные причины ошибок:

  • неверный, истёкший или отозванный токен;

  • истёкший access token;

  • ошибка формата запроса;

  • неподдерживаемый HTTP-метод;

  • запрещённый HTTP-заголовок;

  • недопустимый или заблокированный SSRF-защитой destination;

  • недостаточный баланс;

  • превышение rate limit.

Проблема Что проверить
Не удаётся получить access token Refresh token существует и не был отозван
Access token истёк Повторите /auth/outbound с действующим refresh token
Refresh token потерян или отозван Создайте новый в Outbound → Tokens
Не удаётся отправить сообщение в регион Используйте актуальный domain, полученный из списка регионов
API отклоняет заголовок Удалите зарезервированный или hop-by-hop заголовок
destination отклонён Убедитесь, что это публичный HTTP- или HTTPS-адрес без встроенных credentials
Сообщение долго остаётся в pending Откройте историю попыток в нужном регионе
Сообщение получило failed Изучите техническую ошибку, HTTP-статусы и timings каждой попытки
Сообщение не видно в дашборде Выберите тот же регион, через который выполнялась отправка

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

Ограничения

В текущем потоке Outbound:

  • регион выбирает приложение, автоматического выбора и межрегионального failover нет;

  • одно сообщение предназначено для одного destination;

  • получатель должен быть публично доступен по HTTP или HTTPS;

  • тело HTTP-ответа получателя не сохраняется;

  • история сообщений и попыток доступна в дашборде, но не через публичный API.

Outbound не обеспечивает exactly-once delivery, строгий порядок доставки или успешное выполнение бизнес-операции у получателя.

Полный поток интеграции

1. Создайте refresh token в дашборде и получите с его помощью access token

POST https://cp.adal.cloud/auth/outbound Authorization: Bearer <refresh-token>

Refresh token сохраните при создании в дашборде. Полученный access token используйте для API-запросов.

2. Получите список регионов

GET https://cp.adal.cloud/outbound/servers Authorization: Bearer <access-token>

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

3. Отправьте сообщение

POST https://kz2.adal.cloud/api/send Authorization: Bearer <access-token> Content-Type: application/json
{ "destination": "https://example.com/webhooks", "method": "POST", "headers": { "Content-Type": ["application/json"], "X-Event-Id": ["evt_01K2..."] }, "body_base64": "eyJldmVudCI6Im9yZGVyLmNyZWF0ZWQifQ==", "max_attempts": 3, "add_idempotency": true }

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

4. Обновляйте access token

Когда access token истечёт, получите новый с помощью refresh token. Не запрашивайте список регионов повторно только из-за обновления токена.

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

Откройте Outbound → Messages, выберите регион отправки и проверьте состояние сообщения и историю попыток.

Краткая схема API

Method Endpoint Авторизация Назначение
POST https://cp.adal.cloud/auth/outbound Refresh token Получить или обновить access token
GET https://cp.adal.cloud/outbound/servers Access token Получить список регионов
POST https://{region-domain}/api/send Access token Поставить сообщение на доставку

Связанная документация