Remote API responses and errors
Response shapes, HTTP statuses, error messages and terminal states for the Remote API lookup, transaction, purchase and transaction-state calls.
This page is the response reference for the Remote API calls a wallet uses to take a payment: /lookup, /generateTransactionId, /purchaseTx and /transactionState/{transactionId}/v2. It applies to card (3D Secure and PIN) payments and to bill payments. For the call sequence, see Purchase flow; for bill payments and the hosted bill menu, see Bill payments and VAS.
General behaviour
These rules apply to every Remote API endpoint.
| Rule | Detail |
|---|---|
| Error body | { "status": "ERROR", "message": "<message>" } |
| HTTP status | Business and validation failures return 400 (Bad Request); system-level failures return 500 (Internal Server Error). Integrate against the HTTP status and the message. |
| Null fields are omitted | Responses only contain populated fields. The samples on this page are illustrative; the exact fields vary per merchant and biller configuration. |
| No extended error codes on the wire | The platform's internal error codes (see Errors) are not sent on the Remote API. They are mapped to HTTP 400 or 500; the status and message body fields are the reliable contract. |
Framework-level errors (before business logic)
| Condition | HTTP | message |
|---|---|---|
| Field validation failure | 400 | '<field>' <reason> |
| Malformed JSON body | 400 | Not valid JSON/XML |
| Incorrect content type | 400 | Content type must be application/json |
| Missing request parameter | 400 | Framework-generated |
| Client limits exceeded | 400 | Client Limits Exceeded |
| Not authenticated | 401 | Security-layer default |
| Not authorised for the endpoint | 403 | You are not authorized to access this resource |
| Any unhandled exception | 500 | System error / SYSTEM EXCEPTION |
Framework-level errors share the standard body shape. Field-validation messages join multiple field errors with ; . The unauthenticated 401 response is produced by the security layer and may not follow this shape.
Field validation failure - HTTP 400:
{
"status": "ERROR",
"message": "'msisdn' must match \"^[1-9]\\d{8,16}\"; 'value' must not be null"
}Malformed JSON body - HTTP 400:
{
"status": "ERROR",
"message": "Not valid JSON/XML"
}Not authorised for the endpoint - HTTP 403:
{
"status": "ERROR",
"message": "You are not authorized to access this resource"
}Unhandled system error - HTTP 500:
{
"status": "ERROR",
"message": "System error"
}/lookup
Success (HTTP 200): status is either SUCCESS (code resolved, full lookup data returned) or NEED_INPUT (further input required; inputsRequired populated).
Errors (HTTP 400, status: "ERROR")
status: "ERROR")| Trigger condition | message |
|---|---|
Neither clientIdentifier nor msisdn supplied | Validation error: Either 'clientIdentifier' or 'msisdn' is required |
Client exists but is not in ACTIVE state | Client Suspended |
| Supplied QR / code value cannot be resolved | Code supplied is invalid |
| Scanned QR / code value is not a recognised or supported code | Code not supported |
| Code not found | Code Not found |
| Code expired | Code Expired |
| Code not in active state | Code not in active state |
| Merchant for the code missing or inactive | Merchant is not found/active |
| Server error while preparing code information (HTTP 500) | System Error |
Sample responses
Success - HTTP 200, SUCCESS:
{
"status": "SUCCESS",
"merchantName": "Bob's Store - Sandton",
"merchant": "Bob's Store",
"value": "0123456789",
"merchantId": 123456,
"postilionRouting": true,
"data": {
"amount": 10.50,
"currency": "R",
"currencyCode": "ZAR",
"shortDescription": "Bob's basket of goods",
"tipRequired": false,
"acceptedCards": "master,visa",
"acceptedPaymentMethods": "SECURE_CODE",
"secureCodeMerchantId": "70012345",
"secureCodeUrl": "https://3ds.example.com/api/v2/auth",
"poi": "12"
}
}Need input - HTTP 200, NEED_INPUT: returned when the code needs extra input from the customer before a purchase can proceed. Each entry in inputsRequired describes one prompt (ref, title, and type = NUMBER, NUMERIC, MSISDN, DROPDOWN or TEXT).
{
"status": "NEED_INPUT",
"merchantName": "Bob's Store",
"merchantId": 123456,
"inputsRequired": [
{
"ref": "accountNumber",
"title": "Please enter your account number",
"type": "NUMBER",
"regex": "^[0-9]{6,20}$",
"regexErrorMessage": "Enter digits only"
}
]
}Failure - HTTP 400, ERROR:
{
"status": "ERROR",
"message": "Code Expired"
}/generateTransactionId
This endpoint runs the entire /lookup flow first: every /lookup success, NEED_INPUT and error outcome above applies here unchanged. Only when the lookup returns SUCCESS does it issue a transactionId.
Success (HTTP 200): status: "SUCCESS", with transactionId populated.
You can register an optional webhook URL on this call for the transaction's outcome (see Webhooks).
Additional error (HTTP 400, status: "ERROR")
status: "ERROR")| Trigger condition | message |
|---|---|
webhook supplied but is not a valid URL | Invalid webhook, must be a valid URL |
Sample response - HTTP 200, SUCCESS (transactionId issued)
SUCCESS (transactionId issued){
"status": "SUCCESS",
"merchantName": "Bob's Store - Sandton",
"merchantId": 123456,
"transactionId": 988123456,
"data": {
"amount": 10.50,
"currencyCode": "ZAR",
"acceptedPaymentMethods": "SECURE_CODE"
}
}/purchaseTx
Asynchronous processingA successful submission returns HTTP 200 with
status: "PENDING". The purchase is processed asynchronously and the final outcome is not returned on this call. Most processing failures (including a failed or declined 3D Secure authentication, a bank decline, insufficient funds, a limit exceeded and fraud) surface as a terminal state on/transactionState. The synchronous errors below are limited to validation performed at submission time. This endpoint never returnsNEED_INPUT.
Authentication block. Provide the authentication data matching the payment method the transaction is configured for: pinData (PIN / account based, encrypted, never plain text), or secureCodeData / secureCodeBasic (3D Secure). An applicable block must be supplied unless your profile is configured for card-not-present (CNP) purchases. Use acceptedPaymentMethods from the lookup (or /binLookup) to decide which to send.
Success (HTTP 200): status: "PENDING", with transactionId and date.
Validation errors (HTTP 400, status: "ERROR")
status: "ERROR")| Trigger condition | message |
|---|---|
| Transaction not found | Validation error: Invalid transaction <id> |
| Transaction not in the expected starting state | Validation error: Invalid transaction state. Current state: '<state>' |
| Secure-code data submitted but the transaction could not be advanced to 3D Secure processing | Validation error: Invalid transaction state. Unable to update. |
Neither clientIdentifier nor msisdn supplied | Validation error: Either 'clientIdentifier' or 'msisdn' is required |
scAuthMethod not one of 01 / 02 / 03 / 04 | Validation error: secureCodePreAuthData-scAuthMethod is not valid. Valid values: 01/02/03/04 |
scAuthTimeStamp invalid | Validation error: secureCodePreAuthData-scAuthTimeStamp is not valid. Valid timestamp format: YYYYMMDDHHMM |
| Card BIN not supported by the merchant | Card Error: Card not supported. |
| Required payment data missing, or payment method not configured | Validation error: Required transaction data missing or CNP is not configured for <manager> |
Client not in ACTIVE state | Client Suspended |
| Code not found, expired or not active | Invalid Code: Code not found / Code Expired / Code not active |
| Amount required, below minimum, above maximum or invalid | Validation error: Amount is required on partial payments / is less than minAmount / is more than maxAmount / Invalid amount for purchase tx |
| Tip greater than or equal to the amount, or tip required but missing | Validation error: 'tip' Value cannot be more than 'amount' value / Tip Value is required |
| Contact, shipping or extra reference required but missing | Validation error: Contact is required / Shipping is required / Extra Reference value is required |
| Billing required but missing | Validation error: Bliing is required |
| CVV2 required but blank, or invalid length | Validation error: 'CVV2' required for purchase tx / Invalid CVV length. |
| Card hash check failed | Card Hash Failed |
| Card link / update failed | Card Error: <reason> |
System errors (HTTP 500)
| Trigger condition | message |
|---|---|
| Internal processing failure | System Error |
Sample responses
Success - HTTP 200, PENDING (poll /transactionState for the final outcome):
{
"status": "PENDING",
"transactionId": 988123456,
"date": "2026-07-19 14:32:05"
}Failure - HTTP 400, ERROR:
{
"status": "ERROR",
"message": "Validation error: 'tip' Value cannot be more than 'amount' value"
}Transaction state (/transactionState/{transactionId}/v2)
/transactionState/{transactionId}/v2)Success (HTTP 200): status reflects the transaction's progress. The /v2 variant also returns UTC-normalised date fields alongside the standard transaction detail. /transactionState/{transactionId} (without /v2) applies the same status rules.
status value | Meaning |
|---|---|
PENDING | Not yet in a final state (or final, but the configured notification has not yet succeeded). |
SUCCESS | Reached final success. |
| Terminal state name | Returned as is when the transaction reaches a final state (see below). |
Which states you will seeThe endpoint returns the state name as is for any state beginning with
END_, and forISSUER_NEVER_APPROVE/ISSUER_NOT_APPROVE_NOW. Every other in-flight state (for exampleOFF_TO_BANK) is returned asPENDING, and a completed transaction is returned asSUCCESS. Keep polling while you seePENDING.
Terminal states for card and bill payments
| Terminal state | Meaning |
|---|---|
END_BANK_DECLINE | Declined by the bank or issuer |
END_BANK_NON_00 | Issuer returned a non-approval response |
END_BANK_ERROR | Error response from the bank or acquirer |
END_BANK_OFFLINE | Bank or acquirer unavailable |
END_BANK_EXPIRED | Bank authorisation expired |
END_INSUFFICIENT_FUNDS_OR_OVER_CREDIT_LIMIT | Insufficient funds or over credit limit |
END_ACCOUNT_CLOSED_OR_FRAUDULENT | Account closed or flagged fraudulent |
END_CARD_EXPIRY_NOT_ALLOWED | Card expiry not permitted |
END_BIN_NOT_WHITELISTED | Card BIN not whitelisted |
END_NOTALLOW_BIN_CLASSIFICATION | Card BIN classification not allowed |
END_HASH_FAILED | Card hash check failed |
END_FRAUD_DETECTION | Rejected by fraud screening |
END_BRE_REJECTION | Rejected by the rules engine; includes 3D Secure authentication rejected or declined |
END_BLACKLISTED | Client or card blacklisted |
END_TX_RATE_BLOCKED | Blocked by transaction-rate (velocity) controls |
END_LIMIT_FAILED | Client limit exceeded |
END_VALIDATION_FAILED | Validation failure during processing |
END_INVALID_AMOUNT | Invalid amount |
END_CODE_IN_USE | Payment code already in use |
END_CODE_PURCHASE_ERROR | Purchase processing error |
END_TIME_OUT_IN_QUEUE | Timed out awaiting processing |
END_TIMEOUT_PURCHASE_3DS | 3D Secure purchase timed out |
END_SYSTEM_ERROR | System error during processing |
END_REVERSED | Transaction reversed |
END_REFUNDED | Transaction refunded |
END_CLIENT_CANCEL | Cancelled by the client |
END_CLIENT_TIMEOUT | Client did not complete in time |
ISSUER_NEVER_APPROVE | Issuer will never approve (final) |
ISSUER_NOT_APPROVE_NOW | Issuer will not approve at this time (final) |
Treat the terminal state as an open list: handle any END_* value you do not recognise as a generic failure. The full platform list is in Transaction states.
Errors
| Trigger condition | HTTP | message |
|---|---|---|
| Transaction reference not found | 400 | Invalid Transaction Reference |
| Transaction state cannot be resolved | 500 | Invalid transaction to provice transaction state. |
| Corrupt transaction data while resolving state | 500 | System Error |
Integration notes
- Integrate against the HTTP status and
message. Internal error codes are never sent on the wire; they are mapped to 400 or 500. - Payment is poll-based.
PENDINGis the normal success response from/purchaseTx; poll/transactionState/{transactionId}/v2for the final outcome. An optional webhook can be registered on/generateTransactionIdor/purchaseTxand used alongside polling. Webhook delivery is best effort: a failed delivery is retried and then abandoned, and never reverses the transaction. Rely on polling for the authoritative result. - Echo the returned
valueduring a multi-step lookup. Thevaluein aNEED_INPUTresponse may be rewritten; send it back on the next/lookupor/generateTransactionIdcall./purchaseTxuses thetransactionId. - Sample payloads are illustrative. Responses omit null fields, and the exact field set varies by merchant and biller configuration.
- Match message text exactly as returned. Two messages contain spelling mistakes,
Bliing is required(billing details missing) andInvalid transaction to provice transaction state., and are returned exactly so. If you match on message text, use the exact strings shown.
What's next
- Step through a payment → Purchase flow
- Bill payments and the hosted bill menu → Bill payments and VAS
- Integration rules → Remote API business rules
- Every platform transaction state → Transaction states
Updated about 2 hours ago
