-
-
SubSentry renews clean subscriptions automatically and only interrupts you for real decisions, like a 71% gym price hike.
-
The live app with nothing pending — most months, SubSentry has nothing to ask you.
-
Four subscriptions flagged for review: a price hike, a duplicate charge, an unknown merchant, and an overlapping plan.
-
Six subscriptions renewed silently, four decisions made by a human in under a minute — all logged below.
Inspiration
The hackathon brief put it well: the best everyday agents run in the background and only surface when there's a real decision to make. Subscriptions are a near-perfect fit for that idea. A gym membership doubles in price after the intro period ends, a free trial quietly converts to a paid plan under a company name you don't recognize, a billing system glitches and charges you twice in the same cycle — none of it is dramatic enough to notice in the moment, and almost nobody reads their card statement line by line every month. We wanted an agent that does that reading for you, and only asks for your attention when something actually needs a human call.
What it does
SubSentry scans a billing cycle and checks every subscription against four rules: did the price jump more than 15%, was it charged more than once this cycle, is the merchant new to the ledger, and does it overlap with another subscription in a category where duplicates are pure waste (two gym memberships, two cloud storage plans). Anything clean gets renewed automatically, no questions asked. Anything that trips a rule shows up as a card in the web UI with the reason spelled out — a price hike, a duplicate charge, an unrecognized merchant — and waits for you to approve or reject it. Reject and the agent holds with no action instead of guessing. The same decision can also be made from the CLI (agent.py approve/reject), since it's writing to the exact same table the web UI reads from.
How we built it
The core is a Strands Agent with four tools (scan_subscriptions, request_review, apply_decision, list_processed_log) running on Amazon Bedrock AgentCore Runtime, reasoning with Claude Sonnet 4.6. The part we spent the most time on isn't the reasoning, it's the handoff: the agent's request_review tool call blocks and polls a DynamoDB row while a Next.js web UI on Vercel polls the same row from the other side. Neither process talks to the other directly. The agent writes PENDING, the browser shows a card, a human clicks Approve or Reject (or runs the CLI), the decision gets written to that same row, and the agent's poll picks it up and resumes. That's what lets a single long-running AgentCore invocation coordinate with an async browser click without a websocket or a shared process.
Challenges we ran into
Early on, running the agent locally with six clean subscriptions and four flagged ones produced corrupted approval data and missing log entries. Strands was dispatching several tool calls in the same turn concurrently, and our naive read-JSON-modify-write local storage had no locking, so parallel writes clobbered each other. The fix was a straightforward threading.Lock around the read-modify-write section, but finding it meant noticing that the local .approvals.json file had literally two writes interleaved mid-file.
Separately, wiring the Vercel deployment to AWS didn't go as planned. OIDC federation between Vercel and an IAM role looked right on paper but the deployed app couldn't load credentials at runtime, so we fell back to a scoped IAM user with access keys restricted to just the two DynamoDB tables and InvokeAgentRuntime on this one agent.
Accomplishments that we're proud of
The whole loop works end to end on real infrastructure, not a mock: a browser click on the live Vercel URL triggers a real InvokeAgentRuntime call, the agent reasons with Claude Sonnet 4.6, writes real DynamoDB rows, and a human approving or rejecting — from either the browser or a terminal — resumes a genuinely blocked backend call. We tested it by clicking through the actual approval cards against the deployed AgentCore Runtime, and separately by approving from the CLI and watching the same decision show up in the web UI seconds later.
What we learned
A polling-based rendezvous on a plain DynamoDB row is a small, boring way to solve a problem (coordinating a blocking backend call with an async frontend action) that would otherwise pull in websockets or a message queue. Boring infrastructure that's easy to reason about turned out to matter more here than any particular prompt-engineering trick.
What's next
The billing ledger is currently a fixed mock file so the demo is repeatable. The obvious next step is a real bank or card-issuer connection (something Plaid-shaped) feeding the same scan_subscriptions tool. We'd also like to add a notification channel — email or SMS — so the agent can ping you instead of you having to remember to open the app, which is closer to the "runs quietly in the background" ideal this whole track is built around.
Built With
- amazon-bedrock
- amazon-bedrock-agentcore-runtime
- amazon-dynamodb
- aws-iam
- claude-sonnet4.6
- docker
- nextjs
- python
- react
- starnds-agents-sdk
- typescript
- vercel

Log in or sign up for Devpost to join the conversation.