Error response formats
The API uses standard HTTP status codes. The body format depends on which layer rejected the request.- Business & validation errors
- Signature errors
- Malformed requests
- Empty body
Most errors (When several validation rules fail, the messages are joined with
400, 403, 404, 409, 422) return a single message:{
"error": "This Idempotency-Key was already used for a different order request."
}
; .Signature failures (
401) and oversized bodies (413) return a machine-readable code and a traceId:{
"success": false,
"error": "Request signature verification failed. Sign the request as described in the signature guide.",
"code": "SIGNATURE_INVALID",
"traceId": "b46d6b4e36bdcf0b90f5527593af8b46"
}
Requests that can’t be bound (
400), such as an unknown enum value or a missing Idempotency-Key, return RFC 9110 problem details. (On order creation, a body that isn’t valid JSON is rejected earlier, by the signature check, with 401 SIGNATURE_MALFORMED.){
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Idempotency-Key": ["The idempotencyKey field is required."]
},
"traceId": "08d783466a8cf7d33462b9f43b83ff43"
}
401 for a missing or invalid API key returns no body. Branch on the status code.Branch on the HTTP status and, for signature errors, on
code. The error text is meant for humans and can change. Always log the traceId when one is present and quote it to support.HTTP status codes
| Status | Meaning | Retry? |
|---|---|---|
200 | Success. For order creation, also returned when an Idempotency-Key replay finds the existing order, and when no member is available yet (the order is accepted as Awaiting). | – |
400 | Invalid request: validation failed (including amount precision), malformed JSON, unknown enum value, missing Idempotency-Key, or a status query without an order ID or merchantOrderRef | No. Fix the request first. |
401 | API key missing or invalid (empty body), or request signature rejected (code in body) | No. Fix the credentials or signature. For SIGNATURE_REPLAYED, retry with the original Idempotency-Key. |
403 | Merchant account not active, or caller IP not in your whitelist | No. Contact C2C. |
404 | Order not found for your merchant account | No |
409 | merchantOrderRef already used by an order that is in progress or completed | No. Retry with the same Idempotency-Key, or use a new reference for a new payment. |
413 | Request body over 64 KB (PAYLOAD_TOO_LARGE) | No. Shrink the body. |
422 | Business rule declined the order: payment method (walletType) disabled for your account, insufficient merchant balance (withdrawals), or Idempotency-Key reused with different terms | Method disabled: no. Insufficient balance: after a top-up. Key reused: no. Use a new key for a new order. |
500 | Unexpected server error (problem details with traceId) | Yes, with backoff. For order creation, reuse the same Idempotency-Key. |
Error catalogue
Authentication and signature
| Status | code / message | Cause |
|---|---|---|
401 | (empty body) | No C2C-API-Key (or Authorization: Bearer mk_...) header, a C2C-API-Key that isn’t an mk_ key, or a key that is unknown or revoked |
401 | SIGNATURE_REQUIRED | Order creation body has no signature field, or it is null or empty |
401 | SIGNATURE_MALFORMED | Body isn’t a single JSON object, signature isn’t a string, or a key appears twice in different cases |
401 | SIGNATURE_INVALID | Signature doesn’t match, or the API key has no signing secret |
401 | SIGNATURE_REPLAYED | The same signed body was already accepted with a different Idempotency-Key; retry with the original key |
413 | PAYLOAD_TOO_LARGE | Body larger than 64 KB |
Order creation (POST /api/merchant/orders)
| Status | Message (examples) | Cause |
|---|---|---|
400 | 'Amount' must be greater than '0'. | Invalid amount |
400 | Amount must have at most 4 decimal places and be less than 100,000,000,000,000. | Amount too precise or too large |
400 | Currency must be one of: PKR, INR, BDT. | Unsupported currency |
400 | CustomerAccountName can only be sent together with CustomerAccountNumber. | Withdrawal with an account holder name but no account number |
400 | CallbackUrl must be a valid absolute http(s) URL. | Not an absolute http:// / https:// URL. Same for SuccessUrl / FailureUrl. |
400 | 'Txn Ref Digits Required' must be between 4 and 8. You entered 3. | txnRefDigitsRequired out of range. The option only concerns the account number and Till ID methods; the QR methods don’t ask for transaction ID digits. |
400 | JazzCashQR is a deposit-only payment method. A withdrawal's walletType must be one of: JazzCash, Easypaisa, TillID, BankAccount. | Withdrawal with walletType JazzCashTillID, JazzCashQR or EasypaisaQR. See Payment methods. |
400 | Problem details, errors.Idempotency-Key | Header missing |
400 | Problem details, errors.$.walletType | Unknown orderType / walletType value |
403 | Merchant account is suspended; new orders are not accepted. | Account not active |
403 | IP not in whitelist. | Your IP whitelist doesn’t allow the caller’s IP for this order type |
422 | Insufficient merchant balance: 2500.00 PKR available, 3000.00 PKR required … | Withdrawal’s amount + fee not covered by your available balance, which already excludes withdrawals in progress |
409 | merchantOrderRef 'INV-10045' is already used by order ORD-…. Reuse the same Idempotency-Key to retry a request, or use a new reference for a new payment. | Another order that is in progress or completed already uses this reference. References of failed orders can be reused. |
422 | JazzCash deposits are disabled for this merchant. | That payment method (walletType) is disabled for your account and order type. Each of the methods is switched on or off separately. |
422 | This Idempotency-Key was already used for a different order request. | Same key, different orderType, amount, walletType, currency, merchantOrderRef or customerAccountNumber. See Idempotency. |
There’s no “no member available” error. When no eligible member is online, the order is accepted with status
Awaiting and assigned later. See Order status.Order status and balance
| Status | Message | Cause |
|---|---|---|
400 | Provide an orderId, an orderReference or a merchantOrderRef. | Status query by reference without a merchantOrderRef or orderReference value |
400 | Provide either an orderReference or a merchantOrderRef, not both. | Status query by reference with both parameters |
404 | Order not found. | No order with this orderId, orderReference or merchantOrderRef for your merchant account |
404 | API key not found or inactive. | Key revoked while the request was being processed |
Recommended handling
Node.js
async function handle(response) {
if (response.ok) return (await response.json()).data;
const text = await response.text();
const body = text ? JSON.parse(text) : null;
switch (response.status) {
case 401:
// body === null → API key problem; body.code → signature problem
throw new Error(body?.code ? `Signature rejected: ${body.code} (trace ${body.traceId})` : 'Invalid API key');
case 409:
// merchantOrderRef already in use: look the existing order up instead of creating another
throw new Error(`Duplicate reference: ${body.error}`);
case 422:
// Payment method disabled, insufficient balance, or Idempotency-Key reused with different terms
throw new Error(`Declined: ${body.error}`);
case 500:
throw Object.assign(new Error(`Retryable HTTP ${response.status}`), { retryable: true, traceId: body?.traceId });
default:
throw new Error(`HTTP ${response.status}: ${body?.error ?? body?.title ?? 'request failed'}`);
}
}