Overview
Introduction
The provides a secure, signature-based authentication system for managing users, wallets, and transactions. All endpoints require signature authentication to ensure request integrity and prevent tampering.
Base URL
https://wdcf.xerve.online/api/v1
Authentication
All endpoints require Signature-based Authentication. Each request must include signature headers to verify the request origin and integrity.
Required Headers
| Header | Description | Example |
|---|---|---|
x-signature | HMAC-SHA256 signature of the request | a1b2c3d4e5f6... |
x-timestamp | Unix timestamp in seconds when request was generated | 1706802000 |
How Signature Authentication Works
The signature authentication ensures:
- Request Integrity - The request hasn't been tampered with in transit
- Origin Verification - Only clients with the valid secret can generate valid signatures
- Replay Protection - Requests with old timestamps are rejected (5-minute validity window)
Signature Generation Algorithm
The signature is generated using HMAC-SHA256 with the following components:
StringToSign = METHOD + PATH + TIMESTAMP + PAYLOAD
Signature = HMAC-SHA256(StringToSign, CLIENT_SECRET)
Components Breakdown
| Component | Description | Example |
|---|---|---|
METHOD | HTTP method in UPPERCASE | POST, GET |
PATH | Full API path without query strings | /api/v1/generate-auth-token |
TIMESTAMP | Unix timestamp in seconds | 1706802000 |
PAYLOAD | Request body (POST) or query string (GET) | See below |
CLIENT_SECRET | Your secret key (keep secure!) | your-secret-key |
Payload Serialization
For POST requests:
- Serialize the body object as a JSON string
- Example:
{"clientId":"CLIENT_001","amount":100}
For GET requests:
- Sort query parameters alphabetically by key
- Format as
key=valuepairs joined by& - Example:
clientId=CLIENT_001&limit=10
Code Examples
Node.js / TypeScript
import crypto from 'crypto';
function generateSignature({
method,
path,
payload,
secret,
timestamp
}: {
method: string;
path: string;
payload: any;
secret: string;
timestamp: number;
}): string {
const upperMethod = method.toUpperCase();
// Remove trailing slash if present (except for root "/")
const cleanPath = path.endsWith('/') && path.length > 1
? path.slice(0, -1)
: path;
// Serialize payload based on method
let payloadString = '';
if (upperMethod === 'GET') {
// Sort keys alphabetically for GET requests
const sortedKeys = Object.keys(payload || {}).sort();
const queryParts = sortedKeys.map(key => `${key}=${payload[key]}`);
payloadString = queryParts.join('&');
} else {
// JSON stringify for POST requests
payloadString = typeof payload === 'string'
? payload
: JSON.stringify(payload);
}
// Build string to sign
const stringToSign = `${upperMethod}${cleanPath}${timestamp}${payloadString}`;
// Generate HMAC-SHA256 signature
return crypto
.createHmac('sha256', secret)
.update(stringToSign)
.digest('hex');
}
// Example usage
const timestamp = Math.floor(Date.now() / 1000);
const signature = generateSignature({
method: 'POST',
path: '/api/v1/generate-auth-token',
payload: {
clientId: 'CLIENT_001',
username: 'testuser',
displayName: 'Test User',
ipAddress: '192.168.1.100'
},
secret: 'your-client-secret',
timestamp
});
console.log('Signature:', signature);
console.log('Timestamp:', timestamp);
Python
import hmac
import hashlib
import json
import time
def generate_signature(method, path, payload, secret, timestamp):
# Clean path (remove trailing slash)
clean_path = path.rstrip('/') if len(path) > 1 else path
# Serialize payload
if method.upper() == 'GET':
# Sort keys alphabetically for GET
sorted_keys = sorted(payload.keys())
payload_string = '&'.join([f"{k}={payload[k]}" for k in sorted_keys])
else:
# JSON stringify for POST
payload_string = json.dumps(payload) if isinstance(payload, dict) else payload
# Build string to sign
string_to_sign = f"{method.upper()}{clean_path}{timestamp}{payload_string}"
# Generate signature
return hmac.new(
secret.encode('utf-8'),
string_to_sign.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Example usage
timestamp = int(time.time())
signature = generate_signature(
method='POST',
path='/api/v1/generate-auth-token',
payload={
'clientId': 'CLIENT_001',
'username': 'testuser',
'displayName': 'Test User',
'ipAddress': '192.168.1.100'
},
secret='your-client-secret',
timestamp=timestamp
)
print(f'Signature: {signature}')
print(f'Timestamp: {timestamp}')
PHP
<?php
function generateSignature($method, $path, $payload, $secret, $timestamp) {
// Clean path
$cleanPath = (strlen($path) > 1 && substr($path, -1) === '/')
? substr($path, 0, -1)
: $path;
// Serialize payload
$payloadString = '';
if (strtoupper($method) === 'GET') {
// Sort keys alphabetically for GET
ksort($payload);
$parts = [];
foreach ($payload as $key => $value) {
$parts[] = "{$key}={$value}";
}
$payloadString = implode('&', $parts);
} else {
// JSON stringify for POST
$payloadString = is_string($payload) ? $payload : json_encode($payload);
}
// Build string to sign
$stringToSign = strtoupper($method) . $cleanPath . $timestamp . $payloadString;
// Generate signature
return hash_hmac('sha256', $stringToSign, $secret);
}
// Example usage
$timestamp = time();
$signature = generateSignature(
'POST',
'/api/v1/generate-auth-token',
[
'clientId' => 'CLIENT_001',
'username' => 'testuser',
'displayName' => 'Test User',
'ipAddress' => '192.168.1.100'
],
'your-client-secret',
$timestamp
);
echo "Signature: {$signature}\n";
echo "Timestamp: {$timestamp}\n";
?>
Making Authenticated Requests
Include the generated signature and timestamp in your request headers:
curl -X POST https://wdcf.xerve.online/api/v1/generate-auth-token \
-H "Content-Type: application/json" \
-H "x-signature: YOUR_GENERATED_SIGNATURE" \
-H "x-timestamp: 1706802000" \
-d '{
"clientId": "CLIENT_001",
"username": "testuser",
"displayName": "Test User",
"ipAddress": "192.168.1.100"
}'
Available Endpoints
Authentication & Users
| Method | Endpoint | Description | Documentation |
|---|---|---|---|
| POST | /api/v1/generate-auth-token | Generate a time-limited auth token for user login. Creates a new user if clientId doesn't exist, or updates existing user. Returns a login link and token. | View Docs |
Wallet Operations
| Method | Endpoint | Description | Documentation |
|---|---|---|---|
| GET | /api/v1/get-balance | Retrieve the current balance for a user by clientId. Returns balance amount and last update timestamp. | View Docs |
| POST | /api/v1/credit-balance | Add funds to a user's wallet. Creates a credit transaction and updates balance atomically. | View Docs |
| POST | /api/v1/debit-balance | Deduct funds from a user's wallet. Creates a debit transaction and updates balance atomically. | View Docs |
Transactions
| Method | Endpoint | Description | Documentation |
|---|---|---|---|
| GET | /api/v1/get-transactions | Retrieve paginated transaction history for a user. Supports filtering by date, type (credit/debit), sorting, and pagination. | View Docs |
System Health
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/health | Health check endpoint. Returns server status, timestamp, and uptime. Useful for monitoring. |
| POST | /api/v1/health | Health check with body echo. Returns the same as GET plus echoes back the request body for testing signature validation. |
Common Request/Response Patterns
Success Response Format
{
"status": "success",
"message": "Operation completed successfully",
"data": { ... }
}
Error Response Format
{
"status": "failed",
"message": "Error description",
"data": {
"error": "Detailed error message",
"errors": { ... } // Validation errors (optional)
}
}
Error Codes
| HTTP Status | Code | Description |
|---|---|---|
| 400 | Bad Request | Missing or invalid parameters |
| 401 | Unauthorized | Invalid signature, missing headers, or expired timestamp |
| 404 | Not Found | Resource not found (e.g., user doesn't exist) |
| 500 | Internal Server Error | Server-side error |
Timestamp Validity
Signatures are valid for 5 minutes from the timestamp. Requests with timestamps older than 5 minutes will be rejected with a 401 Unauthorized error.
Security Best Practices
- Keep CLIENT_SECRET Secure
- Never expose the secret in client-side code
- Store it in environment variables or secure vaults
- Rotate secrets periodically
- Use HTTPS
- Always use HTTPS in production
- Never send signatures over unencrypted connections
- Timestamp Validation
- Generate timestamps fresh for each request
- Don't reuse old signatures
- Error Handling
- Don't log the full signature in error messages
- Implement proper retry logic with fresh timestamps
Quick Start Example
Here's a complete example of generating an auth token:
import axios from 'axios';
import crypto from 'crypto';
const API_BASE_URL = 'https://wdcf.xerve.online';
const CLIENT_SECRET = process.env.CLIENT_SECRET; // Keep this secure!
async function generateAuthToken() {
const timestamp = Math.floor(Date.now() / 1000);
const payload = {
clientId: 'CLIENT_001',
username: 'testuser001',
displayName: 'Test User',
ipAddress: '192.168.1.100',
expiration: 5 // 5 minutes
};
// Generate signature
const stringToSign = `POST/api/v1/generate-auth-token${timestamp}${JSON.stringify(payload)}`;
const signature = crypto
.createHmac('sha256', CLIENT_SECRET)
.update(stringToSign)
.digest('hex');
// Make request
const response = await axios.post(
`${API_BASE_URL}/api/v1/generate-auth-token`,
payload,
{
headers: {
'Content-Type': 'application/json',
'x-signature': signature,
'x-timestamp': timestamp.toString()
}
}
);
console.log('Token:', response.data.data.token);
console.log('Login Link:', response.data.data.loginLink);
return response.data;
}
Support
For issues or questions regarding the V1 API, please contact the development team or refer to individual endpoint documentation for detailed usage examples.
