Skip to main content

Webhooks

PDFCanon sends webhook events to your endpoint when asynchronous normalization jobs complete or fail. This guide covers registration, payload format, and HMAC signature verification.

Register a webhook endpoint

Create a webhook in the portal under Settings → Webhooks, or via the API:

curl -X POST https://api.pdfcanon.com/api/portal/webhooks \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/pdfcanon",
"events": ["normalization.success", "normalization.failure", "normalization.rejected"]
}'

The webhook secret is generated server-side and returned in the response. You do not supply it yourself.

Event types

EventDescription
normalization.successA normalization job completed successfully
normalization.failureA normalization job failed with a permanent error
normalization.rejectedA normalization job was rejected by policy

Payload format

{
"event": "normalization.success",
"webhookId": "wh_01jk...",
"timestamp": "2026-01-15T12:34:56Z",
"apiVersion": "2026-01-01",
"data": {
"submissionId": "sub_01jk...",
"status": "SUCCESS",
"processingTimeMs": 342,
"outputHash": "sha256:abcd1234...",
"outputSizeBytes": 98304,
"downloadUrl": "https://api.pdfcanon.com/api/artifacts/abcd1234...",
"warnings": []
}
}

HMAC signature verification

Every webhook request includes a X-PDFCanon-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your webhook secret.

Verification example (Node.js)

const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signature, 'hex')
);
}

Verification example (C#)

using System.Security.Cryptography;
using System.Text;

bool VerifyWebhook(string rawBody, string signature, string secret)
{
var key = Encoding.UTF8.GetBytes(secret);
var body = Encoding.UTF8.GetBytes(rawBody);
var expectedBytes = HMACSHA256.HashData(key, body);
var expected = Convert.ToHexString(expectedBytes).ToLowerInvariant();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expected),
Encoding.UTF8.GetBytes(signature));
}

Retry policy

PDFCanon retries failed webhook deliveries up to 5 times with exponential backoff. Return a 2xx status code to acknowledge receipt.

Next steps