This project was built for the AWS Zero to Shipped hackathon. Category: Social Good. Lane: Community. I built the whole thing over a weekend with an AI coding agent connected to AWS.
I spent the weekend building one small app for the AWS Zero to Shipped hackathon. It reads an eviction notice and tells you the one date you cannot miss. Here is how the idea started, what I used, who it is for, and the one rule that shaped every line of code.
How I got the idea
I was not planning to build this.
My first idea was an "AWS account report card", a tool that grades your account on security and cost. I researched it for a few hours and killed it. Prowler, 6WAF, and AWS Trusted Advisor already do that, and the judges are AWS Solutions Architects who know those tools cold. Low originality, bad odds.
So I scraped the field. Over 240 projects were already submitted. The crowd was DevOps tools and student study assistants. The gap was something else: a trust-first app for people who get hit by a scary document and have no one to help them read it.
Then I found the number that would not leave me alone. Roughly 1 in 4 eviction cases end in a default judgment (MLRI, The Default Project). The tenant does not lose on the merits. They lose because they never responded in time. The same research says those tenants are more likely to have a disability, to speak a language other than English, and to have little access to technology.
The Furman Center summed it up in one line: "half the battle is just showing up."
So the job was simpler than a lawyer-bot. Make one fact impossible to miss: the deadline on the letter.
What it does
You paste the eviction notice you received. DueDate gives you back, in your language:
- The one deadline you cannot miss.
- A dated checklist of what to do before it.
- Your rights, but only the ones your notice actually supports.
- An honesty panel: "what your notice does NOT say", so nothing is assumed.
- Links to real free legal aid near you.
No login. No account. It runs on AWS and it is read-only.
The one rule that shaped everything
An eviction notice is read by someone who often cannot check whether the answer is right. If my app says "you have until January 12" and the real date is January 9, I have not helped. I have hurt them.
So the whole system has one rule: the AI model is never allowed to decide a fact.
Here is the opinion that drove it, and I think most document-reading AI apps get this backwards. The usual pattern hands the whole document to the model and asks it to "extract the deadline and explain it." That makes the model the source of truth for a fact a frightened person cannot check. Wrong job for the model. The model should own language, not facts. Code should own facts.
Dates, dollar amounts, the notice type, the computed deadline. All of that comes from plain code, not from a language model. The model only translates and simplifies the facts after the code has already found them. If the code cannot find a fact, the model is not allowed to fill the gap. It has to say the notice does not state it, and point to free help instead.
That rule is the product. Everything else is how I enforced it on AWS.
One honest limit, because the claim is strong: Textract can still misread a photo of a crumpled notice. So DueDate shows the exact line it read and its confidence, and lets you click any answer to see the line behind it. It cannot invent a fact, and when it is unsure of what it read, it shows you so you can check against the paper in your hand.
How it works
Four steps. Only one of them is allowed to be creative.
Browser (CloudFront + S3)
|
| POST { text, language } (HTTPS, no login)
v
Lambda Function URL
|
| 1. Amazon Textract read the notice line by line
| 2. deterministic engine find the facts (code, no model)
| 3. Amazon Bedrock narrate + translate (guardrailed)
| 4. legal-aid directory route to real free help
v
JSON: cited facts + plain-language summary + dated checklist
Textract reads the notice and gives each line an id (L1, L2, L3) with a confidence score. Those ids are the citation anchors. No id, no citation, nothing on screen.
The engine is code. It classifies the notice by matching rules, pulls out the amount and the parties, and computes the deadline. The tricky case is a notice that says "within THREE (3) days" and "dated January 6, 2026". There is no deadline date on that paper. A lazy build asks the model to guess it. Mine reads the day count from one line, the notice date from another, adds the days by a documented rule, and labels the result as computed with the rule shown on screen. If either input is missing, it produces no date at all.
Bedrock only narrates. I used Nova 2 Lite, cheap and multilingual. It rewrites the facts in plain language at about a sixth-grade reading level, in English or Spanish. After it responds, I throw away any sentence whose fact id is not real. A prompt is a request. A filter is a guarantee.
The last step is not AI. It is a small directory of real free legal aid, picked by the state read off the notice.
I wrote tests that try to make it lie, and it refuses:
def test_R7_never_invents_a_date_absent_from_the_notice():
vague = [L(1, "NOTICE TO PAY RENT OR QUIT"),
L(2, "Rent is overdue. Please contact the office.")]
keys = {f.key for f in extract_facts(vague)}
assert "deadline" not in keys # no date in the paper, no date on screen
Eight of these pass with no AWS calls, so anyone can clone the repo and prove the guarantee in seconds:
$ python3 tests/test_engine.py
PASS test_R10_refuses_when_only_a_day_count_but_no_start_date
PASS test_R7_never_invents_a_date_absent_from_the_notice
PASS test_analyze_surfaces_missing_fields
PASS test_classifies_pay_or_quit_with_citation
PASS test_computes_deadline_from_days_and_marks_it_computed
PASS test_every_fact_has_a_citation_or_is_explicitly_absent
PASS test_extracts_amount_only_when_present_and_cited
PASS test_unknown_notice_is_honest_not_guessed
8/8 passed
The AWS services I used
The whole thing is serverless and scales to zero.
- Amazon CloudFront + Amazon S3 serve one static HTML page from a private bucket (Origin Access Control, so the bucket is never public).
- AWS Lambda (Function URL) is the only compute. No API Gateway, no servers. The Function URL owns CORS, so there is one source of truth for headers.
- Amazon Textract reads the notice line by line.
- Amazon Bedrock (Nova 2 Lite) handles plain language and translation, under guardrails.
-
AWS IAM keeps the Lambda role to exactly
textract:DetectDocumentTextandbedrock:InvokeModel, nothing else. -
AWS CDK defines all of it. One
cdk deployprints the live URL.
At idle it costs nothing. Each analysis is one Textract page read ($0.0015 at $1.50 per 1,000 pages) plus one short Nova 2 Lite call (a few hundred tokens in and out, well under a tenth of a cent at $0.30 per 1M input and $2.50 per 1M output). Call it under half a cent per notice. A demo run a few hundred times stays under a dollar a month. A tool for people with no money cannot be expensive to keep alive.
Who it is for
Tenants who get an eviction notice and have no lawyer. People who read English as a second language. People who miss the response window not because they have no case, but because the letter was confusing and the clock ran out.
The scale is not small. Landlords file about 3.6 million eviction cases in the US in a typical year (Eviction Lab), and in the ones that end in a default judgment, the tenant lost without ever being heard. The single cheapest intervention is making the deadline impossible to miss. That is the whole job.
It is not legal advice, and it says so plainly. It explains your notice, cites your own paper, and hands you to free legal aid. That boundary is the point.
Two bugs that cost me real time
Bedrock threw AccessDeniedException the moment the Lambda tried to narrate, even though it worked from my laptop. Nova 2 Lite's us. model id is a cross-region inference profile, so the IAM policy needed the inference-profile ARN and the foundation-model ARNs across regions, not just one model ARN in us-east-1.
Then the app loaded but every call failed with Access-Control-Allow-Origin header contains multiple values. I was setting CORS in the Lambda and the Function URL was also adding it. Two sources, duplicated header, browser rejects it. I deleted the manual headers. One source of truth fixed it.
Neither bug is exotic. Both only show up when you actually ship instead of demoing on localhost.
Try it and see the code
The live app, no install and no account:
The full source, the CDK, the EARS spec, and the tests:
simplynadaf
/
duedate
Understand your eviction notice before the deadline. Upload the notice, get the one date you cannot miss, what to do, and your rights, cited to your own paper and in your language. Built on AWS (Textract + Bedrock + Lambda + CloudFront). Not legal advice. AWS Zero to Shipped.
📬 DueDate: Understand Your Eviction Notice Before the Deadline (2026)
Paste the eviction notice you received and get, in your language, the one date you cannot miss, exactly what to do before it, and your rights - with every statement pinned to the exact line of your own notice, and a hard refuse-to-invent rule so it never tells a frightened person something their paper does not actually say.
AWS Zero to Shipped · Category: #social-good · Lane: #community
â–¶ Watch the 2-minute demo
Important
DueDate is not legal advice and not a lawyer. It explains what your notice says, cites the exact line it read, and points you to real free legal aid. It never states a right or a step your notice does not actually contain. If it cannot ground an answer in your paper, it says so and routes you to free help. See The Honest Take.
The Builder Center project, with the full write-up and the agent-to-AWS proof:
What I would tell you to copy
The pattern is not specific to eviction notices. It works for any case where an AI reads a document for someone who cannot check the answer: medical bills, debt letters, benefit terminations, immigration notices.
Split the work. Let code own the facts. Let the model own the language. Put a filter between them that drops anything ungrounded. Then write a test that tries to make it lie, and do not ship until that test passes.
The result is an AI you can hand to a frightened person, because the worst thing it can do is say "I do not know, here is who can help."
If you found this useful, I would love a like, a comment, and a share. What document would you want it to read next: medical bills, debt letters, or benefit-termination notices? Tell me in the comments.
Follow me for more on AWS architecture, DevOps, and AI Infrastructure:
Portfolio | LinkedIn | Dev.to | YouTube | Email | AWS Builder Center | X




Top comments (0)