Inspiration

Every AWS customer gets a Cost and Usage Report (CUR), and almost nobody reads it. It is a table with hundreds of columns and millions of rows. Answering a simple question like "why did our bill go up last month?" means opening the console, writing SQL in Athena, and knowing what line_item_unblended_cost means. So the question goes to the one engineer who knows how, and waits.

As an AWS partner we see this with every customer. We wanted the finance lead, the CTO and the engineer to get the answer where they already are: on WhatsApp, in one sentence, from their own billing data.

What it does

AWS CUR Chat Assistant answers questions about an AWS bill in plain language, on WhatsApp and on a website, for many companies from one deployment.

  • Ask on WhatsApp: "What did we spend on EC2 last month, by region?" The app offers a similar question someone already asked, a cleaned-up version of yours, or yours exactly as typed. Tap one and the answer arrives in seconds. Follow-up questions work.
  • Run everything from WhatsApp: the admin sees every company's status, usage and AI cost, and adds a new company in a short conversation. A company's head manages their team. Employees just ask.
  • Automatic onboarding: adding a company starts AWS CodeBuild, which runs Terraform for that company alone: its own landing bucket, data lake, IAM role and Athena workgroup.
  • Strict isolation: a company can only ever see its own data, even if the AI were tricked.
  • Three commands to deploy into any AWS account, as a least-privilege IAM user.

How we built it

WhatsApp with AWS End User Messaging Social. Incoming messages go from the WhatsApp Business account's event destination to an Amazon SNS topic, which triggers our backend Lambda. Every reply, menu and list goes back through the AWS SDK:

socialmessaging.send_whatsapp_message(
    originationPhoneNumberId=phone_number_id,
    metaApiVersion="v20.0",
    message=json.dumps(payload).encode("utf-8"),
)

From a question to SQL. Claude on Amazon Bedrock cleans up the question and writes SQL against the CUR 2.0 schema. A deterministic SQL guard (built on sqlglot) then allows exactly one read-only SELECT, only on the CUR table, and injects the company's customer_id and a month range into it. Amazon Athena runs it in the company's own workgroup, under the company's own IAM role. If the SQL fails, Claude gets up to two attempts to repair it.

Finding similar questions. Questions are embedded with Amazon Titan Text Embeddings V2 and stored in Amazon S3 Vectors. A saved question is offered only when it is very close in meaning to yours:

$$ \text{similarity}(q, s) = 1 - d_{\cos}(q, s) = \frac{q \cdot s}{\lVert q \rVert \, \lVert s \rVert} \;\ge\; 0.85 $$

Getting the billing data in. Each company's CUR 2.0 export lands in its own S3 bucket. The manifest file triggers an EventBridge rule and our ingest Lambda, which converts the files to one schema and only replaces the month in the company's data lake if the total cost is unchanged:

$$ \left| C_{\text{raw}} - C_{\text{converted}} \right| \;\le\; \max!\left(0.01,\; 10^{-4} \cdot \left| C_{\text{raw}} \right|\right) $$

A daily EventBridge Scheduler job re-scans stale months, and CloudWatch alarms report failures.

Keeping AI cost under control. Every Bedrock call is priced from its token counts, with prices \( p \) in USD per 1,000 tokens, and added to the company's daily total:

$$ \text{cost} = \frac{t_{\text{in}} \, p_{\text{in}} + t_{\text{out}} \, p_{\text{out}}}{1000} $$

When a company reaches its daily limit, new questions wait until the next day.

Everything else is serverless and in Terraform: AWS Lambda (arm64, Python 3.12), API Gateway, CloudFront, DynamoDB, SNS, CodeBuild and IAM. About 2,000 automated tests run against moto and fakes, including adversarial tests that try to break the SQL guard.

Challenges we ran into

  • Isolation that does not depend on the AI behaving. Prompting is not a security boundary. We built three layers: the SQL guard, the injected customer_id filter, and per-company IAM roles and Athena workgroups as the final wall. Every bypass we found got an adversarial test first, then the fix.
  • WhatsApp's rules. A business can only reply freely within 24 hours of the person's last message. Buttons allow three options and lists ten. We designed every flow so the person writes first, and fitted long choices into numbered lists.
  • Passwords without a chat. Passwords must never be typed into WhatsApp, so the app sends a one-time link (valid for 10 minutes) to a secure page instead.
  • Timing. Answers can take longer than an incoming WhatsApp event should wait, so questions go to an asynchronous worker and the answer is sent when ready.
  • Stale data must never grant access. Live testing showed that a phone number left behind after it was removed still opened the menu. Now every message checks that the person's own record still lists that number.

What we learned

  • WhatsApp's conversation rules shape the product more than the AI does.
  • Re-checking identity on every message is cheap and catches cases nobody planned for.
  • Embedding search with a high threshold is a simple way to reuse proven questions and get consistent answers.
  • A real deployment test in a clean account finds problems that no unit test does.

What's next

  • Amazon SES: weekly cost summaries by email and alerts when spending jumps.
  • WhatsApp templates for budget and anomaly alerts.
  • Amazon Bedrock AgentCore Memory for longer conversations, and Observability for tracing each answer.
  • An AWS Marketplace listing, so AWS partners can offer it to their customers.

Built With

Share this project:

Updates

Submission history