VAS counterparty
Quick start

OneView documentation

VAS counterparty

Airtime and bill payments with biller fields, fees, and tax.

VAS counterparty

Use counterpartyType: "VAS" for value-added services: airtime and data top-ups, electricity, cable TV, water, betting wallets, and other billers. billerName is required. A request without it is rejected with counterparty.billerName is required for a VAS counterparty.

FieldRequiredWhat it meansWhy it matters for monitoring
billerNameYesThe biller's display name.Shown to analysts.
billerIdNoYour or your aggregator's ID for the biller.Rules can target a biller. It can also match a biller on your blacklist.
billerCategoryNoYour category, such as AIRTIME, DATA, ELECTRICITY, CABLE_TV or BETTING.Category rules, such as limits on betting top-ups. Use consistent values, because rules compare the text exactly.
accountNumberNoThe customer's account or meter number with the biller, or the phone number being topped up.Rules can count how many of your customers pay the same recipient.
accountNameNoThe name registered with the biller, when you have it.Shown to analysts and used for screening.

Airtime top-up

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

{
  "transactionReference": "VAS-20260921-001207",
  "direction": "OUTBOUND",
  "accountHolderType": "CUSTOMER",
  "paymentMethod": "INTERNAL_TRANSFER",
  "channel": "USSD",
  "status": "POSTED",
  "isCrossBorder": false,
  "amount": "2000.00",
  "currency": "NGN",
  "narration": "Airtime top-up",
  "executedAt": "2026-09-21T06:45:18Z",
  "accountHolder": {
    "customerUniqueId": "CUS-100231",
    "accountNumber": "0012345678",
    "accountName": "Ada Demo Okafor"
  },
  "counterparty": {
    "counterpartyType": "VAS",
    "billerName": "Demo Mobile Network",
    "billerId": "BLR-AIRTIME-001",
    "billerCategory": "AIRTIME",
    "accountNumber": "08000000001"
  },
  "telemetry": {
    "sessionId": "ussd-session-000001207",
    "country": "NG"
  }
}

Here accountNumber is the phone number receiving the airtime. Many small top-ups to many different numbers from one account is a common pattern that rules look for.

Electricity bill with fee, tax and ledger postings

The customer pays a prepaid electricity bill with a convenience fee and VAT on the fee. The ledger postings show where each part of the debit went.

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

{
  "transactionReference": "VAS-20260921-001208",
  "externalReference": "BILLER-TOKEN-000001208",
  "direction": "OUTBOUND",
  "accountHolderType": "CUSTOMER",
  "paymentMethod": "INTERNAL_TRANSFER",
  "channel": "MOBILE_APP",
  "status": "POSTED",
  "isGlTransaction": true,
  "isCrossBorder": false,
  "amount": "10000.00",
  "currency": "NGN",
  "feeAmount": "100.00",
  "taxAmount": "7.50",
  "narration": "Prepaid electricity meter 04500000001",
  "executedAt": "2026-09-21T19:30:41Z",
  "accountHolder": {
    "customerUniqueId": "CUS-100231",
    "accountNumber": "0012345678",
    "accountName": "Ada Demo Okafor"
  },
  "counterparty": {
    "counterpartyType": "VAS",
    "billerName": "Demo Electricity Distribution",
    "billerId": "BLR-POWER-014",
    "billerCategory": "ELECTRICITY",
    "accountNumber": "04500000001",
    "accountName": "Ada Demo Okafor"
  },
  "ledgerPostings": [
    {
      "postingId": "VAS-20260921-001208-1",
      "glCode": "20100",
      "glName": "Customer savings deposits",
      "entryType": "DEBIT",
      "amount": "10107.50",
      "currency": "NGN",
      "accountNumber": "0012345678"
    },
    {
      "postingId": "VAS-20260921-001208-2",
      "glCode": "20450",
      "glName": "Biller settlement payable",
      "entryType": "CREDIT",
      "amount": "10000.00",
      "currency": "NGN"
    },
    {
      "postingId": "VAS-20260921-001208-3",
      "glCode": "40230",
      "glName": "Bill payment fee income",
      "entryType": "CREDIT",
      "amount": "100.00",
      "currency": "NGN"
    },
    {
      "postingId": "VAS-20260921-001208-4",
      "glCode": "25010",
      "glName": "VAT payable",
      "entryType": "CREDIT",
      "amount": "7.50",
      "currency": "NGN"
    }
  ]
}
FieldWhat it meansWhy it matters for monitoring
amountThe bill value, 10000.00.The value rules compare against. Do not include the fee or tax in it.
feeAmountThe convenience fee, 100.00.Fee rules, and helps analysts see the full cost. Defaults to "0".
taxAmountVAT on the fee, 7.50.Kept apart from the fee. Defaults to "0".
isGlTransactiontrue, because ledger legs are attached.Lets GL rules include the transaction.
ledgerPostingsOne debit of 10107.50 and three credits totalling 10107.50.Rules can check that the journal balances and inspect each GL leg.
externalReferenceThe biller's token or receipt reference.Reconciliation with the biller.

The ledger legs are optional. Send them if your core banking system already produces them, and see Internal GL account holder for the leg format rules.

billerCategory is not a OneView-defined enum. AIRTIME, DATA, ELECTRICITY, CABLE_TV and BETTING above are this example's own values — OneView accepts and stores whatever text you send. OneView also does not enforce that a ledgerPostings journal balances; see the same warning on Internal GL account holder.

Response

This is the response to the airtime top-up above:

{
  "status": true,
  "message": "Transaction evaluated",
  "data": {
    "transaction": {
      "id": "5a1c9e73-2b4f-4d86-a015-8f3c6e2b0d97",
      "transactionReference": "VAS-20260921-001207",
      "direction": "OUTBOUND",
      "receivedAt": "2026-09-21T06:45:18.203Z",
      "replayed": false
    },
    "decision": {
      "outcome": "PASS",
      "riskScore": 0,
      "evaluatedAt": "2026-09-21T06:45:18.211Z",
      "evaluationMs": 6.774,
      "complete": true,
      "rulesEvaluated": 18,
      "rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
      "gradingVersion": 3,
      "triggeredRules": []
    }
  }
}

Status is 201 for a new transaction, 200 for a replay. See Decision responses for every outcome.

Errors specific to a VAS counterparty

Causefieldcodemessage
No billerNamecounterpartyrequiredcounterparty.billerName is required for a VAS counterparty
A ledger leg on the electricity example has no glCodeledgerPostings.0requiredledgerPostings.0.glCode is required

See Errors for the complete catalogue.

Idempotency

Deduplication is by transactionReference and direction. A resubmitted identical body, including identical ledgerPostings, returns 200 with replayed: true without re-debiting the customer or re-posting the ledger legs. A different body under the same reference and direction, for example a retried top-up with a different amount, is rejected with 409 IDEMPOTENCY_CONFLICT — use a new reference for a genuinely new top-up. See Idempotency in Submitting transactions.

Billers are not screened by name

PEP and sanction screening resolves a counterparty's name from firstName/lastName, companyName, then accountName (see Customer account holder). billerName is not in that list. A VAS counterparty sent with only billerName, billerId, billerCategory and accountNumber — the airtime example above — is not screened by name. The electricity example additionally sends accountName on the counterparty (the customer's own name registered with the biller); because accountName is in the fallback list, that field alone is what would be screened, not the biller itself. Rules and blacklist matching on billerId/billerCategory are unaffected either way.