Inspiration
I live in Ibarra, Ecuador — a mid-sized city full of small businesses: neighborhood coffee shops, gyms, hair salons, family-run stores. Many of them already use loyalty cards, but paper stamp cards get lost, and even the ones who've moved to Nidu (the Apple/Google Wallet loyalty platform I built and run as a real product) still have to guess who to send a promotion to and when. They're good at running their business, not at reading a churn chart or writing marketing copy — and every week that guessing doesn't happen, a customer quietly stops coming back.
When AWS announced the Agents for Humans Hackathon, the theme clicked immediately: an agent that does the boring, judgment-heavy analysis in the background and only shows up when there's a real decision for a human to make. That's exactly the gap for the business owners I know here — not a chatbot, not another dashboard to check, just a quiet second brain that reads the data they already have and hands them something useful.
What it does
Nidu is a real, production digital loyalty card platform — customers enroll by scanning a QR (issuing a real Apple or Google Wallet pass), and businesses stamp/redeem by scanning that pass with the camera. For this hackathon I added an AI layer on top: a new floating button in the merchant app opens the agent, which reads the business's real customer data (active/new/at-risk segments, stamps, redemptions) and drafts 2–4 concrete campaign suggestions — each with a target segment, a ready-to-send message, and the reasoning behind it in plain language ("12 clients haven't visited in 30+ days"). The owner reviews the drafts and, if one is useful, taps it to pre-fill the existing campaign-creation form. The agent never sends anything on its own — it only surfaces a decision, exactly like the hackathon brief describes.
How we built it
The agent (IntelligenceFn) is a Python 3.12 AWS Lambda built with the Strands Agents SDK — the only Python Lambda in an otherwise all-Node.js serverless backend (API Gateway, Cognito, Aurora Serverless v2 via the RDS Data API). It exposes two read-only tools, get_business_analytics and get_segment_counts, that query Aurora directly, then calls Gemini through strands.models.gemini.GeminiModel with structured_output to get typed, validated campaign drafts back — no manual JSON parsing, no hallucinated fields. Below a minimum customer-history threshold, the agent doesn't run at all; with too little data the suggestions would just be noise.
On the Flutter side, the feature lives in lib/features/intelligence/: an isolated cubit (following the app's cubit-per-feature architecture), a bottom sheet UI, and a small addition to the existing campaigns form so a suggestion can pre-fill it instead of duplicating logic.
Challenges we ran into
Almost every challenge was a real infrastructure lesson, not a code bug. First, Amazon Bedrock: I originally built the agent against Claude Haiku via Bedrock, wired the IAM permissions for InvokeModel, and only at the last step discovered Anthropic requires a one-time "use case details" form per AWS account before the model will actually respond — plus, once that was clear, I realized Strands' BedrockModel actually calls the Converse/ConverseStream API, which needed InvokeModelWithResponseStream too, not just InvokeModel. Rather than wait on a manual AWS approval step, I swapped the model provider to Gemini mid-build, which meant re-plumbing credentials through Secrets Manager (matching the same pattern the app already used for Google/Apple Wallet certs) and learning Strands' Gemini provider along the way.
Then Gemini itself was unstable right after I switched — Google had just shipped a new Flash model days earlier, and the classic generateContent endpoint was returning 503s under launch-day demand while Google's error messages nudged toward a newer "Interactions API" that the SDK doesn't speak yet. I added exponential backoff and a clean error path instead of chasing a moving target.
I also hit a very unglamorous but very real bug: Aurora Serverless v2 auto-pauses after 5 minutes idle in dev to save cost, and the very first query after a pause throws a transient error — which was surfacing as a raw 500 that logged users out of the whole app. Fixing that retry logic in the shared database helper ended up mattering more for the product than the AI feature itself.
What we learned
The most durable lesson wasn't about Strands or Gemini specifically — it was that "the agent works" and "the agent works in production, gracefully, after an idle weekend" are very different bars, and the second one is the one that actually helps a real shop owner in Ibarra trust it enough to open it on a Monday morning.
Built With
- amazon-web-services
- apple
- claude
- flutter
- github
- ocr
- postgresql
- python
- ts
Log in or sign up for Devpost to join the conversation.