GREMDocs

Вебхуки

Останнє оновлення: 2026-07-213 хв читання

Довгі інструменти завершуються асинхронно. Замість опитування зареєструйте вебхук, і GREM надішле POST-подію на ваш URL, коли завдання завершиться.

Реєстрація ендпоінта

У кабінеті AI-інструменти → API → Вебхуки: додайте публічний https://-URL і виберіть потрібні події. Секрет підпису (whsec_…) показується один раз — збережіть його для перевірки доставок.

Події мають вигляд <інструмент>.<результат>, наприклад:

  • valuation.completed, valuation.failed
  • content.completed, content.partial, content.failed
  • photo.completed, avatar.completed, pdf.completed (і варіанти .failed)

Корисне навантаження

Кожна доставка — це POST із JSON-тілом:

{
  "id": "…",
  "type": "content.completed",
  "createdAt": "2026-07-21T10:00:00.000Z",
  "data": { }
}

І заголовками:

x-grem-event: content.completed
x-grem-signature: t=1721556000,v1=3b78a03b78058d229023db2a0b4ee072…

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

Завжди перевіряйте підпис, перш ніж довіряти доставці. Підпис — це HMAC-SHA256 від рядка "<t>.<сире-тіло>" із ключем-секретом вашого вебхука. t — Unix-таймстемп; відхиляйте старі таймстемпи, щоб захиститися від повторів (replay).

Перевіряйте за сирим тілом запиту точно як отримано — до будь-якого JSON-парсингу. Повторна серіалізація JSON змінює байти, і підпис не збігатиметься.

Node.js (Express)

const crypto = require('crypto');

function verifyGremSignature(secret, header, rawBody, toleranceSec = 300) {
  // header: "t=<unix>,v1=<hex>"
  const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // застаріло
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Зберігаємо СИРЕ тіло для цього маршруту.
app.post('/grem-hook', express.raw({ type: 'application/json' }), (req, res) => {
  const raw = req.body.toString('utf8');
  if (!verifyGremSignature(process.env.GREM_WEBHOOK_SECRET, req.get('x-grem-signature'), raw)) {
    return res.status(400).send('invalid signature');
  }
  const event = JSON.parse(raw);
  // обробляємо event.type / event.data …
  res.sendStatus(200);
});

Python (Flask)

import hmac, hashlib, time
from flask import Flask, request

def verify_grem_signature(secret, header, raw_body, tolerance=300):
    parts = dict(kv.split('=', 1) for kv in header.split(','))
    t = int(parts.get('t', 0))
    if not t or abs(time.time() - t) > tolerance:
        return False  # застаріло
    expected = hmac.new(secret.encode(), f"{t}.{raw_body}".encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get('v1', ''))

app = Flask(__name__)

@app.post('/grem-hook')
def grem_hook():
    raw = request.get_data(as_text=True)  # СИРЕ тіло, до парсингу
    if not verify_grem_signature(GREM_WEBHOOK_SECRET, request.headers.get('x-grem-signature', ''), raw):
        return 'invalid signature', 400
    event = request.get_json()
    # обробляємо event['type'] / event['data'] …
    return '', 200

Доставка, повтори та безпека

  • Відповідайте статусом 2xx швидко. Важку роботу виконуйте асинхронно.
  • Неуспішні доставки повторюються з експоненційним backoff; після серії невдач ендпоінт вимикається, і ви отримуєте сповіщення.
  • Ендпоінти мають бути публічними https://-URL. Приватні, loopback та внутрішні адреси відхиляються (анти-SSRF).
  • Секрет підпису можна ротувати будь-коли на вкладці «Вебхуки»; кнопкою Надіслати тест надішліть пробну доставку та перевірте журнал доставок.

Ця стаття була корисною?