How it works
What runs where, what the AI can and cannot see, where a person decides, and the commands for day-to-day running.
In short
Care Desk drafts replies to routine patient messages from the patient's own records and a written support policy, and a person sends every one. Before any AI is involved, personal details are swapped for placeholders and plain safety rules read the original message. Anything clinical, a crisis or a report of a death stops there: no AI reply, the patient's orders go on hold and a clinician takes it. Routine messages are sorted by a fast Claude model and answered by a stronger one using only numbered sources, and every date, amount and order number in a draft is checked against those sources before an agent sees it.
- Safety rules run before any AI
- A person sends every reply
- Every fact traced to a source
How a message moves
Seven steps, always in this order. The same pipeline runs ahead of time on the demo messages and live in the Try it box.
Where data lives
A static site and one Worker. There is no database of patient data in this demo, and nothing typed into the live box is stored.
What the AI never sees
Names, email addresses, phone numbers, addresses, dates of birth and health numbers are replaced in code before the first AI step. Here are three of the demo's fictional messages, before and after.
What the agent sees
The message as it arrived
What the AI sees
Placeholders, before any AI step
- Removed before the AI saw it
- Kept, because the reply needs them
The same code runs on every message, in the desk and in the live box. It over-removes on purpose: hiding a doctor's name costs nothing, leaking a patient's would.
Open it on the desk- The patient's records reach the drafter as numbered sources (plan, orders, charges and appointments) with no patient name, contact details or date of birth in them.
- Drafts open with
[FIRST_NAME]and sign off with[AGENT_NAME]. The desk fills both in on screen, so the AI never sees or invents a name.
Where a person decides
Care Desk only acts on its own to protect a patient: it holds orders and withdraws drafts. It never sends anything.
- Open the desk
Every reply that is sent
A draft waits for an agent. Send, Edit and Escalate are the only ways forward, and Undo is there straight after sending.
The desk: the Send button, or Ctrl+Enter
- Open the clinician queue
Every clinical item
Clinical questions, side effects, serious reactions, crisis language and bereavements never get an AI reply. A clinician reads why it was escalated, the words that triggered it and the patient's context, then replies personally.
The clinician queue
- See a hold
Every hold release
A safety stop puts the patient's orders on hold straight away. Only a clinician can resume them, and resuming needs a written note, kept with the message.
The clinician queue: orders on hold
When something fails
Every failure lands on a person, never on the patient. The examples open demo messages in that state.
| When | What Care Desk does | Who acts next |
|---|---|---|
| The AI is unavailable | Details are still removed and the safety rules still run, because both are plain code. Nothing is drafted: every message goes to a person. The live box says it is resting and shows the saved examples. | Next:Agents reply as they do today |
| The sorter is not sure | Below 75% confidence there is no draft. The message shows as Write the reply, with the sorter's best guess shown. | Next:An agent writes the reply |
| The fact check fails | Any date, amount, order or tracking number that is not in the sources blocks the draft, and so does any dosing figure. Send is never offered, and the trail names the fact that failed. | Next:An agent writes the reply |
| A safety rule fires | The trail stops at the safety rules. No AI reply, the patient's orders go on hold, and the message joins the clinician queue, urgent first. | Next:A clinician |
| The drafter declines | When the sources do not hold the answer, the drafter says so instead of guessing. No draft; the reason is shown on the trail. | Next:An agent writes the reply |
| A death is reported later | Every unsent draft to that patient is withdrawn at once and their orders are held. Nothing is sent to them unless the clinician finds the report was about someone else. | Next:A clinician, then the family |
Agent guide
docs/AGENT-GUIDE.md
One page for anyone working the Care Desk queue. Plain words, no training needed.
The one habit
Read, then send. Care Desk drafts the routine replies. You read each draft, check it against the patient's records beside it, and you decide. Nothing goes to a patient until you press Send.
Your queue
Messages arrive from Australia, New Zealand and the UK, by email and chat. The most serious come first, and within each group the longest waiting come first. Every row shows the type, the risk (an icon and a word, never colour alone), how long it has waited and the country.
The filters above the queue group the rows: Write the reply, Ready to send and Clinician and urgent. All shows everything still open, and Done shows what you have sent or escalated. Rows you cannot act on yet sit at the bottom of the queue, under Waiting on a clinician for this patient.
| What the row says | Filter | What it means | What you do |
|---|---|---|---|
| Ready to send | FilterReady to send | What it meansA drafted reply passed every check | What you doRead it, then send, edit or escalate |
| Write the reply | FilterWrite the reply | What it meansNo draft: the patient asked for a person, it is a complaint or privacy request, the AI was unsure, or a check failed | What you doWrite the reply yourself |
| Held: clinician first | FilterWrite the reply | What it meansA draft exists, but this patient reported something serious in another message | What you doAsk the on-call clinician before anything is sent |
| Withdrawn | FilterAll | What it meansSomeone has told us this patient may have died | What you doSend nothing. The clinician handling that message decides what happens next |
| Clinician or Urgent | FilterClinician and urgent | What it meansA safety rule stopped the message: no AI reply, and a clinician answers | What you doNothing to write. Urgent ones also have the patient's orders on hold |
Working a message
- Open it. The message is in the middle, with the patient's orders, charges and appointments. The check trail is on the right.
- Read the draft. Every fact in it has a number, like [1], pointing to the order, charge or policy it came from. The fact check has already matched every date, amount and order number to those sources.
Decide.
The draft greets the patient by first name and signs off with your name. The AI never saw either: the desk fills them in for you.
Keyboard
| Key | Action |
|---|---|
| J and K | ActionNext and previous message |
| E | ActionEdit the draft |
| Ctrl + Enter (Cmd + Enter on a Mac) | ActionSend |
| X | ActionEscalate to a clinician |
| ? | ActionShow every shortcut |
Always
Never
When something looks wrong
Open See what the AI saw on the trail to see exactly what it was given. If a draft is wrong, edit it or write your own, then tell your team lead which message it was, so the rule or prompt can be fixed and the case added to the test set.
Runbook
docs/RUNBOOK.md
How to run Care Desk day to day. Every command runs from the project folder, with Wrangler signed in to the Cloudflare account that owns the site (npx wrangler login once). --remote means the live settings, not a local copy.
What is running
Raise or lower the live-box limits
One command. It takes effect within about a minute, with no redeploy.
npx wrangler kv key put --binding=CARE_DESK_KV --remote config '{"enabled":true,"dailyUsd":5,"perVisitorPerHour":20,"vipDailyUsd":10,"vipPerVisitorPerHour":60}'The value replaces the whole config, so always write every field. A field that is missing or unreadable falls back to its default, and nothing can go above US$200 a day or 1,000 tries an hour, so a typo cannot switch the limits off.
| Field | What it limits | Default |
|---|---|---|
dailyUsd | What it limitsPublic spend per day, in US dollars | Default3 |
perVisitorPerHour | What it limitsPublic tries per visitor per hour | Default10 |
vipDailyUsd | What it limitsPrivate-link spend per day, in US dollars | Default10 |
vipPerVisitorPerHour | What it limitsPrivate-link tries per visitor per hour | Default60 |
enabled | What it limitsThe on/off switch (see "Pause the live box") | Defaulttrue |
Days are UTC. To see the current values:
npx wrangler kv key get --binding=CARE_DESK_KV --remote configNo output means the key is not set and the defaults apply.
Add or revoke a VIP link
A VIP link is a private link for someone who needs more tries, such as a team lead trialling the live box. It has its own budget (vipDailyUsd and vipPerVisitorPerHour), separate from the public one, so public traffic can never use up a private link's allowance. It also skips the bot check.
Make a code that cannot be guessed (24 letters, digits,
-or_; codes shorter than 12 characters are ignored):node -e "console.log(require('crypto').randomBytes(18).toString('base64url'))"
Store every live code in the
VIP_TOKENSsecret, separated by commas. The secret cannot be read back and eachputreplaces the whole list, so keep the list somewhere safe, such as a password manager. Paste it when asked:npx wrangler secret put VIP_TOKENS
- Send the link:
https://caredesk.autopilotyourworkflow.com/try/?k=CODE
The page takes the code out of the address bar and keeps it for that browser tab only.
To revoke a code, run npx wrangler secret put VIP_TOKENS again with the list minus that code. It stops working as soon as the secret is saved. To see how many codes are live, use the status check under "Check spend".
Rotate the API key
- In the Claude Console, create a new key named
care-desk. Store it in the Worker. Paste it when asked; the Worker picks it up at once.
npx wrangler secret put ANTHROPIC_API_KEY
- Send one message through "Try it" and check the trail reaches "Facts checked".
- Revoke the old key in the Console.
- Put the new key in
.env.localtoo, sonpm run precomputekeeps working on this machine. Never commit it.
Check spend
Pause the live box
Write the config with "enabled": false, keeping the other fields as they are:
npx wrangler kv key put --binding=CARE_DESK_KV --remote config '{"enabled":false,"dailyUsd":5,"perVisitorPerHour":20,"vipDailyUsd":10,"vipPerVisitorPerHour":60}'Within about a minute every live try, public and VIP, is turned away before any AI call. "Try it" shows the same notice as when the day's live AI budget is used up, and still runs the safety rules in the visitor's browser, with the labelled offline stand-in for the AI. The rest of the site is static and carries on as normal. To resume, put the same value back with "enabled": true.
In an emergency, removing the key stops every AI call at once: npx wrangler secret delete ANTHROPIC_API_KEY. The live box then answers with the offline stand-in until a key is put back.
Re-run the evaluation
Run this after any change to the safety rules, the prompts or the test set.
npm run precompute # runs every demo message through the pipeline with Claudenpm run eval # scores the results against the labelled test setnpm run deploy # rebuilds the site data and publishes itprecompute keeps saved results that already match the current rules, prompts and models, so a second run only pays for what changed. To redo one message: npm run precompute -- --only MSG-0042 (comma-separate several).
What the release gate means
npm run eval is the release gate. It fails (exit code 1) when:
Do not deploy on a failed gate. Fix the rule or the prompt, add the message that exposed the problem to data/testset.json as a new case, and run the three commands again. The Test results page shows exactly what the last passing run measured.
Stack
Next.js 16, React 19 and TypeScript, styled with Tailwind CSS, served as a static site by one Cloudflare Worker, with the Claude API (Claude Haiku 4.5 to sort, Claude Opus 5 to draft) and Vitest for the tests.