Errors
Any 4xx or 5xx response means no decision was made for the request. The transaction was not evaluated, and
your system must apply its own fallback. A lapsed licence or an outage never appears as a BLOCK.
The error envelope
Every error has the same shape:
{
"message": "Transaction payload validation failed",
"status": false,
"statusCode": 400,
"code": "VALIDATION_FAILED",
"errors": [
{
"field": "transactionReference",
"code": "required",
"message": "transactionReference is required"
}
]
}
| Field | Meaning |
|---|---|
message | A human-readable summary. Log it, but do not branch on it: the wording can vary. |
status | Always false for an error. |
statusCode | The HTTP status, repeated in the body. |
code | A stable machine-readable code. Branch on this. A few errors, such as 413 and 500, have no code. |
errors | Present only on validation failures. Up to 20 entries, each with field, code and message. |
400 Bad Request
VALIDATION_FAILED
The body broke one or more validation rules. Every problem found is listed, up to 20.
{
"message": "Transaction payload validation failed",
"status": false,
"statusCode": 400,
"code": "VALIDATION_FAILED",
"errors": [
{
"field": "riskScore",
"code": "not_accepted",
"message": "riskScore is assigned by One View and must not be supplied"
},
{
"field": "transactionReference",
"code": "required",
"message": "transactionReference is required"
},
{
"field": "counterparty",
"code": "required",
"message": "counterparty.merchantName is required for a CARD counterparty"
},
{
"field": "currency",
"code": "format",
"message": "currency has an unsupported format"
}
]
}
Fix the request and send it again. Do not retry it unchanged.
INVALID_JSON
The body is not well-formed JSON, or it is a bare value such as 5, "text" or null instead of an object. This
check runs before authentication, so it is returned even when the API key is wrong. Fix the body and send it again.
{
"message": "Request body must be a well-formed JSON object or array",
"status": false,
"statusCode": 400,
"code": "INVALID_JSON"
}
A well-formed JSON array passes this check but is then rejected with VALIDATION_FAILED, because the transaction
body must be an object.
INVALID_TENANT_HEADER
You sent an X-Tenant-Id header that is not a UUID. Remove the header: your credential already determines the
tenant.
{
"message": "X-Tenant-Id must be a UUID",
"status": false,
"statusCode": 400,
"code": "INVALID_TENANT_HEADER"
}
Validation error codes
Each entry in errors[] has a field, which is the path to the problem (such as accountHolder.riskLevel,
counterparty or ledgerPostings.0), and one of the codes below. The messages shown are the ones OneView returns.
errors[].code | Meaning |
|---|---|
required | A required value is missing. |
invalid | The value has the wrong type, is not an allowed value, or breaks a rule. |
format | The value is the right type but has the wrong shape. |
unknown | A field OneView does not recognise. |
not_accepted | A field OneView will not take from a caller: one it assigns itself, or one that does not apply to the rest of the request. |
too_large | An object has too many keys or a value is too long. |
required
field | message | Cause |
|---|---|---|
transactionReference | transactionReference is required | The field is missing or null. The same message pattern applies to every required top-level field. |
accountHolder | accountHolder.customerUniqueId is required when accountHolderType is CUSTOMER | accountHolderType is CUSTOMER and customerUniqueId is missing, not a string, or blank. |
counterparty | counterparty.accountName is required for this counterparty type | An EXTERNAL_BANK, INTERNAL_USER or INTERNAL_GL counterparty has no accountName, or it is blank. |
counterparty | counterparty.merchantName is required for a CARD counterparty | A CARD counterparty has no merchantName, or it is blank. |
counterparty | counterparty.billerName is required for a VAS counterparty | A VAS counterparty has no billerName, or it is blank. |
counterparty | a WALLET counterparty requires at least one of: accountNumber, email, emailFingerprint, counterpartyFingerprint | A WALLET counterparty carries none of those four identifiers, or every one it carries is blank. accountName is optional for this type and does not satisfy the check. See Collection channels. |
collectionAccount | collectionAccount is required when paymentMethod is STATIC_ACCOUNT and direction is INBOUND | A static-account collection with no collectionAccount, or with null in its place. |
collectionAccount.accountNumber | collectionAccount.accountNumber is required | The object was sent without an accountNumber, or it is blank. |
usdExchangeRate | usdExchangeRate is required for cross-border or foreign-currency transactions | A non-USD transaction is cross-border or in a currency other than your base currency, and has no rate. |
ledgerPostings.0 | ledgerPostings.0.glCode is required | A ledger leg has no glCode. |
Only one counterparty problem is reported at a time. After you fix it, a second one may appear.
invalid
field | message | Cause |
|---|---|---|
paymentMethod | paymentMethod must be one of: NIP, NEFT, RTGS, SWIFT, INTERNAL_TRANSFER, CARD, CASH, CHEQUE, DIRECT_DEBIT, STANDING_ORDER, STATIC_ACCOUNT, WALLET, MOBILE_MONEY | A value outside the list, such as BANK_TRANSFER. Enums are case-sensitive. |
accountHolder.riskLevel | accountHolder.riskLevel must be one of: LOW, MEDIUM, HIGH | A value such as medium or VERY_HIGH. |
accountHolder.identityType | accountHolder.identityType must be one of: BVN | Any identity type other than BVN, such as NIN. |
accountHolder.identityNumber | accountHolder.identityNumber must be an 11-digit BVN | Not an 11-digit string, for example a JSON number. |
counterparty | counterparty.counterpartyType is not a supported value | counterpartyType is missing or not one of the six types. |
counterparty | counterparty.bankCode is the former name of counterparty.institutionCode; they must not be supplied with different values | Both names of an institution field are sent with different values. bankName against institutionName gives the same message with those two names. Sending a pair with identical values is accepted. See Counterparty object in Submitting transactions. |
counterparty.country | counterparty.country must be an ISO 3166-1 alpha-2 country code | A three-letter code such as NGA, or an unassigned code. |
telemetry.country | telemetry.country must be an uppercase ISO 3166-1 alpha-2 or alpha-3 country code | Lowercase, unassigned or numeric codes. |
amount | amount must be a decimal with at most 4 fractional digits | Too many decimal places, leading zeros, commas, or exponent notation such as 1e21. |
amount | amount must be a non-negative decimal | A negative amount. balanceBefore and balanceAfter are the only amounts that may be negative. |
usdExchangeRate | usdExchangeRate must be 1 when currency is USD | A USD transaction with a rate other than 1. |
usdExchangeRate | usdExchangeRate must be greater than zero | A rate of 0. |
usdExchangeRate | usdExchangeRate is too small for this amount: the derived USD amount would exceed 14 whole digits | The rate is so small that amount / usdExchangeRate would be larger than 14 whole digits. Usually a sign the rate was sent the wrong way round. |
isCrossBorder | isCrossBorder must be a boolean | A string such as "false" instead of false. |
executedAt | executedAt must be an ISO 8601 timestamp | A value that is not a date and time, or is longer than 40 characters. |
externalReference | externalReference must be a string of 1 to 128 characters | An empty string, a value that is not a string, or more than 128 characters. The same message pattern applies to other length-limited fields, with their own maximum: sourceOfFunds and ledger costCenter 64, transactionReference 64, remittancePurposeCode 32. |
accountHolder.firstName | accountHolder.firstName must be at most 64 characters | A customer profile value over its limit. See Customer account holder for every limit. |
accountHolder.nationality | accountHolder.nationality must be an ISO 3166-1 alpha-2 country code | A customer nationality or country that is not an assigned two-letter code, such as NGA. |
accountHolder.dateOfBirth | accountHolder.dateOfBirth must be a calendar date in YYYY-MM-DD format | A customer date of birth such as 15/06/1990 or 1990-02-30. |
counterparty | counterparty must not contain the NUL character (U+0000) | A NUL character in any key or value. The same applies to accountHolder, telemetry and collectionAccount. |
accountHolder | accountHolder contains an unsupported value | An array inside the object, or a nested object with more than 64 keys. |
ledgerPostings | ledgerPostings must be an array of at most 64 | Not an array, or more than 64 legs. |
ledgerPostings.1 | ledgerPostings.1.entryType must be DEBIT or CREDIT | The second leg has a missing or wrong entryType. Legs are numbered from 0. |
collectionAccount | collectionAccount must be an object | collectionAccount was sent as a string, number or array. The same message pattern applies to accountHolder, counterparty and telemetry. |
body | A JSON object body is required | The body is not a JSON object, for example an array. |
format
field | message | Cause |
|---|---|---|
currency | currency has an unsupported format | Not three uppercase letters, for example ngn. |
transactionReference | transactionReference has an unsupported format | A space or another disallowed character, or it starts with -. |
sourceOfFunds | sourceOfFunds has an unsupported format | A code with spaces or other disallowed characters, or one that starts with ., _ or -. |
narration | narration must not contain control characters | A line break, tab or other control character. |
collectionAccount.accountNumber | collectionAccount.accountNumber has an unsupported format | A disallowed character, such as a space, a value starting with -, or more than 64 characters. |
unknown
field | message | Cause |
|---|---|---|
usdAmount | usdAmount is not a recognised field | A top-level field that is not part of the request. usdAmount, baseAmount and isForeignCurrency are calculated by OneView and are rejected this way. |
initiator | initiator is not a recognised field; it was renamed to accountHolder | A request using the earlier field name. Rename initiator to accountHolder. |
initiatorType | initiatorType is not a recognised field; it was renamed to accountHolderType | A request using the earlier field name. Rename initiatorType to accountHolderType. |
body | body contains a field name that is not recognised | An unknown top-level key that is not a plain field name, for example one that starts with a digit or underscore, contains spaces or non-ASCII characters, or is longer than 64 characters. It is reported once, however many such keys there are. |
ledgerPostings.0 | ledgerPostings.0 contains the unrecognised field narration | A ledger leg with a key outside the eight allowed leg fields. |
ledgerPostings.0 | ledgerPostings.0 contains an unrecognised field | The same, when the unknown key is not a plain field name. |
not_accepted
field | message | Cause |
|---|---|---|
riskScore | riskScore is assigned by One View and must not be supplied | You sent a value that OneView assigns. The same applies to transactionId, id, receivedAt, riskStatus, decision and decisionOutcome. |
collectionAccount | collectionAccount is only accepted when paymentMethod is STATIC_ACCOUNT and direction is INBOUND | You sent a collection account on any other payment method or direction. See Collection channels. |
too_large
field | message | Cause |
|---|---|---|
accountHolder | accountHolder may contain at most 64 fields | More than 64 keys in accountHolder, counterparty, telemetry or collectionAccount. |
counterparty | counterparty contains an oversized value | A string longer than 512 characters. |
401 Unauthorized
AUTH_REQUIRED
No Authorization header was sent.
{
"message": "Authentication is required",
"status": false,
"statusCode": 401,
"code": "AUTH_REQUIRED"
}
INVALID_API_CREDENTIAL
The API key is wrong, unknown, revoked or expired.
{
"message": "API credential is invalid",
"status": false,
"statusCode": 401,
"code": "INVALID_API_CREDENTIAL"
}
Check the key in your secret manager, and check the service account's status and expiry in OneView.
403 Forbidden
SERVICE_ACCOUNT_INTEGRATION_ONLY
The path is not available to API service accounts. This happens with a trailing slash (/api/transactions/) or
with GET /api/transactions/{id}. Use the exact path, and look transactions up through the list endpoint.
{
"message": "API Service Accounts can access integration APIs only",
"status": false,
"statusCode": 403,
"code": "SERVICE_ACCOUNT_INTEGRATION_ONLY"
}
SERVICE_ACCOUNT_SCOPE_FORBIDDEN
The service account's module scope is not Transaction Monitoring or All licensed modules.
{
"message": "API credential is not scoped to this module",
"status": false,
"statusCode": 403,
"code": "SERVICE_ACCOUNT_SCOPE_FORBIDDEN"
}
SERVICE_ACCOUNT_TENANT_MISMATCH
You sent an X-Tenant-Id for a tenant other than the one the credential belongs to. Remove the header.
{
"message": "X-Tenant-Id does not match this credential's bound tenant",
"status": false,
"statusCode": 403,
"code": "SERVICE_ACCOUNT_TENANT_MISMATCH"
}
LICENSE_MISSING
{
"message": "A valid license is required",
"status": false,
"statusCode": 403,
"code": "LICENSE_MISSING"
}
LICENSE_INVALID_OR_EXPIRED
{
"message": "The license is invalid or expired",
"status": false,
"statusCode": 403,
"code": "LICENSE_INVALID_OR_EXPIRED"
}
LICENSE_MODULE_NOT_INCLUDED
The licence does not include the Transaction monitoring module or its Transactions sub-module. Optional controls, such as rules or screening, never cause this error: without them, the decision changes instead.
{
"message": "Module is not included in the license",
"status": false,
"statusCode": 403,
"code": "LICENSE_MODULE_NOT_INCLUDED"
}
For any licence error, apply your fallback and alert the team that manages OneView.
Origin not allowed
A request with an Origin header that OneView does not allow is rejected. This response has no code. Call the API
from your servers without an Origin header.
{
"message": "Origin is not allowed",
"status": false,
"statusCode": 403
}
409 Conflict
TRANSACTION_MONITORING_NOT_CONFIGURED
The base currency has not been set. Ask an administrator to set it under Transaction Monitoring → Settings.
{
"message": "Transaction monitoring base currency must be configured before ingestion",
"status": false,
"statusCode": 409,
"code": "TRANSACTION_MONITORING_NOT_CONFIGURED"
}
IDEMPOTENCY_CONFLICT
A transaction with the same transactionReference and direction already exists with a different body. OneView
does not overwrite a stored transaction. If this is a genuinely new transaction, give it a new reference. If it is a
retry, resend the original body exactly.
{
"message": "A different transaction already exists for this reference and direction",
"status": false,
"statusCode": 409,
"code": "IDEMPOTENCY_CONFLICT"
}
413 Payload Too Large
The body is larger than 1 MB. This response has no code.
{
"message": "Request body is too large",
"status": false,
"statusCode": 413
}
503 Service Unavailable
TRANSACTIONS_UNAVAILABLE
Transaction monitoring cannot accept transactions right now.
{
"message": "Transaction monitoring is unavailable",
"status": false,
"statusCode": 503,
"code": "TRANSACTIONS_UNAVAILABLE"
}
AUTH_UNAVAILABLE
OneView could not check the credential right now.
{
"message": "API authentication is unavailable",
"status": false,
"statusCode": 503,
"code": "AUTH_UNAVAILABLE"
}
500 Internal Server Error
An unexpected error. This response has no code.
{
"message": "An unexpected error occurred",
"status": false,
"statusCode": 500
}
Retry guidance
| Status | Retry? | What to do |
|---|---|---|
400 | No | Fix the request, including INVALID_JSON. |
401 | No | Fix or replace the credential. |
403 | No | Fix the path, scope, tenant header or licence. Apply your fallback meanwhile. |
409 TRANSACTION_MONITORING_NOT_CONFIGURED | No | Retry only after an administrator sets the base currency. |
409 IDEMPOTENCY_CONFLICT | No | Use a new reference for a new transaction, or resend the original body. |
413 | No | Reduce the body size. |
500 | Yes | Retry with the identical body, with increasing delays. Apply your fallback meanwhile. |
503 | Yes | Retry with the identical body, with increasing delays. Apply your fallback meanwhile. |
Retrying with the identical body is always safe. If the first attempt was stored before the error reached you, the
retry returns 200 with replayed: true and the stored decision, instead of creating a second transaction.
OneView does not rate-limit these requests, so there is no 429 to handle. It also never returns 404 or 422
for a submission.