Inspiration

Appointment-based businesses (clinics, dentists, physiotherapists, salons) lose a large share of revenue to no-shows, and the follow-up is usually manual: someone phones, or one SMS goes into the void. We wanted the miss itself to trigger the recovery, and the patient to be able to fix it with one tap.

What it does

RebookAI watches a clinic's appointment book in DynamoDB. When a patient misses an appointment it automatically:

  1. Detects the miss. The start time plus a grace period has passed and the appointment is still open.
  2. Reaches the patient on RCS (AWS End User Messaging) with a rich carousel of the next three open slots, written in the patient's own timezone (for example "Tue 27 Oct, 2:00 PM CET" for a patient in Germany).
  3. Rebooks on a tap. The appointment is actually moved to the tapped slot, atomically, with the original time kept for audit.
  4. Confirms by email (Amazon SES) with the new time, plus a short RCS "you're all set" message.

If RCS can't deliver (an unsupported country, an unreachable device) the same slots are emailed instead, so a patient is never left in silence. A patient who misses the new slot is chased again, capped at three attempts.

Try it yourself (live, no login)

https://8plz4cq8i5.execute-api.us-east-1.amazonaws.com runs the whole journey with your own email, and optionally your own phone. It creates a demo appointment that started 40 minutes ago, detects the miss with the same grace-window rule as the production sweep, offers three slots in your timezone, moves the appointment when you pick one, and confirms.

  • Email only (always works): three signed links by email (SES). Open one, press Confirm, then a confirmation email arrives.
  • RCS + email: the page registers your phone as an RCS test device through the API. AWS sends a message from "RBM Tester Management"; tap Make me a tester (up to about 20 minutes), then the RCS carousel arrives.
  • RCS not possible (for example India, a VoIP number, no RCS on the phone, or no free test slot): the page says why and switches to the email version automatically, so you can always finish.

Why the extra RCS step: our RCS agent is in AWS's testing state, which only messages registered test devices, and production launch needs per-country carrier approval that cannot finish inside the hackathon. The account quota is 10 test devices (not adjustable), so at most 7 judges hold an RCS slot at once. Everything is simulated and nothing real is booked. Your details are kept at most 7 days (DynamoDB TTL), you can delete them from the progress screen, and limits protect cost and abuse (3 sessions per email and 2 per phone per day, 6 per hour per IP, 300 sessions and 80 RCS sends per day). It is a separate CDK stack (RebookAiJudgeStack) that does not change the production stack.

How we built it

  • Serverless on AWS. An EventBridge schedule runs a sweep Lambda. Taps come back RCS two-way event, SNS, Lambda. DynamoDB holds appointments and the turn log. All of it is one AWS CDK stack (RebookAiStack, us-east-1), plus the separate Try-It stack.
  • AWS CDS services, called at runtime:
    • AWS End User Messaging (RCS): pinpoint-sms-voice-v2 SendRcsMessage sends the tap-to-rebook carousel and the confirmation text. The RCS agent's two-way setting delivers the patient's tap to an SNS topic and on to a Lambda. A configuration set writes RCS_DELIVERED / RCS_READ receipts to CloudWatch so we can prove delivery.
    • Amazon SES: sesv2 SendEmail sends the confirmation and the email fallback.
    • Amazon Bedrock: converse (Nova Lite) reasons about free-text replies such as "none of those work, anything Friday?".
    • End User Messaging Social (WhatsApp): the adapter and inbound handler are built and tested, but the sender could not be linked (Meta Embedded Signup error #2655122, open with AWS Support). We are not claiming the WhatsApp prize. The journey does not depend on it.
  • The agentic part, and where we deliberately do not trust the model. Bedrock decides what to do with open-ended replies. A tap on a slot is an unambiguous choice, so the orchestrator guarantees it in code: whatever the model returns, a valid tap confirms that exact slot. A test feeds the agent a deliberately wrong decision ("close the case") and still gets the correct booking.
  • Correctness first. Rebooking is a single conditional DynamoDB update, so at-least-once delivery and double taps cannot double-book or send two emails. Inbound messages are de-duplicated with a conditional write.
  • Honest scheduling note. The production sweep schedule rule ships disabled so a stray test row can never be messaged by accident; the demo runner triggers it, and the live Try-It page has its own enabled 1-minute rule, so automatic detection is demonstrable there.

Measurable impact (illustrative arithmetic, not a measured result)

For a clinic with 1,000 appointments a month and a 20% no-show rate (200 misses) at an average of \$80 per visit, recovering a third of the misses is about \$5,300 a month per location, with no staff time. The recovery loop runs in minutes (a 5-minute sweep plus one tap) rather than hours of phone calls.

What we learned

  • "Accepted" is not "delivered". An API accepting an RCS message proves little. Real delivery receipts (RCS_DELIVERED, RCS_READ) in CloudWatch were the only proof, so we built the receipts in from the start.
  • A dry run does not catch everything. SendRcsMessage with DryRun passed, but a real send failed with RESOURCE_NOT_ACCESSIBLE: the sending Lambda also needs s3:GetObject on the card image, not just a bucket policy for the service. The fallback to email hid the failure until we read the logs.
  • Fakes hide real behaviour. Running the live Try-It found three defects our unit-test fakes had hidden. For example, looking up an unregistered phone number raises ResourceNotFoundException in the real service instead of returning an empty list, which would have made every RCS attempt fail.
  • Don't let a model own a guarantee. A tapped button is a decision the user already made, so the code enforces it and the model only handles the open-ended part.
  • Tests must be able to fail. We broke the code on purpose (8 mutations of the main flow, 51 of the Try-It's safety guarantees) and confirmed a test fails each time. A test that cannot fail proves nothing.
  • Concurrency bites quietly. A lazily created DynamoDB client raced across threads (2 of 30 runs double-claimed a message). A lock brought it to 0 of 30.

Challenges

  • RCS reach. RCS covers a limited set of countries (not India), so we test on a verified device in Germany and handle timezone and daylight saving time correctly.
  • The testing agent. The RCS agent is in testing state and reaches only verified testers, and each device must accept a one-time invitation first. For judges we automated the registration and a safe email fallback.
  • WhatsApp linking. Linking was blocked by an account-model conflict we escalated to AWS, which is why WhatsApp is a clearly labelled placeholder and not part of the demo.
  • Cost and abuse on a public demo. A page anyone can use must not spam real people or run up a bill, so it has consent, per-email, per-phone and per-IP limits, a daily cap and a kill switch.

Verified

  • Live, on a real phone (25 Oct 2026): the RCS carousel was delivered to and read on a verified device in Germany (RCS_DELIVERED and RCS_READ receipts in CloudWatch). A tap moved the appointment to rebooked, and the RCS "you're all set" message and the SES confirmation email went out about two seconds later.
  • Tests: 62 automated tests run the full journey against real DynamoDB semantics, and the Try-It adds 164 more. All are blocked from touching real AWS.
  • Deployed code equals the repository (checked by a script that downloads each deployed Lambda and diffs it against the repo).

What's next

Link a WhatsApp sender, launch the RCS agent to production, add a global secondary index for phone lookup at scale, and package RebookAI as an AWS Marketplace offering for appointment-based businesses.

Disclosure

Newly built during the submission period. Uses standard AWS SDKs (boto3), AWS CDK, pytest and moto; AI coding assistance (Kiro) was used. Design informed by AWS's own CDS sample repos (Chat Orchestrator, Engagement Database, Email Categorization) and CloudNestle's general Bedrock and agent patterns. No customer data or proprietary customer code.

Built With

Share this project:

Updates

Submission history