Authentication
Business Sznd API uses HMAC-based authentication on top of API keys.
HMAC headers
Send these headers with every authenticated request:
X-API-Key: your_api_key_here
X-Signature: generated_signature_here
X-Timestamp: 2025-01-15T10:30:00ZX-API-Key: the public identifier for your business API key.X-Timestamp: RFC3339 UTC timestamp (e.g.2025-01-15T10:30:00Z), used to prevent replay attacks (5‑minute freshness window, 1‑minute future tolerance).X-Signature: hex-encoded HMAC-SHA256 ofbody + "|" + timestampusing your secret key.
The server recomputes the HMAC using the request body and timestamp; if anything has been tampered with (body or timestamp), the signature fails and the request is rejected.
Signing algorithm (conceptual)
- Serialize the JSON request body (empty string if there is no body).
- Generate an RFC3339 timestamp.
- Build the string:
data_to_sign = body + "|" + timestamp. - Compute
HMAC-SHA256(secret_key, data_to_sign)and hex-encode the result. - Send the signature as
X-Signaturewith the same body and timestamp.
Authentication errors
Common authentication failures return 401 Unauthorized or 403 Forbidden, for example:
- Missing headers:
x-api-key,x-signatureorx-timestamp. - Invalid timestamp format or a timestamp that is too old / too far in the future.
- Invalid signature (body or timestamp changed in transit).
- Inactive or expired API key.
Business API key management (/business routes)
Business API keys are managed via dedicated /business endpoints. For example:
- List business API keys
- Method:
GET - Path:
/api/v1/business/api-keys - Description: Returns all active API keys associated with your business user, including metadata such as
name,is_active,last_used_at, andexpires_at. Secret keys are never returned once created.
- Method:
Use these endpoints to rotate keys regularly, configure webhook URLs per key, and keep inactive keys disabled.
Postman Setup
To use HMAC authentication in Postman, add this pre-request script to automatically generate signatures for all your requests.
Pre-request Script
Add this script to Postman's Pre-request Script tab (in your request or collection):
// Postman Pre-request Script for HMAC Authentication
// This script automatically generates HMAC signatures for API requests
// Get API key and secret key from environment variables
const apiKey = pm.environment.get("api_key");
const secretKey = pm.environment.get("secret_key");
if (!apiKey || !secretKey) {
console.error("⚠️ Please set 'api_key' and 'secret_key' in your Postman environment variables");
throw new Error("Missing API credentials - set api_key and secret_key in environment");
}
// Get request body (empty string if no body)
let requestBody = "";
if (pm.request.body) {
// Handle different body types
if (pm.request.body.raw) {
requestBody = pm.request.body.raw;
} else if (pm.request.body.urlencoded) {
// Convert URL encoded to string representation
requestBody = pm.request.body.urlencoded.toString();
} else if (pm.request.body.formdata) {
// Convert form data to string representation
requestBody = pm.request.body.formdata.toString();
}
// If body is JSON object, it will be in raw as string already
}
// Generate RFC3339 timestamp (UTC)
const now = new Date();
const timestamp = now.toISOString();
// Create data to sign: body + "|" + timestamp
const dataToSign = requestBody + "|" + timestamp;
// Generate HMAC-SHA256 signature using CryptoJS
const signature = CryptoJS.HmacSHA256(dataToSign, secretKey).toString(CryptoJS.enc.Hex);
// Remove existing headers if they exist (to avoid duplicates)
pm.request.headers.remove("x-api-key");
pm.request.headers.remove("x-timestamp");
pm.request.headers.remove("x-signature");
// Set headers automatically
pm.request.headers.add({
key: "x-api-key",
value: apiKey
});
pm.request.headers.add({
key: "x-timestamp",
value: timestamp
});
pm.request.headers.add({
key: "x-signature",
value: signature
});
// Log for debugging (optional - remove in production)
console.log("🔐 HMAC Authentication");
console.log("API Key:", apiKey);
console.log("Timestamp:", timestamp);
console.log("Data to sign:", dataToSign);
console.log("Signature:", signature);Setup Instructions
-
Create/Select Environment:
- Click the environment dropdown in Postman (top right)
- Create a new environment or select an existing one
- Click "Edit" to add variables
-
Add Environment Variables:
- Variable name:
api_key→ Value: Your API key (e.g.,tf_live_abc123...) - Variable name:
secret_key→ Value: Your secret key (e.g.,sk_live_def456...) - Click "Save"
- Variable name:
-
Add Pre-request Script:
- Option A - Collection Level (Recommended):
- Right-click your collection → "Edit"
- Go to "Pre-request Script" tab
- Paste the script above
- All requests in the collection will use it automatically
- Option B - Request Level:
- Open your request
- Go to "Pre-request Script" tab
- Paste the script above
- Only this request will use it
- Option A - Collection Level (Recommended):
-
Make Requests:
- The script runs automatically before each request
- Headers are generated and added automatically
- No manual signature calculation needed!
Testing the Setup
- Create a test request to any client API endpoint (e.g.,
POST /api/v1/client/customers) - Add your request body in the "Body" tab
- Send the request
- Check the Postman Console (View → Show Postman Console) to see the generated signature
- If authentication fails, check:
- Environment variables are set correctly
- Secret key matches the API key
- Request body matches what's being signed
Troubleshooting
Error: "Missing API credentials"
- Ensure
api_keyandsecret_keyare set in your active environment - Check that the correct environment is selected
Error: "CryptoJS is not defined"
- Postman includes CryptoJS by default
- If you see this error, try updating Postman to the latest version
Error: "Invalid signature"
- Check the Postman Console to see what data is being signed
- Ensure the request body matches exactly (no extra spaces, correct JSON format)
- Verify your secret key is correct
Headers not being added
- Check that the script is in the correct tab (Pre-request Script, not Tests)
- Ensure you're not manually setting these headers (they'll conflict)