Connect your lending system to Vistroy

Vistroy tracks borrowers by Ghana Card across every SIM, every network and every lender. One API call before you pay out a loan tells you whether this person defaulted on another number, is borrowing from several lenders at once, or cannot afford the repayment.

This guide is for the technical team at a bank, microfinance company, savings and loans company or digital lender. Every example below can be copied and run as it is against the sandbox.

ItemValue
Base URLhttps://vistroy-credit-score-1.onrender.com
FormatJSON over HTTPS. Send Content-Type: application/json on every POST.
AuthenticationYour institution key in the X-API-Key header
Phone numbersAny Ghana format: 0241234567, +233241234567 or 233241234567
Ghana CardGHA-123456789-0
MoneyGhana cedis (GHS) as numbers, e.g. 1500 or 1500.50

Getting access

  1. Apply for an account. You get your API key straight away. Copy it: it is shown only once.
  2. Wait for approval. The Vistroy registry team checks your institution. Until then, requests return 403 with "waiting for approval".
  3. Build and test your integration with the examples on this page and the test borrowers below.

Send your key on every request:

X-API-Key: vsk_your_institution_key
Keep the key on your server. Never put it in a mobile app, a web page or a USSD menu that customers can inspect. If a key leaks, ask Vistroy to rotate it. The old key stops working straight away.

The lending flow

  1. Get consent. Send the borrower an SMS code. When they read it back, you receive a consent_id.
  2. Run the pre-disbursement check with that consent_id, right before paying out. You get APPROVE, MANUAL_REVIEW or DECLINE, the safe amount, and the reasons.
  3. Optional: read the borrower's MoMo statement to get their real income, and pass the statement_id to the check.
  4. Report the loan once it is paid out, and report every status change after that: late, repaid, defaulted.
  5. Agree a repayment plan if the borrower falls behind, and record each payment.
  6. Answer disputes when a borrower says a record is wrong.

Good reporting is what makes the network work: every lender that reports defaults, and every lender that checks before lending, is protected by everyone else's data.

2. Pre-disbursement check

Call this right before you pay out. It looks at every phone number linked to the Ghana Card, open loans at other lenders, recent applications, shared devices and affordability, and gives one decision.

POST/api/v1/check
curl -X POST BASE/api/v1/check \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{
    "ghana_card_number": "GHA-000000103-0",
    "consent_id": "CNS-06D33EB3C0CD",
    "msisdn": "0551110103",
    "requested_amount": 1000,
    "tenor_days": 30,
    "avg_monthly_inflow": 2000,
    "momo_history_months": 18,
    "device_id": "DEV-12345",
    "loan_reference": "LN-0003"
  }'
FieldRequiredMeaning
ghana_card_numberYesThe applicant's Ghana Card
consent_idYesFrom step 1
msisdnYesThe phone number applying now
requested_amount, tenor_daysYesLoan size in GHS and repayment period (1 to 365 days)
avg_monthly_inflowRecommendedAverage monthly mobile money inflow in GHS. Used for affordability.
momo_history_monthsOptionalHow long the wallet has been active
declared_monthly_incomeOptionalUsed when inflow is not available
network, device_idOptionalNetwork is detected from the number if left out. Device ID catches one phone used for many identities.
statement_idOptionalFrom step 7. Income and history then come from the statement instead of the typed-in figures, and statement warnings are added.
loan_referenceRecommendedYour own loan ID. Use the same one when you report the loan, so the pilot report can match the check to the outcome.

The response (shortened):

{
  "application_id": "APP-21F9AC8919",
  "decision": "MANUAL_REVIEW",
  "requested_amount": 1000.0,
  "approved_amount": 600.0,
  "score": 649,
  "risk_band": "Fair (Limited History)",
  "flags": [
    { "code": "UNAFFORDABLE", "severity": "MEDIUM",
      "message": "Needs about GHS 1,000.00/month; safe capacity is GHS 600.00/month." }
  ],
  "identity": {
    "applying_number": "055****103",
    "linked_numbers": [ { "number": "055****103", "network": "MTN", "loans": 1,
                          "unpaid_default": false, "verified": true } ]
  },
  "exposure": { "open_loans": 0, "lenders_with_open_loans": 0, "outstanding_ghs": 0 },
  "affordability": { "monthly_capacity_ghs": 600.0, "affordable_max_ghs": 600.0 },
  "verification_details": { "report_id": "...", "signature": "...", "scoring_model_version": "vistroy-score-0.3" }
}

Keep application_id and verification_details with your loan file. The signature proves the report came from Vistroy and was not changed. Anyone can check it with POST /api/reports/verify.

3. Report loans

Report each loan when you pay it out, then send the same loan_reference again whenever its status changes. Sending it again updates the loan, it never creates a duplicate.

POST/api/loans/submit
curl -X POST BASE/api/loans/submit \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{
    "ghana_card_number": "GHA-000000103-0",
    "loan_reference": "LN-2026-0001",
    "loan_amount": 600,
    "monthly_payment": 600,
    "repayment_status": "ACTIVE",
    "msisdn": "0551110103",
    "network": "MTN"
  }'
StatusWhen to send it
ACTIVELoan paid out, repayments on time
LATEA repayment is overdue
DEFAULTEDYou consider the loan in default under your policy
REPAIDPaid in full
SETTLEDClosed for less than the full balance

Always include msisdn. The phone number is what lets Vistroy catch a borrower who defaults on one SIM and applies on another. You can also send opened_date (YYYY-MM-DD) if the loan was paid out on an earlier day.

4. Upload a loan book

Send many loans in one request, for example your whole portfolio on the first day, then a weekly update. Upload the CSV text inside JSON. First check it with dry_run: true to see errors row by row, then import.

GET/api/loans/bulk-template

Downloads a CSV with the right columns: ghana_card_number, loan_reference, loan_amount, monthly_payment, repayment_status, msisdn, network, opened_date. The last three are optional. Dates can be 2026-03-15 or 15/03/2026.

POST/api/loans/bulk
curl -X POST BASE/api/loans/bulk \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{
    "csv_text": "ghana_card_number,loan_reference,loan_amount,monthly_payment,repayment_status,msisdn\nGHA-000000901-0,LN-0001,1500,300,ACTIVE,0241234567\n",
    "dry_run": true
  }'
{
  "dry_run": true, "imported": 0,
  "rows_valid": 1, "rows_with_errors": 0,
  "new_loans": 1, "updated_loans": 0, "borrowers": 1, "defaulted": 0,
  "errors": []
}

Then send the same request with "dry_run": false. If some rows have errors, the import is refused unless you add "skip_errors": true, which imports only the valid rows. Up to 5,000 rows per request.

5. Repayment plans

When a borrower falls behind, agree a plan instead of writing the loan off. Vistroy tracks each payment, sends SMS reminders, and marks the loan REPAID (or SETTLED if you accepted less) when the plan is finished. Other lenders see that the borrower is on a plan.

POST/api/repayment-plans
curl -X POST BASE/api/repayment-plans \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{
    "loan_reference": "LN-2026-0001",
    "instalments": 3,
    "frequency": "MONTHLY",
    "first_due_date": "2026-11-01"
  }'

Frequencies: WEEKLY, BIWEEKLY, MONTHLY, ONE_OFF. For a settlement, add outstanding_balance and a lower agreed_amount.

POST/api/repayment-plans/{plan_id}/payments
curl -X POST BASE/api/repayment-plans/RPL-9D3CEE2562/payments \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 200, "channel": "MOMO", "reference": "MP240930.1234.A56789" }'

Use the MoMo transaction ID as reference. The same reference can't be recorded twice, which prevents double counting.

EndpointUse
GET /api/repayment-plansAll your plans with progress
GET /api/repayment-plans/{plan_id}One plan with schedule, payments and reminders
POST /api/repayment-plans/{plan_id}/remindSend the borrower an SMS reminder now (once per 24 hours)
POST /api/repayment-plans/{plan_id}/cancelCancel with a reason
GET /api/collections/summaryCollected, still to collect, recovery rate, overdue payments
GET /api/remindersThe last 50 SMS reminders sent for your plans
Automatic reminders go out between 8:00 and 20:00 Ghana time: 2 days before each payment, on the due date, and the day after a missed payment. A plan breaks after 2 missed payments (3-day grace period).

Overdue loans, repayment permissions and evidence packs

For a loan marked Late or Defaulted with no open plan, you can send an SMS reminder. It goes only to the borrower's own numbers: the number the loan was paid to, and numbers the borrower confirmed with an SMS code. Never to contacts. At most once every 3 days and 4 times in 30 days, and never while a dispute is open.

EndpointUse
GET /api/loans/{reference}/collectionsReminder preview and whether one can be sent now, past reminders, repayment permissions
POST /api/loans/{reference}/remindSend an overdue reminder now
POST /api/loans/{reference}/mandatesRecord a repayment permission the borrower signed: method (DIRECT_DEBIT, MOMO_AUTO_DEBIT, STANDING_ORDER, PAYROLL_DEDUCTION), provider, account_last4, max_amount, frequency, signed_on, evidence_reference
POST /api/mandates/{mandate_id}/endEnd a repayment permission, with a reason
GET /api/loans/{reference}/evidence-packPDF record of the loan for legal use, with a SHA-256 fingerprint (managers and admins)
Vistroy records repayment permissions; it doesn't collect money. Collecting from any account the borrower didn't sign a mandate for needs a court order.

6. Disputes

Borrowers can challenge a record from their own page. Disputes about your loans appear for you to answer within 30 days.

GET/api/disputes
POST/api/disputes/{dispute_id}/respond
curl -X POST BASE/api/disputes/DSP-700AAC1EDB/respond \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "CORRECT",
    "corrected_status": "REPAID",
    "clear_default_history": true,
    "notes": "Payment confirmed in our ledger on 12 August."
  }'

Use "action": "NO_CHANGE" with a clear explanation if the record is correct.

7. Mobile money statements

Upload the borrower's statement exactly as MTN MoMo, Telecel Cash or AT Money sent it, as a PDF or CSV (up to 5 MB). Vistroy reads the transactions and works out real monthly income, leaving out loan payouts from other apps and reversals. It needs the same consent_id as the check.

POST/api/momo/statements
curl -X POST BASE/api/momo/statements \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{
    "ghana_card_number": "GHA-000000901-0",
    "consent_id": "CNS-06D33EB3C0CD",
    "msisdn": "0241234567",
    "file_name": "statement.pdf",
    "file_base64": "JVBERi0xLjQK..."
  }'

The response (shortened):

{
  "statement_id": "STM-6AB787C69A",
  "account_number": "024****567",
  "account_name": "Kwame Mensah Demo",
  "period_start": "2026-06-01", "period_end": "2026-09-28", "months_covered": 3.9,
  "transactions_read": 105,
  "avg_monthly_income_ghs": 3140.82,
  "income_stability": 0.95,
  "loan_payouts_received": 1, "loan_payouts_ghs": 500.0,
  "gambling_ghs": 250.59, "gambling_share_pct": 2.0,
  "balance_checks": 104, "balance_breaks": 0,
  "monthly": [ { "month": "2026-06", "income": 3088.99, "loan_payouts": 0.0, "money_out": 2139.76 } ],
  "flags": [ { "code": "GAMBLING_SPEND", "severity": "LOW", "message": "Some betting payments (GHS 250.59, 2.0% of income)." } ]
}

Then send "statement_id": "STM-6AB787C69A" with the check. Vistroy keeps only this summary, not the individual transactions. The read appears in the borrower's activity log.

Sample files for testing: sample PDF and sample CSV, for the made-up borrower GHA-000000901-0 on 0241234567.

Vistroy checks that the balances add up from one transaction to the next, so an edited statement is usually caught. It also spots the same statement being used for a different Ghana Card.

8. Pilot results

See what the checks caught and what happened to the loans you paid out afterwards. Each check is matched to the loan you reported: by loan_reference if you sent one, otherwise the first loan for the same borrower opened within 30 days.

GET/api/pilot/report?days=90
GET/api/pilot/report.csv?days=90
curl BASE/api/pilot/report?days=90 -H "X-API-Key: vsk_your_institution_key"

You get checks run, decisions, loans declined and not paid out (and how many of those borrowers defaulted at another lender later), loan sizes reduced, default rates by decision, the most common warnings, and money recovered through repayment plans. The CSV has one row per check. A printable version is at /report.

9. Your account and keys

Check your approval status, and manage your keys without contacting Vistroy. You can have up to 3 active keys at a time.

GET/api/account
POST/api/account/keys
POST/api/account/keys/{key_id}/revoke
curl -X POST BASE/api/account/keys \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{ "label": "Production server" }'
  1. Create a new key and put it in your system.
  2. Check that it works. GET /api/account shows when each key was last used.
  3. Turn off the old key. It stops working straight away. You can't turn off your last active key.

10. Staff logins

People at your company sign in to the dashboard with their own email and password. Your systems keep using the API key. Every action is recorded with the name of the person who did it.

RoleCan do
OFFICERRecord consent, read statements, run checks, report loans and update their status, record plan payments
MANAGEREverything an officer can, plus answer disputes, create and cancel repayment plans, upload loan books, reopen closed loans, see the pilot report and staff activity
ADMINEverything, plus add and turn off staff, reset passwords and manage API keys
POST/api/staff/login
curl -X POST BASE/api/staff/login \
  -H "Content-Type: application/json" \
  -d '{ "email": "officer@yourcompany.com", "password": "..." }'

Send the returned token as X-Staff-Token instead of X-API-Key. It lasts 12 hours. After 5 wrong passwords, the email is locked for 15 minutes.

GET/api/staff/users
POST/api/staff/users
POST/api/staff/users/{user_id}
POST/api/staff/users/{user_id}/reset-password
GET/api/activity

New staff get a temporary password, shown once, and must choose their own when they first sign in. The API key can add the first admin, for lenders who signed up before staff logins existed. A company always keeps at least one admin.

11. List and update loans

GET/api/loans?status=LATE&q=LN-00

Your loans, newest change first, with counts by status, the decision from the matching check, and any repayment plan.

POST/api/loans/{loan_reference}/status
curl -X POST BASE/api/loans/LN-2026-0001/status \
  -H "X-API-Key: vsk_your_institution_key" \
  -H "Content-Type: application/json" \
  -d '{ "status": "LATE", "note": "Missed the October payment" }'

Changes only the status. Reopening a Repaid or Settled loan needs a manager. A loan with an active repayment plan closes through the plan, not here.

12. Borrower data rights

Borrowers can download everything Vistroy holds about them from the My Credit File page: loans, phone numbers, consents, checks, statement summaries and who looked at their file. They can also ask for a correction, deletion, or for Vistroy to stop using their data. The Vistroy team answers within 30 days.

A completed deletion removes statement summaries and device records and withdraws all consents. Loan records are kept, so lenders keep a fair history. After consents are withdrawn, checks return 403 until the borrower gives a new consent.

13. Borrower approves on their phone

After you call /api/consents/otp/start, the borrower can read you the SMS code as before, or open the Vistroy USSD or WhatsApp menu and choose 3 Approve a lender. They have 15 minutes. Check the answer with:

curl BASE/api/consents/otp/OTP-1A2B3C4D5E6F \
  -H "X-API-Key: vsk_your_institution_key"

status is WAITING, APPROVED (with the consent to use in your check), DECLINED or EXPIRED. Ask every few seconds while the borrower is with you. The dashboard does this for you.

Decisions and risk flags

DecisionMeaning
APPROVENo serious risk found. Lend up to approved_amount.
MANUAL_REVIEWSomething needs a person to look at it, or lend only the lower approved_amount.
DECLINEAt least one high-severity flag. Don't lend.
FlagSeverityWhat it means
DEFAULT_ON_LINKED_NUMBERHIGHAn unpaid default exists on another phone number linked to this Ghana Card
NEW_SIM_AFTER_DEFAULTHIGHThe applying number first appeared after a default on another number
LOAN_STACKINGHIGH / MEDIUMNew loans from other lenders in the last 7 days
UNAFFORDABLEHIGH / MEDIUMRepayments would take more than 30% of monthly income
DEVICE_SHARED_ACROSS_IDENTITIESHIGH / MEDIUMThe same phone handset is used with several Ghana Cards
APPLICATION_VELOCITYMEDIUMMany applications at other lenders in the last 24 hours
HIGH_ACTIVE_EXPOSUREMEDIUMMany open loans at the same time
MANY_LINKED_NUMBERSMEDIUMAn unusually high number of SIMs used for borrowing
OWNER_REPORTED_FRAUDHIGHThe Ghana Card owner pressed "This wasn't me" on a fraud alert, or reported identity fraud. Confirm identity in person before paying out.
NUMBER_SHARED_ACROSS_IDENTITIESMEDIUMThis phone number was used with another Ghana Card
BROKEN_REPAYMENT_PLANMEDIUMThe borrower broke a repayment plan with a lender
ON_REPAYMENT_PLANLOWThe borrower is paying off an earlier loan through a plan
THIN_MOMO_HISTORYLOWThe mobile money wallet is new
AFFORDABILITY_UNKNOWNLOWNo income information was sent
From a MoMo statement (step 7)
STATEMENT_USED_FOR_OTHER_IDHIGHThe same statement was already used for a different Ghana Card
STATEMENT_NUMBER_NOT_LINKEDMEDIUMThe statement is for a number not linked to this Ghana Card
STATEMENT_BALANCE_MISMATCHMEDIUMBalances don't add up between transactions. The statement may have been edited.
RECENT_INCOME_SPIKEMEDIUMIncome in the last 30 days is much higher than before, as if money was moved in to look better
GAMBLING_SPENDMEDIUM / LOWPayments to betting companies. MEDIUM when 10% of income or more.
LOAN_PAYOUTS_FROM_OTHER_APPSMEDIUM / LOWLoans received from other apps. MEDIUM when 3 or more in the last 30 days.
IRREGULAR_INCOMELOWMonthly income goes up and down a lot
SHORT_STATEMENTLOWLess than 3 months of transactions
STATEMENT_OUT_OF_DATELOWThe latest transaction is more than 45 days old

Errors

Errors return a JSON body with a plain-language detail you can show to your staff.

CodeMeaningWhat to do
401Missing, wrong, turned-off or deactivated API key, or an expired staff loginCheck the X-API-Key header, or sign in again
403Your account is still waiting for approval, your staff role can't do this, or no valid consent for this borrower (missing, expired, withdrawn, or given to another lender)Get a new consent from the borrower
404Borrower, loan or plan not found in your recordsCheck the reference
409Conflict, e.g. a payment reference already recorded, or a loan already closedDon't retry the same request
410The SMS code has expiredSend a new code
422A field is missing or in the wrong formatRead detail and fix the request
429Too many requests, e.g. too many SMS codes to one numberWait and try again later
502 / 503The SMS provider could not be reached or is not set upTry again. Contact Vistroy if it continues.

Test data (sandbox)

These test borrowers exist in the sandbox so you can see each outcome. They are not real people.

Ghana CardApply withExpected result
GHA-000000101-00201110101DECLINE: unpaid default on another SIM, new SIM after default
GHA-000000102-00541110102, inflow 2500DECLINE: loan stacking across lenders
GHA-000000103-00551110103, amount 3000, inflow 2000MANUAL_REVIEW: good history, lend up to GHS 600
GHA-000000104-00241110104DECLINE: defaulted loan at another lender, now on a repayment plan (1 of 3 paid)

Data rules

  • Only check a borrower who has given consent, and only for the purpose they agreed to.
  • Every check and every loan update appears in the borrower's own activity log, with your institution's name.
  • Borrowers get an SMS fraud alert when you check their file (at most one per lender per day) and when a new phone number is linked to their Ghana Card. If they press "This wasn't me", your permissions for that borrower are withdrawn and the file shows OWNER_REPORTED_FRAUD to every lender until the report is closed.
  • Phone numbers of other lenders' customers are always masked (024****104).
  • Report accurately and on time. Correct a wrong record as soon as you know about it.
  • Answer borrower disputes within 30 days.

Questions: contact the Vistroy team. To get a key, apply for an account. The full list of endpoints and fields is in the interactive API reference.