Errors
Error Response Shape
All errors return a JSON body with a machine-readable code and a human-readable message:
{
"success": false,
"error": {
"code": "INVALID_BODY",
"message": "amount is required and must be a positive integer"
}
}
Error Codes
Authentication
| Code | HTTP | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid X-API-Key |
INVALID_SIGNATURE | 401 | X-Signature header does not match computed HMAC |
MERCHANT_INACTIVE | 403 | Merchant account is suspended or disabled |
Request Validation
| Code | HTTP | Description |
|---|---|---|
INVALID_BODY | 400 | Malformed JSON or missing required field |
INVALID_JSON | 400 | Request body is not valid JSON |
UNSUPPORTED_PAYMENT_METHOD | 400 | V1 order creation accepts QRIS only |
IDEMPOTENCY_KEY_REUSED | 400 | The key was reused with a different raw request body |
MERCHANT_ORDER_ID_REUSED | 400 | The same merchant order ID was used with a different amount |
INVALID_ID | 400 | Path parameter is not a valid UUID |
Orders
| Code | HTTP | Description |
|---|---|---|
ORDER_PENDING_CONFIRMATION | 409 | Existing order's gateway result is unresolved; reconcile before another attempt |
ORDER_NOT_FOUND | 404 | No order found with the given ref_code |
INVALID_STATUS | 422 | Operation not allowed for the order's current status (e.g., refunding a non-PAID order) |
AMOUNT_EXCEEDS_ORDER | 422 | Refund amount is greater than the remaining refundable amount |
NO_VENDOR | 503 | No payment gateway is configured or available for this payment method |
VENDOR_ERROR | 503 | All configured payment gateways returned errors |
Disbursements
| Code | HTTP | Description |
|---|---|---|
NOT_WALLET_MERCHANT | 403 | Merchant's settlement type is not WALLET; disbursements not available |
BANK_NOT_FOUND | 400 | bank_short_code does not match any active bank |
BANK_DISABLED | 422 | Bank exists but is not enabled for disbursement |
MISSING_BANK_INFO | 400 | account_number or account_name is missing for a BANK channel |
MISSING_EWALLET_INFO | 400 | ewallet_provider or ewallet_phone is missing for an EWALLET channel |
INSUFFICIENT_BALANCE | 422 | Wallet balance is less than the requested amount plus the Rupiah fee |
AMOUNT_EXCEEDS_LIMIT | 422 | Single transaction amount exceeds the per-transaction limit |
DAILY_LIMIT_EXCEEDED | 422 | Daily disbursement total would exceed the daily limit |
DUPLICATE_IDEMPOTENCY_KEY | 409 | A disbursement with this idempotency_key already exists |
DISBURSEMENT_NOT_FOUND | 404 | No disbursement found with the given identifier |
Beneficiaries
| Code | HTTP | Description |
|---|---|---|
BENEFICIARY_NOT_FOUND | 404 | No beneficiary found with the given ID |
Handling Errors
Node.js — error handling pattern
const res = await fetch('https://api.flypay.asia/v1/orders', {
method: 'POST',
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const body = await res.json();
if (!body.success) {
console.error(`[${body.error.code}] ${body.error.message}`);
// Handle specific codes:
if (['ORDER_PENDING_CONFIRMATION', 'VENDOR_ERROR'].includes(body.error.code)) {
// Query GET /v1/orders?merchant_order_id=<your-id> before any retry.
}
}
tip
Always branch on error.code, not HTTP status alone. Multiple distinct business errors share the same HTTP status (e.g., 400).