EyeQ Docs

Webhooks

Receive automatic delivery notifications when a correction job completes

Instead of polling GET /v2/status/{taskId}, you can supply a webhookUrl on your /v2/pfc request and Perfectly Clear will POST the result to your endpoint the moment the job reaches COMPLETED or FAILED.

Webhooks also fire on cache hits — if the same image with the same parameters was processed before, the webhook still delivers the cached result.

Adding a webhook URL

Append webhookUrl as a query parameter on your /v2/pfc call. The value must be URL-encoded and must use HTTPS — a plain http:// URL returns a 400 error before any processing begins.

# URL-encode your endpoint first, e.g. https://example.com/hook → https%3A%2F%2Fexample.com%2Fhook
curl -s -X GET "https://api.perfectlyclear.io/v2/pfc?fileKey=${fileKey}&webhookUrl=https%3A%2F%2Fexample.com%2Fhook" \
  -H "X-API-KEY: ${PFC_API_KEY}"

If webhookUrl is omitted the API behaves exactly as before — the job completes normally and you can retrieve the result by polling GET /v2/status/{taskId}.

Webhook request

When the job finishes, Perfectly Clear sends a POST request to your endpoint with the following headers and body.

Headers

HeaderFormatDescription
webhook-idevt_{taskId}Unique event identifier
webhook-timestampUnix secondsTime the webhook was sent
webhook-signaturev1,<base64>HMAC-SHA256 signature (see Verifying signatures)
Content-Typeapplication/jsonAlways JSON
User-AgentEyeQ-Webhook/1.0Identifies the sender

Body

The body is identical to the response of GET /v2/status/{taskId} and includes fields such as _id, status, correctedFile, usage, originalDimensions, and others.

{
  "_id": "abc123",
  "status": "COMPLETED",
  "correctedFile": "https://...",
  "usage": { ... },
  "originalDimensions": { "width": 3000, "height": 2000 }
}

Verifying signatures

The webhook-signature header proves the request came from Perfectly Clear and not a third party.

Algorithm: HMAC-SHA256
Secret: the API key used in the /v2/pfc request
Signed content: {webhook-id}.{webhook-timestamp}.{raw_body} — literal dots, raw JSON body (do not re-serialize)

The v1, prefix is a versioning marker. If the signing algorithm ever changes (for example, to Ed25519) the prefix becomes v1a, allowing receivers to support both during migration.

Quick verification with shell

ID="evt_abc123"
TS="1714500000"
BODY='{"_id":"abc123","status":"COMPLETED",...}'
APIKEY="${PFC_API_KEY}"

EXPECTED=$(printf "%s.%s.%s" "$ID" "$TS" "$BODY" \
  | openssl dgst -sha256 -hmac "$APIKEY" -binary \
  | base64)

echo "v1,$EXPECTED"
# Compare this output to the webhook-signature header — they must match exactly.

What to test

  • Signature matches when computed with the correct API key.
  • Signature changes when webhook-timestamp changes (replay protection — include the timestamp in signed content so receivers can reject deliveries older than a chosen tolerance window).
  • Signature does not match if you re-format the body (e.g. JSON.parse then JSON.stringify may reorder keys or change whitespace).
  • Signature does not match if you use a different API key.

Retries

If your endpoint returns a non-2xx status code, Perfectly Clear retries delivery 8 times over 48 minutes. After all retries are exhausted the job itself is unaffected — GET /v2/status/{taskId} still returns the result.

Testing with webhook.site

webhook.site provides a free, disposable HTTPS receiver — useful for verifying end-to-end delivery before wiring up your own endpoint.

  1. Open webhook.site — a unique URL is generated automatically (e.g. https://webhook.site/08f51eb0-...). Keep the tab open to inspect incoming requests in real time.
  2. URL-encode that URL (e.g. using urlencoder.org).
  3. Submit a correction request with the encoded URL as webhookUrl.
  4. When the job finishes, the browser tab refreshes automatically. Verify the headers and body match what you expect.

To test retry behavior, set the Default response code in webhook.site's top-right dashboard to 500 and re-run the request — you should see 8 retry attempts arrive over the next 48 minutes.

WEB-API Version 2 built on 05-14-2026.

Copyright © 2026 EyeQ Imaging Inc. All rights reserved.

On this page