Перейти до вмісту
NimbusNexus

Трансформації

Трансформація — це коротка функція на JavaScript, підʼєднана до ендпоінта. Вона отримує кожну подію, перш ніж ми її доставимо, і повертає запит, який треба надіслати замість неї, або null, щоб нічого не надсилати. Вона виконується один раз на доставку в пісочниці без доступу до мережі, з фіксованим бюджетом CPU і памʼяті; те, що вона повертає, підписується секретом ендпоінта й надсилається, як будь-яка інша доставка.

Для чого вона потрібна

  • Отримувачі, яким потрібен власний формат. Slack, Microsoft Teams і Discord приймають лише власні формати повідомлень і відхиляють наш конверт. Трансформація перетворює кожну подію на повідомлення, яке вони приймуть; пресети зроблять це за вас.
  • Окремий формат для кожного отримувача. Ваш власний сервіс отримує весь payload, CRM — три поля, а сервіс аналітики — жодних персональних даних.
  • Фільтрування за payload-ом. Підписка зіставляється лише за типом події. Для умови «лише invoice.paid на суму понад $1 000» потрібен payload, а скрипт може його прочитати.

Скрипт

Трансформація — це одна функція з назвою transform:

JavaScript
function transform(event, context) {
  // event:   { id, type, created_at, data, version }, the envelope we would otherwise send
  // context: { endpoint: { id, description }, attempt }
  if (event.type === "invoice.paid" && event.data.amount < 100000) {
    return null; // send nothing for small invoices
  }
  return {
    body: { text: `Invoice ${event.data.id} paid: ${event.data.amount / 100} ${event.data.currency}` },
    headers: { "X-Team": "billing" },
  };
}

Від того, що вона повертає, залежить, що буде надіслано:

Що повертаєЩо надсилається
{ body: { … } } або { body: [ … ] }Тіло як JSON, з Content-Type: application/json.
{ body: "…" }Рядок як є, з Content-Type: text/plain; charset=utf-8.
{ body: …, contentType: "…" }Тіло з цим типом вмісту.
{ body: …, headers: { … } }Тіло з доданими заголовками, для яких діють ті самі правила, що й для користувацьких заголовків ендпоінта. Задавайте contentType, а не заголовок Content-Type.
nullНічого. Доставка відкидається.

URL і метод беруться з ендпоінта: це той URL, який було зареєстровано, перевірено й, де це обовʼязково, верифіковано. Усе, що скрипт виводить через console.log, зберігається разом із доставкою — до 4 KiB.

Скрипт — це звичайний сучасний JavaScript в одному файлі. Він не має доступу до мережі й файлової системи, не має таймерів і не може імпортувати модулі, тож потрібну йому бібліотеку треба вбудувати в сам скрипт.

Пресети

Пресети — це скрипти, які ми пишемо й підтримуємо для отримувачів, про яких питають найчастіше:

ПресетЩо надсилає
Slack messageТип, ідентифікатор і дані події як повідомлення Slack — для вхідного вебхука Slack.
Microsoft Teams cardПодію як Adaptive Card — для вебхука робочого процесу Teams.
Discord messageТип, ідентифікатор і дані події як повідомлення Discord — для вебхука каналу Discord.
Keep only these fieldsКонверт лише з тими полями data, які ви перелічите. Назва з крапками, як-от customer.id, вказує на вкладене поле.

Якщо вибрати пресет, його скрипт зберігається як ваша власна трансформація, тож її можна відкрити й змінити, як будь-яку іншу. Покрокове налаштування кожного отримувача з нуля описано на сторінці Slack, Teams і Discord.

Як налаштувати трансформацію

У консолі відкрийте меню ендпоінта на сторінці Endpoints і виберіть Transformation. Оберіть Preset або Script, протестуйте її й натисніть Save: тепер вона виконуватиметься для наступних доставок ендпоінта. Off знову її прибирає.

Через API створіть трансформацію в проєкті ендпоінта, а потім підʼєднайте її до самого ендпоінта:

Shell
curl -sX POST https://webhooks.nimbusnexus.net/v1/transformations \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "CRM fields", "project_id": "prj_…", "code": "function transform(event) { return { body: { id: event.id, total: event.data.total } }; }"}'

curl -sX PATCH https://webhooks.nimbusnexus.net/v1/endpoints/ep_3f9a… \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"transformation_id": "tr_…"}'

Перш ніж зберегти скрипт, ми завантажуємо його в пісочниці, але не викликаємо: скрипт із синтаксичною помилкою, без функції transform або з кодом верхнього рівня, що падає, відхиляється з 422 invalid_transformation, і нічого не зберігається. На ендпоінті може бути щонайбільше одна трансформація; одна трансформація може обслуговувати кілька ендпоінтів свого проєкту. {"transformation_id": null} відʼєднує її.

Тестування скрипту

Test запускає скрипт із редактора, збережений чи ні, у тій самій пісочниці, що й доставки, на нещодавній події з доставок самого ендпоінта або на події, яку ви вставите. Результат показує, що було б надіслано, заголовки, логи й тривалість запуску. Нічого не надсилається й не зберігається.

Через API:

Shell
curl -sX POST https://webhooks.nimbusnexus.net/v1/transformations/test \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code": "function transform(event) { return { body: { text: event.type } }; }",
       "event": {"id": "evt_1", "type": "invoice.paid", "created_at": "2026-10-30T12:00:00+00:00", "data": {}, "version": "1"}}'
JSON
{ "status": "ok", "body": "{\"text\":\"invoice.paid\"}", "content_type": "application/json", "headers": {}, "logs": "", "reason": null, "error": null, "fuel": 2764502, "duration_ms": 3.6 }

status — це ok, dropped (скрипт повернув null) або failed, а reason і error пояснюють причину. Навіть коли скрипт падає, відповідь — 200. Тести й перевірки під час збереження мають спільний ліміт: 60 на хвилину на робочий простір, зі сплесками до 10.

Send test ping ендпоінта й далі надсилає фіксоване тіло пінгу без трансформації; щоб побачити, що надійде отримувачу, скористайтеся Test.

Що записує доставка

Скрипт виконується один раз на доставку, під час її першої спроби. Те, що він повертає, зберігається разом із доставкою й підписується секретом ендпоінта, як будь-яке інше тіло, а кожен повтор надсилає ті самі байти без повторного запуску скрипту, тож повтор не може змінити того, що надходить отримувачу. Повторна доставка знову запускає поточну версію трансформації.

У консолі відкрийте доставку на сторінці Deliveries, щоб побачити трансформацію та версію, які її сформували, тривалість запуску, надіслане тіло й логи або помилку. Через API GET /v1/deliveries/{id}/transform повертає цей запуск або null, якщо трансформація не запускалася.

Відкинуті доставки

Коли скрипт повертає null, нічого не надсилається й жодної спроби не робиться. Доставка отримує стан sent з "dropped": true, консоль показує її як dropped, а статистика ендпоінта й робочого простору рахує її окремо від доставлених: частку успішних доставок обчислюють лише серед тих, які мали бути надіслані.

Коли скрипт падає

Запуск провалюється, коли скрипт кидає виняток, вичерпує час чи памʼять, заходить у надто глибоку рекурсію або повертає щось, що не є запитом (обʼєкт із body або null). Тоді доставка одразу потрапляє в dead-letter чергу з last_error, що починається з transform_failed:. Ми ніколи не надсилаємо замість цього подію без трансформації: отримувачем може бути стороння компанія, від якої скрипт якраз і має приховати дані.

Кожен невдалий запуск враховується для трансформації так само, як невдалі доставки — для ендпоінта. Після 20 невдач поспіль трансформація вимикається автоматично, доставки її ендпоінтів чекають, а ми надсилаємо лист власнику робочого простору й операційний вебхук transformation.disabled. Виправте скрипт, протестуйте його й знову ввімкніть трансформацію (Resume у консолі або PATCH /v1/transformations/{id} з {"status": "enabled"}): доставки, що чекали, буде надіслано, а ті, що потрапили в dead-letter чергу, можна повторно доставити.

Призупинення та версії

Pause ({"status": "disabled"}) зупиняє трансформацію, не відʼєднуючи її. Доставки її ендпоінтів чекають, а не надсилаються без трансформації: вони показуються як failed з last_error «paused: the endpoint's transformation is not running» і надсилаються, коли ви відновите трансформацію.

Кожне збереження нового коду створює нову версію. Зберігаються останні 50 версій (GET /v1/transformations/{id}/versions), і кожна доставка записує версію, яка її сформувала. Щоб повернутися до попередньої версії, завантажте її в редактор і збережіть: вона стане найновішою версією.

Ліміти

Кожен запуск отримує:

ЛімітЗначення
CPUБлизько 50 мс часу скрипту, які рахуються в інструкціях, а не вимірюються, тож той самий скрипт отримує той самий бюджет незалежно від того, наскільки ми завантажені
Загальний час1 секунда
Памʼять64 MiB
Розмір скрипту1 MiB
Результат256 KiB
Логи4 KiB

Скрипти виконуються в окремому сервісі, ізольовано від доставки, і кожен робочий простір може виконувати одночасно лише кілька скриптів, тож повільний чи зламаний скрипт зачіпає лише власні доставки й нічиї інші. Типовий скрипт виконується за кілька мілісекунд.

Плани

Трансформації — і пресети, і скрипти — входять у кожен платний план: Starter, Pro і Custom. Free їх не включає. Плани відрізняються тим, скільки скриптів робочого простору виконується одночасно: 2 на Starter, 4 на Pro і стільки, скільки передбачає договір, на Custom. Картка плану на сторінці Overview у консолі показує кількість запусків за цей місяць. Запуски рахуються, але не тарифікуються.

Робочий простір, який відмовився від платного плану, зберігає свої трансформації: їх і далі можна перейменовувати, призупиняти, відʼєднувати й видаляти, але не можна зберігати новий код, тестувати чи відновлювати. Доставки на ендпоінт, до якого ще підʼєднано трансформацію, чекають, доки план знову не включатиме трансформації або доки ви її не відʼєднаєте.

Помилки

СтатусКодКоли
402not_entitled (feature: transformations)Створення трансформації, збереження нового коду, її ввімкнення або тестування на плані без трансформацій.
422invalid_transformationСкрипт не завантажився: syntax_error, no_transform, code_too_large або load_failed, із зазначенням причини.
409conflict (transformation_in_use)Видалення трансформації, яку ще виконує ендпоінт. Спершу відʼєднайте її.
404not_found (transformation, transformation_version)Трансформації вже немає, або ця версія більше не зберігається.
429rate_limitedПеревищено 60 тестів і перевірок під час збереження на хвилину.
503unavailable (transform)Пісочниця не змогла виконати скрипт саме в той момент. Нічого не збережено; спробуйте ще раз.

Що далі