OnLink
Concepts

References and attribution

Which reference attributes a payment to an order, per rail — and why the answer differs between buying and selling.

Attribution answers "which order is this money for?". The answer differs by leg and by rail, and getting it wrong is the most common integration mistake.

There are three references in play. Keeping them straight is most of this page.

ReferenceWhoseWhere it comes from
partnerReferenceYoursYou choose it. Your reconciliation handle.
paymentReferenceOursWe issue it on the order. OL plus 10 characters.
providerReferenceThe rail'sAn M-PESA code, or a bank reference.

partnerReference makes creation idempotent. paymentReference attributes a payment. They are not interchangeable.

Buying — you send KES

On M-PESA the reference cannot be transmitted at all

M-PESA Pay Bill carries no narration field. There is nowhere to put paymentReference, so keep it in your own records rather than trying to send it. Your Account Number is your dedicated collection account — that is what identifies you, and the amount plus the time window identify the order.

mpesa — pay the Pay Bill number, with your collection account number as the Account Number. Pay the kesAmount exactly, and before expiresAt.

bank_transfer (PesaLink, RTGS, EFT) — put paymentReference in the narration. It is delivered on these rails and gives exact attribution regardless of amount. This is the stronger path; prefer it where you have the choice.

Two identical amounts in flight at once

On mpesa, two of your open orders with the same kesAmount inside the same window are genuinely ambiguous. We will not guess: both are held rather than one being picked arbitrarily. Avoid it by using bank_transfer for same-amount orders, or by not holding two identical open orders at once.

Selling — you send USDT

Your Tron deposit address is long-lived and the same for every one of your orders, so it cannot tell two of your orders apart.

The transaction hash is what attributes a deposit. Attach it with PATCH /v1/orders/{id} after you send. Until you do, nothing links your send to the order you meant.

A hash is normalised before comparison — case and a leading 0x are ignored, because those are the same on-chain transaction. One hash attributes exactly one order.

Reading both references back

Once a payment is attributed, GET /v1/orders/{id} carries the rail's own code alongside ours:

{
  "paymentReference": "OLA1B2C3D4E5",
  "payment": {
    "providerReference": "SBX0EXAMPLE",
    "bankTransactionId": "SBXTRANS00000000EXAMPLE"
  }
}

providerReference is the code your own payer will recognise — the M-PESA code from their handset, or their bank's reference. If you show your end user a receipt, that is the number to show; ours means nothing to them. Both fields are null until a payment is attributed.

The values above are placeholders. Rail codes have rail-specific formats that we do not control, so treat both fields as opaque strings: store them, show them, and do not parse them.

Rules of thumb

  • One payment funds one order. Do not send a single payment intending to cover two orders; the surplus is not applied to the second.
  • Never reuse a paymentReference. It is issued per order and belongs to that one.
  • partnerReference is for creation, not payment. Reusing it on create returns the original order, which is what makes retries safe — it does nothing for attribution.

On this page