# Clinical Intake (healthcare)

> A behavioral-health intake that makes compliance *visible*, not just claimed — for digital-health builders. Synthetic data only.

## What it is — compliance you can see

`clinical-intake` is the healthcare-lead exemplar blueprint: a behavioral-health
intake record whose PHI fields are handled by the platform, not by your app code.
The point is that you can *demonstrate* the handling — open a record, look at its
history, run a search — in about 60 seconds, rather than take a compliance claim on
faith. Everything below runs on synthetic data against an invite-only 0.x preview.

## What `bootstrap` provisions

`vectros bootstrap --blueprint clinical-intake` provisions one context
(`clinical-intake`), one schema (`intake`, `indexMode: HYBRID`), a service
principal, a least-privilege access profile, a narrow scoped key, and one synthetic
seed record. The `intake` schema separates working fields from PHI:

**Non-sensitive working fields** (safe to index, search, and filter):

- `caseId` — required; the stable dedup/lookup key.
- `presentingConcern` — `searchable: true`; a non-PHI summary, safe to put in the index.
- `program` — `filterable: true`; the service line the intake routes to.
- `status` — `filterable: true`; enum `new | in_review | scheduled | closed`.
- `submittedAt` — ISO-8601 date; range-queryable (a coordinator can pull "intakes submitted this week").

**Sensitive (PHI) fields** — each marked `sensitive: true`:

- `clientName`
- `dateOfBirth`
- `ssn`
- `clinicalNote`

The schema sets `capabilities.auditHistory: true` explicitly (it is the platform
default, but a compliance exemplar self-documents its posture).

It declares five lookup fields — four exact-match, one range — each a deliberate,
**permanent** choice (a field's lookup shape is locked once the schema is live):

- `caseId` (unique) — exact get/upsert by the stable intake id.
- `program` and `status` (non-unique equality) — so a coordinator can **enumerate a
  worklist** directly via `record_query`: "all `outpatient-counseling` intakes", "all
  `new` intakes". The deterministic, no-query counterpart to semantic `hybrid_search`.
- `clientName` — a **sensitive blind-index** lookup. The PHI value is HMAC'd into a
  per-tenant index, never stored in the clear and never in the search index, yet a
  coordinator can still **find the intake for an exact client name**. A blind hash is
  not orderable, so this lookup is exact-match only (never range).
- `submittedAt` — a **range** lookup: ordered date windows ("intakes submitted this
  week"), distinct from the equality enumerations above.

The other PHI fields (`dateOfBirth`, `ssn`, `clinicalNote`) are deliberately **not**
lookup fields here. (Equality lookups draw on the schema's 7 fast index slots; the
range lookup uses a relationship row instead.)

The access profile is deliberately narrow:

```
allowedActions: ['records:r', 'records:c', 'records:u', 'search:r', 'schemas:r']
```

Note what is *absent*: there is no `records:d` (intakes are retained, not deleted)
and **no `s` reveal scope** — so the bootstrapped key itself cannot un-redact the
PHI fields. That is the whole point of the demo: the credential you are handed
literally has no path to the plaintext sensitive data.

## Before you start

Honest prerequisites:

- An invite to the 0.x preview and the Vectros CLI.
- A dev-portal Cognito bridge token (a human step) so the CLI can talk to the API.
- For the visual part of the demo, access to **app.vectros.ai** (the reference
  data-plane app) and/or the MCP server wired into your agent.

> **Synthetic data only — never enter real PHI.** This is a preview demo tenant.
> The seed record and every example field below are fictional. Do not type a real
> name, real date of birth, real SSN, or any real clinical text into it. The
> `clientName: 'Jordan Sample'`, `ssn: '000-00-0000'`, and clinical note in the
> seed are illustrative, not a real person.

## 1. Bootstrap the blueprint

```bash
# install once: npm i -g @vectros-ai/cli  (or use npx @vectros-ai/cli, below)
npx @vectros-ai/cli bootstrap --blueprint clinical-intake
```

This provisions the schema, context, service principal, gated access profile, and
the seed record, then prints a scoped key (`ssk_*`). The bootstrap is idempotent.
If you are driving an agent via MCP, the key is auto-merged into your Claude Desktop
config (for Cursor/Cline, pass `--print` and paste the `mcpServers.vectros` block)
so the agent can act as the intake service principal — with exactly the five scopes
above and nothing more.

## 2. Create a synthetic intake

Create an intake either through the MCP server (`record_create`) or directly in the
data-plane app. Use synthetic PHI only — for example:

```json
{
  "caseId": "demo-intake-001",
  "presentingConcern": "Sleep difficulty and low mood over the past month; seeking counseling.",
  "program": "outpatient-counseling",
  "status": "new",
  "submittedAt": "2026-06-14",
  "clientName": "Jordan Sample",
  "dateOfBirth": "1990-01-01",
  "ssn": "000-00-0000",
  "clinicalNote": "Synthetic note for demonstration. Reports difficulty sleeping; no acute safety concerns noted."
}
```

The four `sensitive` fields are written through the same API as the rest — your app
does nothing special. The platform takes over from there.

> **Expected:** when you read the record back through this blueprint's key, the four
> `sensitive` fields come back masked (`[redacted]`) — that is the feature working, not
> a bug. The bootstrapped key deliberately carries **no `s` reveal scope**, so it
> cannot un-mask PHI; revealing plaintext requires a separate, more-privileged key
> minted with `records:r:<type>:s`. The non-sensitive fields read back normally.

## 3. Watch the redaction (the aha)

Three observations, each of which proves a specific, implemented claim.

**(a) Open the record's History in app.vectros.ai.**
Navigate to the record detail page and look at the History card. Every version row
shows the change type, when it happened, who made it, and which fields changed —
but the `sensitive` fields are never rendered in plaintext, in any version row,
regardless of your scope. *Why this proves it:* the PHI was **redacted at write
time** — destroyed before the audit snapshot was ever persisted, not masked behind
a permission check. There is no plaintext copy in the history to leak, so no scope
can surface one.

**(b) Edit a non-sensitive field and watch the audit trail grow.**
Change `status` from `new` to `in_review` and reload the History. A new immutable
version row appears, recording the change to `status` — while the sensitive fields
stay redacted across every row. *Why this proves it:* the audit/version history is
append-only and tamper-evident (a SHA-256 state-continuity chain), so the trail of
*who changed what, when* is preserved — without ever capturing the PHI.

**(c) `hybrid_search` for a phrase from the clinical note → zero hits.**
Run a hybrid search for a distinctive phrase that exists only in `clinicalNote`
(e.g. "difficulty sleeping"). It returns no results. Then search for a phrase from
`presentingConcern` and confirm the record *does* come back. *Why this proves it:*
sensitive fields are **excluded from the search index** at write time, so a note
phrase can never leak through search results — while the non-PHI working fields
remain fully searchable.

## How it maps to Vectros compliance

Each bullet is tied to an implemented capability:

- **Redact-at-write (PHI):** `sensitive` fields are stripped from audit snapshots
  and structured diffs at write time — destroyed before persist, unrecoverable
  regardless of scope. This is *not* reversible masking.
- **Immutable, tamper-evident audit / version history:** every write to an audited
  model emits an immutable version row (change type, who, previous content, changed
  fields), with heavy content externalized to a WORM Object-Lock (GOVERNANCE)
  bucket. The state-continuity chain is tamper-**evident** (SHA-256), not
  tamper-proof.
- **Search-index exclusion:** sensitive fields never enter the search index, so PHI
  cannot leak via search results — while non-PHI fields stay searchable.
- **Blind-index lookup (find-by-PHI without exposing it):** `clientName` is a
  sensitive lookup — the value is HMAC'd into a per-tenant blind index, so an exact
  find-by-name works without the plaintext ever being stored in the clear or entering
  search. (The lookup value travels in the request body, not the URL.)
- **Read-time masking + the `s` scope:** in responses, sensitive fields are masked
  unless the token carries the `s` reveal scope. This blueprint's key does **not**
  carry `s`, so the demo credential cannot un-redact PHI.
- **Context isolation:** `contextId` is a mandatory, auth-derived, fail-closed
  partition key on records/schemas/documents/folders, and lookups are
  same-context-only.
- **Least-privilege keys:** the bootstrapped `ssk_*` carries exactly five
  data-plane scopes — no delete, no reveal, no control-plane.
- **Independent security auditing:** the platform has been through independent
  security audits with **prior Criticals remediated**, and stays under active
  pre-launch re-auditing through the preview.

## Notes & limits

- **Synthetic data only.** Never enter real PHI into the demo tenant.
- **Tamper-evident, not tamper-proof.** The audit chain detects tampering; a
  continuous external verifier is not part of this claim.
- **Not claimed here:** end-subject right-to-erasure, data residency, and BYOK /
  per-tenant CMK are *not* part of this walkthrough and are not implemented for this
  demo. Do not read them in.
- **Legal terms.** BAA and single-region posture are scoped to the partner data
  plane and are available for discussion under NDA — treat specific legal coverage
  as soft until confirmed with us directly.
