Odbieranie wyników
Webhooki z podpisem HMAC, ponowienia i ponowna wysyłka, odpytywanie jako alternatywa oraz odświeżanie wygasłych adresów pobierania.
Co osiągniesz
Generowanie jest asynchroniczne — wysyłasz sesję, a wyniki spływają po kilkudziesięciu sekundach lub minutach. W tym samouczku skonfigurujesz odbiór webhooków (z weryfikacją podpisu), poznasz odpytywanie jako prostszą alternatywę i nauczysz się odświeżać wygasłe adresy pobierania.
Wymagania wstępne
- Klucz API instalacji (jak go zdobyć) z uprawnieniem
plugin.jobs:read(do odpytywania) orazplugin.webhooks:manage(do ponownej wysyłki webhooków). - Dla webhooków: publicznie dostępny adres HTTPS Twojej wtyczki, ustawiony jako
callback_urlinstalacji, oraz sekret HMAC instalacji.
Przebieg
webhook (push) GET /jobs/{id} (pull)
├─ zweryfikuj podpis HMAC ├─ sprawdzaj status
├─ odpowiedz 2xx └─ pobierz outputs[].url
└─ pobierz outputs[].url
wygasł adres? → POST /jobs/{id}/refresh-url
nieodebrany webhook? → POST /webhooks/{delivery_id}/replay
Kroki — webhooki
1. Odbierz powiadomienie
Gdy zadanie osiąga stan końcowy, wysyłamy POST na callback_url Twojej instalacji. Pole event przyjmuje wartości job.completed, job.failed lub 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" }
}
Pole external_metadata wraca dokładnie takie, jakie wysłałeś przy tworzeniu sesji — użyj go, by dopasować wynik do swojego zamówienia (np. wpisz tam ID produktu ze swojego sklepu).
2. Zweryfikuj podpis
Każde powiadomienie ma nagłówek:
X-Qamera-Signature: t=<czas-unix>,v1=<hmac_sha256_hex>
Podpisywany jest ciąg <t>.<surowe-body> sekretem HMAC Twojej instalacji. Odrzucaj powiadomienia z t starszym niż 5 minut. Przykład w 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'));
}
i w 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']);
}
Po wymianie sekretu (POST /installations/{id}/rotate-hmac) przez 48 godzin powiadomienia mają dwa segmenty v1= — jeden na stary, drugi na nowy sekret. Zaakceptuj dowolny z nich. Pełny kontrakt podpisu opisuje protokół webhook.
3. Odpowiedz szybko i przetwarzaj idempotentnie
- Odpowiedz dowolnym statusem
2xx— najlepiej od razu, a przetwarzanie wykonaj w tle. - Przetwarzaj idempotentnie (np. po
job.id+event): przy wolnej odpowiedzi możemy ponowić wysyłkę już dostarczonego powiadomienia. - Zapisuj
(event, job.id, delivered_at)— przyda się przy diagnostyce.
4. Ponowienia i ponowna wysyłka
Jeśli Twój endpoint nie odpowiada 2xx, ponawiamy do 8 razy z rosnącym odstępem (do 1 godziny). Po 5 kolejnych nieudanych dostarczeniach wstrzymujemy wysyłkę na 30 minut; po 3 takich cyklach instalacja zostaje zawieszona.
Powiadomienie, którego nie udało się dostarczyć, możesz wysłać ponownie:
curl -X POST https://qamera.ai/api/v1/plugin/webhooks/00000000-0000-0000-0000-0000000000d1/replay \ -H "X-Api-Key: mk_live_xxxxxxxx.yyyyyyyy"
Zwraca 202 z identyfikatorem nowego dostarczenia.
Kroki — odpytywanie (alternatywa)
Nie chcesz utrzymywać publicznego endpointu? Odpytuj o stan zadań:
# jedno zadanie curl https://qamera.ai/api/v1/plugin/jobs/00000000-0000-0000-0000-0000000000a1 \ -H "X-Api-Key: mk_live_xxxxxxxx.yyyyyyyy" # wszystkie ukończone od wskazanej chwili 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"
Wskazówki:
- Odpytuj co 15–30 sekund, nie częściej — limit zapytań klucza (domyślnie 60/min) musi pomieścić też inne wywołania.
- Stan całej sesji zbiorczo zwraca
GET /orders/{id}— per produkt zobaczyszjobs_total,jobs_completed,jobs_failedi listę wyników. - Webhooki i odpytywanie można łączyć: webhook jako główny kanał, odpytywanie jako siatka bezpieczeństwa.
Wygasłe adresy pobierania
Adresy w outputs[].url są ważne co najmniej 7 dni. Po tym czasie pobierz świeże:
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"
Odpowiedź zawiera nowe outputs[] i expires_at. Najlepiej jednak pobierz pliki na swoją stronę zaraz po otrzymaniu wyniku — nie traktuj naszych adresów jako stałego hostingu.
Częste błędy
| Błąd | Dlaczego wystąpił | Co zrobić |
|---|---|---|
| Webhooki nie przychodzą | Brak callback_url na instalacji albo endpoint nie odpowiada 2xx | Ustaw callback_url w ustawieniach instalacji; sprawdź logi swojego endpointu |
| Weryfikacja podpisu nie przechodzi | Weryfikujesz przetworzone body zamiast surowego; zły sekret; minęło okno rotacji | Podpisuj dokładnie surowe bajty body; po rotacji zaktualizuj sekret w ciągu 48 h |
409 przy replay | Oryginalne dostarczenie nie jest w stanie nadającym się do ponowienia | Ponawiaj tylko nieudane/porzucone dostarczenia |
409 job_not_completed przy refresh-url | Zadanie jeszcze trwa | Poczekaj na status: "completed" — szczegóły |
410 przy refresh-url | Pliki zostały usunięte zgodnie z polityką przechowywania | Pobieraj pliki na swoją stronę od razu po wygenerowaniu — szczegóły |
Co dalej
- Protokół webhook — pełny kontrakt podpisu i niezawodności.
- Sesje hurtowo — webhooki przy dużej liczbie zadań.
- Ponowna sesja — kolejna runda zdjęć.