Hackathon project — built for the Kenya AI Challenge 2026 / Mercy Corps AgriFin.
A decentralized farming fintech platform connecting Kenyan smallholder farmers with lenders, agents, buyers, and chama groups — powered by Neo4j for the knowledge graph and M-Pesa for payments and loan disbursements.
- Tech Stack
- Architecture Overview
- User Roles
- Getting Started
- Environment Variables
- Database
- Scripts
- Seed Data
- Neo4j Verification Commands
- Project Structure
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript |
| Styling | Tailwind CSS + cn utility |
| Database | Neo4j (AuraDB / graph database) |
| Payments | M-Pesa (Daraja API) |
| USSD | Africa's Talking API |
| AI Agents | LangChain agents for farm assessment & pest detection |
| PWA | Service worker with offline support |
| Package Manager | pnpm |
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Farmer │ │ Agent │ │ Buyer │
│ (Mobile) │ │ (Mobile) │ │ (Mobile) │
└──────┬──────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└───────────────────┼─────────────────────┘
│
┌──────▼──────┐
│ Next.js 16 │
│ App Router │
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
┌──────▼─────┐ ┌───▼────┐ ┌────▼─────┐
│ Neo4j │ │M-Pesa │ │ Africa's │
│ AuraDB │ │Daraja │ │ Talking │
└────────────┘ └────────┘ └──────────┘
| Role | Description | Key Pages |
|---|---|---|
| Farmer | Registers farm profile, applies for loans, views risk assessments, lists produce, joins chamas | /chat, /loans, /marketplace/my-listings, /chama, /dashboard |
| Agent | Verifies farmers in the field, schedules visits, verifies produce quality | /agent/verify, /agent/schedule, /agent/listings |
| Lender | Creates loan products, reviews applications, manages portfolio | /lender/products, /lender, /lender/portfolio |
| Buyer | Browses verified produce listings, places orders | /buyer |
| Admin | Manages users, views analytics, system settings | /admin, /admin/analytics, /admin/users |
# Install dependencies
pnpm install
# Start development server
pnpm devOpen http://localhost:3000.
pnpm build
pnpm startCopy .env.example to .env.local and configure:
| Variable | Description |
|---|---|
NEO4J_URI |
Neo4j AuraDB or local instance URI |
NEO4J_USERNAME |
Neo4j database username |
NEO4J_PASSWORD |
Neo4j database password |
MPESA_CONSUMER_KEY |
M-Pesa Daraja API consumer key |
MPESA_CONSUMER_SECRET |
M-Pesa Daraja API consumer secret |
MPESA_PASSKEY |
M-Pesa Daraja API passkey |
APP_URL |
Public URL of the app (e.g. https://kilimo-ai.vercel.app) — used in password reset & verification email links |
AT_API_KEY |
Africa's Talking API key (USSD) |
AT_USERNAME |
Africa's Talking username |
AT_SHORTCODE |
Africa's Talking shortcode (*384*53298#) |
The application uses Neo4j as its primary database. The graph model centers around User nodes connected to loans, crops, counties, chamas, and marketplace listings through typed relationships.
(User)-[:HAS_ROLE]->(Role)
(User)-[:LOCATED_IN]->(County)
(User)-[:GROWS]->(Crop)
(User)-[:APPLIED_FOR]->(LoanApplication)
(User)-[:RECEIVED_LOAN]->(ActiveLoan)
(User)-[:MEMBER_OF]->(ChamaGroup)
(User)-[:HAS_LISTING]->(MarketListing)
(User)-[:SUPERVISES]->(User) // agent → farmer
(Lender)-[:DISBURSED]->(ActiveLoan)
(Agent)-[:VERIFIED]->(MarketListing)
(ActiveLoan)-[:HAS_PAYMENT]->(PaymentRecord)
| Command | Description |
|---|---|
pnpm dev |
Start development server |
pnpm build |
Production build with PWA precache generation |
pnpm start |
Start production server |
pnpm seed |
Seed the Neo4j database with demo data |
pnpm lint |
Run ESLint |
The USSD endpoint is at /api/ussd and supports both:
- POST — Africa's Talking form-encoded requests (production)
- GET — Query parameter testing for local development
Shortcode: *384*53298#
GET /api/ussd?phone=2547XXXXXXXX&sessionId=test123&text=1
pnpm seedCreates the following demo dataset in Neo4j:
- 5 users — one per role (farmer, agent, lender, buyer, admin)
- Loan products — 6 products (e.g., M-Pesa Farm Boost, Equity Agro Loan)
- Loan applications — 6 applications with varying statuses
- Active loans — approved loans with repayment tracking
- Chama groups — 4 groups with members and savings
- Market listings — 8 produce listings (maize, beans, tea, etc.)
- Orders — 2 sample orders (delivered, confirmed)
- Notifications — sample notifications for testing
- Visit schedules — agent field visit records
The following Cypher queries can be executed in Neo4j Browser or Neo4j Workspace to verify that the Kilimo AI graph database has been seeded correctly and that all major relationships are present. These queries were also used during the Neo4j Technical Proof Video submitted for the hackathon.
Displays every node label and the total number of nodes for each type.
MATCH (n)
RETURN labels(n) AS Label, count(n) AS Total
ORDER BY Total DESC;Expected node types include:
- User
- Role
- County
- Crop
- LoanProduct
- LoanApplication
- ActiveLoan
- ChamaGroup
- MarketListing
- Order
- VisitSchedule
- Notification
- PaymentRecord
Displays the complete graph with nodes and relationships.
MATCH (n)
OPTIONAL MATCH (n)-[r]->(m)
RETURN n,r,m
LIMIT 300;Purpose:
- Verify graph connectivity.
- Demonstrate how farmers, crops, loans, counties, agents, lenders, and chamas are interconnected.
Shows each farmer together with their county, crops, and agent assignments.
MATCH (u:User {role: 'farmer'})
OPTIONAL MATCH (u)-[:LOCATED_IN]->(co:County)
OPTIONAL MATCH (u)-[:GROWS]->(c:Crop)
OPTIONAL MATCH (a:User)-[:SUPERVISES]->(u)
RETURN u.name AS Farmer,
co.name AS County,
collect(DISTINCT c.name) AS Crops,
a.name AS AssignedAgent
ORDER BY Farmer;Shows all loan applications and approved loans with their current status.
MATCH (u:User)-[:APPLIED_FOR]->(la:LoanApplication)
OPTIONAL MATCH (u)-[:RECEIVED_LOAN]->(al:ActiveLoan)
RETURN u.name AS Farmer,
la.amount AS AppliedAmount,
la.status AS ApplicationStatus,
la.date AS ApplicationDate,
al.amount AS ApprovedAmount,
al.remainingBalance AS RemainingBalance,
al.status AS LoanStatus
ORDER BY la.date DESC;Shows all scheduled field visits and their current status.
MATCH (a:User {role: 'agent'})-[:SUPERVISES]->(f:User)
OPTIONAL MATCH (vs:VisitSchedule {agentId: a.id})
RETURN a.name AS Agent,
f.name AS Farmer,
vs.date AS VisitDate,
vs.time AS VisitTime,
vs.status AS VisitStatus
ORDER BY vs.date DESC;Shows all chama groups and their members.
MATCH (cg:ChamaGroup)
OPTIONAL MATCH (u:User)-[:MEMBER_OF]->(cg)
RETURN cg.name AS ChamaName,
cg.totalSavings AS TotalSavings,
collect(u.name) AS Members
ORDER BY cg.totalSavings DESC;Shows all produce listings and any orders placed.
MATCH (ml:MarketListing)
OPTIONAL MATCH (o:Order {listingId: ml.id})
RETURN ml.crop AS Crop,
ml.quantity AS Quantity,
ml.pricePerUnit AS PricePerUnit,
ml.verificationStatus AS VerificationStatus,
ml.seller AS Seller,
o.status AS OrderStatus,
o.buyerName AS Buyer
ORDER BY ml.date DESC;Shows all M-Pesa payment transactions against active loans.
MATCH (al:ActiveLoan)-[:HAS_PAYMENT]->(pr:PaymentRecord)
RETURN pr.id AS PaymentID,
pr.amount AS Amount,
pr.mpesaRef AS MpesaRef,
pr.date AS PaymentDate,
al.remainingBalance AS RemainingBalance
ORDER BY pr.date DESC;Counts every relationship type in the graph.
MATCH ()-[r]->()
RETURN type(r) AS Relationship, count(r) AS Total
ORDER BY Total DESC;Shows all notifications sent to users.
MATCH (n:Notification)
RETURN n.userId AS UserID,
n.type AS Type,
n.title AS Title,
n.read AS Read
ORDER BY n.createdAt DESC
LIMIT 50;kilimo-ai/
├── app/
│ ├── (app)/ # Authenticated app routes
│ │ ├── admin/ # Admin dashboard, analytics, users, settings
│ │ ├── agent/ # Agent verify, schedule, flagged, listings
│ │ ├── buyer/ # Buyer marketplace
│ │ ├── lender/ # Lender dashboard, portfolio, products
│ │ ├── chat/ # AI assessment chat
│ │ ├── chatbot/ # AI assistant
│ │ ├── chama/ # Chama groups
│ │ ├── dashboard/ # Farmer dashboard
│ │ ├── loans/ # Loan products & applications
│ │ ├── marketplace/ # Farmer yield listings
│ │ └── profile/ # User profile
│ ├── (marketing)/ # Public marketing pages
│ ├── api/ # API routes (Neo4j queries, auth, payments)
│ └── auth/ # Login, signup, password reset, email verify
├── components/
│ ├── shared/ # Reusable UI components
│ ├── marketing/ # Landing page components
│ ├── chat/ # Chat interface components
│ └── ui/ # Base UI primitives (shadcn-style)
├── lib/
│ ├── types.ts # TypeScript interfaces
│ ├── constants.ts # UI text, crop list, counties
│ ├── i18n.ts # Internationalization
│ ├── neo4j.ts # Neo4j driver singleton
│ ├── auth.ts # Client-side auth helpers
│ └── utils.ts # Utility functions
├── public/
│ ├── icons/ # PWA icons
│ ├── manifest.webmanifest
│ └── offline.html # Offline fallback page
├── scripts/
│ ├── seed.cypher # Neo4j seed data (Cypher)
│ ├── clear.cypher # Database clear script
│ └── generate-precache.js # PWA precache manifest
├── proxy.ts # Route protection middleware
└── sw.ts # Service worker
Kilimo AI uses Featherless AI via its REST API (api.featherless.ai/v1) to power two core features: Farm Risk Assessment and Pest Detection.
| Aspect | Details |
|---|---|
| Model | deepseek-ai/DeepSeek-V4-Pro (open-weight, served through Featherless) |
| What it does | Acts as a financial advisor for smallholder farmers. In "assessment" mode, it evaluates whether a farmer should take an agricultural loan and recommends a safe amount. In "general" mode, it answers any farming-related questions (crop management, weather, market prices). |
| Input sent to Featherless | A structured POST /v1/chat/completions request containing: a system prompt (bilingual English/Swahili instructions with the assessment rubric), a farmer profile block (name, county, crops, acreage, rental costs, phone), the conversation history, and a Neo4j-derived context block (pest/disease data for the farmer's crops + recent weather data for their county). |
| Output from Featherless | A standard OpenAI-compatible chat completion response ({ choices: [{ message: { content: "..." } }] }) containing a natural-language assessment in English or Swahili, or a farming advice answer. |
| How it appears in the product | The response is displayed in the chat interface at /chat (assessment mode) or /chatbot (general mode). Assessment responses include a risk level badge (LOW/MEDIUM/HIGH/UNKNOWN) rendered from the model's output, and the conversation is persisted in localStorage so farmers can revisit their assessment history on the dashboard. |
| Aspect | Details |
|---|---|
| Model | meta-llama/Llama-3.2-11B-Vision-Instruct (open-weight vision model, served through Featherless) |
| What it does | Identifies crop pests and diseases from farmer-submitted photos. Returns the pest name, confidence score, severity level, and localized treatment recommendations using solutions available in East Africa. |
| Input sent to Featherless | A structured POST /v1/chat/completions request containing: a system prompt instructing the model to act as a specialized agricultural pest expert and return structured JSON, and a multimodal user message with the text "Identify the pest or disease affecting this crop." plus the uploaded image as a base64 data URL (image_url). |
| Output from Featherless | A JSON object parsed from the model's response: { pest, confidence, recommendation, severity, isPest, commonName, scientificName, affectedCrops }. |
| How it appears in the product | The parsed result renders a pest detection card on the /pest-check page: pest name with confidence percentage, severity badge (color-coded LOW/MEDIUM/HIGH), treatment recommendations, scientific name where available, and an icon indicating whether it is an insect pest or plant disease. The scan is also saved to Neo4j as a PestScan node linked to the matching Pest/Disease and Crop nodes for future reference. |
Both integrations are configured via environment variables:
| Variable | Default | Used By |
|---|---|---|
FEATHERLESS_API_KEY |
— | Chat + Pest Detection |
FEATHERLESS_BASE_URL |
https://api.featherless.ai/v1 |
Chat + Pest Detection |
FEATHERLESS_MODEL |
deepseek-ai/DeepSeek-V4-Pro |
Chat |
FEATHERLESS_VISION_MODEL |
meta-llama/Llama-3.2-11B-Vision-Instruct |
Pest Detection |
This project was developed for hackathon purposes. License rights belong to the hackathon organizing body until updated otherwise!