Features Pricing About Us Developers
API Documentation

Connect your own systems to pawa Loans.

For accounting software, a mobile app, or another internal system that needs to read member/loan/savings/share data or record a loan repayment, this is the real reference, published here so you can evaluate it before you sign up. There's no separate sandbox or API-only account: a token is generated from inside a real business account.

Already a customer?
Generate a token from Settings > API Tokens inside your account, copy it immediately, it's shown only once.

Authentication

Bearer-token auth. A token isn't a standalone API key, it is a staff user. Every request runs with that user's own role and branch access, exactly as if they were using the app themselves.

Authorization: Bearer <your token> Accept: application/json

Base URL: https://pawaloans.openteq.co.ke/api/v1

Record a loan repayment

1
Identify the loan, you don't need our internal id

A payroll/HR system usually only knows the borrower by their own National ID, never our internal loan id, so the repayment endpoint accepts that directly, alongside three other identifiers. Send exactly one:

FieldUse when
id_numberYou only know the borrower's National ID (the natural key for an external HR/payroll system, same as Paylist HR's own API).
member_numberYou have our member reference instead.
loan_numberYou have our human-readable loan reference (e.g. L00001).
loan_idYou already looked it up via GET /loans.

id_number/member_number resolve to that member's active/overdue loan. More than one? This never guesses, it 422s with every candidate listed:

{ "message": "This member has more than one active loan, include loan_number to specify which one.", "loans": [ { "loan_number": "L00001", "status": "active", "total_balance": 8500 }, { "loan_number": "L00002", "status": "overdue", "total_balance": 15750 } ] }

Prefer to check first? total_balance tells you what's actually still owed:

GET /api/v1/loans?id_number=12345678
2
Record the repayment

Call this once the payment has already happened on your side, it records a repayment that occurred, it does not itself collect one. The loan must be active or overdue.

POST /api/v1/repayments
FieldRequiredNotes
loan_id / loan_number / id_number / member_numberExactly oneSee above.
amountYesKES, > 0. May be less than, equal to, or more than the balance.
payment_dateYesYYYY-MM-DD
payment_methodYescash | mpesa | bank | other
reference_numberNoYour own transaction reference.
phone_numberNoPayer's phone.
notesNoFree text, max 1000 chars.
{ "id_number": "12345678", "amount": 5000, "payment_date": "2026-08-12", "payment_method": "mpesa", "reference_number": "QGH7XYZ001" }
201 CREATED
{ "data": { "id": 42, "receipt_number": "RCT000042", "loan_id": 1, "amount": 5000, "principal_portion": 4200, "interest_portion": 800, "penalty_portion": 0, "excess_amount": 0, "payment_date": "2026-08-12", "payment_method": "mpesa", "reference_number": "QGH7XYZ001" } }

The member is notified automatically (SMS/email/WhatsApp, per their own preference) the moment this call succeeds.

Multi-branch behavior

There's no separate "branch-restricted API key" concept, a token's branch access is exactly whatever the user it belongs to already has. If the issuing user is branch-restricted:

EndpointBehavior
GET list endpointsFiltered to that user's own branch(es) only.
GET one record by idA record outside that branch returns 404, indistinguishable from "doesn't exist."
POST /repaymentsThe target loan must be within the token's branch access, or 404 before anything is recorded.

All endpoints

MethodEndpointDescription
GET/membersList members (paginated, 20/page)
GET/members/{id}Get one member
GET/loansList loans
GET/loans/{id}Get one loan
GET/savings-accountsList savings accounts
GET/savings-accounts/{id}Get one savings account
GET/share-accountsList share accounts
GET/share-accounts/{id}Get one share account
GET/branchesList branches
GET/repaymentsList repayments, optional ?loan_id=
POST/repaymentsRecord a repayment (see above)

Errors

HTTPMeaningFix
401Missing or invalid tokenCheck the Authorization: Bearer header.
403Token's user has no role, no business, or an inactive subscriptionIssue a token from a real staff user with an active subscription.
404Doesn't exist, or exists outside the token's branch accessCheck the id and the issuing user's branch access.
422Validation failed, or the loan isn't open for repaymentSee message/errors in the response.
429Rate limit exceeded60 requests/minute per token, back off and retry.
Postman collection
Every endpoint above, ready to import, base_url is pre-filled to https://pawaloans.openteq.co.ke/api/v1, just set your token after importing. Download the collection →

Employer payroll check-off integration

If your organization's staff are members of a SACCO on pawa Loans and repay loans through payroll deduction, your own payroll system can push deduction data directly, no separate account, no polling. Unlike the token-based API above, this isn't per-business, it's per-employer: each connected employer gets its own webhook URL and secret, generated from inside the SACCO's account (Check-off Centre > Employers), never shared across employers or businesses.

1
Get your webhook URL and secret

Ask the SACCO to connect your organization as an employer with an API connection method (Check-off Centre > Employers > Connect Employer). They'll generate a secret and share your webhook URL with you, shown to them only once, so ask them to send it securely.

2
Push each payroll run's deductions
POST /sacco/webhooks/checkoff/<employer_id>/deductions Content-Type: application/json X-Checkoff-Secret: <your secret>

Identify each employee by their National ID, same natural key most payroll systems already use. loan_reference is optional, only needed to disambiguate an employee who has more than one loan with the SACCO at once.

{ "period_month": "2026-08", "deductions": [ { "id_number": "12345678", "amount": 8500, "loan_reference": "LN00220", "reference": "PR-2026-08-001" }, { "id_number": "23456789", "amount": 3000 } ] }

The batch for that period is created automatically if it doesn't exist yet. Every deduction is reconciled against the SACCO's own loan schedule, deducted from a payslip is never treated as money the SACCO has actually received until it's separately confirmed as remitted, so this call reports what payroll withheld, it doesn't itself move money.

HTTPMeaning
200Processed - check matched/unmatched/ambiguous counts in the response, a 200 doesn't mean every line matched.
401Missing or wrong X-Checkoff-Secret, or this employer isn't set up for API connection.
422Malformed payload, see message/errors.
Postman collection (employer webhook)
The one request above, ready to import - set employer_id and checkoff_webhook_secret after importing. Download the collection →
Not a customer yet?
Get started, then generate a token from Settings once you're in.