Skip to content

Developers

Payment numbers

A payment number is a person's permanent way to pay you: a learner every term, a patient every visit, a customer on account. Your system sends what they owe; anyone types the number in Wellth and pays it, from home or abroad.

The number

  • 10 digits, numbers only, never starting with 0. Works on a phone keypad. Shown grouped as 2047 1185 30; typed with or without spaces or dashes.
  • The last digit is a Luhn check digit, so a mistyped number is refused before anything is looked up.
  • Made by Wellth, random and unique across Wellth. It never holds a name, a date of birth or an admission number.
  • One per person per organisation, for as long as they are with you. Never reused, even after you retire it.

To check a number in your own screens (say, before printing a fee slip):

isWellthPayNumber (JavaScript)
// "2047 1185 30", "2047118530" or "2047-1185-30" → true; one wrong digit → false.
function isWellthPayNumber(input) {
  const s = String(input).replace(/[\s-]/g, "");
  if (!/^[1-9]\d{9}$/.test(s)) return false;
  let sum = 0;
  for (let i = 0; i < 10; i++) {
    let d = Number(s[9 - i]);
    if (i % 2 === 1) {
      d *= 2;
      if (d > 9) d -= 9;
    }
    sum += d;
  }
  return sum % 10 === 0;
}

Who keeps the balance

REPORTEDbillingYour system keeps the balance and sends it to Wellth after every change (a bill, a payment, a discount, a reversal). A school system's learners. Use PUT /learners/:externalRef, or PUT /payers/:externalRef with balanceMinor.
BILLSbillingWellth keeps the balance: the sum of the payer's open bills. Use PUT /payers/:externalRef without balanceMinor, then send bills.
REMITTANCEbillingA boda boda or taxi rider on a remittance plan, made in the Wellth portal. Your system can read it, never change it.

A payer never changes kind: sending balanceMinor for a BILLS payer, or leaving it out for a REPORTED one, is a 409.

Create or update a learner

PUT/api/org/v1/learners/:externalRef

Also at /api/school/v1/learners/:externalRef. Creates the learner with a new payment number (201), or updates it (200). Idempotent by itself: no Idempotency-Key needed.

:externalRef is your own opaque id for the learner, URL-encoded: 1 to 120 printable characters, no spaces. Never a name or an admission number.

learnerLabelstring, requiredWhat the payer sees: initials and class, like N. K., S.2. 1 to 40 characters.
currencystring, requiredYour organisation's currency: UGX in Uganda, KES in Kenya. Anything else is a 400.
balanceMinorstring or null, requiredWhat is owed now, in whole shillings, as a string: "350000". "0" when nothing is owed; null when unknown (the payer chooses the amount). At most 2,000,000,000.
termLabelstring or nullWhat the balance is for, at most 40 characters: Term 3, 2026. A school may send any text; any other organisation sends null or one of its descriptions.
includesWellthRefsstring[], optionalThe wellthRefs (WP-8F3K2Q7A) of this learner's Wellth payments that your balanceMinor already counts. At most 50, newest first. See below.
Request
curl -X PUT https://wellth.example/api/org/v1/learners/oca_stu_ckx123 \
  -H "Authorization: Bearer $WELLTH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "learnerLabel": "N. K., S.2",
    "currency": "UGX",
    "balanceMinor": "350000",
    "termLabel": "Term 3, 2026",
    "includesWellthRefs": []
  }'
201 Created
{
  "externalRef": "oca_stu_ckx123",
  "payNumber": "2047118530",
  "payNumberDisplay": "2047 1185 30",
  "payUrl": "https://wellth.example/pay/n/2047118530",
  "learnerLabel": "N. K., S.2",
  "balanceMinor": "350000",
  "reportedBalanceMinor": "350000",
  "currency": "UGX",
  "termLabel": "Term 3, 2026",
  "balanceUpdatedAt": "2026-10-27T15:20:00.000Z",
  "status": "ACTIVE"
}
payNumberstringThe 10 digits. Store it against the learner and print it on fee statements.
payNumberDisplaystringThe same, grouped for reading.
payUrlstringThe learner's pay page, /pay/n/<payNumber>. Put its QR on fee slips.
balanceMinorstring or nullWhat Wellth lets payers pay now: your figure, less Wellth payments you haven't counted yet, less payments since.
reportedBalanceMinorstring or nullYour figure as you last sent it.
balanceUpdatedAtstringWhen you last sent it. Payers see “as the school's records stood on …”.
statusstringACTIVE or RETIRED.

Keeping the balance right: includesWellthRefs

When a payment lands, Wellth lowers the balance at once, so the next payer sees the right figure, and tells your system by the learner.payment_succeeded webhook. But your system may send a balance worked out before it booked that webhook. Without help, that would bring back money already paid, and a second parent could pay it again.

So with every PUT, list the wellthRefs your figure already counts. Wellth keeps balance = max(0, balanceMinor − every settled Wellth payment on this learner you have never listed). Once listed, a payment counts for good, so listing your newest 50 each time is enough. Unknown references are ignored.

An example

  • You send "350000". Wellth shows 350,000.
  • A parent pays 150,000 on Wellth: WP-8F3K2Q7A. Wellth shows 200,000 and sends the webhook.
  • Before your system books it, a fee change makes it send "350000" again, listing nothing. Wellth still shows 200,000: WP-8F3K2Q7A isn't counted in your figure yet.
  • Your system books the webhook and sends "200000" with ["WP-8F3K2Q7A"]. Wellth shows 200,000, and reportedBalanceMinor is 200,000 too.

Read or retire a learner

GET/api/org/v1/learners/:externalRef

The same shape as the PUT answer. 404 if Wellth doesn't know the learner.

DELETE/api/org/v1/learners/:externalRef

The learner left: status RETIRED. The number is never given to anyone else. A payment already on its way still lands and is reported. Retiring twice is fine. A later PUT for the same externalRef makes the learner ACTIVE again with the same number (200).

Payers whose balance comes from bills

PUT/api/org/v1/payers/:externalRef

Without balanceMinor: a BILLS payer, made with its number (201) or its label and site updated (200). With balanceMinor: exactly the learner PUT above (send learnerLabel then).

payerLabelstring, requiredInitials, 1 to 40 characters: S. A.. No phone, file or ID numbers (5 or more digits in a row is refused). learnerLabel is accepted too.
currencystring, requiredUGX or KES: your organisation's.
siteCodestring or nullWhich of your sites the payer belongs to, by its code: the “Poster code” on the portal's Staff screen.
Request
curl -X PUT https://wellth.example/api/org/v1/payers/pt_4471 \
  -H "Authorization: Bearer $WELLTH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "payerLabel": "S. A.", "currency": "UGX", "siteCode": "kyanja" }'
201 Created
{
  "externalRef": "pt_4471",
  "payNumber": "4027186354",
  "payNumberDisplay": "4027 1863 54",
  "payUrl": "https://wellth.example/pay/n/4027186354",
  "learnerLabel": "S. A.",
  "balanceMinor": "0",
  "reportedBalanceMinor": null,
  "currency": "UGX",
  "termLabel": null,
  "balanceUpdatedAt": "2026-11-01T09:02:11.000Z",
  "status": "ACTIVE",
  "payerRef": "pt_4471",
  "billing": "BILLS",
  "siteCode": "kyanja"
}

Every payer answer adds payerRef (your externalRef again), billing and siteCode to the learner shape. A BILLS payer's balanceMinor is the sum of what is open on its bills, and its termLabel is the one bill's description, or “2 bills”.

GET/api/org/v1/payers/:externalRef

Any payer, any kind. 404 if unknown.

DELETE/api/org/v1/payers/:externalRef

Retires a learner or a BILLS payer, as for learners. A rider is ended in the portal instead (409).

Riders on a remittance plan (read-only)

A boda boda or taxi fleet keeps each rider's plan in the Wellth portal. GET /api/org/v1/payers/:externalRef for a rider adds remittance, and balanceMinor is what the rider's number takes today. Amounts are strings; a field not set is null.

remittance
{
  "remittance": {
    "dailyAmountMinor": "15000",
    "arrearsMinor": "30000",
    "aheadMinor": "0",
    "todayMinor": "15000",
    "ownershipTargetMinor": "6500000",
    "paidTowardTargetMinor": "2310000",
    "status": "ACTIVE"
  }
}

A rider can't be changed or retired through the API: PUT and DELETE answer 409.

How people pay a number

  • The direct link /pay/n/<payNumber> (the payUrl; a QR on the slip), your poster page /s/<handle>, or School fees in the Wellth app. Payers sign in first.
  • They see your name with its tick, the label, what is owed and as of when. They pay all of it or part: any whole amount up to what is owed, less what is already on its way.
  • Balance null: any whole amount. Balance "0": refused with “Nothing is owed right now”.
  • Mobile money, bank transfer, from abroad, or a Wellth balance. The payer's fee is their own line, on top: you receive the full amount, and amountMinor in the webhook never includes it.
  • A payment on its way holds the balance (15 minutes by phone, 30 from abroad), so two payers can't both pay the same money.