Developer docsSocial Gateway API reference Postman العربية

Verifying signatures

The signature is HMAC-SHA256 over "timestamp + '.' + raw body" with the subscription secret. During rotation the header carries two signatures, comma separated; accept either. Reject timestamps older than 5 minutes to stop replays.

C#

static bool Verify(string secret, string timestamp, string body, string header)
{
    if (!long.TryParse(timestamp, out var ts) || Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - ts) > 300) return false;
    var mac = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes($"{timestamp}.{body}"));
    var expected = Encoding.ASCII.GetBytes("sha256=" + Convert.ToHexString(mac).ToLowerInvariant());
    return header.Split(',').Any(s => CryptographicOperations.FixedTimeEquals(expected, Encoding.ASCII.GetBytes(s.Trim())));
}

Node.js

const crypto = require("crypto");
function verify(secret, timestamp, rawBody, header) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  return header.split(",").some(s => {
    const a = Buffer.from(s.trim()), b = Buffer.from(expected);
    return a.length === b.length && crypto.timingSafeEqual(a, b);
  });
}

Python

import hmac, hashlib, time
def verify(secret: str, timestamp: str, raw_body: bytes, header: str) -> bool:
    if abs(time.time() - int(timestamp)) > 300:
        return False
    mac = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return any(hmac.compare_digest("sha256=" + mac, s.strip()) for s in header.split(","))

PHP

function verify(string $secret, string $timestamp, string $rawBody, string $header): bool {
    if (abs(time() - (int)$timestamp) > 300) return false;
    $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
    foreach (explode(',', $header) as $s) { if (hash_equals($expected, trim($s))) return true; }
    return false;
}

Important: sign the raw body exactly as received, before any JSON parsing.