Setup and Configuration
This guide covers how to create a purchase invoice webhook in your Complyance workspace and configure it for secure event delivery.
Before You Begin
Make sure you have:
- Access to the correct Complyance workspace
- A backend endpoint that can accept
POSTrequests - HTTPS enabled for production use
- A plan for storing and rotating webhook secrets securely
Step 1: Open the Webhooks Area
Navigate to your workspace and open the Webhooks section.
Step 2: Create a New Webhook
Click New Webhook and complete the initial configuration fields.
| Field | Required | Description |
|---|---|---|
| Webhook Name | Yes | A descriptive name for internal identification |
| Country | Yes | One supported country: AE, SA, MY, DE, or BE |
| Webhook URL | Yes | The HTTPS endpoint that will receive webhook deliveries |
| Environment | Yes | Choose sandbox or production |
Environment Selection Note
Choose the environment carefully when creating the webhook. The environment selector locks after creation and cannot be changed later.
Step 3: Enable HMAC Signing
HMAC signing is strongly recommended for all webhook integrations.
- Turn
HMAC Signingon - Select a signing algorithm
- Copy and securely store the generated secret
Supported signing algorithms:
SHA-256recommendedSHA-512
Secret Management Note
The signing secret is shown only once during setup and cannot be retrieved later. If the secret is lost, you will need to regenerate it and update your receiving service.
Signature Verification
When HMAC signing is enabled, Complyance includes an X-Webhook-Signature header with each webhook delivery. Your receiver should verify this signature before trusting or processing the payload.
Why Verification Matters
Signature verification helps ensure that:
- The webhook was sent by a trusted source
- The payload was not modified in transit
- Your endpoint does not process forged requests
Verification Steps
- Read the raw request body exactly as received
- Load the signing secret captured during webhook creation
- Compute an HMAC digest over the raw body using the configured algorithm
- Compare your computed digest with
X-Webhook-Signatureusing a timing-safe comparison
Important Implementation Detail
Use the raw JSON request body, not a reformatted, parsed, or pretty-printed version of the payload. Even harmless formatting changes can produce a different digest and cause signature verification to fail.
Node.js / TypeScript Example
import { createHmac, timingSafeEqual } from 'crypto';
function verifyWebhookSignature(rawBody: string, secret: string, signatureHeader: string) {
const expected = createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
return timingSafeEqual(
Buffer.from(signatureHeader, 'hex'),
Buffer.from(expected, 'hex')
);
}Python Example
import hmac
import hashlib
def verify_webhook_signature(raw_body: str, secret: str, signature_header: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
raw_body.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)Java Example
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
public static boolean verifyWebhookSignature(String rawBody, String secret, String signatureHeader) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
String expected = bytesToHex(mac.doFinal(rawBody.getBytes("UTF-8")));
return MessageDigest.isEqual(expected.getBytes(), signatureHeader.getBytes());
} catch (Exception e) {
return false;
}
}Go Example
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func verifyWebhookSignature(rawBody, secret, signatureHeader string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(rawBody))
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signatureHeader))
}PHP Example
function verifyWebhookSignature(string $rawBody, string $secret, string $signatureHeader): bool {
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signatureHeader);
}.NET Example
using System.Security.Cryptography;
using System.Text;
bool VerifyWebhookSignature(string rawBody, string secret, string signatureHeader)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody));
var expected = Convert.ToHexString(hash).ToLower();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expected),
Encoding.UTF8.GetBytes(signatureHeader)
);
}Security Best Practices
- Keep the signing secret in a secure secret store or server-side environment variable
- Rotate secrets when there is any suspicion of exposure
- Reject requests with invalid signatures before business processing
- Avoid logging raw secrets or full sensitive payloads
- Keep webhook endpoints server-side only, never in browser code
Operational Recommendation
Return an error response when signature verification fails, and make sure those failures are monitored. Invalid-signature patterns are often your earliest signal of a misconfiguration or malicious traffic.
Step 4: Select Events
Choose the purchase invoice events your endpoint should receive:
purchase.invoice.storedpurchase.invoice.validation_failed
Depending on the UI flow, you may also see quick presets such as:
All EventsFinancial Events- Category-based event selection
For a focused integration, subscribe only to the purchase invoice events your system is prepared to process.
Step 5: Create the Webhook
Click Create Webhook to save the configuration and activate deliveries for the selected environment and country.
Recommended Configuration Practices
- Use separate webhook endpoints for
sandboxandproduction - Keep webhook naming descriptive so teams can identify ownership easily
- Store signing secrets in environment variables or a secrets manager
- Restrict endpoint access to server-side infrastructure only
- Log delivery receipt, event type, and event ID for support and audits
Operational Checklist
Before going live, confirm that your receiver:
- Returns a successful HTTP response quickly
- Verifies signatures before processing
- Handles duplicate deliveries safely
- Stores the event ID for idempotency
- Captures failed deliveries in logs or monitoring