Inspiration

This came out of a real moment, not a hypothetical. The plaster on the wall of an old house was literally falling off. We had one contact for repairs they didn't pick up. We searched again, called again, waited again. That's when we realized the problem wasn't finding a number it was getting a confirmed human response.

That's the same underlying problem behind every "I need this handled today" moment a burst pipe, an AC failure, a repair that can't wait. The loop is always the same: search, call, wait on hold, get told "fully booked," call again, or get no answer at all. We wanted an AI agent that runs that calling loop itself, in real time, so the person just states what they need and gets a confirmed answer back.

What it does

Ashtrac turns an urgent request into a confirmed real-world response. The core loop is:

Need → Find → Call → Check → Adapt → Confirm

Given a stated need, "the plaster on my wall is falling off, and my usual contact isn't answering," or a plumber, electrician, or AC repair that can't wait, Ashtrac:

Identifies the type of service and urgency Pulls a shortlist of providers Calls Provider 1 via CALL-E, explains the situation, asks for availability, ETA, and price If unavailable or the answer is unclear, adapts and calls the next provider live, without a human re-triggering it Stops as soon as a provider confirms, with structured availability, ETA, and price extracted from that call

The demo moment that shows this off:

Provider 1: "Sorry, we're fully booked today." Ashtrac: Immediately calls Provider 2. Provider 2: "Yes, we can send someone at 4 PM for ₹500." Ashtrac: Returns the structured result and offers to confirm.

That's the moment that separates this from a voice chatbot: the agent doesn't just place a call and read a script; it holds a real conversation, adapts in real time when the first answer is "no," and returns a structured, actionable result. The problem was never making the phone call. The problem is getting the job done after the call, and Ashtrac handles that loop end to end.

How we built it

Ashtrac Dispatch sits on top of CALL-E's one-shot Calls API rather than a pre-published Goal, because every call needs task text specific to that moment and the user's actual situation, not a fixed script. For each provider on the shortlist, we create a call task with task set to a natural-language brief (what the problem is, what we need to know) and a result_schema that constrains the response to exactly what our orchestration logic needs: an availability field (yes/no / unknown), a price field, an eta field, and an evidence field so we can see what in the call actually supports the extracted answer. We set additionalProperties: false and include unknown in every enum, since a real call can end in "no answer" or an ambiguous response, and we don't want the pipeline to treat that as a no silently.

Each request carries a stable Idempotency-Key built from the dispatch request ID plus the provider ID, so a retried request during the demo can't accidentally place a duplicate call. We poll with waitForResult for the terminal state rather than firing every provider in parallel. The API doesn't support cancelling a call once it's in flight, so we dispatch providers as controlled waves: call Provider 1, wait for a terminal result, and only call Provider 2 if the first result is no, unknown, or the call fails. That sequencing is what produces the adaptive "no → immediately calls the next provider" moment in the demo, and it also avoids paying for calls we no longer need once a provider confirms. Once a provider comes back yes, we read structured_result alongside task_completed and completion_confidence before treating it as a real confirmation. A completed call status on its own doesn't mean the provider actually said yes, so we check the business answer, not just the call lifecycle status.

Challenges we ran into

The hardest part wasn't placing the call; it was designing for the "no," and doing it against the API's actual guarantees rather than assumptions. structured_result comes back null whenever CALL-E can't produce a schema-valid extraction, so our orchestration logic has to treat null and an explicit unknown answer the same way as "not a confirmed yes," never as a silent failure to route around. We also had to learn not to trust a completed call status by itself: a call can complete cleanly while the actual business question got an unfavorable or unclear answer, so we always check structured_result and task_completed together before deciding to move to the next provider. Because the Calls API has no cancel-in-flight operation, we couldn't just fire every provider at once and take the first "yes"; we had to dispatch in controlled waves and wait for each terminal result, which shaped the whole adaptive loop around sequencing rather than parallelism. On top of that, keeping the demo reliable and repeatable, given we relied on real phone calls rather than static responses, meant being deliberate about which endpoints we called for the demo itself.

Accomplishments that we're proud of

Getting the full adaptive loop working end-to-end a stated need turning into a real call, a real "no," a real second call, and a structured, confirmable result as a two-person team within the hackathon window. The moment we're most proud of showing off is the live adapt: Provider 1 says they're booked, and without any manual intervention, Ashtrac is already calling Provider 2.

What we learned

Building for a phone-based agent is a different discipline than building for a text-based one; there's no "let me think" pause, no re-editing a message before it's sent; the conversation has to hold up in real time, handle interruptions and unexpected turns gracefully, and still come out the other side with something structured and usable. We also learned a lot about the gap between "the agent can talk" and "the agent can get something done"; the second one is where most of the actual engineering effort went.

What's next for Ashtrac

Broader provider categories beyond home repair and services, real business directory integrations so the shortlist isn't hand-seeded, price negotiation logic so the agent can push back rather than compare, and multilingual support for Hindi and regional languages so it's usable well beyond an English-first audience.

Built With

  • ai-agents
  • call-e
  • call-e-api
  • conversational-ai
  • fastapi
  • json-schema
  • llm
  • pydantic
  • pytest
  • python
  • react
  • rest-api
  • tailwindcss
  • telephony
  • typescript
  • uvicorn
  • vite
  • voice-ai
  • voip
Share this project:

Updates

Submission history