Inspiration
Courier scams are the one kind of elder fraud that has to come to the house. The call, the fear and the secrecy all happen at a distance; the handover does not. The FBI warned about this pattern in 2024 (IC3 PSA240129), and Boston's field office reported that 98% of the losses were reported by people over 60. A Scottish council (East Renfrewshire) installs video doorbells in older residents' homes to fight doorstep scams. Those doorbells show who is there, and nothing more. We wanted the doorbell to say something at the one moment it matters.
What it does
- An unexpected visitor rings: not a recurring visit (a carer, Tuesdays 10 to 12) and not a delivery declared in advance. The chime plays a reminder that points to the family's code word, the one code the scammer cannot know.
- The door opens for them: the trusted people are emailed at once, with the doorbell's still image, a printable report sheet for IC3 and the FTC, and the confidence: a contact sensor saw the door open, or, without one, the visitor stayed over a minute.
- The courier comes back: for seven days the family is told as soon as anyone unexpected appears, before the door opens.
- The resident is talked into switching it off: pausing, switching off, adding a visit or a delivery for today, adding a new "trusted" person: each one is told to the family first. A pause ends by itself after 24 hours.
The resident switches it on, chooses who is told, and can delete everything in one step. Family members see alerts, never the schedule.
How we built it
Python and FastAPI receive Ring webhooks (button_press, motion_detected with
sub_type: human, contact_sensor_faulted, device_online/offline), verify
Ring's HMAC signature over the raw body before parsing anything, and reject stale or
replayed deliveries. A rules engine, ordinary tested code, decides everything. The
Partner API plays chime slots and fetches one still image, only when an alert is
sent. Households, alerts and the journal are encrypted at rest in SQLite; access
links are stored as hashes. Pasted delivery messages are read by rules, or
optionally by an OpenAI model (GPT-5.6 Luna) with a single strict function that
can only return fields. Either
way, code validates them against the pasted text. Ring publishes no simulator, so
everything runs against ring-sandbox,
an MIT emulator we read in full and pinned by hash in its own virtualenv.
None of this existed before the hackathon, and the repository's history is the evidence rather than a claim:
| First commit | 19 September 2026 — "Design, agreed before any code" |
| Commits | 19 September to 4 October 2026 |
| Written before the hackathon | nothing |
The order is the one we would defend, and it is visible in the log: the design document was agreed first, then the webhook receiver and the rules engine, then the measurement scripts, then the pages and an accessibility check — which found a real defect, a focus ring at 1.88:1 on a page whose readers need 3:1.
Challenges we ran into
- No simulator, and chime audio slots whose provisioning is undocumented. That
decided a product question: the reminder is a prepared recording, not a
per-family one. Logged in
FRICTION.md. - Webhooks get lost. The emulator's chaos mode drops one in ten. A lost "door opened" is now recovered by asking the sensor while an unexpected visitor is at the door. A lost doorbell press cannot be: 52 to 56 of 60 reminders play, and we say so.
- And the claim we assert had to be narrowed, because a run proved it wrong. "Every family is told" read 59 of 60, and the silent household had seen nothing: both its doorbell webhooks were dropped, so only the door opening arrived, and a door opening with no visitor known is the resident fetching the post. The claim is now every family whose doorbell was seen, asserted by attributing each dropped delivery to its household through the emulator's fault log. Losing one of the two webhooks costs nothing; losing both costs the visit, about one in a hundred, and the Partner API offers no way to ask the doorbell what it saw.
- Our own bug under load: one SQLite connection shared by threads returned errors. The chaos run found it; every statement is now serialised.
- Tests that find leaks: a test that greps the raw database file found the schedule, then e-mail addresses, stored in clear. Both are encrypted now.
Accomplishments that we're proud of
Every number is measured: a scenario table in both modes, a chaos run at sixty households, and a mutation run that breaks 48 rules one at a time and checks that a test fails each time. 261 tests. The false alerts and the misses are published next to the successes — and so is the claim we had to narrow when a run proved the wider one wrong.
What we learned
The hardest part of fraud protection is the victim being on the scammer's side, for now. A system that the scammer can talk the victim out of is not protection. So every way to weaken it has to be loud.
And something about testing that we did not expect. 261 tests found none of the
last five defects, because all of them call the API — and all five lived in the
part a person touches: a required form field that made the one refusal this
project exists for unreachable; a question rendered below the fields it was meant
to fill, so the first person to use it filled them by hand and never saw it; the
instruction "Call Margaret now" hidden behind the change alert a scammer's
next move produces a minute later; a report link in the alert e-mail that needed
a session the phone it arrived on had never had; and an installation with no way
to send e-mail at all, reporting its alerts as delivered.
The suite proves the rules. Walking the screens proves the product. Reading the shot plan aloud, one click at a time, found more in two hours than the suite had in two days.
What's next for Call First
A real door-sensor household (Ring's sensors are Early Access), the uninstall webhook once its payload is documented, and a pilot with a council or a senior centre that already hands out doorbells.
Built With
- anthropic
- css
- fastapi
- html5
- javascript
- openai
- pytest
- python
- ring-sandbox
- sqlite
- uvicorn
Log in or sign up for Devpost to join the conversation.