Skip to main content

REST API Authentication

All REST API requests must include authentication headers. ZenPays uses API Key + HMAC Signature authentication for secure request verification.

Required Headers​

HeaderRequiredDescription
AuthorizationYesBearer {api_key} - Your API key
X-SignatureYesHMAC-SHA256 signature of the request
X-TimestampYesISO 8601 timestamp (UTC)
X-Secret-SaltYesYour secret salt for HMAC signature validation
Content-TypeYes*application/json for POST/PUT/PATCH requests
X-Merchant-IDNoOptional merchant ID for multi-merchant setups

API Keys​

ZenPays uses prefixed API keys to identify the environment:

PrefixEnvironmentUsage
zp_live_ProductionReal payments
zp_test_SandboxTesting and development

Getting Your API Key​

  1. Log in to your ZenPays Dashboard
  2. Navigate to Settings → API Keys
  3. Copy your API key and Secret Salt
warning

Keep your API keys and Secret Salt secure. Never expose them in client-side code or commit them to version control.

Signature Generation​

Every request must include an HMAC-SHA256 signature to verify its integrity.

Signature Formula​

signature = HMAC-SHA256(timestamp + body, secret_salt)

Where:

  • timestamp - The value of the X-Timestamp header
  • body - JSON stringified request body (empty string for GET requests)
  • secret_salt - Your secret salt from the dashboard

Implementation Examples​

const crypto = require('crypto');

function generateSignature(timestamp, body, secretSalt) {
const data = timestamp + JSON.stringify(body);
return crypto
.createHmac('sha256', secretSalt)
.update(data)
.digest('hex');
}

// Example usage
const timestamp = new Date().toISOString();
const body = { amount: 1000, currency: 'USD' };
const signature = generateSignature(
timestamp,
body,
process.env.ZENPAYS_SECRET_SALT
);

console.log('Signature:', signature);
// Output: a1b2c3d4e5f6...

Complete Request Example​

#!/bin/bash

API_KEY="zp_test_xxxxx"
SECRET_SALT="your_secret_salt"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")

BODY='{"amount":1000,"currency":"USD","description":"Order #123"}'

# Generate signature (requires openssl)
SIGNATURE=$(echo -n "${TIMESTAMP}${BODY}" | openssl dgst -sha256 -hmac "$SECRET_SALT" | cut -d' ' -f2)

curl -X POST https://api.zenpays.com/api/v1/payment-intents \
-H "Authorization: Bearer $API_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: $SIGNATURE" \
-H "X-Secret-Salt: $SECRET_SALT" \
-H "Content-Type: application/json" \
-d "$BODY"

Timestamp Validation​

The X-Timestamp header must be within ±5 minutes of the server time. Requests with timestamps outside this window are rejected to prevent replay attacks.

{
"success": false,
"error": {
"code": "TIMESTAMP_EXPIRED",
"message": "Request timestamp is outside the allowed window"
}
}
tip

Always use UTC timestamps and ensure your server clock is synchronized with NTP.

GET Request Authentication​

For GET requests without a body, use an empty object {} when calculating the signature:

// For GET requests
const timestamp = new Date().toISOString();
const data = timestamp + '{}';
const signature = crypto
.createHmac('sha256', secretSalt)
.update(data)
.digest('hex');

const response = await fetch('https://api.zenpays.com/api/v1/payment-intents/pi_xxx', {
method: 'GET',
headers: {
'Authorization': `Bearer ${apiKey}`,
'X-Timestamp': timestamp,
'X-Signature': signature,
'X-Secret-Salt': secretSalt,
},
});

IP Whitelisting​

For enhanced security, you can restrict API access to specific IP addresses:

  1. Go to Dashboard → Settings → API Security
  2. Add your server IP addresses to the whitelist
  3. Enable IP restriction for your API keys

Requests from non-whitelisted IPs will receive:

{
"success": false,
"error": {
"code": "IP_NOT_WHITELISTED",
"message": "Request from unauthorized IP address"
}
}

Authentication Errors​

Error CodeHTTP StatusDescription
UNAUTHORIZED401Missing or invalid API key
INVALID_SIGNATURE401Signature verification failed
TIMESTAMP_EXPIRED401Timestamp outside allowed window
API_KEY_REVOKED401API key has been revoked
IP_NOT_WHITELISTED403Request from unauthorized IP
INSUFFICIENT_PERMISSIONS403API key lacks required scope

Best Practices​

  1. Use environment variables - Never hardcode API keys or secrets
  2. Rotate keys regularly - Create new keys and revoke old ones periodically
  3. Use minimal scopes - Only grant necessary permissions to each key
  4. Enable IP whitelisting - Restrict access to known server IPs
  5. Monitor API usage - Check the dashboard for unusual activity
  6. Use HTTPS only - Never send credentials over unencrypted connections

Next Steps​