Skip to content

Repository files navigation

Attest

An AI agent that handles the prior-authorization lifecycle for small specialty practices — from deciding whether a service needs a prior authorization (PA), to assembling a criteria-matched request, to auto-drafting the appeal when a payer denies.

License: Apache 2.0 Status: in development Live demo Hackathon: Agents for Humans Built with: Strands Agents

Project status — the full loop runs, end to end, on a public URL. Phases P0 through P6 are complete and gated, plus multi-agent orchestration and the AgentCore entrypoint (P7-S1–S3): you upload a clinical note, and it becomes a criteria-matched submission packet behind a clinician approval gate; upload the payer's denial and it becomes an evidence-backed appeal behind a second one. The whole suite — 366 tests — replays offline from recorded model responses in about thirty seconds, with no API key and no network, on Linux, macOS and Windows alike. What remains is the demo video and the Devpost entry (P8-S4, P8-S5), plus an open final-improvements phase (P9). See Project status & roadmap, and STATUS.md for the live board.


Contents


The problem

Prior authorization is insurer pre-approval required before a clinician can deliver a covered service. It is the largest administrative time-sink in outpatient care, and small/solo specialty practices (behavioral health, physical therapy, occupational/speech therapy, pain, imaging) absorb it directly because they have no dedicated PA staff.

  • Practices complete ~39–43 PA requests per physician per week (AMA 2024/2025 surveys).
  • ~13 hours/week of physician + staff time is consumed by PA.
  • ~31% of physicians report requests are often or always denied — and denials have risen over five years.
  • Appeals work but are under-used — many practices don't appeal because they expect to lose, and those who do rebuild each appeal from scratch.

Existing PA vendors target large health systems and deep EHR integrations. Solo and small practices are left with manual portals, faxes, and copy-paste appeal letters. The judgment-heavy part — does this need a PA? does the note satisfy the payer's criteria? how do we rebut this specific denial? — is exactly what current tooling leaves to a human. That is the gap Attest fills.

What Attest does

Given a patient's clinical note and insurance details, Attest:

  1. Reads and structures the case — service requested, diagnosis, requested duration/units, payer, and plan.
  2. Determines whether a prior authorization is required for that service under that payer/plan, and explains why, pointing to the policy it relied on.
  3. Finds the payer's own clinical criteria and checks the note against each one, marking each as met, unmet, or insufficiently supported — quoting the exact note evidence for each.
  4. Flags gaps — where a criterion isn't supported, it says so and asks the practice for the missing detail rather than guessing.
  5. Assembles a submission-ready request — completed request fields, a medical-necessity justification written only from approved evidence, and a plain-language checklist of how each criterion is covered.
  6. Pauses for the clinician to approve or edit every clinical assertion before anything is submitted. (Gate 1)
  7. Produces the outbound submission artifact once approved.
  8. Turns a denial into an appeal — reads the denial reason, identifies which criteria the payer contests, and drafts an appeal citing the payer's own policy language and the note evidence — again held for human approval. (Gate 2)
  9. Tracks each case and its appeal deadline, and reuses language from prior successful appeals on similar future denials.

The goal: cut the human time per PA from ~20–30 minutes to a short review-and-approve step, raise first-pass approval rates by mapping every submission to the payer's stated criteria before it goes out, and make appeals the default rather than the exception.

Who it's for

  • Primary: the office manager, front-desk staff, or owner-clinician at a 1–10 provider specialty practice who personally handles PAs.
  • Secondary: the treating clinician who must approve the clinical justification.
  • Specialty focus: one specialty at a time, so the product speaks that specialty's language and payer rules precisely. The flagship demo case is outpatient behavioral health — TMS (transcranial magnetic stimulation) for treatment-resistant depression, whose criteria are among the most enumerable of any common PA (age, confirmed severe MDD, failed antidepressant trials at adequate dose and duration, psychotherapy trial, seizure/implant contraindications, baseline PHQ‑9/HAM‑D, FDA-cleared device). Physical therapy ships later as an extensibility pack to prove the engine is specialty-agnostic.

How it works

The engine runs autonomously between two hard human-approval gates:

Attest architecture — a clinical note is read by the intake specialist, routed to the payer's policy by find_pack, matched criterion by criterion, and every quote checked verbatim by a deterministic verifier before Gate 1 stops for a named clinician. A denial letter arrives separately and the appeal specialist drafts rebuttals, held again at Gate 2.

The same flow as text
flowchart TD
    A["Clinical note + insurance details"] --> B["Intake: structure the case"]
    B --> C{"PA required?"}
    C -- "unmapped" --> C1["Return UNKNOWN<br/>(never assume 'not required')"]
    C -- "yes" --> D["Find the payer's clinical criteria"]
    D --> E["Match each criterion to note evidence<br/>met / unmet / insufficient"]
    E --> F["Deterministic evidence verifier<br/>every quote must appear verbatim in the note"]
    F --> G["Gap list: ask the practice for missing detail"]
    F --> H["Assemble submission packet<br/>medical-necessity justification from verified evidence only"]
    H --> GATE1{{"Gate 1 · clinician approves & edits"}}
    GATE1 --> I["Emit submission artifact (Markdown + PDF)"]
    I --> J["Payer denies"]
    J --> K["Parse denial → contested criteria"]
    K --> L["Draft appeal citing the payer's own policy + verified evidence"]
    L --> GATE2{{"Gate 2 · clinician approves & edits"}}
    GATE2 --> M["Emit appeal artifact + track appeal deadline"]

    classDef gate fill:#f6c343,stroke:#7a5901,color:#1a1a1a;
    class GATE1,GATE2 gate;
Loading

Attest is built as a multi-step, tool-using agent system on the Strands Agents SDK, with specialist agents (intake, criteria matching, packet assembly, appeal drafting) composed under an orchestrator. Both approval gates are implemented on Strands' first-class human-in-the-loop primitive (BeforeToolCallEvent.interrupt(...)), so the gate holds even when the agent is driven headlessly — it is a product requirement, not UI logic.

Design principles

These are non-negotiable and encoded as tests, not aspirations:

  • The agent assembles and argues; humans decide and submit. It never makes a coverage or medical-necessity decision on its own. Two hard gates — before submission and before an appeal is sent — require a clinician to review and approve every clinical assertion.
  • Evidence-traceable by design. No clinical claim enters any document unless a deterministic verifier confirms the quote appears verbatim in the source note (whitespace runs and Markdown emphasis markers are treated as equivalent; nothing else is). A semantically correct paraphrase is rejected on purpose — paraphrase is the hallucination failure mode being defended against. Unverifiable spans downgrade their criterion to insufficient and are logged, never silently dropped.
  • Absence of a policy is not evidence that no PA is needed. An unmapped service returns UNKNOWN, never "not required" — telling a practice "no PA needed" because a policy wasn't found is the worst possible failure.
  • The engine is specialty-agnostic; specialties are data. Policy packs and extraction schemas are data files; no specialty knowledge is hardcoded. Adding a specialty costs a pack, not a rewrite.
  • Synthetic data only. All notes and denial letters are synthetic and banner-marked; ground-truth expected outcomes are committed before any matching code exists, which is what makes every later step objectively verifiable. Live payer-portal / EHR integration and real-PHI handling are explicitly post-hackathon.

Tech stack

Area Choice
Language Python
Agent framework strands-agents, strands-agents-tools
Model Gemini via Google AI Studio — gemini-3.5-flash-lite (fast) and gemini-3.6-flash (reasoning). Amazon Bedrock is the intended provider and the swap is one constructor in src/attest/llm.py — nothing else names a provider. See DECISIONS.md.
Structured output Strands structured output → Pydantic models (typed verdicts, never parsed from prose)
Human-in-the-loop BeforeToolCallEvent.interrupt(...)
Deployment Amazon Bedrock AgentCore Runtime (BedrockAgentCoreApp + @app.entrypoint) — planned, P7
UI Streamlit, hosted on Streamlit Community Cloud for a free, public, judge-testable link
Documents fpdf2 for the submission and appeal PDFs
Validation / data pydantic, pyyaml
Testing pytest, with every build step gated behind a cumulative test marker

Project status & roadmap

Work is organized into small, individually verifiable steps. A step is done only when its gate (./scripts/verify.sh <STEP_ID>) exits zero — running that step's tests plus every prior step's, so a later step cannot silently break an earlier one. The authoritative Definitions of Done live in PLAN.md; live progress lives in STATUS.md.

Phase Focus State
P0 Foundation, model provider access, engineering protocol, repo skeleton ✅ done
P1 Domain models, policy-pack format, real public TMS policies, synthetic corpus + ground truth ✅ done
P2 Intake (note → structured case) and PA-required determination ✅ done
P3 Criteria engine — per-criterion evidence matching + the deterministic verifier (the core) ✅ done
P4 Packet assembly & Gate 1 (approval before submission) ✅ done
P5 Denial → appeal loop & Gate 2 (approval before appeal) ✅ done
P6 Case tracking, precedent reuse, Streamlit UI, public deploy ✅ done
Submittable product complete through here
P7 Multi-agent orchestration depth + AgentCore deployment 🟡 S1–S3 done; S4 blocked on AWS Bedrock authorization
P8 Submission deliverables — README, architecture diagram, metrics, demo video, Devpost 🟡 S1–S2 done; S4 (video) and S5 (Devpost) outstanding
P9 Final improvements — upload-driven intake, a keyless green gate, an error boundary, criteria that say what they mean (S1–S3, S8 done); phase deliberately open 🟡 in progress

▲ = required for a viable submission.

The submission and appeal loops both run end to end, and every number below is reproducible from a clean clone with no credentials:

Measure Result
Criterion verdicts vs. committed ground truth 30/30 across three cases and two payers
Evidence spans verifying verbatim against their note 39/39 (100%), with zero criteria downgraded
Gap list vs. ground truth exact on all three cases
Contested criteria parsed from the denial letter exact — plus the one objection that maps to no criterion, surfaced rather than dropped
An uploaded note reaching its own payer's pack derived from the extracted payer/plan/CPT — an unlisted payer stops the review rather than guessing
./scripts/verify.sh ALL --offline exits zero, 366 tests, no API key, on Linux, macOS and Windows

The same command runs in CI on every push, on Linux, with no credentials configured — the judge's scenario rather than ours.

Repository layout

Alongside the engine, the repository holds the product definition and the engineering protocol that lets two contributors hand work off cleanly across sessions and machines:

File Purpose
Attest-PRODUCT.md The product specification — what it is, what it must achieve, and the rules it must satisfy. No architecture or build planning.
PLAN.md The contract: every phase, step, and machine-checkable Definition of Done. Changes rarely.
STATUS.md The live status board — one row per step; the single source of truth for progress.
DECISIONS.md Append-only decision log — every non-obvious choice and the reasoning behind it.
LICENSE Apache License 2.0.
src/attest/ The engine: domain models, policy-pack loader, intake, criteria matching, the evidence verifier, and the gap list.
data/synthetic/ Synthetic notes and denial letters, plus ground truth committed before the matching code existed.
cassettes/ Recorded model responses, so the whole suite replays offline with no API key and no quota.
scripts/verify.sh The step gate. Runs a step's tests plus every prior step's.
src/attest/demo.py python -m attest.demo — one case end to end in a terminal, stopping at Gate 1. No browser, no key.
docs/architecture.svg The agent graph, the verifier, both gates, and where it runs.
docs/setup.md Developer setup, model provider, and the daily-quota notes.
docs/uiux-review.md A walkthrough of the console judged against the scoring criteria, and the P9 steps drawn from it.

All of the above has landed, along with src/attest/packet/, src/attest/appeal/, the case store, the orchestrator, and app.py — the reviewer console, which takes its case as an uploaded document and reaches nothing preloaded.

Working protocol

This is a two-person, sequential build (one contributor works until their usage limit, then the other resumes on another machine). Because context is lost at every handoff, the three committed files above — plan, status, decisions — are the shared memory. A session starting cold reads STATUS.mdDECISIONS.mdPLAN.md, runs the gate for the last step marked done to verify (not trust) the previous session's claim, then continues.

Try it without installing anything

https://attest.streamlit.app — the live console, public and free, no account and no API key.

Nothing is preloaded. Upload a clinical note in the sidebar and walk it: Attest reads the note, works out from the note itself which payer's policy applies, checks the record against that policy criterion by criterion, shows each verdict with the exact quote behind it, and stops at Gate 1. Upload the payer's denial letter — a second document, as it is in a practice — and it drafts the appeal, stopping again at Gate 2.

No note of your own? The landing screen offers the synthetic corpus as downloads. Upload clean.md for a fully documented case, gap.md to see the agent ask the practice for what is missing, or denial.md plus denial_001.md for the full denial-to-appeal loop. Uploading denial.md after clean.md is the thing worth watching: the same screen, unchanged, reaches a different payer's criteria, because the pack is derived from what was read rather than chosen from a menu.

Every model call replays from committed cassettes, so the hosted app needs no credentials and shows the same results this repository reproduces offline — provided you upload the committed files unmodified, since the cassette key is a hash of the note text.

The hosted container is ephemeral — Streamlit Community Cloud restarts it when the app sleeps, so stored cases do not persist across days and precedent reuse demonstrates within a session. That is a property of the free hosting tier, not of the case store, which is durable and proven across real processes by test_case_survives_process_restart.

Getting started

The engine, the reviewer console, and the full test suite all run today, offline, with no API key and no network — every model call replays from a recorded cassette. Once installed, streamlit run app.py opens the console locally; upload a note from data/synthetic/notes/ to walk a case.

git clone https://github.com/ojassug/Attest.git
cd Attest

Python 3.12 specifically — 3.13+ is too new for the Strands dependency tree. CPython's venv writes executables to bin/ on POSIX and Scripts/ on Windows:

# macOS / Linux
python3.12 -m venv .venv && .venv/bin/pip install -e ".[dev]"
# Windows
py -3.12 -m venv .venv && .venv/Scripts/python.exe -m pip install -e ".[dev]"

See it work in about ten seconds, with no browser and no key:

python -m attest.demo --case clean

It reads a synthetic note, works out which payer's policy applies, checks the record criterion by criterion, prints the evidence counts and the gap list, and stops at Gate 1 with the content hash a clinician would approve. It writes nothing — there is no --approve flag, because approval is a person reading the assertions. Try --case gap to watch it ask the practice for what is missing, --case denial for a different payer's criteria entirely, or --case pt for a different specialty. --note path/to/note.md reads your own.

Then run the full gate. It needs nothing else — no key, no network:

./scripts/verify.sh ALL --offline

Expect 366 passed, 5 deselected. The five are the provider-connectivity tests, which cannot be replayed from a cassette; --offline deselects them and that is a valid pass. The same command runs in CI on every push, on a clean Linux machine with no credentials configured — the judge's scenario rather than ours.

And to use the console locally:

streamlit run app.py

To re-record cassettes or run the three provider-connectivity tests you need a free Google AI Studio key in .env. Note the free tier allows 20 requests per day per model — see docs/setup.md for the model roster, rotation, and why cassettes exist. Amazon Bedrock is a one-constructor swap in src/attest/llm.py; the AgentCore entrypoint is written and verified, and deployment is blocked on Bedrock model access rather than on code — see STATUS.md.

Compliance, safety & scope

  • Privacy-first. The product is designed around encryption in transit and at rest, least-privilege access, and a complete audit trail of every agent action and every human approval.
  • Synthetic data only for the hackathon. No real patient data is used in the build or the demo video. Live payer-portal / EHR integration and real-PHI handling are explicitly post-hackathon, gated on the appropriate agreements.
  • Honest scope. Attest is an assistant that produces submission-ready and appeal-ready documents for human approval — not an autonomous authority over care decisions.

Explicitly out of scope: it is not an EHR, practice-management, or billing/clearinghouse system; not a coverage or medical decision-maker; not a general healthcare chatbot; and it does no live payer/EHR integration or real-PHI handling in the hackathon build.

Hackathon context

Attest is being built for the AWS "Agents for Humans" hackathon (sponsored by Amazon Web Services, administered by Devpost), in the Professional Agents track. The submission window runs Aug 10 – Sep 14, 2026. The hackathon requires a newly built agent on the Strands Agents SDK that does real work end to end; deploying with Amazon Bedrock AgentCore is optional but strengthens the technical-implementation score. Judging weighs Technical Implementation, Design, Potential Impact, Creativity & Originality, and Presentation equally.

License

Licensed under the Apache License 2.0.


"Attest" is a working name and may change.

About

An AI agent that handles the prior-authorization lifecycle end to end for small specialty practices: decides whether a service needs a PA, assembles a criteria-matched request from the clinical note, and auto-drafts the appeal when a payer denies. Built with the Strands Agents SDK on Amazon Bedrock — human-approved, evidence-traceable.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages