Inspiration

I run a small web and AI studio in Chittagong, Bangladesh. Like every freelancer, I have clients who pay late — and the follow-up is the worst part of the job. Emails get ignored. The phone call that actually works is awkward, easy to postpone, and impossible to do consistently across a handful of invoices at once.

So most of us just wait. Some write the money off entirely.

The existing software for this is built for large receivables teams: platforms you configure over a week. A freelancer with three overdue invoices needs a call placed tonight, not a workflow platform.

What it does

CALL-E Invoice Recovery turns each overdue invoice into a drafted recovery call.

The agent writes the script, the tone, and the escalation level based on how late the payment is — seven days overdue sounds friendly, sixty days sounds firm. That draft goes to a human, who approves, edits, or rejects it. Nothing dials without an explicit sign-off.

On approval, CALL-E places the call, holds the conversation, and returns a structured outcome: paid_now, committed_to_date, disputed, refused, or a non-contact state. Spoken dates come back machine-readable — say "the 20th of August" on the call and the system stores 2026-08-20. The invoice status updates immediately.

Two safety properties are built into the script text itself: the first spoken sentence discloses that the call is automated, before any money is mentioned; and an identity gate blocks the invoice amount until the person confirms they're the right contact. A third party who picks up never learns that a debt exists.

How we built it

  • CALL-E — the @call-e/calle TypeScript SDK, imported and called at runtime. The placeCall payload carries the per-invoice script, the region code, and the structured result schema; the polling loop and transcript fetch are CALL-E-specific.
  • Next.js (App Router) — invoice list, invoice detail, and the /queue review page where drafts are approved.
  • Supabase — invoices, calls, and arrangements tables with RLS on every table.
  • Vercel — deployment.
  • TypeScript — strict, no any.

Every CALL-E interaction runs through a LoggingCalleClient wrapper that records the method, the call ID, the wall-clock API duration, and success or failure as structured JSON. Anyone asking "does it really call CALL-E?" can read the logs.

The arrangement state machine only moves drafted → approved → executed. It cannot skip a step or reverse. Rejected arrangements stay rejected rather than being deleted.

Challenges we ran into

Latency I couldn't honestly measure. I wanted to report conversational latency — the gap between the client finishing a sentence and the agent replying. The payload type declares per-turn offset_seconds, but I had reconstructed that type because the documentation wouldn't render, and even if the field is present it's a per-turn start offset at whole-second granularity. There's no turn-end, so the gap can't be isolated. I decided to report no number at all rather than publish one I couldn't defend. The metrics harness prints "insufficient data."

Trigger phrases decide outcomes. During testing I said "I already paid that yesterday," expecting paid_now. It came back disputed — correctly. The agent can't verify a payment it didn't witness, so a claim of prior payment is a dispute until a human reconciles it. paid_now requires a forward commitment. That distinction only surfaced by placing real calls.

The phone and the network. My first live calls kept dying mid-poll with fetch failed. The incoming call was knocking my PC's connection offline — phone and computer were on the same link. Obvious in hindsight, invisible while it was happening.

A silently broken deployment. Production sat on a stale commit for days while every push reported success. vercel.json declared a 15-minute cron, and the Hobby plan only allows daily crons, so Vercel refused every build containing it — silently. Three "deployed successfully" reports were wrong. Nothing is deployed until you load the real URL yourself.

Accomplishments that we're proud of

Eight real calls, placed to my own phone, covering all four conversation branches. 100% connected, 45-second median duration, every one resolving to the correct structured outcome.

That's a small sample and I'm reporting it as measured, not projected. No figure in this submission is extrapolated. Where I couldn't measure something, it says so.

I'm also glad the approval gate survived the temptation to remove it. Fully-autonomous dialing would have demoed better. But debt collection is regulated, an unapproved call can breach consumer-protection law, and the honest version of this product has a human in the loop. The gate is the design, not a limitation.

What we learned

That an integration is only worth something if removing it breaks the product. Take CALL-E out of this and there's nothing left — no alternative path produces a voice conversation with a structured outcome. That's a different thing from bolting an API onto a demo.

That reporting a real small number beats inventing a big one. Seven calls is not validation at scale, and saying so plainly is more useful to anyone evaluating this than a rounded-up figure would be.

And that "pushed" is not "deployed" is not "a judge can see it." Verify by loading the actual URL. Every time.

What's next for CALL-E Invoice Recovery

Batch-approval recurrence, which is already built: a scheduled sweep drafts recovery calls for invoices crossing overdue thresholds, the operator approves them in one review moment, and approved calls dial at their scheduled times — one at a time, within calling hours for the client's region, bounded by a per-destination cap. Every queued call is visible on its invoice with a cancel control.

A wider result schema. The current four fields can't represent a partial payment or an instalment plan, so a client offering "half now, half next month" currently maps to refused with that limitation stated explicitly rather than hidden. Widening the schema to carry instalment terms is the most useful next change.

Persisting per-turn timings, so conversational and API latency become reportable rather than absent.

Fully-autonomous dialing is deliberately not on this list. If it's ever built, it sits behind an explicit opt-in with a spend cap — never a default.

Built With

Share this project:

Updates

Submission history