Ntlo: connecting property operations from lease to payment
A product engineering case study in project leadership, company-specific access, and consistent operational rules.
Ntlo is a property management platform designed for Botswana, bringing leasing, rent collection, maintenance, and resident services into a shared application. Its engineering challenge is coordinating those workflows across companies and user roles while keeping permissions, approvals, and payment status consistent.
My role
I led Ntlo's development, overseeing the whole project, its features, and code implementation. My role connected product requirements with implementation across the property management platform.
The examples below describe engineering choices visible in the codebase under that project oversight.
The product challenge
Property management connects people with different responsibilities. Managers need to track properties, units, and leases. Residents need to understand their rent and report maintenance issues. Contractors need assigned work, while security staff handle visitor access. Company administrators need control over who can see and change information.
The challenge is making those activities agree. A payment should change a rent balance only when it is confirmed. A maintenance request should pass through approval before a contractor is assigned. Each action needs to belong to the correct company and be available to the appropriate role.
Implementation stack
The application uses Next.js 16, React 19, and TypeScript, with Tailwind CSS and shadcn/ui for the interface. Next.js API routes connect to PostgreSQL through Drizzle ORM, with Zod validation and NextAuth authentication.
Supporting services include Redis-backed rate limiting, MinIO object storage accessed through the S3 API, and Resend email delivery. The codebase also contains PayGate payment integration and Docker Compose infrastructure configuration.
Three engineering decisions
1. Resolve company context and permissions for each request
Shared API middleware derives company context from the authenticated session and resolves the company's role permissions. Request-scoped permission handling allows company-specific overrides alongside the built-in role defaults. Individual routes use that context for access checks and record scoping.
This supports multiple property management businesses within one application while allowing their staff responsibilities to differ. The important design distinction is between permission to perform an action and access to the particular company's record; both matter to the workflow.
2. Make rent records reflect confirmed payments
Recurring billing creates rent charges by lease and billing period. A database uniqueness constraint prevents duplicate charges for the same lease and period when a billing job is retried or runs concurrently. Due dates account for shorter months when a lease starts near month-end.
Payment reconciliation adds together confirmed payments against a charge. For example, three confirmed payments of 3,000 can settle a 9,000 charge, even though no individual payment covers the full amount. Pending, failed, and reversed payments are excluded from that total.
The PayGate notification handler verifies the callback checksum before updating payment status and reconciling the associated rent charge. This connects the payment integration to the operational record: starting a payment does not itself settle rent.
3. Require maintenance approval before assignment
The maintenance update route rejects contractor assignment until a request has already reached an approved or later working state. Approving and assigning in the same request is also rejected, preserving a separate review step.
The implementation records who approved the request and when, advances an approved request to assigned when a contractor is selected, and logs updates for audit. These rules make the handoff between review and execution explicit in the application.
Operating the integrations
Payments, SMS, and accounting share a rule for selecting live or simulated operation. Credentials do not silently determine the mode: an integration configured as live must report missing credentials instead of quietly simulating success.
This makes configuration failures visible and keeps a successful demonstration distinct from a completed external operation.
Evidence and outcomes
The codebase includes automated tests for payment reconciliation, billing, maintenance approvals, company isolation, and integration modes. These provide examples of the business rules the project aims to preserve as it evolves; they were inspected, but not executed as part of this case-study review.
Ntlo demonstrates my experience overseeing a product whose interfaces, data model, and operational rules need to work together. The implementation supports a concrete engineering account; adoption, live transaction volumes, and improvements in collection rates or maintenance turnaround have not been verified for this case study.