Fiat Fund Transfer API

Authentication

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)

How to sign a request

message  = "{timestamp}.{rawJsonBody}"
signature = HMAC-SHA256(hmac_secret, message).hex()

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.

TypeScript signing example

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.


Endpoints

Legend


Payee

POST /payee AUTH

Create 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 AUTH

Retrieve a payee by ID. Only returns payees belonging to the authenticated customer.

Response:

{
  "success": true,
  "message": "Payee retrieved successfully",
  "data": { ... }
}

Fund Transfer

POST /fund-transfer AUTH

Initiate 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 AUTH

List all fund transfers for the authenticated customer.

Response:

{
  "success": true,
  "message": "3 fund transfer(s) retrieved successfully",
  "data": [ ... ]
}

GET /fund-transfer/:id AUTH

Get a single fund transfer by ID. Returns 404 if the transfer does not belong to the authenticated customer.


Service Provider

GET /service-provider PUBLIC

List 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 PUBLIC

Get a single service provider by ID.

POST /service-provider AUTH

Create a new service provider.

PATCH /service-provider/:id/toggle-status AUTH

Activate or deactivate a service provider.


Error responses

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