Base Information
Base Path
/v1
All external API endpoints in this reference are served under
/v1.Scope
External v1 API
This page documents the external API serving the
/v1 business endpoints.Authentication
Signed API key
Protected business endpoints use signed API key authentication instead of the previous token login flow.
Time Fields
Unix timestamp
Time fields returned by business APIs are Unix timestamps in seconds.
Amounts
Decimal strings
Monetary values are returned as decimal strings (never floats) to preserve precision — parse them with a decimal type.
Replay Protection
recvWindow
Signed requests must reach the server within
recvWindow of their timestamp (default 5s, max 60s), otherwise they are rejected as expired.Authentication
- Header
X-MBX-APIKEY: <ACCESS_ID>is required for every protected request.x-access-idis also accepted by the backend. - Signed parameter
timestampis required (milliseconds since epoch; microseconds are also accepted and normalized). A timestamp more than ~1s in the future is rejected. - Signed parameter
recvWindowis optional (default5000ms, maximum60000ms). A request is rejected as expired onceserverTime - timestampexceedsrecvWindow. - Signed parameter
signatureis required for all protected requests.
Signing Rules
- Place all signed parameters in exactly one location: query string or request body. Do not split them across both.
signatureitself is not part of the payload string being signed.- Parameters are signed in the exact order they are sent by the client.
- Each key and value is URL-encoded with the safe character set
-_.~before joining askey=value. - For
POST /v1/deposit/order,POST /v1/withdraw/order, andPOST /v1/exchange/order, sign the JSON body fields excludingsignature, then include the generatedsignaturein that same JSON body.
Payload
key1=value1&key2=value2×tamp=1717200000000&recvWindow=5000HMAC-SHA256
import crypto from "crypto";
function buildSignedPayload(params) {
return Object.entries(params)
.filter(([, value]) => value !== undefined && value !== null && value !== "")
.map(([key, value]) => {
const encodedKey = encodeURIComponent(String(key))
.replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase());
const encodedValue = encodeURIComponent(String(value))
.replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase());
return `${encodedKey}=${encodedValue}`;
})
.join("&");
}
function signPayload(payload, secretKey) {
return crypto.createHmac("sha256", secretKey).update(payload, "utf8").digest("hex");
}
const params = {
currency: "NGN",
timestamp: Date.now(),
recvWindow: 5000
};
const payload = buildSignedPayload(params);
const signature = signPayload(payload, process.env.PAYANY_SECRET_KEY);Permissions
| Endpoint | Permissions |
|---|---|
| GET /v1/balance | UserData |
| GET /v1/balance/history | UserData |
| GET /v1/rate | UserData |
| POST /v1/deposit/order | Deposit |
| GET /v1/deposit/order/{id} | UserData |
| DELETE /v1/deposit/order/{id} | Deposit |
| POST /v1/withdraw/order | Withdrawal |
| GET /v1/withdraw/order/{id} | UserData |
| DELETE /v1/withdraw/order/{id} | Withdrawal |
| POST /v1/exchange/order | Trade |
| GET /v1/exchange/order/{id} | UserData |
| POST /v1/file | Valid signed API key required |
Response Example
{
"code": 0,
"data": {},
"message": "Success"
}
{
"code": 7012,
"data": {},
"message": "Invalid signature"
}Client Examples
Java
LinkedHashMap<String, String> params = new LinkedHashMap<>();
params.put("currency", "NGN");
params.put("timestamp", String.valueOf(System.currentTimeMillis()));
params.put("recvWindow", "5000");
String payload = buildPayload(params);
String signature = sign(payload, secretKey);
String url = domain + "/v1/balance?" + payload + "&signature=" + signature;JavaScript
const params = {
currency: "NGN",
timestamp: Date.now(),
recvWindow: 5000
};
const payload = buildPayload(params);
const signature = sign(payload, secretKey);
const url = `${domain}/v1/balance?${payload}&signature=${signature}`;Go
params := [][2]string{
{"currency", "NGN"},
{"timestamp", fmt.Sprintf("%d", time.Now().UnixMilli())},
{"recvWindow", "5000"},
}
payload := buildPayload(params)
signature := sign(payload, secretKey)Python
params = [
("currency", "NGN"),
("timestamp", int(time.time() * 1000)),
("recvWindow", 5000),
]
payload = build_payload(params)
signature = hmac.new(
SECRET_KEY.encode("utf-8"),
payload.encode("utf-8"),
hashlib.sha256,
).hexdigest()PHP
$params = [
'currency' => 'NGN',
'timestamp' => (string) round(microtime(true) * 1000),
'recvWindow' => '5000',
];
$payload = build_payload($params);
$signature = hash_hmac('sha256', $payload, $secretKey);Notes
- If a request is initiated from a browser app, do not expose the secret key in browser code. Generate signatures on a trusted backend or server-side integration service.
file_idsindepositandwithdraware currently stringified JSON arrays, not native JSON arrays.depositandwithdraworders are single-currency — onecurrency+amount(no price/rate); onlyexchangeconverts between two currencies.- The backend also supports RSA and Ed25519 signature verification when the API key is provisioned with that key type, but HMAC-SHA256 is the default client integration path documented here.