Internal user counterparty
Use counterpartyType: "INTERNAL_USER" when the other party is another customer of your institution, for example
an intra-bank transfer between two accounts you hold. accountName is required. A request without it is rejected
with counterparty.accountName is required for this counterparty type.
| Field | Required | What it means | Why it matters for monitoring |
|---|---|---|---|
counterpartyType | Yes | INTERNAL_USER. | Identifies an on-us movement. |
accountName | Yes | The other customer's account name. | Shown to analysts and used for screening when no personal or company name is sent. |
customerUniqueId | No, but strongly recommended | The other customer's ID, in the same form as the account holder's. | Links the transaction to the other customer's profile, so analysts see both of your customers involved. |
accountNumber | No | The other customer's account. | Velocity rules can group by the receiving account. It can also match your blacklist. |
country | No | Two-letter ISO 3166-1 code. | Country rules. |
Sending both legs of an intra-bank transfer
An intra-bank transfer debits one customer and credits another. Send it as two transactions with the same
transactionReference:
- An
OUTBOUNDtransaction whose account holder is the sender and whose counterparty is the receiver. - An
INBOUNDtransaction whose account holder is the receiver and whose counterparty is the sender.
OneView identifies a transaction by its reference together with its direction, so the two legs are separate transactions and are not treated as a replay or a conflict. Each leg is evaluated against the customer it belongs to: the sender's outgoing activity and the receiver's incoming activity.
Set isInternal: true on both legs.
Leg 1: OUTBOUND from the sender
POST /api/transactions
Authorization: Bearer <oneview-service-account-api-key>
Content-Type: application/json
{
"transactionReference": "INT-20260921-000501",
"direction": "OUTBOUND",
"accountHolderType": "CUSTOMER",
"paymentMethod": "INTERNAL_TRANSFER",
"channel": "MOBILE_APP",
"status": "POSTED",
"isInternal": true,
"isCrossBorder": false,
"amount": "250000.00",
"currency": "NGN",
"narration": "Monthly allowance",
"executedAt": "2026-09-21T07:55:30Z",
"accountHolder": {
"customerUniqueId": "CUS-100231",
"accountNumber": "0012345678",
"accountName": "Ada Demo Okafor",
"riskLevel": "LOW"
},
"counterparty": {
"counterpartyType": "INTERNAL_USER",
"customerUniqueId": "CUS-100877",
"accountNumber": "0012345690",
"accountName": "Tunde Demo Okafor",
"country": "NG"
},
"telemetry": {
"deviceId": "dev-7f3a9c2e51d04b88",
"ipAddress": "192.0.2.24",
"country": "NG"
}
}
Leg 2: INBOUND to the receiver
POST /api/transactions
Authorization: Bearer <oneview-service-account-api-key>
Content-Type: application/json
{
"transactionReference": "INT-20260921-000501",
"direction": "INBOUND",
"accountHolderType": "CUSTOMER",
"paymentMethod": "INTERNAL_TRANSFER",
"channel": "MOBILE_APP",
"status": "POSTED",
"isInternal": true,
"isCrossBorder": false,
"amount": "250000.00",
"currency": "NGN",
"narration": "Monthly allowance",
"executedAt": "2026-09-21T07:55:30Z",
"accountHolder": {
"customerUniqueId": "CUS-100877",
"accountNumber": "0012345690",
"accountName": "Tunde Demo Okafor",
"riskLevel": "LOW"
},
"counterparty": {
"counterpartyType": "INTERNAL_USER",
"customerUniqueId": "CUS-100231",
"accountNumber": "0012345678",
"accountName": "Ada Demo Okafor",
"country": "NG"
}
}
Points to note:
- The reference is identical and only
directiondiffers. Sending the same reference and direction again with a different body is rejected withIDEMPOTENCY_CONFLICT, so never reuse one leg's reference and direction for the other leg. - The device telemetry belongs to the sender's session, so it is sent only on the outbound leg.
- Each leg receives its own decision. Your system decides how to combine them. For example, if either leg is
BLOCK, stop the whole transfer. See Decision responses. - If your core banking system posts only the debit side, send only the
OUTBOUNDleg. The receiver's activity is then not monitored for this transfer.
Response
Each leg is a distinct transaction with its own transaction.id and its own decision, even though they share a
transactionReference. This is leg 1 (OUTBOUND, the sender):
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "1e7a3c92-4f6d-4b80-9a15-6e2b0c8d7f34",
"transactionReference": "INT-20260921-000501",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T07:55:30.118Z",
"replayed": false
},
"decision": {
"outcome": "PASS",
"riskScore": 0,
"evaluatedAt": "2026-09-21T07:55:30.131Z",
"evaluationMs": 8.402,
"complete": true,
"rulesEvaluated": 18,
"rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
"gradingVersion": 3,
"triggeredRules": []
}
}
}
And this is leg 2 (INBOUND, the receiver), submitted with the same transactionReference but the opposite
direction. It is not a replay of leg 1 — direction is part of the idempotency key, so OneView evaluates it as
a separate transaction with its own transaction.id:
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "9d4b2f61-8a3e-4c05-b716-2f9c0e5a3d87",
"transactionReference": "INT-20260921-000501",
"direction": "INBOUND",
"receivedAt": "2026-09-21T07:55:30.204Z",
"replayed": false
},
"decision": {
"outcome": "PASS",
"riskScore": 0,
"evaluatedAt": "2026-09-21T07:55:30.219Z",
"evaluationMs": 7.115,
"complete": true,
"rulesEvaluated": 18,
"rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
"gradingVersion": 3,
"triggeredRules": []
}
}
}
Both are 201 responses. See Decision responses for every outcome, and note that a HOLD
or BLOCK on one leg has no automatic effect on the other — your system decides how to combine them, as above.
Errors specific to an internal user counterparty
| Cause | field | code | message |
|---|---|---|---|
No accountName on the counterparty | counterparty | required | counterparty.accountName is required for this counterparty type |
counterparty.country is a three-letter or unassigned code | counterparty.country | invalid | counterparty.country must be an ISO 3166-1 alpha-2 country code |
accountHolderType is CUSTOMER and the sender's customerUniqueId is missing | accountHolder | required | accountHolder.customerUniqueId is required when accountHolderType is CUSTOMER |
A validation failure on leg 1 does not stop you from submitting leg 2 — they are independent requests. See Errors for the complete catalogue.
Idempotency across both legs
Each leg is keyed by transactionReference and direction together, so the two legs of one intra-bank
transfer never collide: INT-20260921-000501 + OUTBOUND and INT-20260921-000501 + INBOUND are two separate
idempotency keys. If your core banking system retries leg 1 after a timeout, resend it with the identical body and
you get back 200 with replayed: true and the original decision — no duplicate debit. The same applies
independently to leg 2. Only resending one leg's reference and direction with a changed body triggers
409 IDEMPOTENCY_CONFLICT; retrying with the exact original body is always safe. See Idempotency in
Submitting transactions.