Skip to Content
Parent Loans APILoan Application

Loan Application

Submit a loan after a completed credit assessment, accept terms, set up the repayment mandate, or apply for multiple children at once.

Unlike the Partner API, these endpoints use EdPay’s own schoolId / studentId GUIDs, not your externalSchoolId / externalStudentId. Your UI typically gets these from EdPay’s own school/student selection screens (or from a prior lookup) rather than from your own system’s ids directly.

Submit loan application

POST /api/loans

One-shot submit. Same end state as the internal loan-draft submit.

Documents (product-gated)

Upload files via Utils → Upload file (POST /api/uploads), then pass the returned S3 key strings:

ProductRequired keys
K-12 (partner / booked school)utilityBill + idCard
Exam (ProfessionalBody)examInvoice, utilityBill, bankStatement, idCard, payslips, not admissionLetter
Graduate (Institution)same as Exam plus admissionLetter

otherDocuments is always optional.

Headers

HeaderValue
Acceptapplication/json
AuthorizationBearer {parentAccessToken}
Content-Typeapplication/json

Request body

FieldDescription
schoolId / studentIdEdPay GUIDs for the school and student.
creditAssessmentIdOptional, from a Completed credit assessment. Omit to submit into manual review.
loanTypeTermly or Session.
terme.g. "FirstTerm".
academicSessione.g. "2025/2026".
tenureOneMonthSixteenMonths.
utilityBill / idCardS3 keys from Upload file, required for K-12.
requestedAmountOptional, partial K-12 fees need at least 40% of the catalog amount; institution fees can request up to maxCustomAmount. Omit (null) to request the full catalog amount.
nextOfKinname, phone, relationship, occupation.

Sample below is K-12 (typical partner school):

{ "schoolId": "00000000-0000-0000-0000-000000000010", "studentId": "00000000-0000-0000-0000-000000000011", "creditAssessmentId": "{{assessmentId}}", "loanType": "Termly", "term": "FirstTerm", "academicSession": "2025/2026", "tenure": "SixMonths", "utilityBill": "{{utilityBillKey}}", "idCard": "{{idCardKey}}", "requestedAmount": null, "nextOfKin": { "name": "Ngozi Eze", "phone": "08012345678", "relationship": "Spouse", "occupation": "Teacher" } }

Response 201

{ "success": true, "message": "Resource created successfully", "data": { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "applicationNumber": "LN-20250901-0001", "schoolName": "Greenfield Academy", "studentName": "Ada Eze", "className": "JSS 2", "academicSession": "2025/2026", "loanType": "Termly", "term": "FirstTerm", "tuitionAmount": 250000.00, "discountAmount": 0, "loanAmount": 250000.00, "isCustomAmount": false, "interestRate": 12.5, "totalRepayableAmount": 281250.00, "monthlyRepaymentAmount": 46875.00, "tenure": "SixMonths", "tenureMonths": 6, "status": "Submitted", "termsAccepted": false, "isMandateSetup": false, "isDisbursed": false, "outstandingBalance": 281250.00, "createdAt": "2025-09-01T10:30:00Z", "monthlyInterestRatePercent": 2.08, "totalInterestAmount": 31250.00, "insuranceFeePercent": 0, "insuranceFeeAmount": 0, "firstInstallmentAmount": 46875.00 }, "errors": [], "statusCode": 201 }
FieldDescription
idThis is {loanId} in every other endpoint on this page.
applicationNumberHuman-readable reference.
statusStarts at Submitted, see Key enums for the full list.
termsAccepted / isMandateSetup / isDisbursedFlip to true as the parent completes Accept terms and Setup repayment mandate, and once EdPay disburses.
totalRepayableAmount / monthlyRepaymentAmount / outstandingBalanceThe financing breakdown, interest included.

Try it

POST/api/loans

Accept terms

POST /api/loans/{loanId}/accept-terms

Parent accepts the loan’s terms and conditions. Required before mandate setup when status is Submitted.

Headers

HeaderValue
Acceptapplication/json
AuthorizationBearer {parentAccessToken}
Content-Typeapplication/json

Request body

Empty, this is a confirmation call.

{}

Response 200

{ "success": true, "message": "Request successful", "data": "Terms and conditions accepted.", "errors": [], "statusCode": 200 }

Note data here is a plain string, not an object, unlike most other endpoints on this page.

Try it

POST/api/loans/00000000-0000-0000-0000-000000000001/accept-terms

Setup repayment mandate

POST /api/loans/{loanId}/setup-mandate

Initialize the direct-debit mandate. Body needs parent address (saved on the profile if empty). Bank account is chosen during Mono authorization, do not send accountNumber / bankCode.

Response includes authorizationUrl, redirect the parent there, then poll Verify mandate.

Headers

HeaderValue
Acceptapplication/json
AuthorizationBearer {parentAccessToken}
Content-Typeapplication/json

Request body

FieldDescription
loanApplicationIdSame as {loanId} in the path.
addressParent address, saved on profile if empty.
{ "loanApplicationId": "{{loanId}}", "address": "12 Example Street, Ikeja, Lagos" }

Response 200

{ "success": true, "message": "Request successful", "data": { "mandateId": "mono_mandate_abc123", "authorizationUrl": "https://connect.withmono.com/mandate/authorize/xyz789", "message": "Mandate initiated. Please complete authorization via the provided URL." }, "errors": [], "statusCode": 200 }
FieldDescription
authorizationUrlRedirect the parent here to complete bank authorization.
mandateIdReference for the mandate provider.

Try it

POST/api/loans/00000000-0000-0000-0000-000000000001/setup-mandate

Verify mandate

GET /api/loans/mandates/verify?reference={loanId}

Poll after the Mono mandate redirect (?reference=...&status=...). Confirms mandate activation on the loan.

Headers

HeaderValue
Acceptapplication/json
AuthorizationBearer {parentAccessToken}

Query

ParamDescription
referenceUsually the {loanId} from setup.

Try it

GET/api/loans/mandates/verify

Submit batch loan (multi-child)

POST /api/loans/batch

K-12 multi-child only. Same docs as single K-12 application: utilityBill + idCard from Upload file, applied to every loan in the batch.

Headers

HeaderValue
Acceptapplication/json
AuthorizationBearer {parentAccessToken}
Content-Typeapplication/json

Request body

FieldDescription
schoolIdEdPay GUID, same school for every child in the batch.
creditAssessmentIdFrom a Completed credit assessment (optional, omit for manual review).
loanType / term / academicSession / tenureSame meaning as Submit loan application.
utilityBill / idCardS3 keys from Upload file.
studentsArray of { studentId, requestedAmount }.
nextOfKinOptional, same shape as Submit loan application, or null.
{ "schoolId": "00000000-0000-0000-0000-000000000010", "creditAssessmentId": "{{assessmentId}}", "loanType": "Termly", "term": "FirstTerm", "academicSession": "2025/2026", "tenure": "SixMonths", "utilityBill": "{{utilityBillKey}}", "idCard": "{{idCardKey}}", "students": [ { "studentId": "00000000-0000-0000-0000-000000000011", "requestedAmount": null }, { "studentId": "00000000-0000-0000-0000-000000000012", "requestedAmount": 120000 } ], "nextOfKin": null }

Response 201

{ "success": true, "message": "Resource created successfully", "data": { "batchId": "b8e3f1a2-4c5d-6e7f-8a9b-0c1d2e3f4a5b", "batchReference": "BATCH-20250901-0001", "schoolName": "Greenfield Academy", "totalLoanAmount": 470000.00, "totalCatalogAmount": 500000.00, "studentCount": 2, "loans": [ { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "applicationNumber": "LN-20250901-0001", "schoolName": "Greenfield Academy", "studentName": "Ada Eze", "className": "JSS 2", "academicSession": "2025/2026", "loanType": "Termly", "term": "FirstTerm", "tuitionAmount": 250000.00, "discountAmount": 0, "loanAmount": 250000.00, "isCustomAmount": false, "interestRate": 12.5, "totalRepayableAmount": 281250.00, "monthlyRepaymentAmount": 46875.00, "tenure": "SixMonths", "tenureMonths": 6, "status": "Submitted", "termsAccepted": false, "isMandateSetup": false, "isDisbursed": false, "outstandingBalance": 281250.00, "createdAt": "2025-09-01T10:30:00Z", "monthlyInterestRatePercent": 2.08, "totalInterestAmount": 31250.00, "insuranceFeePercent": 0, "insuranceFeeAmount": 0, "firstInstallmentAmount": 46875.00 }, { "id": "c3d4e5f6-a7b8-9012-cdef-345678901234", "applicationNumber": "LN-20250901-0002", "schoolName": "Greenfield Academy", "studentName": "Chidi Eze", "className": "Primary 4", "academicSession": "2025/2026", "loanType": "Termly", "term": "FirstTerm", "tuitionAmount": 250000.00, "discountAmount": 0, "loanAmount": 220000.00, "isCustomAmount": true, "interestRate": 12.5, "totalRepayableAmount": 247500.00, "monthlyRepaymentAmount": 41250.00, "tenure": "SixMonths", "tenureMonths": 6, "status": "Submitted", "termsAccepted": false, "isMandateSetup": false, "isDisbursed": false, "outstandingBalance": 247500.00, "createdAt": "2025-09-01T10:30:00Z", "monthlyInterestRatePercent": 2.08, "totalInterestAmount": 27500.00, "insuranceFeePercent": 0, "insuranceFeeAmount": 0, "firstInstallmentAmount": 41250.00 } ] }, "errors": [], "statusCode": 201 }

Each entry in loans is shaped exactly like Submit loan application’s data, isCustomAmount: true here reflects the second child’s explicit requestedAmount.

Try it

POST/api/loans/batch

Get loan batch

GET /api/loans/batch/{batchId}

Detail for a multi-student batch application.

Headers

HeaderValue
Acceptapplication/json
AuthorizationBearer {parentAccessToken}

Response 200

Same shape as Submit batch loan’s data above.

Try it

GET/api/loans/batch/00000000-0000-0000-0000-000000000003