Getting started
HMAC signing guide
Sign each request so the platform can verify it came from you and wasn’t tampered with.
The recipe
- • Signing string:
`${X-Timestamp}.${rawRequestBody}` - • Algorithm: HMAC-SHA256, output as lowercase hex
- • Secret: your signing secret (shown once at key creation)
- • Body: sign the exact bytes you send — serialize once, sign that string, send that string
- • Bodyless requests: sign
`${timestamp}.`(empty body)
Signing code
const crypto = require('crypto');
function sign(secret, timestamp, rawBody) {
// signing string = "${timestamp}.${rawBody}"
return crypto.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
}
const body = JSON.stringify({ externalOrderId: 'ORD-1001', orderAmount: '2999.00' });
const ts = Math.floor(Date.now() / 1000).toString();
const signature = sign(process.env.AFF_API_SECRET, ts, body);
await fetch(process.env.AFF_BASE_URL + '/v1/conversions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.AFF_API_KEY,
'X-Timestamp': ts,
'X-Signature': signature,
},
body, // sign over the EXACT bytes you send
});Full runnable SDK samples (with product, conversion, order-status and webhook calls) are on the overview page downloads.
Common mistakes
- • Re-serializing the body after signing (e.g. a framework re-encodes JSON) — the bytes change and the signature fails. Sign the final string.
- • Uppercase hex — the platform compares case-insensitively, but emit lowercase to be safe.
- • Wrong separator — it’s a literal dot between timestamp and body:
timestamp + "." + body. - • Clock drift — a timestamp outside ±300s is rejected as stale. Sync via NTP.
- • Reusing a signature — each is single-use within the window; generate a new timestamp + signature per request.
- • Signing a pretty-printed body but sending minified (or vice-versa).
Troubleshooting
A signature failure returns 401 and logs an error code in your API logs: SIG_BAD_SIGNATURE (mismatch), SIG_STALE_TIMESTAMP (clock), SIG_REPLAY (reused). Reproduce locally by signing a known timestamp + body and comparing against the expected hex.
Verify locally (Node.js)
const sig = require('crypto').createHmac('sha256', secret).update('1718000000.{"a":1}').digest('hex');
console.log(sig); // compare with what you sent in X-Signature