WaffarX Signature¶
Overview¶
WaffarX API requires each request to be accompanied by a digital signature for authentication. This signature is an HMAC-SHA256 digest that ensures the request data has not been altered and that it comes from an authorized client. To generate the signature, you will use your Client Key (a public identifier for your application) and your Client Secret (a private key known only to you and WaffarX). The Client Secret is used to sign each request, while the Client Key identifies which client is making the request.
Caution
Protect your client secret. Do not expose it in client-side code or share it. Anyone with your secret could generate valid signatures and impersonate your requests.
The signature generation involves hashing the request payload, formatting the current timestamp, and computing an HMAC-SHA256 using your client secret. The resulting signature is then sent in the request headers for WaffarX to verify. If any part of the signature is incorrect, the server will reject the request with an authentication error.
When to Use the Signature¶
The signature is required for all authenticated API endpoints. It must be generated for each request and included alongside:
wxc-auth
: Contains the generated signaturewxc-date
: UTC timestamp used in the signaturewxc-id
: Your WaffarX client IdOther required headers (e.g.,
accept-language
,content-type
, etc.)
Signature Generation Steps¶
Follow these simplified steps in order to create your request signature:
Hash the payload If your request has a body, first trim whitespace and lowercase the content, then compute its SHA-256 and hex-encode it:
Note
If there is no request body, skip this step entirely and do not include a payload hash in your string to sign.
Build the string to sign
No payload: Concatenate four components with hyphens (-):
METHOD-ACTION_NAME-CLIENT_KEY-TIMESTAMP
Example (no payload)¶get-/api/valu/getstores-9E102E81-2EDF-4C79-8EBA-25DD2B563281-20240424T123456Z
With payload: Concatenate five components with hyphens (-):
METHOD-ACTION_NAME-CLIENT_KEY-TIMESTAMP-PAYLOAD_HASH
Example (with payload)¶post-/api/valu/search-9E102E81-2EDF-4C79-8EBA-25DD2B563281-20240424T123456Z-3a5f1d2e4c...
Where:
METHOD =
get | post (lower-case path)
ACTION_NAME =
/api/valu/getstores` (lower-case path)
CLIENT_KEY =
<CLIENT_KEY>
TIMESTAMP =
20240424T123456Z` (ISO8601 format `yyyyMMddTHHmmssZ`)
PAYLOAD_HASH is the lowercase hex SHA-256 of your trimmed-and-lowercased body.
3a5f1d2e4c...
Note
Make sure each component (especially ACTION_NAME and TIMESTAMP) matches exactly how you send the request.
Compute HMAC-SHA256 Convert your
secret key
toUTF-8
bytes, compute the HMAC over thestring-to-sign
, and hex-encode the result then convert it to string, you will get the final signature.Send the request Include this signature in your HTTP call:
wxc-auth: {signature}
Note
The timestamp header value must exactly match the one you used when building the string to sign.
Code Examples¶
using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
public class HelloWorld
{
public static string GenerateSignature(
string payload,
string method,
string actionName,
string clientkey,
string clientSecret,
string HeaderDate)
{
// parse date as UTC
var datetime = DateTime.Parse(
HeaderDate,
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal);
// 1) trim & lowercase payload
var serializedData = payload.Trim().ToLowerInvariant();
// 2) SHA-256 of payload
byte[] hashedBytes;
using (var sha256 = SHA256.Create())
hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(serializedData));
var payloadHashHex = new StringBuilder();
foreach (var b in hashedBytes)
payloadHashHex.Append(b.ToString("x2"));
// 3) format date as "yyyyMMddTHHmmssZ"
const string fmt = "yyyyMMddTHHmmssZ";
var dateString = datetime.ToString(fmt, CultureInfo.InvariantCulture);
// 4) build the string-to-sign (note lowercase method & actionName)
string stringToSign = serializedData.Length == 0
? $"{method.ToLowerInvariant()}-{actionName.ToLowerInvariant()}-{clientkey}-{dateString}"
: $"{method.ToLowerInvariant()}-{actionName.ToLowerInvariant()}-{clientkey}-{dateString}-{payloadHashHex}";
// 5) HMAC-SHA256 over stringToSign
var keyBytes = Encoding.UTF8.GetBytes(clientSecret);
byte[] hmacBytes;
using (var hmac = new HMACSHA256(keyBytes))
hmacBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
// 6) hex-encode the HMAC
var sb = new StringBuilder();
foreach (var b in hmacBytes)
sb.Append(b.ToString("x2"));
return sb.ToString();
}
public static void Main()
{
// test (should now match the Python version below)
Console.WriteLine(GenerateSignature(
"YourPayloadString",
"POST",
"myEndpoint",
"ClientKeyABC123",
"SecretKeyXYZ456",
"2025-05-01T12:34:56Z"
));
}
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.security.MessageDigest;
public class HmacSignature {
public static void main(String[] args) throws Exception {
String payload = "YourPayloadString";
String method = "POST";
String actionName = "myEndpoint";
String clientKey = "ClientKeyABC123";
String clientSecret = "SecretKeyXYZ456";
String headerDate = "2025-05-01T12:34:56Z";
// 1) Trim & lowercase payload
String trimmed = payload.trim().toLowerCase();
// 2) SHA-256 hash of payload
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = sha256.digest(trimmed.getBytes(StandardCharsets.UTF_8));
StringBuilder ph = new StringBuilder();
for (byte b : hashBytes) {
ph.append(String.format("%02x", b));
}
String payloadHash = ph.toString();
// 3) Format timestamp (yyyyMMddTHHmmssZ)
OffsetDateTime odt = OffsetDateTime.parse(headerDate);
String formattedDate = odt.format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
// 4) Build stringToSign
String mLower = method.toLowerCase();
String aLower = actionName.toLowerCase();
String stringToSign = trimmed.isEmpty()
? String.join("-", mLower, aLower, clientKey, formattedDate)
: String.join("-", mLower, aLower, clientKey, formattedDate, payloadHash);
// 5) Compute HMAC-SHA256
Mac hmac = Mac.getInstance("HmacSHA256");
hmac.init(new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] macBytes = hmac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
// 6) Hex-encode the result
StringBuilder macHex = new StringBuilder();
for (byte b : macBytes) {
macHex.append(String.format("%02x", b));
}
System.out.println(macHex.toString());
}
}
// Save as hmacSignature.js
const crypto = require('crypto');
/**
* Generate HMAC-SHA256 signature.
*
* @param {string} payload – The request body (will be trimmed & lowercased).
* @param {string} method – HTTP method (e.g. 'POST').
* @param {string} actionName – API endpoint (e.g. 'myEndpoint').
* @param {string} clientKey – Your client key.
* @param {string} clientSecret – Your client secret.
* @param {string} headerDate – ISO 8601 UTC timestamp (e.g. '2025-05-01T12:34:56Z').
* @returns {string} – Lowercase hex HMAC-SHA256 signature.
*/
function generateSignature(
payload,
method,
actionName,
clientKey,
clientSecret,
headerDate
) {
// 1) Trim & lowercase payload
const trimmed = payload.trim().toLowerCase();
// 2) SHA-256 hash of payload (hex)
const payloadHash = crypto
.createHash('sha256')
.update(trimmed, 'utf8')
.digest('hex');
// 3) Parse & format date as yyyyMMddTHHmmssZ (UTC)
const dt = new Date(headerDate);
const pad = (n) => String(n).padStart(2, '0');
const formattedDate =
dt.getUTCFullYear() +
pad(dt.getUTCMonth() + 1) +
pad(dt.getUTCDate()) +
'T' +
pad(dt.getUTCHours()) +
pad(dt.getUTCMinutes()) +
pad(dt.getUTCSeconds()) +
'Z';
// 4) Lowercase method & actionName
const m = method.toLowerCase();
const a = actionName.toLowerCase();
// 5) Build stringToSign
const stringToSign =
trimmed === ''
? `${m}-${a}-${clientKey}-${formattedDate}`
: `${m}-${a}-${clientKey}-${formattedDate}-${payloadHash}`;
// 6) Compute HMAC-SHA256 and return hex
return crypto
.createHmac('sha256', Buffer.from(clientSecret, 'utf8'))
.update(stringToSign, 'utf8')
.digest('hex');
}
// Example usage:
const signature = generateSignature(
'YourPayloadString',
'POST',
'myEndpoint',
'ClientKeyABC123',
'SecretKeyXYZ456',
'2025-05-01T12:34:56Z'
);
console.log(signature);
import hmac
import hashlib
from datetime import datetime, timezone
def generate_signature(payload, method, action_name, client_key, client_secret, header_date):
trimmed = payload.strip().lower()
payload_hash = hashlib.sha256(trimmed.encode('utf-8')).hexdigest()
dt = datetime.fromisoformat(header_date.replace('Z', '+00:00')).astimezone(timezone.utc)
formatted_date = dt.strftime("%Y%m%dT%H%M%SZ")
if not trimmed:
string_to_sign = f"{method.lower()}-{action_name.lower()}-{client_key}-{formatted_date}"
else:
string_to_sign = (
f"{method.lower()}-{action_name.lower()}-{client_key}-{formatted_date}-" +
f"{payload_hash}"
)
signature = hmac.new(
client_secret.encode('utf-8'),
string_to_sign.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature
if __name__ == "__main__":
sig = generate_signature(
"YourPayloadString",
"POST",
"myEndpoint",
"ClientKeyABC123",
"SecretKeyXYZ456",
"2025-05-01T12:34:56Z"
)
print(sig)
import 'dart:convert';
import 'package:crypto/crypto.dart';
void main() {
String payload = " YourPayloadString ";
String method = "POST";
String actionName = "myEndpoint";
String clientKey = "ClientKeyABC123";
String clientSecret = "SecretKeyXYZ456";
String headerDate = "2025-05-01T12:34:56Z";
String trimmed = payload.trim().toLowerCase();
String payloadHash = sha256.convert(utf8.encode(trimmed)).toString();
DateTime dt = DateTime.parse(headerDate).toUtc();
String formattedDate =
"${dt.year.toString().padLeft(4,'0')}${dt.month.toString().padLeft(2,'0')}"
"T${dt.hour.toString().padLeft(2,'0')}${dt.minute.toString().padLeft(2,'0')}"
"${dt.second.toString().padLeft(2,'0')}Z";
String m = method.toLowerCase();
String a = actionName.toLowerCase();
String stringToSign = trimmed.isEmpty
? '$m-$a-$clientKey-$formattedDate'
: '$m-$a-$clientKey-$formattedDate-$payloadHash';
var hmacSha256 = Hmac(sha256, utf8.encode(clientSecret));
var signatureBytes = hmacSha256.convert(utf8.encode(stringToSign));
print(signatureBytes.toString());
}