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.

RuleDetail
Error body{ "status": "ERROR", "message": "<message>" }
HTTP statusBusiness 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 omittedResponses 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 wireThe 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)

ConditionHTTPmessage
Field validation failure400'<field>' <reason>
Malformed JSON body400Not valid JSON/XML
Incorrect content type400Content type must be application/json
Missing request parameter400Framework-generated
Client limits exceeded400Client Limits Exceeded
Not authenticated401Security-layer default
Not authorised for the endpoint403You are not authorized to access this resource
Any unhandled exception500System 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")

Trigger conditionmessage
Neither clientIdentifier nor msisdn suppliedValidation error: Either 'clientIdentifier' or 'msisdn' is required
Client exists but is not in ACTIVE stateClient Suspended
Supplied QR / code value cannot be resolvedCode supplied is invalid
Scanned QR / code value is not a recognised or supported codeCode not supported
Code not foundCode Not found
Code expiredCode Expired
Code not in active stateCode not in active state
Merchant for the code missing or inactiveMerchant 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")

Trigger conditionmessage
webhook supplied but is not a valid URLInvalid webhook, must be a valid URL

Sample response - HTTP 200, 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 processing

A 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 returns NEED_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")

Trigger conditionmessage
Transaction not foundValidation error: Invalid transaction <id>
Transaction not in the expected starting stateValidation error: Invalid transaction state. Current state: '<state>'
Secure-code data submitted but the transaction could not be advanced to 3D Secure processingValidation error: Invalid transaction state. Unable to update.
Neither clientIdentifier nor msisdn suppliedValidation error: Either 'clientIdentifier' or 'msisdn' is required
scAuthMethod not one of 01 / 02 / 03 / 04Validation error: secureCodePreAuthData-scAuthMethod is not valid. Valid values: 01/02/03/04
scAuthTimeStamp invalidValidation error: secureCodePreAuthData-scAuthTimeStamp is not valid. Valid timestamp format: YYYYMMDDHHMM
Card BIN not supported by the merchantCard Error: Card not supported.
Required payment data missing, or payment method not configuredValidation error: Required transaction data missing or CNP is not configured for <manager>
Client not in ACTIVE stateClient Suspended
Code not found, expired or not activeInvalid Code: Code not found / Code Expired / Code not active
Amount required, below minimum, above maximum or invalidValidation 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 missingValidation error: 'tip' Value cannot be more than 'amount' value / Tip Value is required
Contact, shipping or extra reference required but missingValidation error: Contact is required / Shipping is required / Extra Reference value is required
Billing required but missingValidation error: Bliing is required
CVV2 required but blank, or invalid lengthValidation error: 'CVV2' required for purchase tx / Invalid CVV length.
Card hash check failedCard Hash Failed
Card link / update failedCard Error: <reason>

System errors (HTTP 500)

Trigger conditionmessage
Internal processing failureSystem 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)

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 valueMeaning
PENDINGNot yet in a final state (or final, but the configured notification has not yet succeeded).
SUCCESSReached final success.
Terminal state nameReturned as is when the transaction reaches a final state (see below).
📘

Which states you will see

The endpoint returns the state name as is for any state beginning with END_, and for ISSUER_NEVER_APPROVE / ISSUER_NOT_APPROVE_NOW. Every other in-flight state (for example OFF_TO_BANK) is returned as PENDING, and a completed transaction is returned as SUCCESS. Keep polling while you see PENDING.

Terminal states for card and bill payments

Terminal stateMeaning
END_BANK_DECLINEDeclined by the bank or issuer
END_BANK_NON_00Issuer returned a non-approval response
END_BANK_ERRORError response from the bank or acquirer
END_BANK_OFFLINEBank or acquirer unavailable
END_BANK_EXPIREDBank authorisation expired
END_INSUFFICIENT_FUNDS_OR_OVER_CREDIT_LIMITInsufficient funds or over credit limit
END_ACCOUNT_CLOSED_OR_FRAUDULENTAccount closed or flagged fraudulent
END_CARD_EXPIRY_NOT_ALLOWEDCard expiry not permitted
END_BIN_NOT_WHITELISTEDCard BIN not whitelisted
END_NOTALLOW_BIN_CLASSIFICATIONCard BIN classification not allowed
END_HASH_FAILEDCard hash check failed
END_FRAUD_DETECTIONRejected by fraud screening
END_BRE_REJECTIONRejected by the rules engine; includes 3D Secure authentication rejected or declined
END_BLACKLISTEDClient or card blacklisted
END_TX_RATE_BLOCKEDBlocked by transaction-rate (velocity) controls
END_LIMIT_FAILEDClient limit exceeded
END_VALIDATION_FAILEDValidation failure during processing
END_INVALID_AMOUNTInvalid amount
END_CODE_IN_USEPayment code already in use
END_CODE_PURCHASE_ERRORPurchase processing error
END_TIME_OUT_IN_QUEUETimed out awaiting processing
END_TIMEOUT_PURCHASE_3DS3D Secure purchase timed out
END_SYSTEM_ERRORSystem error during processing
END_REVERSEDTransaction reversed
END_REFUNDEDTransaction refunded
END_CLIENT_CANCELCancelled by the client
END_CLIENT_TIMEOUTClient did not complete in time
ISSUER_NEVER_APPROVEIssuer will never approve (final)
ISSUER_NOT_APPROVE_NOWIssuer 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 conditionHTTPmessage
Transaction reference not found400Invalid Transaction Reference
Transaction state cannot be resolved500Invalid transaction to provice transaction state.
Corrupt transaction data while resolving state500System 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. PENDING is the normal success response from /purchaseTx; poll /transactionState/{transactionId}/v2 for the final outcome. An optional webhook can be registered on /generateTransactionId or /purchaseTx and 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 value during a multi-step lookup. The value in a NEED_INPUT response may be rewritten; send it back on the next /lookup or /generateTransactionId call. /purchaseTx uses the transactionId.
  • 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) and Invalid transaction to provice transaction state., and are returned exactly so. If you match on message text, use the exact strings shown.

What's next


Did this page help you?