Webhooks

Get a POST to your URL when an async job completes or fails. Durable delivery: 6 attempts with exponential backoff (30 s, 60 s, 120 s, 240 s, 480 s, 960 s), survives gateway restarts.

Register

POST /v1/webhooks HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json
 
{ "url": "https://yourapp.example.com/hooks/pixozip" }

Response (the secret is shown once):

{ "id": "...", "url": "...", "secret": "whsec_...", "createdAt": "..." }

Payload

{
  "event": "job.completed",
  "jobId": "f5012a57-9bd7-47b9-9eaf-6f9c00356872",
  "status": "completed",
  "outputBytes": 8465,
  "ratio": 0.74,
  "finishedAt": "2026-05-24T09:45:11.402Z"
}

event is one of job.completed, job.failed. The header X-Img-Signature: sha256=<hex hmac> is computed with your secret on the raw body.

Verify (Node)

import crypto from "node:crypto";
 
function verify(rawBody: Buffer, header: string, secret: string): boolean {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(header);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Verify (PHP)

$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_IMG_SIGNATURE'] ?? '')) {
  http_response_code(401); exit;
}

Idempotency on your side

We may deliver the same event more than once on the rare retry — dedupe on (jobId, event, finishedAt).

Manage

  • GET /v1/webhooks — list registered URLs.
  • DELETE /v1/webhooks/:id — remove.