Inspiration

You pay hundreds of euros to be at Zandvoort, and you see the cars for ten seconds a lap. You don't hear the radio, you can't see the timing screens, and you find out about the safety car from the person next to you. At a festival it's the set times that moved, the stage you never found, the last ferry. So you end up doing what you came to get away from: watching a livestream on your phone or refreshing the artist's Instagram, in the middle of the crowd.

We wanted the moments to come to you. A notification when something happens that you care about, in the chat you already have open, so you can put the phone back in your pocket and watch.

What it does

Fans scan a QR code (or press on a link) and start a WhatsApp conversation with an AI companion for the event. From then on:

  • Notifications for the moments that matter to them. A safety car, an overtake, a set starting, the last ferry home, sent only to fans who follow that driver or artist, with personalised buttons ("How's Verstappen?"). No livestream, no second app.
  • Ask anything, in the same thread. The agent answers from live event state, remembers who you follow, and looks up history ("who was on pole here last year?") on trusted sources only.
  • Have your say. In-thread polls and predictions, with live results.
  • A recap about your event, built from the fan's own activity: their driver's result and the predictions they got right.

The platform is event-agnostic. A race and a music festival run on the same code; each event is a config-only "pack" (timeline, entities, persona, message templates, trusted sources). Switching from the F1 pack to the festival pack in the organiser dashboard turns the whole platform into a different event, with no code change.

How we built it

  • AWS End User Messaging Social for WhatsApp, AWS End User Messaging for SMS, and Amazon SES for email recaps. Inbound messages arrive via SNS.
  • Amazon Bedrock AgentCore: Runtime hosts a Strands agent on Claude Haiku 4.5; Memory gives cross-session recall; the Gateway provides AgentCore Web Search restricted to each event's trusted domains.
  • Amazon Bedrock Guardrails on input, output and search results: content filters, prompt-attack detection, a betting-advice denied topic, and PII masking.
  • DynamoDB for fans, consent, votes and the shared event log; EventBridge, Lambda, API Gateway, and CloudFront + S3 for the organiser dashboard and landing page. Everything is defined in AWS CDK.

The agent never decides how a message looks. It produces channel-neutral intent, and one renderer turns it into WhatsApp buttons, a WhatsApp list, or numbered SMS options. One agent serves every channel.

Challenges we ran into

  • A QR scan is not consent. Messaging a business opens WhatsApp's 24-hour window, but it doesn't authorise later alerts, and Dutch law requires consent per purpose. We built a consent ledger (alerts, conversation and recap granted separately, each with the exact wording the fan agreed to) and a "Just Q&A, no alerts" option. STOP is handled before the model, so it works even if the model is down.
  • The best moment was being silenced. Our rate limiter dropped the safety-car poll for every fan because it arrived 18 seconds after the safety-car alert. Polls now bypass the spacing rule but still count towards the hourly cap.
  • AgentCore in practice. CreateAgentRuntime takes a container image, and it implicitly creates an endpoint, a workload identity and a service-linked role, each needing permissions we discovered one 403 at a time. Model access needed a use-case submission (put-use-case-for-model-access) plus Marketplace permissions on the invoking role.
  • Serverless state. Each Lambda container kept its own copy of the event, so the dashboard flickered between them. We moved the event log to DynamoDB and rebuild state from the ordered list of fired moments, so every container sees the same event.
  • Guardrails that bite the wrong thing. The address filter redacted "Zandvoort" as {ADDRESS}. Every event has a venue, so we dropped that filter and kept email, phone and card masking.

Accomplishments that we're proud of

  • One platform, any event. The race and the festival share every line of code; the difference is two config folders.
  • Fewer notifications, not more. Fans hear only about what they follow, rate limits protect them from bursts, and QUIET turns alerts down without opting out.
  • An agent that says "I don't know." Asked for data it doesn't have, it says so rather than guessing. Asked about betting odds, or told to ignore its instructions, it declines.
  • Web search that can't be widened. AWS rejects any search outside the event's trusted sources, so neither a bug nor a malicious message can open it up.
  • Tested from the fan's side. 280+ offline unit tests, integration tests against the live deployment, and a browser test that joins as a fan, fires a moment, votes, and fails if the layout moves.

What we learned

  • Decide the user flows first. Writing down signup and consent before any code exposed two design mistakes while they were still cheap to fix.
  • Guardrails belong in layers. The search allowlist is enforced by AWS at the gateway, the model only chooses the query, and results are treated as untrusted data.
  • Test what users actually see. A page that returns HTTP 200 can still render blank; only a real browser catches that.

What's next for Merapar Fan Engagement

More event packs (football, tennis, conferences), a live data-feed adapter alongside the simulator, RCS rich cards once carrier approval lands, and packaging for AWS Marketplace so an organiser can launch a new event from a config file.

Built With

Share this project:

Updates

Submission history