Inspiration

Cocoa farming employs millions of smallholder farmers across Cameroon and the wider CEMAC region, yet the data that drives pricing, logistics planning, and cooperative decisions is often collected manually — weeks late, incomplete, or not at all. Most of these farmers don't own smartphones and have no realistic way to fill out a digital form or use an app. But almost all of them own a basic phone and can answer a call. We wanted to build something that meets farmers exactly where they already are: a normal phone call, in their own language, asking simple questions — no app to install, no literacy requirement, no smartphone needed. AgroVoice was born from that idea: use AI-powered voice calls (via CALL-E) to have natural conversations with cocoa farmers and turn their spoken answers into clean, structured, analysis-ready data.

What it does

AgroVoice automatically calls cocoa farmers using an AI voice agent that greets them in the local Bulu language ("Mbolo!"), asks three simple questions in plain French — their village, how many bags of cocoa they harvested this week, and any road or logistics difficulties they're facing — and closes the call with another Bulu phrase ("Akiba", meaning "thank you"). Instead of leaving the AI's answer as unstructured text to interpret later, AgroVoice defines a strict JSON schema upfront (village, harvest_bags, logistics_issues, call_outcome) and asks CALL-E to extract and validate this structured data natively, live from the real conversation. While our working prototype focuses on cocoa, the schema-driven design means the same engine can be pointed at coffee, cotton, or any other crop simply by changing the questions asked — the underlying "call, extract, structure" pipeline stays identical. The pipeline then: Stores every call's structured result in a SQLite database Exports a clean Excel report (Cocoa_Monitoring_Report.xlsx) with aggregated statistics Supports single-farmer testing, a batch demo list, or bulk calling from a CSV of many farmers Never fabricates data — if extraction genuinely fails for a call, that record is explicitly flagged (NOT_EXTRACTED) rather than silently filled with guessed values

How we built it

The pipeline follows a classic ETL structure adapted to voice AI: Extract: a Python script calls the official CALL-E REST API (POST /v1/calls), passing both the natural-language call task (in French + Bulu, matched to the real farmers we're calling) and a JSON result_schema describing exactly what structured fields we expect back. Transform: CALL-E's own AI performs the phone conversation and extracts the structured result live, validated against our schema — no separate LLM post-processing step needed to "guess" the data from a free-text summary. Load: results are written to SQLite (agrovoice.db), preserving both the clean structured fields and the raw API response for traceability. Analyze/Export: pandas aggregates the data (total bags harvested, farmers contacted, villages covered) and exports it to Excel for cooperative staff who are comfortable with spreadsheets but not with databases. We consulted CALL-E's official developer documentation (docs.heycall-e.com) directly to build against the real REST API and its documented result_schema structured-extraction feature, rather than guessing at response formats.

Challenges we ran into

Our first working prototype technically "worked" — calls went through, data got saved — but a closer look revealed it was quietly recording the same fake values (a hardcoded village name, a hardcoded bag count) on every single call, regardless of what the farmer actually said. That's a serious integrity problem for a data-collection tool, so we rebuilt the transform step around CALL-E's native result_schema extraction instead of trusting a hand-written parser. We also had an early version that displayed a manually copied, static "account balance" instead of a real check — again, technically running, but dishonest about what it actually verified. We removed that entirely and now state plainly when a balance check isn't available via the documented API, rather than faking one. On the infrastructure side, calls occasionally sat in a queued state for one to two minutes under real network/API load before ringing — our first timeout values were too aggressive and cut calls off before they ever connected. We extended the timeout, added exponential patience with reassuring status messages, and used idempotency keys so that a retried request never triggers a duplicate real phone call to the same farmer.

Accomplishments that we're proud of

Getting a fully real, end-to-end voice-AI data pipeline working — a real phone call, answered live, transcribed and understood in French with a local-language greeting, and turned into clean structured JSON we could immediately export to Excel. Our first genuinely successful run extracted a real village name, a real harvest count, and a real, specific logistics complaint about a road that becomes impassable in the rain — exactly the kind of nuanced, real-world detail a hardcoded fallback could never have produced. We're also proud of deliberately keeping the actual phone conversation in French with authentic Bulu greetings, even while translating the rest of the codebase to English for this hackathon submission — the tool has to work for real Cameroonian farmers first, not just look good in a demo.

What we learned

We learned that "the demo works" and "the data is trustworthy" are two very different bars to clear — and that it's easy to accidentally ship the first one while believing you've shipped the second. Reading a service's official API documentation closely (rather than assuming a response shape) paid off directly: CALL-E's native structured-extraction feature let us delete an entire fragile LLM-based data-guessing step we had originally built as a workaround. We also learned to design for honest failure: a data pipeline that clearly flags "we couldn't extract this" is far more useful — and far more trustworthy for a jury or a real cooperative — than one that always looks complete but sometimes lies.

What's next for AgroVoice

Move from the current demo CSV/manual list to real integration with cooperative membership databases, enabling weekly automated calling rounds across hundreds of farmers. Expand beyond cocoa to other crops and value chains: the same call-and-extract pattern applies directly to coffee, cotton, maize, cassava, or livestock — only the questions and the result schema need to change (e.g., swapping "harvest_bags" for "liters of milk" or "kilograms of coffee cherries"). Cameroon alone has large coffee, cotton, and food-crop sectors facing the exact same data-collection gap as cocoa. Add SMS or WhatsApp fallback for farmers who don't answer a call within a few attempts. Close the loop with buyers, using the same calling engine (not yet built — a planned next step): once enough farmers have reported their harvest, AgroVoice could place a second type of outbound call — this time to cooperatives or exporters — to orally relay an aggregated summary ("40 bags available around Zoé Télé this week, contact farmer X"). This deliberately stays inside CALL-E's "make a call, get something done" logic rather than becoming a separate marketplace/payment product, while demonstrating the platform being pushed in a genuinely new direction: the same pipeline both collecting structured data by phone and distributing it by phone. Expand the result schema to capture pricing expectations and input needs (fertilizer, tools), turning AgroVoice into a two-way channel rather than a one-way survey. Generalize internationally: CALL-E already documents support for 17+ regions and languages, including Kenya — the same pipeline could serve coffee growers in Colombia, rice farmers in Vietnam, cashew farmers in Ivory Coast, or disaster-response check-ins in other low-connectivity regions, with only the call script and local-language greeting needing to change. Longer term, we see AgroVoice less as a single cocoa tool and more as a reusable "voice-first data collection engine" for any agricultural (or humanitarian) supply chain where the people holding the answers don't have — and shouldn't need — a smartphone. Add a lightweight dashboard on top of the Excel export for cooperative managers who want a visual, up-to-date view without opening a spreadsheet. ## Feedback for the CALL-E team Building AgroVoice against the real CALL-E REST API surfaced two concrete, hands-on observations we think are worth sharing: No documented balance/credits endpoint. As of our testing, neither the Calls API nor the Goal Runs API documentation exposes a way to programmatically check remaining call credits before launching a batch of calls. We initially worked around this with a manually copied balance from an email notification — which we recognized was dishonest and removed, choosing instead to clearly warn users to check manually. A GET /v1/account/balance-style endpoint would let integrators like us build genuine pre-flight guardrails before bulk-calling operations. queued status can last 1–2+ minutes with no visibility into why. During testing, a call sat in queued for over two minutes before ringing, with no additional field (queue position, estimated wait time, or reason) to distinguish "everything is fine, just busy" from "something is stuck." We had to raise our client-side timeout defensively and add our own reassuring status messages. Exposing even a rough queue_position or estimated_start_at field in the call status response would make it much easier for developers to build a good waiting experience instead of guessing. status: completed reported even when the phone never actually rang. On several test calls to the same verified Cameroonian number, the API returned status: "completed" after ~245 seconds of processing, but with an entirely empty structured result (village: "", harvest_bags: -1, logistics_issues: "", and — notably — call_outcome: "unknown" rather than the no_answer value our own schema explicitly defines for exactly this situation). On the recipient's phone, there was zero trace of any incoming or missed call — not a brief ring, nothing at all. This suggests either a call-delivery issue for this destination that the status field doesn't currently surface (a call that never technically reached the handset is arguably not "completed"), or a gap in the AI's own outcome-classification logic that fails to recognize "no evidence of any conversation" as no_answer. We're happy to share the exact call_ids involved if useful for debugging on your side. We hope these observations — arising from a real integration attempt, not a hypothetical one — are useful input for the roadmap.

Built With

Share this project:

Updates