Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developer.finogates.com/llms.txt

Use this file to discover all available pages before exploring further.

All webhooks sent by Finogates are signed using HMAC-SHA256.
You must verify this signature to confirm that the request:
  • Originated from Finogates
  • Was not modified in transit
  • Is not a replayed request

Required Headers

Every Finogates webhook request includes the following headers:
HeaderDescription
Finogates-SignatureSignature header in the format t=timestamp,v1=signature
Finogates-Signature-VersionSignature version (currently 1)
Content-Typeapplication/json

Example

Finogates-Signature: t=1704978452,v1=5f0c2d7f0c0e9b...
Finogates-Signature-Version: 1

Obtaining Your Webhook Secret

Navigate to the Developer Panel to obtain your secret key for verifying webhook signatures.

Signature Construction

Finogates signs the webhook payload using the following steps:

1. Build the Signed Payload

{timestamp}.{raw_request_body}
  • timestamp → Unix timestamp (seconds)
  • raw_request_body → Exact raw body bytes (no formatting changes)

2. Generate HMAC

HMAC_SHA256(secret, signed_payload)
  • secret → Your webhook signing secret
  • Output → Hex-encoded SHA-256 digest

Verification Steps

Your webhook handler must:
  1. Read the raw request body
  2. Parse t and v1 from Finogates-Signature
  3. Reject requests older than your allowed time window (recommended: 5 minutes)
  4. Recompute the HMAC signature
  5. Compare using a constant-time comparison
  6. Respond with 200 OK only if verification succeeds

Signature Verification Examples

const crypto = require("crypto");

function verifyFinogatesWebhook(req, secret) {
  const signatureHeader = req.headers["finogates-signature"];
  if (!signatureHeader) throw new Error("Missing Finogates-Signature header");

  const parts = Object.fromEntries(
    signatureHeader.replace(/\s/g, "").split(",").map(p => p.split("="))
  );

  const timestamp = parseInt(parts.t, 10);
  const receivedSignature = parts.v1;

  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - timestamp) > 300) {
    throw new Error("Webhook timestamp outside allowed window");
  }

  const rawBody = req.rawBody; // MUST be raw bytes
  const signedPayload = `${timestamp}.` + rawBody;

  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");

  if (
    !crypto.timingSafeEqual(
      Buffer.from(expectedSignature),
      Buffer.from(receivedSignature)
    )
  ) {
    throw new Error("Invalid webhook signature");
  }

  return true;
}

Security Recommendations

  • Always read the raw request body (do not re-serialize JSON)
  • Enforce a timestamp tolerance window (recommended: 5 minutes)
  • Store your webhook secret securely
  • Reject requests with missing or malformed headers
  • Respond with 200 OK only after successful verification

Need help? Contact support@finogates.com