All protected endpoints require three HTTP headers on every request:
| Header | Description |
|---|---|
X-API-Key |
Your API key string |
X-Timestamp |
Current Unix time in seconds (integer) |
X-Signature |
HMAC-SHA256 signature (hex-encoded) |
message = "{timestamp}.{rawJsonBody}"
signature = HMAC-SHA256(hmac_secret, message).hex()
timestamp — the same value sent in X-TimestamprawJsonBody — the exact JSON string sent as the request body; use "" (empty string) if there is no body, making the message "{timestamp}."hmac_secret — the shared secret tied to your customer account (provided out-of-band)The JSON body string used when computing the signature must be byte-for-byte identical to the body sent in the HTTP request. Compute the signature from JSON.stringify(body) and send that exact string — do not re-serialize.
import * as crypto from 'crypto';
import axios from 'axios';
function buildHeaders(apiKey: string, hmacSecret: string, body?: object) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const rawBody = body ? JSON.stringify(body) : '';
const message = `${timestamp}.${rawBody}`;
const signature = crypto
.createHmac('sha256', hmacSecret)
.update(message)
.digest('hex');
return {
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Signature': signature,
'Content-Type': 'application/json',
};
}
// Example usage
const body = { payee_id: '...', amount: '1000.00' };
const headers = buildHeaders(API_KEY, HMAC_SECRET, body);
await axios.post('http://localhost:3000/fund-transfer', body, { headers });
The timestamp window is 5 minutes — requests with a timestamp older than 5 minutes are rejected.
AUTH — requires HMAC authentication headersPUBLIC — no authentication requiredPOST /payee AUTHCreate a new payee (recipient of a fund transfer).
Request body:
{
"full_name": "John Silva",
"service_provider_id": "uuid",
"bank_code": "7083",
"type": "PERSONAL",
"service_data": {
"account_number": "1234567890",
"mobile_number": "+94771234567",
"email": "john@example.com"
}
}
| Field | Type | Required | Notes |
|---|---|---|---|
full_name |
string | Yes | 2–100 chars, letters/spaces/hyphens/apostrophes/periods only |
service_provider_id |
UUID | Yes | Must be an active service provider |
bank_code |
string | Yes | Bank code used to look up the bank |
type |
string | Yes | PERSONAL or BUSINESS |
service_data.account_number |
string | Yes | 5–20 digits |
service_data.mobile_number |
string | No | 10–15 chars |
service_data.email |
string | No | Valid email format |
Response:
{
"success": true,
"message": "Payee created successfully",
"data": {
"id": "uuid",
"full_name": "John Silva",
...
}
}
GET /payee/:id AUTHRetrieve a payee by ID. Only returns payees belonging to the authenticated customer.
Response:
{
"success": true,
"message": "Payee retrieved successfully",
"data": { ... }
}
POST /fund-transfer AUTHInitiate a fund transfer to a payee.
Request body:
{
"payee_id": "uuid",
"amount": "1500.00",
"remark": "Invoice payment"
}
| Field | Type | Required | Notes |
|---|---|---|---|
payee_id |
UUID | Yes | Must belong to the authenticated customer |
amount |
string | Yes | Positive number, max 2 decimal places, max 1,000,000 |
remark |
string | No | Max 140 characters |
Response:
{
"success": true,
"message": "Fund transfer completed successfully",
"data": {
"id": "uuid",
"status": "COMPLETED",
"amount": "1500.00",
...
}
}
Possible status values: PENDING, COMPLETED, FAILED
GET /fund-transfer AUTHList all fund transfers for the authenticated customer.
Response:
{
"success": true,
"message": "3 fund transfer(s) retrieved successfully",
"data": [ ... ]
}
GET /fund-transfer/:id AUTHGet a single fund transfer by ID. Returns 404 if the transfer does not belong to the authenticated customer.
GET /service-provider PUBLICList active service providers. Optional query parameters:
| Query param | Description |
|---|---|
type |
Filter by service provider type |
active |
Set to false to include inactive providers (only applies when type is set) |
country |
Filter by country code |
GET /service-provider/:id PUBLICGet a single service provider by ID.
POST /service-provider AUTHCreate a new service provider.
PATCH /service-provider/:id/toggle-status AUTHActivate or deactivate a service provider.
All errors follow the same shape:
{
"success": false,
"message": "Description of the error",
"errors": ["Detail 1", "Detail 2"]
}
| HTTP status | Meaning |
|---|---|
400 |
Validation failed or bad request |
401 |
Missing, expired, or invalid HMAC headers |
404 |
Resource not found |
409 |
Conflict (e.g. duplicate account number) |
429 |
Rate limit exceeded (10 req/min, 50 req/10 min) |
500 |
Internal server error |