Inspiration

Americans 60+ reported $7.748 billion lost to online scams in 2025 — up 59% from $4.885B in 2024 (FBI/IC3, via AARP's reporting on the 2025 FBI/FTC data). The FTC's own December 2025 report to Congress estimates real losses may be as high as $81.5 billion, since fewer than 1 in 20 victims ever report. Grandparent/family-emergency scams alone accounted for $5M+ in reported 2025 losses, with the FBI explicitly naming AI voice-cloning as a growing driver — criminals mimicking the sound of a loved one in distress to pressure a wire transfer through before anyone can think twice.

We wanted to fight that with the same weapon: an AI phone call. Not one that talks to a victim mid-scam (CALL-E can't intercept a live call, and that would collide with two-party-consent recording law anyway) — one that gives a bank's own fraud system a second, independent voice channel to check with the account holder before the money actually moves.

What it does

RingFence is a B2B fraud-verification service. A bank or payment processor's own fraud-scoring system flags a transaction as unusual — RingFence doesn't build that part, it starts where that system ends. RingFence places an independent verification call and asks a disclosure-first set of documented scam red-flag questions (secrecy demands, manufactured urgency, an unexplained relationship, insistence on an irreversible payment method), then returns one of three advisory recommendations to the institution: ADVISE_ALLOW, ADVISE_BLOCK, or ESCALATE_TO_HUMAN. Every recommendation carries requires_human_review — nothing RingFence returns is an action on the money, only evidence for the institution's own reviewer. Ambiguous or unreachable always means blocked or escalated — never guessed as fine.

The one rule everything else depends on: RingFence only ever calls the number already on file for the account — never a "callback number" that arrived as part of the flagged request itself, which is exactly what a scammer would supply to redirect the "verification" call to themselves. This is tested directly, not just asserted.

Surfaces: a CLI, a webhook (POST /cases / GET /cases/{id}), an MCP server, and a two-sided demo — a customer "send money" page and a bank fraud-ops dashboard — where the bank decides automatically whether a transaction needs a call at all; the customer never chooses.

How we built it

Python, the calle-ai SDK against CALL-E's real Calls API. The decision engine (resolve.classify() + decide()) cross-references CALL-E's raw status/failure_code against the actual attempt/event timeline rather than trusting it directly — CALL-E's own docs admit the raw fields don't reliably distinguish a no-answer from a connection failure from a policy refusal. We measured this rather than just asserting it: a 98-case exhaustive synthetic corpus (every combination of our 5 verification signals, every documented ambiguity marker) checked against an independently-written oracle shows naively trusting the raw fields disagrees with the correct classification in 28/98 cases — 2 of them unsafe (would let a fraudulent, never-actually-verified transaction through as clean).

Then we stopped trusting fixtures alone and placed real calls. 9 of them, across 2 CALL-E accounts and 2 real phone numbers. Nothing from those live runs is committed to this repo — no call outputs, transcripts, identifiers, or timings — a verification call's contents aren't something that belongs in a public repo. What remains is the code and tests those calls produced.

Challenges we ran into

Every one of these was found by an actual real call, not code review:

  • A classifier bug: a connection failure (0-second ring, provider error code) and a genuine no-answer produced identical raw data — our first real call got mislabeled. Fixed and locked in with a regression test built from the real API response.
  • A missing field: region/locale were never sent on outbound calls — found by comparing against other real-call-tested apps in this same hackathon's repo. Fixing it didn't fully stabilize the route, either — across several real dial attempts to one documented-supported region, we hit different provider error codes before one connected, which we now believe is genuine carrier-level flakiness, not something fixable client-side.
  • An audit-trail bug that was silent and total: our own transcript-parsing code assumed speaker labels that don't exist in a real CALL-E transcript. It would have returned an empty audit trail for every real call, including a fully successful one — exactly backwards for a compliance feature.
  • A platform-level surprise: partway through, CALL-E started refusing every new call with "account concurrency limit of 1," even though every call we could query directly was confirmed complete and the dashboard showed zero active calls. We reproduced this on a second, independent CALL-E account too — a real, cleanly-reproducible finding we've reported back.
  • The headline moment: after fixing an undocumented content-policy rejection (CALL-E refused a task that named the caller only as "their financial institution" — a vague-identity check, which is genuinely good behavior we didn't expect), a real call finally went all the way through: the account holder described an actual tech-support scam pattern live on the call, and RingFence correctly extracted the red flags and returned ADVISE_BLOCK.

Accomplishments that we're proud of

That last one. A real phone call, a real scam pattern described out loud, and our own decision engine catching it correctly — not a fixture, not a mock, an actual placed call. Alongside that: 107 tests, a fail-closed security invariant that's directly tested rather than just documented, and an evaluation methodology (exhaustive combinatorial coverage + an independent oracle) that's arguably more rigorous than random sampling for a decision engine this size.

What we learned

CALL-E's own documentation is honest about the ambiguity in its outcome fields — but the shape that ambiguity actually takes in practice (a zero-duration attempt with a rotating set of SIP-style error codes, a concurrency gate that outlives its own calls, an undocumented content-policy check) only shows up once you place real calls and read the real transcripts. Every one of our real bugs survived a green test suite until an actual call found it.

What's next

A public, deployed demo (deliberately deferred — the real-call path is rate-limited and gated on CALLE_API_KEY being configured, and we wanted the safety model finished before making it reachable by anyone). Beyond that: a dedicated CALL-E number to get past the free-tier concurrency limit, and widening the verification question set beyond the current red-flag categories.

Built With

Share this project:

Updates

Submission history