Webhook Signing

Verify that incoming requests to your endpoints are genuinely from Fliq using HMAC-SHA256 signatures.

Signature verification is optional but strongly recommended for production workloads. Without it, anyone who discovers your endpoint URL can send forged requests.

How it works

Every outgoing request from Fliq — both job executions and webhook notifications — includes two signature headers:

X-Fliq-Timestamp: 1774076020
X-Fliq-Signature: v1=661165d836a1ea...
  • X-Fliq-Timestamp — Unix timestamp (seconds) when the request was signed. Use this to reject stale requests (replay protection).
  • X-Fliq-Signature — HMAC-SHA256 signature prefixed with v1=. The version prefix allows future algorithm upgrades without breaking existing integrations.

Getting your signing secret

Go to Dashboard → Settings → Webhook Signing to view your signing secret. It starts with whsec_. You can rotate it at any time — rotation takes effect immediately.

After rotation, in-flight jobs may temporarily use the previous secret. They will be retried automatically with the new secret.

Signature scheme

The signed payload is a dot-separated string of four fields:

signed payload
{timestamp}.{METHOD}.{url}.{body}
  • timestamp — exact value of the X-Fliq-Timestamp header
  • METHOD — uppercase HTTP method (e.g. POST)
  • url — full URL of the request, as configured in the job
  • body — raw request body, or empty string if none

The HMAC is computed using SHA-256 with your signing secret as the key, then hex-encoded and prefixed with v1=.

Verification examples

Copy the verification function for your language and call it in your request handler before processing the payload.

import hmac, hashlib, time

def verify_fliq_signature(secret, timestamp, method, url, body, signature):
    # Reject requests older than 5 minutes
    if abs(time.time() - int(timestamp)) > 300:
        raise ValueError("Timestamp too old — possible replay attack")

    payload = f"{timestamp}.{method}.{url}.{body or ''}"
    expected = "v1=" + hmac.new(
        secret.encode(), payload.encode(), hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        raise ValueError("Invalid signature")

# Usage in your request handler:
# verify_fliq_signature(
#     secret="whsec_...",
#     timestamp=request.headers["X-Fliq-Timestamp"],
#     method=request.method,
#     url=request.url,
#     body=request.get_data(as_text=True),
#     signature=request.headers["X-Fliq-Signature"],
# )

Best practices

  • Always check the timestamp. Reject requests where X-Fliq-Timestamp is more than 5 minutes old to prevent replay attacks.
  • Use constant-time comparison. Use hmac.compare_digest (Python), crypto.timingSafeEqual (Node.js), or hmac.Equal (Go) to prevent timing attacks.
  • Store the secret securely. Use environment variables or a secrets manager — never hardcode it in your source code.
  • Rotate periodically. You can rotate your signing secret at any time from the dashboard. In-flight jobs will self-heal via automatic retries.