Тригери й відповідь

Флоу має рівно один тригер — саме він визначає, коли флоу запускається.

Вебхук

Кожен флоу з вебхук-тригером має власну адресу з токеном:

POST https://app.oxta.io/webhook/<id флоу>/<токен>

Вихід тригера — headers, query, body, method, path, timestamp. Читайте його за іменем тригера: {{ outputs.incoming.body.title }}.

Тіло розбирається за Content-Type: JSON стає об'єктом, application/x-www-form-urlencoded — об'єктом полів (усі значення рядками), будь-що інше приходить рядком як є. Якщо сервіс надсилає JSON із заголовком форми (так робить, наприклад, WayForPay), тіло все одно розбирається як JSON.

Перевірка підпису

Токен в адресі захищає лише доти, доки адресу ніхто не дізнався. Платіжні системи й магазини додатково підписують кожен запит, і тоді флоу може приймати тільки справжні. Увімкніть це в тригері «Вебхук», блок «Перевірка підпису»:

СхемаЗаголовокЯк перевіряємо
Загальний HMACзадаєте самі, типово X-SignatureHMAC сирого тіла: SHA-256, SHA-1 або SHA-512, hex або base64, з префіксом або без (sha256=)
WooCommerceX-WC-Webhook-Signaturebase64 HMAC-SHA256 сирого тіла
ShopifyX-Shopify-Hmac-Sha256base64 HMAC-SHA256 сирого тіла
StripeStripe-Signaturehex HMAC-SHA256 від мітка часу.тіло, не старше 5 хвилин
GitHubX-Hub-Signature-256sha256= + hex HMAC-SHA256 сирого тіла
Monobank (еквайринг)X-SignECDSA P-256 відкритим ключем банку
LiqPayполе signature у тіліbase64 SHA1 (або SHA3-256) від private_key + data + private_key
WayForPayполе merchantSignature у тіліhex HMAC-MD5 полів платежу через ;

Секрет береться лише зі змінної флоу з позначкою «секрет». У тригері зберігається тільки назва змінної, самого значення в налаштуваннях вузла немає. Змінна без позначки «секрет» не підійде.

  • WooCommerce. У WooCommerce → Налаштування → Розширені → Webhooks вставте адресу вебхука і в поле «Secret» — те саме значення, що у змінній. Під час збереження WooCommerce надсилає тестовий запит webhook_id=… без підпису: на нього Oxta відповідає 200, але флоу не запускає. Флоу при цьому має бути увімкнене, інакше буде 403, і WooCommerce вебхук не збереже. Після п'яти невдалих доставок поспіль WooCommerce сам вимикає вебхук, тож спершу переконайтеся, що підпис проходить.
  • Monobank. У змінну покладіть токен мерчанта (X-Token): за ним Oxta сама отримає відкритий ключ банку й запам'ятає його. Можна покласти і сам ключ із /api/merchant/pubkey. Якщо банк змінить ключ, Oxta перечитає його (не частіше ніж раз на 5 хвилин).
  • Shopify. У змінну — client secret застосунку. Для вебхуків, створених у Налаштування → Сповіщення → Вебхуки, ключ показано там же під списком.
  • Stripe. У змінну — signing secret із префіксом whsec_… зі сторінки вебхука в Stripe (цілком, разом із префіксом). Підписано мітку часу разом із тілом, тож запит, старший за 5 хвилин, відхиляється як expired — перехоплений запит не вийде повторити. Поки Stripe змінює секрет, у заголовку буває кілька підписів: досить, щоб збігся один.
  • GitHub. У змінну — той самий Secret, що в налаштуваннях вебхука репозиторію чи організації. Тестовий ping, який GitHub надсилає одразу після створення вебхука, отримує 200 без запуску флоу.
  • LiqPay. Підпис лежить не в заголовку, а в полі signature тіла; у змінну — private_key. Поле data LiqPay кодує в base64, тож Oxta додає до тіла розкодований decoded: статус платежу читайте як {{ outputs.incoming.body.decoded.status }}. Документація LiqPay згадує і SHA1, і SHA3-256 — приймаються обидва.
  • WayForPay. Підпис лежить у полі merchantSignature; у змінну — SecretKey мерчанта. Підписано поля merchantAccount;orderReference;amount;currency;authCode;cardPan;transactionStatus;reasonCode. WayForPay чекає у відповідь підписане accept і без нього повторює сповіщення 4 дні, тому на правильний запит Oxta одразу відповідає {"orderReference", "status": "accept", "time", "signature"}, а флоу виконується у фоні. Вузол Response і ?wait=1 у цьому режимі не впливають на відповідь.
  • Інші сервіси — через загальний HMAC: заголовок, алгоритм, кодування й префікс беруться з документації сервісу.

Запит без правильного підпису отримує 401 з причиною в тілі, і флоу не запускається:

reasonЩо це означає
missing-signatureу запиті немає підпису — ні в заголовку, ні в потрібному полі тіла
mismatchпідпис не збігся: інший секрет, або тіло змінили дорогою
expiredпідпис Stripe правильний, але запит старший за 5 хвилин
missing-secretу збереженому флоу не обрана секретна змінна або вона порожня
key-unavailableне вдалося отримати ключ Monobank — перевірте токен

Відхилені запити не з'являються в історії запусків, бо запуску не було. Останні 20 відмов видно в тому ж блоці тригера з часом і причиною; вміст тіла не зберігається. Кнопка «Перевірити на прикладі» звіряє вставлене тіло й підпис, не запускаючи флоу (для LiqPay і WayForPay достатньо тіла — підпис уже в ньому; старий приклад від Stripe покаже expired). Тіло вставляйте точно як його надіслав сервіс: зайвий пробіл уже дає інший підпис. Зміни налаштувань діють після збереження флоу.

Коли викликач чекає на відповідь

Типово вебхук відповідає {"ok": true} одразу, а флоу добігає у фоні. Якщо потрібна справжня відповідь, викликач просить її в самому запиті:

ЗапитЩо буде
без сигналу{"ok": true} одразу, флоу далі у фоні
?wait=1чекаємо на вузол Response і віддаємо його
Prefer: wait=10те саме, стандартом RFC 7240, і 10 секунд як межа
X-Oxta-Wait: 1те саме, якщо адресу змінити не можна
Prefer: respond-asyncне чекати, навіть якщо є ?wait=1

Межа очікування — 15 секунд типово, максимум 30. Після неї викликач отримує 504, але флоу не переривається і добігає до кінця.

Відповідь формує вузол Response: статус, заголовки, тіло з {{виразами}}. Перша відповідь виграє — якщо Response є на кількох гілках, візьметься той, що виконався першим. Щоб віддати змістовну помилку, поставте Response на гілку error.

Форма

Тригер «Форма» запускає флоу з полів, які заповнює людина. У вузлі описуються поля: назва, підпис, тип (текст, багаторядковий текст, число, так/ні, вибір із варіантів, дата), обов'язковість. Форма відкривається на сторінці флоу «Запустити» (/flows/<id>/run) і в тестовому запуску в редакторі. Відповіді потрапляють у флоу як input.form.<назва>, уже приведені до типу: число буде числом, прапорець true або false, дата у форматі YYYY-MM-DD. Порожнє обов'язкове поле чи чужий варіант зупиняють запуск ще до старту, помилка показується під полем. Приклад: заявка на відпустку з полями «співробітник», «днів», «з дати» → Slack керівнику → рядок у Google Sheets. Форму поки бачить лише власник акаунта; публічні форми на сайтах з'являться пізніше.

Розклад

Крон-тригер приймає вираз із п'яти полів і зону IANA:

0 9 * * 1-5     о 9:00 у робочі дні
*/15 * * * *    кожні 15 хвилин
0 0 1 * *       першого числа щомісяця

Дрібніше за хвилину не буває. Зона типово UTC — якщо потрібен Київ, вкажіть Europe/Kyiv, інакше розклад поїде на дві-три години.

Обмеження частоти

У налаштуваннях флоу можна задати ліміт: скільком запитам дозволено пройти за вікно. Понад ліміт вебхук відповідає 429 із Retry-After. Без налаштування діє платформний дефолт: 60 запитів на хвилину на флоу. Додатково всі вебхуки одного акаунта разом не можуть перевищити 600 запитів на хвилину. Для флоу, адреса якого потрапляє в інтернет, варто задати жорсткіший ліміт.