Customer account holder
Quick start

OneView documentation

Customer account holder

Individual, corporate, and sole proprietorship customers, BVN rules, and direction.

Customer account holder

Use accountHolderType: "CUSTOMER" when the entry posts to one of your customers' accounts. The accountHolder object describes that customer: the customer whose account the entry posts to, whichever way the money moves. On an OUTBOUND transaction the account holder is the sender. On an INBOUND transaction the account holder is the recipient, and the sender is the counterparty.

The only required account holder field is customerUniqueId, a non-blank string of up to 64 characters. Use the stable identifier from your core banking or customer master system, never an email address, phone number or display name. Velocity and history rules group activity by this value, so it must stay the same across every transaction for the same customer.

Minimal customer example

POST /api/transactions
Authorization: Bearer <oneview-service-account-api-key>
Content-Type: application/json

{
  "transactionReference": "TXN-20260921-000101",
  "direction": "OUTBOUND",
  "accountHolderType": "CUSTOMER",
  "paymentMethod": "NIP",
  "channel": "MOBILE_APP",
  "status": "POSTED",
  "isCrossBorder": false,
  "amount": "15000.00",
  "currency": "NGN",
  "executedAt": "2026-09-21T09:20:00Z",
  "accountHolder": {
    "customerUniqueId": "CUS-100231"
  },
  "counterparty": {
    "counterpartyType": "EXTERNAL_BANK",
    "accountName": "Demo Recipient Limited",
    "accountNumber": "9000000001",
    "institutionCode": "999999"
  }
}

This passes validation, but most customer rules read more than the ID. The examples below show the fields worth sending.

A customer profile is created the first time you send a customer

The first time OneView receives a customerUniqueId it has not seen, it creates a customer profile from the accountHolder fields listed below. Later transactions for the same customerUniqueId do not overwrite that profile. Send the full profile on the customer's first transaction, and keep sending the fields your rules read on every transaction, because rules evaluate the accountHolder object exactly as it arrives with each transaction.

Profile fields: firstName, middleName, lastName, companyName, dateOfBirth (YYYY-MM-DD), gender, nationality, identityType, identityNumber, kycLevel, riskLevel, isPep, occupationOrBusiness, sourceOfWealth, email, phoneNumber, addressLine, city, state, country, postalCode and status. Send only what you need. When entityType is omitted, the profile is recorded as an individual.

OneView checks these profile values on every customer transaction, not only the first, and rejects a request whose value would not fit the profile:

FieldRule
customerUniqueId, firstName, middleName, lastName, sourceOfWealth, city, stateUp to 64 characters
companyName, occupationOrBusinessUp to 128 characters
emailUp to 254 characters
phoneNumberUp to 32 characters
kycLevelUp to 24 characters
postalCodeUp to 20 characters
genderUp to 16 characters
country, nationalityA two-letter ISO 3166-1 code in any case, such as NG or ng. Stored in uppercase.
dateOfBirthA real calendar date in YYYY-MM-DD format

An over-long value is rejected with a message such as accountHolder.firstName must be at most 64 characters. An invalid country returns accountHolder.country must be an ISO 3166-1 alpha-2 country code, and an invalid date returns accountHolder.dateOfBirth must be a calendar date in YYYY-MM-DD format. An empty string or a non-string value is not checked and is not copied to the profile.

Full individual customer

POST /api/transactions
Authorization: Bearer <oneview-service-account-api-key>
Content-Type: application/json

{
  "transactionReference": "TXN-20260921-000102",
  "externalReference": "CBS-20260921-88410",
  "direction": "OUTBOUND",
  "accountHolderType": "CUSTOMER",
  "paymentMethod": "NIP",
  "channel": "MOBILE_APP",
  "status": "POSTED",
  "isCrossBorder": false,
  "amount": "185000.00",
  "currency": "NGN",
  "feeAmount": "26.88",
  "taxAmount": "2.02",
  "balanceBefore": "412500.50",
  "balanceAfter": "227471.60",
  "narration": "Rent contribution September",
  "sourceOfFunds": "SALARY",
  "initiatedAt": "2026-09-21T09:24:51Z",
  "executedAt": "2026-09-21T09:25:03Z",
  "accountHolder": {
    "customerUniqueId": "CUS-100231",
    "entityType": "INDIVIDUAL",
    "accountNumber": "0012345678",
    "accountName": "Ada Demo Okafor",
    "accountType": "SAVINGS",
    "accountOpeningDate": "2019-04-02",
    "firstName": "Ada",
    "middleName": "Demo",
    "lastName": "Okafor",
    "dateOfBirth": "1990-06-15",
    "gender": "FEMALE",
    "nationality": "NG",
    "identityType": "BVN",
    "identityNumber": "12345678901",
    "kycLevel": "TIER_3",
    "riskLevel": "LOW",
    "isPep": false,
    "occupationOrBusiness": "Software engineer",
    "sourceOfWealth": "Employment income",
    "email": "ada.okafor@example.com",
    "phoneNumber": "+2348000000001",
    "addressLine": "1 Example Street",
    "city": "Ikeja",
    "state": "Lagos",
    "country": "NG",
    "postalCode": "100001",
    "status": "ACTIVE"
  },
  "counterparty": {
    "counterpartyType": "EXTERNAL_BANK",
    "accountName": "Demo Properties Limited",
    "accountNumber": "9000000002",
    "institutionCode": "999999",
    "accountType": "CURRENT",
    "country": "NG"
  }
}
FieldWhat it meansWhy it matters for monitoring
entityTypeINDIVIDUAL, CORPORATE or SOLE_PROPRIETORSHIP.Rules often apply different thresholds to people and businesses.
accountNumber, accountTypeThe debited account and its product type.Velocity rules can group by account. The account number is also shown to analysts and can be searched.
accountOpeningDateWhen the account was opened.New-account rules, such as large transfers in an account's first 30 days.
firstName, middleName, lastNameThe customer's legal name.Used as the name for PEP and sanction screening when that control is enabled. For customers without a personal name, screening uses companyName, then accountName.
identityType, identityNumberIdentity document type and number.BVN is the only supported identity type. See the BVN rules below.
kycLevelYour KYC tier, in your own values.Tier-based limits. Use the same values consistently, because rules compare the text exactly.
riskLevelLOW, MEDIUM or HIGH.Rules can tighten thresholds for higher-risk customers. OneView also uses the value you send as a source for the customer's risk classification.
isPepBoolean.PEP-specific rules and enhanced review.
nationality, countryTwo-letter ISO 3166-1 country codes, such as NG. Three-letter codes are rejected.Country rules. The account holder country can match a country on your blacklist.
statusACTIVE, SUSPENDED, FROZEN or CLOSED.Activity on a frozen or closed account is a common rule.
balanceBefore, balanceAfterAccount balance around the posting. Negative values are allowed.Rules that look for accounts being emptied.
sourceOfFundsYour code for where the money came from.Mismatch rules, such as a large transfer funded by an unexpected source.
initiatedAtWhen the customer started the transaction.Shows delays between initiation and execution.

BVN rules

  • identityType accepts only BVN. Any other value is rejected.
  • identityNumber must be an 11-digit string, for example "12345678901". A JSON number, a shorter value or a value with spaces is rejected with accountHolder.identityNumber must be an 11-digit BVN.
  • identityNumber is stored with the transaction and is visible to analysts. Send it only if your institution needs it in OneView.

Corporate customer

POST /api/transactions
Authorization: Bearer <oneview-service-account-api-key>
Content-Type: application/json

{
  "transactionReference": "TXN-20260921-000103",
  "direction": "OUTBOUND",
  "accountHolderType": "CUSTOMER",
  "paymentMethod": "RTGS",
  "channel": "INTERNET_BANKING",
  "status": "POSTED",
  "isCrossBorder": false,
  "amount": "48500000.00",
  "currency": "NGN",
  "narration": "Supplier settlement invoice INV-2026-0911",
  "sourceOfFunds": "BUSINESS_REVENUE",
  "executedAt": "2026-09-21T11:02:40Z",
  "accountHolder": {
    "customerUniqueId": "CUS-500871",
    "entityType": "CORPORATE",
    "accountNumber": "0012345679",
    "accountName": "Demo Logistics Limited",
    "accountType": "CURRENT",
    "accountOpeningDate": "2015-01-19",
    "companyName": "Demo Logistics Limited",
    "occupationOrBusiness": "Freight and haulage",
    "kycLevel": "CORPORATE_FULL",
    "riskLevel": "MEDIUM",
    "isPep": false,
    "email": "treasury@example.com",
    "phoneNumber": "+2348000000002",
    "addressLine": "10 Demo Industrial Avenue",
    "city": "Apapa",
    "state": "Lagos",
    "country": "NG",
    "status": "ACTIVE"
  },
  "counterparty": {
    "counterpartyType": "EXTERNAL_BANK",
    "accountName": "Demo Fuel Supplies Limited",
    "accountNumber": "9000000003",
    "institutionCode": "999999",
    "accountType": "CURRENT",
    "country": "NG"
  }
}

A corporate customer has no personal name, so send companyName. Screening uses it as the customer's name, and analysts see it on the transaction. Do not send firstName or lastName for the company itself, because a personal name takes priority over companyName for screening. BVN belongs to individuals, so the corporate example sends no identityType or identityNumber.

Sole proprietorship

POST /api/transactions
Authorization: Bearer <oneview-service-account-api-key>
Content-Type: application/json

{
  "transactionReference": "TXN-20260921-000104",
  "direction": "INBOUND",
  "accountHolderType": "CUSTOMER",
  "paymentMethod": "NIP",
  "channel": "CORE_BANKING",
  "status": "POSTED",
  "isCrossBorder": false,
  "amount": "320000.00",
  "currency": "NGN",
  "narration": "Catering order payment",
  "executedAt": "2026-09-21T13:47:12Z",
  "accountHolder": {
    "customerUniqueId": "CUS-300412",
    "entityType": "SOLE_PROPRIETORSHIP",
    "accountNumber": "0012345680",
    "accountName": "Demo Kitchen Enterprises",
    "companyName": "Demo Kitchen Enterprises",
    "firstName": "Bola",
    "lastName": "Adeyemi",
    "identityType": "BVN",
    "identityNumber": "10987654321",
    "occupationOrBusiness": "Catering services",
    "kycLevel": "TIER_2",
    "riskLevel": "LOW",
    "isPep": false,
    "country": "NG",
    "status": "ACTIVE"
  },
  "counterparty": {
    "counterpartyType": "EXTERNAL_BANK",
    "accountName": "Demo Events Limited",
    "accountNumber": "9000000004",
    "institutionCode": "999999",
    "country": "NG"
  }
}

A sole proprietorship is a business owned by one person. Send the trading name in companyName and the owner's name in firstName and lastName. The owner's BVN can be sent because it identifies an individual. Screening uses the owner's personal name, because it takes priority over companyName.

INBOUND and OUTBOUND for a customer

direction describes the movement from the account holder's side. The account holder is always your customer, whose account the entry posts to, and the counterparty is always the other party.

SituationdirectionaccountHoldercounterparty
Your customer sends money to someoneOUTBOUNDYour customer (the sender)The recipient
Your customer receives money from someoneINBOUNDYour customer (the recipient)The sender

The sole proprietorship example above is INBOUND: the customer received a payment, and the counterparty describes the external sender. Send inbound credits as well as outbound debits, because rules such as rapid movement of funds compare what arrives with what leaves.

For a transfer between two of your own customers, see Internal user counterparty, which sends one transaction for each side.

Response

A successful submission always returns the same envelope, whichever account holder or counterparty type you sent. This is the response to the full individual customer example above:

{
  "status": true,
  "message": "Transaction evaluated",
  "data": {
    "transaction": {
      "id": "8f2a6c14-9d3b-4e71-b825-1c6f0a4d2e91",
      "transactionReference": "TXN-20260921-000102",
      "direction": "OUTBOUND",
      "receivedAt": "2026-09-21T09:25:03.412Z",
      "replayed": false
    },
    "decision": {
      "outcome": "PASS",
      "riskScore": 0,
      "evaluatedAt": "2026-09-21T09:25:03.431Z",
      "evaluationMs": 11.482,
      "complete": true,
      "rulesEvaluated": 18,
      "rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
      "gradingVersion": 3,
      "triggeredRules": []
    }
  }
}

Status is 201 for a new transaction, 200 when the same transactionReference and direction are resubmitted with an identical body. The full response shape, every outcome, and the risk score bands are documented once in Decision responses rather than repeated per account holder type.

Errors specific to a customer account holder

Causefieldcodemessage
accountHolderType is CUSTOMER and customerUniqueId is missing or blankaccountHolderrequiredaccountHolder.customerUniqueId is required when accountHolderType is CUSTOMER
A profile value, such as firstName, is over its length limitaccountHolder.firstNameinvalidaccountHolder.firstName must be at most 64 characters
country or nationality is not an assigned two-letter codeaccountHolder.nationalityinvalidaccountHolder.nationality must be an ISO 3166-1 alpha-2 country code
dateOfBirth is not a real calendar date in YYYY-MM-DDaccountHolder.dateOfBirthinvalidaccountHolder.dateOfBirth must be a calendar date in YYYY-MM-DD format
identityType is anything other than BVNaccountHolder.identityTypeinvalidaccountHolder.identityType must be one of: BVN
identityNumber is not an 11-digit stringaccountHolder.identityNumberinvalidaccountHolder.identityNumber must be an 11-digit BVN

Every request-level error uses the same envelope and the same status codes, whatever account holder type triggered it. See Errors for the complete catalogue, including INVALID_JSON, licence errors, and retry guidance.

Idempotency

A customer transaction is deduplicated the same way as any other: by transactionReference and direction together. Resubmitting the identical body returns 200 with replayed: true and does not re-evaluate the transaction or re-check the customer profile. Resubmitting a different body under the same reference and direction is rejected with 409 IDEMPOTENCY_CONFLICT. See Idempotency in Submitting transactions and Replays in Decision responses.

How PEP and sanction screening picks the name

When PEP and sanction screening is licensed and enabled, it resolves one name per party in this order, stopping at the first match: firstName/lastName (joined with middleName if present), then companyName, then accountName. A party with none of these fields is simply not screened by name — this is not an error, and rules and blacklist matching still apply to whatever fields you did send. This order is what makes a personal name take priority over companyName for the sole proprietorship example above, and it applies identically to the counterparty object. See Card counterparty and VAS counterparty for two counterparty types this order does not cover by default.