Отримання результатів

Вебхуки з підписом HMAC, повтори та повторне надсилання, опитування як альтернатива й оновлення прострочених адрес для завантаження.

Що ви отримаєте

Генерація асинхронна — ви надсилаєте сесію, а результати надходять за кілька десятків секунд або хвилин. У цьому матеріалі ви налаштуєте приймання вебхуків (з перевіркою підпису), дізнаєтеся про опитування як простішу альтернативу й навчитеся оновлювати прострочені адреси для завантаження.

Передумови

  • Ключ API інсталяції (як його отримати) з дозволом plugin.jobs:read (для опитування) та plugin.webhooks:manage (для повторного надсилання вебхуків).
  • Для вебхуків: публічно доступна адреса HTTPS вашого плагіна, встановлена як callback_url інсталяції, та секрет HMAC інсталяції.

Перебіг

вебхук (push)                   GET /jobs/{id} (pull)
  ├─ перевірте підпис HMAC        ├─ перевіряйте статус
  ├─ відповідайте 2xx             └─ завантажте outputs[].url
  └─ завантажте outputs[].url
адреса прострочена? → POST /jobs/{id}/refresh-url
неотриманий вебхук? → POST /webhooks/{delivery_id}/replay

Кроки — вебхуки

1. Прийміть сповіщення

Коли завдання досягає кінцевого стану, ми надсилаємо POST на callback_url вашої інсталяції. Поле event набуває значень job.completed, job.failed або job.cancelled.

{
  "event": "job.completed",
  "delivered_at": "2026-06-03T08:00:00.000Z",
  "job": {
    "id": "00000000-0000-0000-0000-0000000000a1",
    "status": "completed",
    "order_id": "00000000-0000-0000-0000-000000000099",
    "completed_at": "2026-06-03T07:59:40.000Z",
    "error": null
  },
  "outputs": [
    {
      "url": "https://…?token=…",
      "type": "image/jpeg",
      "width": 1024,
      "height": 1280
    }
  ],
  "external_metadata": { "sku": "SKU-001" }
}

Поле external_metadata повертається точно таким, яким ви його надіслали під час створення сесії — використайте його, щоб зіставити результат зі своїм замовленням (наприклад, впишіть туди ID продукту зі свого магазину).

2. Перевірте підпис

Кожне сповіщення має заголовок:

X-Qamera-Signature: t=<час-unix>,v1=<hmac_sha256_hex>

Підписується рядок <t>.<сире-body> секретом HMAC вашої інсталяції. Відхиляйте сповіщення з t старшим за 5 хвилин. Приклад на Node.js:

import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody, headerValue, secret) {
  const parts = Object.fromEntries(
    headerValue.split(',').map((p) => p.split('=')),
  );
  if (!parts.t || !parts.v1) return false;
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;

  const expected = createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex');
  return timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(parts.v1, 'hex'));
}

і на PHP:

function qamera_verify($rawBody, $header, $secret) {
  parse_str(strtr($header, ',', '&'), $parts);
  if (!isset($parts['t']) || !isset($parts['v1'])) return false;
  if (abs(time() - (int)$parts['t']) > 300) return false;
  $expected = hash_hmac('sha256', $parts['t'] . '.' . $rawBody, $secret);
  return hash_equals($expected, $parts['v1']);
}

Після заміни секрета (POST /installations/{id}/rotate-hmac) протягом 48 годин сповіщення мають два сегменти v1= — один для старого, другий для нового секрета. Приймайте будь-який із них. Повний контракт підпису описує протокол вебхука.

3. Відповідайте швидко й обробляйте ідемпотентно

  • Відповідайте будь-яким статусом 2xx — найкраще одразу, а обробку виконуйте у фоні.
  • Обробляйте ідемпотентно (наприклад, за job.id + event): за повільної відповіді ми можемо повторно надіслати вже доставлене сповіщення.
  • Зберігайте (event, job.id, delivered_at) — знадобиться при діагностиці.

4. Повтори та повторне надсилання

Якщо ваш ендпойнт не відповідає 2xx, ми повторюємо до 8 разів зі зростаючим інтервалом (до 1 години). Після 5 поспіль невдалих доставлень ми призупиняємо надсилання на 30 хвилин; після 3 таких циклів інсталяція зупиняється.

Сповіщення, яке не вдалося доставити, ви можете надіслати повторно:

curl -X POST https://qamera.ai/api/v1/plugin/webhooks/00000000-0000-0000-0000-0000000000d1/replay \
  -H "X-Api-Key: mk_live_xxxxxxxx.yyyyyyyy"

Повертає 202 з ідентифікатором нового доставлення.

Кроки — опитування (альтернатива)

Не хочете утримувати публічний ендпойнт? Опитуйте про стан завдань:

# одне завдання
curl https://qamera.ai/api/v1/plugin/jobs/00000000-0000-0000-0000-0000000000a1 \
  -H "X-Api-Key: mk_live_xxxxxxxx.yyyyyyyy"

# усі завершені від вказаного моменту
curl "https://qamera.ai/api/v1/plugin/jobs?status=completed&created_after=2026-06-03T00:00:00Z&limit=50" \
  -H "X-Api-Key: mk_live_xxxxxxxx.yyyyyyyy"

Підказки:

  • Опитуйте кожні 15–30 секунд, не частіше — ліміт запитів ключа (за замовчуванням 60/хв) має вмістити й інші виклики.
  • Стан усієї сесії сукупно повертає GET /orders/{id} — для кожного продукту побачите jobs_total, jobs_completed, jobs_failed і список результатів.
  • Вебхуки й опитування можна поєднувати: вебхук як основний канал, опитування як страхувальна сітка.

Прострочені адреси для завантаження

Адреси в outputs[].url дійсні щонайменше 7 днів. Після цього отримайте свіжі:

curl -X POST https://qamera.ai/api/v1/plugin/jobs/00000000-0000-0000-0000-0000000000a1/refresh-url \
  -H "X-Api-Key: mk_live_xxxxxxxx.yyyyyyyy"

Відповідь містить нові outputs[] і expires_at. Утім, найкраще завантажте файли на свій бік одразу після отримання результату — не сприймайте наші адреси як постійний хостинг.

Поширені помилки

ПомилкаЧому виниклаЩо зробити
Вебхуки не надходятьНемає callback_url на інсталяції або ендпойнт не відповідає 2xxВстановіть callback_url у налаштуваннях інсталяції; перевірте логи свого ендпойнта
Перевірка підпису не проходитьПеревіряєте оброблене body замість сирого; неправильний секрет; минуло вікно ротаціїПідписуйте точно сирі байти body; після ротації оновіть секрет протягом 48 год
409 при replayОригінальне доставлення не в стані, придатному для повторуПовторюйте лише невдалі/покинуті доставлення
409 job_not_completed при refresh-urlЗавдання ще триваєЗачекайте на status: "completed"подробиці
410 при refresh-urlФайли видалено згідно з політикою зберіганняЗавантажуйте файли на свій бік одразу після генерації — подробиці

Що далі