Skip to content

Webhook Security

Verify that webhook requests are genuinely from PackEdge using signature verification.

Signing Secret

Each webhook has a unique signing secret (starts with whsec_). This secret is used to sign each webhook payload, allowing you to verify authenticity.

Find your signing secret in Settings > Integrations > [Your Webhook].

Signature Header

Every webhook request includes a X-PackEdge-Signature header containing the HMAC-SHA256 signature of the request body.

X-PackEdge-Signature: sha256=abc123...

Verification

PHP Example

php
function verify_webhook_signature($payload, $signature, $secret) {
    $expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
    return hash_equals($expected, $signature);
}

// In your webhook handler
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PACKEDGE_SIGNATURE'] ?? '';
$secret = 'whsec_your_signing_secret';

if (!verify_webhook_signature($payload, $signature, $secret)) {
    http_response_code(401);
    exit('Invalid signature');
}

// Process the webhook
$event = json_decode($payload, true);

Node.js Example

javascript
const crypto = require('crypto');

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

// Express.js handler
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
    const signature = req.headers['x-packedge-signature'];
    const secret = 'whsec_your_signing_secret';

    if (!verifyWebhookSignature(req.body, signature, secret)) {
        return res.status(401).send('Invalid signature');
    }

    const event = JSON.parse(req.body);
    // Process the webhook

    res.status(200).send('OK');
});

Best Practices

  1. Always verify signatures — Never process webhooks without signature verification
  2. Use timing-safe comparison — Prevent timing attacks with hash_equals() (PHP) or timingSafeEqual() (Node.js)
  3. Use HTTPS — Webhook URLs must use HTTPS to prevent interception
  4. Return 200 quickly — Process webhooks asynchronously if they take time
  5. Handle duplicates — Use the event ID to detect and ignore duplicate deliveries

Regenerating Secrets

If your signing secret is compromised:

  1. Go to Settings > Integrations > [Your Webhook]
  2. Click Regenerate Secret
  3. Update your webhook handler with the new secret
  4. The old secret stops working immediately

Troubleshooting

Signature Mismatch

Common causes:

  • Using the wrong signing secret
  • Payload was modified (e.g., by middleware)
  • Incorrect encoding

Ensure you're verifying the raw request body, not a parsed/modified version.

Request Timeout

PackEdge expects a response within 10 seconds. If your processing takes longer:

  • Queue the webhook for background processing
  • Return 200 immediately
  • Process the event asynchronously