Inspiration

Last year, during my industrial training as a mechanical engineering student, I worked at a Toyota-franchise auto workshop. Every vehicle that came in got written down by hand, in a ledger.

When a vehicle was getting close to its next service, someone had to dig through that ledger and start calling customers one by one — 15 days before it's due, then 10, then 5. That's the actual standard these workshops follow. It was repetitive, slow, easy to lose track of, and it only got harder as the number of vehicles grew.

When I found the CALL-E hackathon, it clicked immediately — this was a process I'd watched happen with my own eyes, and it was exactly the kind of thing that should be automated. So I built OdoSync to do that job: track service cycles, and actually call vehicle owners before their next service is due.

What it does

OdoSync is a full-stack vehicle service reminder platform, powered by real AI phone calls.

A workshop can:

  • Register vehicles and their owners
  • Record the last service date and mileage
  • Set each owner's preferred time to be called
  • Automatically schedule reminders for 15, 10, and 5 days before the next service
  • Have CALL-E place the actual reminder call
  • See whether the customer booked, declined, asked for a callback, or didn't answer
  • Review pending reminders and past call outcomes
  • Close out a completed service, which rolls straight into the next cycle
  • Update the workshop's own details — name, address, hours — and have every future call use the new info right away, no restart needed The dashboard pulls all of this together: what's due today, how many calls turned into bookings, and what still needs a human to look at.

How I built it

OdoSync has its own frontend, backend, database, scheduler, and CALL-E integration — all wired together, not just sitting side by side.

The frontend is Next.js, React, TypeScript, and Tailwind, talking to the backend through a typed REST client. The backend is Node.js, Express, and TypeScript, with Prisma connecting to PostgreSQL for vehicles, reminder jobs, workshop settings, and call results.

A node-cron scheduler checks for due reminders and respects each owner's preferred calling window before firing anything. That same scheduling logic can also be triggered manually, which is what I use during demos.

Each reminder call is built as a stage-specific task for CALL-E:

  • 15 days out: a friendly, low-pressure heads-up
  • 10 days out: a nudge to actually pick an appointment
  • 5 days out: a more direct, final reminder CALL-E hands back a structured result — outcome, proposed appointment date, notes — which OdoSync normalizes and stores, and that's what shows up in the Activity screen.

It runs in both live and dry-run mode. Dry-run builds the exact same task and payload CALL-E would receive, but simulates the result instead of actually dialing — so the whole flow is testable without calling anyone. Seeded demo data uses placeholder numbers on purpose; I only ever tested live calls against numbers I actually had permission to call.

Frontend's on Vercel, backend and Postgres are on Railway.

Challenges I ran into

The trickiest part was making sure a scheduled reminder couldn't get processed twice — once by the automatic scheduler, once by a manual trigger. I fixed this by having both paths call the exact same internal scheduling function, so there's only ever one place that logic actually lives.

Turning an actual human workflow into scheduling rules was harder than I expected too — three separate reminder dates, per-owner call windows, keeping stages straight, and cancelling old reminders the moment a service gets closed out so nothing stacks up.

Getting real calls working reliably took some real debugging. Nigerian local numbers have to be normalized into E.164 format, and I found out the hard way that CALL-E also needs explicit Nigerian region and locale metadata — without it, a call can come back as "completed" on CALL-E's end without the phone ever actually ringing.

I also learned I needed to separate "the conversation didn't go anywhere" from "something failed on the provider's end" — so OdoSync stores the normalized outcome alongside the raw CALL-E/provider details, which makes it possible to actually tell those two things apart instead of lumping every failed attempt into "no answer."

And since I didn't want judges (or anyone else) to have to actually receive a phone call just to see the product work, I built dry-run mode to exercise the entire pipeline — scheduler, task-building, database, activity log — without ever placing a real call unless someone chooses to.

What I learned

This was less about building screens and more about building a workflow that actually runs on its own. Along the way I picked up:

  • How to wire an AI calling API into a real TypeScript backend
  • How to structure AI call tasks and their outcomes so they're actually useful data, not just text
  • Normalizing phone numbers for international calling
  • Building scheduled background jobs that respect time windows
  • Modeling a recurring service cycle in Prisma and Postgres
  • Keeping provider-level diagnostic detail instead of throwing it away
  • Deploying independent frontend, backend, and database services that actually talk to each other in production
  • Why a safe dry-run mode matters for anything with real-world side effects The biggest thing I took away: automating something isn't just "trigger an action." A system that's actually reliable has to schedule things correctly, avoid duplicates, capture what happened, surface failures honestly, and know how to safely start the next cycle.

Accomplishments I'm proud of

The whole loop actually works, start to finish:

  1. A vehicle gets registered on the frontend
  2. The API stores it in Postgres
  3. Reminder jobs get created for the right dates
  4. The scheduler catches when one's due
  5. CALL-E places (or simulates) the call
  6. The outcome gets stored
  7. It shows up in Activity
  8. Closing out the service rolls it into the next cycle None of this is hardcoded for a demo, either — workshop name, address, hours, service info, and callback number all live in Postgres and flow into the next CALL-E call without touching the backend or restarting anything.

There's also real test coverage — Nigerian phone routing and normalization, CALL-E task construction, stage-specific call behavior, and live database-backed workshop settings are all tested, not just eyeballed.

What's next for OdoSync

Next up is authentication and role-based access, so different people at a workshop can have appropriate access.

Beyond that, I'd like to add:

  • SMS and WhatsApp fallback after an unanswered call
  • Calendar integration for confirmed appointments
  • Customer-facing confirmation and rescheduling links
  • Support for multiple workshop branches
  • Fleet-manager accounts
  • Retry policies for calls that didn't land
  • Deeper operational analytics
  • Broader browser and API test coverage
  • Audit logs and protected admin actions Long-term, I'd like OdoSync to grow into a full service-retention platform — something that helps workshops actually keep in touch with their customers, instead of losing them to a missed reminder call.

Built With

Share this project:

Updates

Submission history