> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# HIPAA readiness checklist — pre-go-live runbook

> Turn the free HIPAA/BAA readiness checklist into a go/no-go run: map each of the five tenant-owned steps to the control that sets it, walk them in order, then prove the send-time gate passes before your first PHI message.

# HIPAA readiness checklist — pre-go-live runbook

The [HIPAA/BAA readiness checklist](https://orbit.devotel.io/en/tools/hipaa-checklist)
is a free, browser-side self-assessment on the [developer tools hub](https://orbit.devotel.io/en/tools).
Mark the five tenant-owned steps you have covered and the panel scores your
readiness, linking each open item to the page that configures it — no
account required.

This runbook takes that checklist from a score to a go/no-go decision: it
maps each step to the exact control that sets it, walks the five steps in
order against a live workspace, and ends with a test PHI send that proves
the send-time gate passes. The [BAA flow](/compliance/baa) remains the
authoritative reference for the state-machine vocabulary; this page is the
operator sequence.

<Warning>
  Every step is **tenant-owned**: you decide whether Protected Health
  Information (PHI) is in scope and configure the controls on your own
  workspace. Orbit supplies the e-sign pipeline and the enforcement point —
  the send-time gate that rejects PHI traffic with `HIPAA_BAA_REQUIRED`
  until a BAA is executed — but the legal determination that PHI is in
  scope is yours to make with counsel. The checklist reports the coverage
  you self-attest; it does not enforce it. This tool is not legal advice.
</Warning>

## Where each step runs

| # | Checklist step | Control that sets it | Readiness signal the checklist looks at | Deep guide |
| - | - | - | - | - |
| 1 | PHI-scope attestation | **Settings → Compliance → BAA → "Start handling PHI"** (`POST /api/v1/compliance/baa/require`) | `hipaa_required = true`, state `pending` | [BAA flow](/compliance/baa) |
| 2 | BAA execution | **Settings → Compliance → BAA → execute form** (`POST /api/v1/compliance/baa/execute`, owner-only) | `baa_status = executed`, `days_until_expiry` counting | [BAA flow](/compliance/baa) |
| 3 | HIPAA mode toggle | **Settings → Compliance → HIPAA controls** (`PUT /api/v1/settings/hipaa`) | HIPAA mode enabled after BAA reads `executed` | [HIPAA controls](/compliance/hipaa) |
| 4 | Role-based PHI access | **Settings → Members/Roles** for owners/admins; re-auth required to toggle | Owners + admins read message content and PHI log; developers/viewers/billing do not | [HIPAA controls](/compliance/hipaa) |
| 5 | Minimum-necessary audit | `GET /api/v1/settings/hipaa/phi-access-log` pageable log; export via **Settings → Audit log** | Recent PHI reads carry a reason code; export lines up | [Audit log export](/compliance/audit-export) |

One deep destination per step — the checklist item and the table above both
point to the same page, so a visitor and an operator land on the same
control.

## Walk the five steps

Run these in order on the workspace you intend to take live. Steps 1–2 are
owner-level acts; steps 3–5 are operator verifications.

### Step 1 — Attest that PHI is in scope

1. Open **Settings → Compliance → BAA** in the dashboard.
2. Confirm the current state: for a fresh organization it reads
   `not_required` (no PHI declared).
3. Click **"Start handling PHI"** (or call `POST /api/v1/compliance/baa/require`
   with an optional recorded `reason`).
4. The state moves to `pending` and the execute form opens — the attestation
   is the event that raises `hipaa_required`, and it appends a
   `compliance.baa.hipaa_required` audit entry.

Checklist signal: mark step 1 covered when `GET /api/v1/compliance/baa`
reads `hipaa_required: true` and `baa_status: pending`. Full endpoint
detail in the [BAA flow](/compliance/baa#3-attest-that-phi-is-in-scope).

### Step 2 — Execute the BAA

1. While the state is `pending`, an **owner** opens the execute form on the
   same page (`POST /api/v1/compliance/baa/execute`).
2. Preview the rendered template (organization legal name filled in), then
   type the signer's legal name into `typed_attestation` — the server
   requires it to match `signer_name` exactly and rejects a mismatch with
   `400`.
3. On success the API returns `baa_status: executed` with the stored
   document reference, `expires_at` one year out, and
   `days_until_expiry: 365`.

Checklist signal: mark step 2 covered when `GET /api/v1/compliance/baa`
reads `executed`. Only `executed` satisfies the gates; the roles matrix
(owner/admin read, owner execute) is in the
[BAA flow](/compliance/baa#4-execute-with-a-type-the-name-e-signature).

### Step 3 — Enable HIPAA mode

1. With the BAA `executed`, enable HIPAA mode from **Settings → Compliance →
   HIPAA controls** (`PUT /api/v1/settings/hipaa`).
2. The call refuses with `403` while the BAA is any state other than
   `executed`; disabling later requires re-authentication.
3. Flipping it on engages the PHI control set: PHI access logging, enforced
   retention, residency pinning for voice recordings, and the PHI-adjacent
   audience registry for campaign prechecks.

Checklist signal: mark step 3 covered when the toggle is on and
`PUT /api/v1/settings/hipaa` no longer returns `403`. The full control set
is on [HIPAA controls](/compliance/hipaa).

### Step 4 — Restrict PHI access to the right roles

1. Map the people in your workspace to dashboard surfaces: message-content
   reads and the PHI access log are restricted to **owners and admins**;
   **developers and viewers** do not read the PHI access log; **billing
   roles** read neither.
2. Trim roles in **Settings → Members** before go-live so the first PHI
   read lands in an allowed role.

Checklist signal: mark step 4 covered when each PHI-bearing surface
(Messages, Contacts, the PHI access log) reads under an owner/admin role
only. The roles-versus-surface matrix is on
[HIPAA controls](/compliance/hipaa).

### Step 5 — Prove the minimum-necessary audit row

1. Make one PHI read (open a PHI-bearing message or contact).
2. Page through `GET /api/v1/settings/hipaa/phi-access-log` and confirm a
   row exists with the actor, the resource, and the **reason code** — that
   entry is your minimum-necessary evidence.
3. Queue an export of the audit trail and confirm the export lines up with
   the live log.

Checklist signal: mark step 5 covered when the PHI access log shows
reason-coded reads and the export path works. Export mechanics are on
[Audit log export](/compliance/audit-export).

## The go/no-go sequence: self-check → production send

Run this end to end before the first production PHI-bearing send:

1. **Run the free checklist** on the
   [tools hub](https://orbit.devotel.io/en/tools/hipaa-checklist) — it
   scores the five items and links each open one to its deep guide. Close
   the open items before you proceed.
2. **Walk steps 1–2 above** — attest PHI in scope and execute the BAA.
3. **Walk steps 3–4** — enable HIPAA mode and trim roles; the toggle now
   accepts instead of returning `403`.
4. **Read the gate inputs** — `GET /api/v1/compliance/baa` returns
   `baa_status: executed`, `hipaa_required: true`, `days_until_expiry`
   counting down from 365.
5. **Send a test PHI-bearing message.** A blocked gate returns `422` before
   wallet hold or provider dispatch, so the test send is free to run: if it
   passes, the gate verdict is clean and the workspace is live; if it
   returns `422 HIPAA_BAA_REQUIRED`, stop and re-read the failed reason
   (below).
6. **Mark step 5** — the send itself wrote a PHI access-log row; verify it
   carries a reason code.

Re-run the checklist after a BAA renewal reminder, a role change, or a new
channel launch; a fresh self-check takes under a minute.

## The four BAA states and the gate predicate

The checklist mirrors the BAA state machine; the deep vocabulary lives on
the [BAA flow](/compliance/baa#states-of-baa_status) page. For the go/no-go
read, the gate predicate is:

| State | Gate open for PHI sends? | Meaning |
| - | - | - |
| `not_required` | Yes (no PHI declared) | Default; `require` (PHI in scope) moves it to `pending`. |
| `pending` | **No** — `422`, reason `pending` | Attested PHI in scope; execute opens. |
| `executed` | Yes — the only state that opens the gate | Signed, within the one-year term; expiry closes it. |
| `expired` | **No** — `422`, reason `expired` | Term elapsed; re-execute opens 60 days early. |

Additionally: an `executed` row with a missing, future-dated, or malformed
execution timestamp, and an `executed` row whose term has lapsed before the
daily check flips the status, both fail as `not_signed`/`expired` — the
term re-derives from the timestamp on every enforced call. See the
[BAA flow](/compliance/baa) for the verdict table and transitions.

## Worked example: the `HIPAA_BAA_REQUIRED` 422 verdict

A send on a PHI-scope workspace that has not executed the BAA returns:

```json theme={null}
{
  "statusCode": 422,
  "error": "HIPAA_BAA_REQUIRED",
  "message": "Business Associate Agreement required before sending PHI traffic.",
  "details": {
    "reason": "pending",
    "docs_url": "https://orbit.devotel.io/docs/compliance/baa"
  }
}
```

The `reason` is one of `pending`, `expired`, or `not_signed`, and the block
lands **before** wallet hold, quota, or dispatch — a blocked send never
burns balance. If the status cannot be verified at all (a read failure),
the response is `500 HIPAA_BAA_GATE_DB_FAIL` and sends **stay blocked**:
the gate fails closed. Treat a `verification_unavailable` reason as a
verification outage — retry shortly — not a missing BAA.

Safe-failure sequence before the first PHI send:

1. Attempt the send deliberately on a draft/staging recipient.
2. If `422` with `reason: pending` → execute the BAA (step 2), then
   re-attempt.
3. If `422` with `reason: expired` → re-execute (the window opens 60 days
   before `expires_at`), then re-attempt.
4. If `422` with `reason: not_signed` → check the executed row's timestamp;
   `decline` (no PHI) or re-run `execute` to clear it.
5. If `500 HIPAA_BAA_GATE_DB_FAIL` → wait and retry; do not treat it as a
   posture defect.
6. Only when the send passes unblocked is the workspace go-live ready.

## API-equivalent: enable HIPAA mode and read the PHI access log

Steps 3 and 5 of the runbook map to two API calls. The Node SDK does not expose a typed compliance resource, so reach these routes through the generic `orbit.request(method, path, body)` escape hatch and unwrap the response envelope's `data` field. `PUT /settings/hipaa` returns `403` until the BAA reads `executed`; `GET /settings/hipaa/phi-access-log` is the pageable log step 5 reads.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    # Step 3 — enable HIPAA mode (owner-only; 403 until BAA is executed)
    curl -X PUT "https://api.orbit.devotel.io/api/v1/settings/hipaa" \
      -H "Authorization: Bearer $ORBIT_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"enabled": true,"data_retention_days": 365}'

    # Step 5 — page the PHI access log (reason-coded reads)
    curl "https://api.orbit.devotel.io/api/v1/settings/hipaa/phi-access-log?cursor=&limit=50" \
      -H "Authorization: Bearer $ORBIT_TOKEN"
    ```
  </Tab>

  <Tab title="Node.js SDK">
    Reach the HIPAA settings routes through the generic `orbit.request(method, path, body)` escape hatch — the typed SDKs do not wrap a compliance resource, so the raw call is the supported path:

    ```typescript theme={null}
    import { Orbit } from "@devotel-orbit/node";

    const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

    interface HipaaSettings {
      enabled: boolean;
      data_retention_days: number;
      phi_access_count: number;
    }

    // Step 3 — enable HIPAA mode. Returns 403 while the BAA is any state
    // other than "executed"; disabling later requires a re-auth challenge.
    const settings = (
      await orbit.request<{ data: HipaaSettings }>("PUT", "/settings/hipaa", {
        enabled: true,
        data_retention_days: 365,
      })
    ).data;

    // Step 5 — page the PHI access log. Each row carries the actor,
    // resource, and a reason code — the minimum-necessary evidence.
    const log = await orbit.request<{
      data: { actor: string; resource: string; reason: string; created_at: string }[];
      meta: { cursor?: string };
    }>("GET", "/settings/hipaa/phi-access-log?limit=50");
    ```
  </Tab>
</Tabs>

`PUT /settings/hipaa` returns the full status in the same response, so no follow-up read is needed after the toggle. A `403` from the enable call means the BAA is not yet `executed` — return to step 2 and execute it before retrying.

## See also

* [Business Associate Agreement (BAA) flow](/compliance/baa) — authoritative
  states, endpoints, roles, and gate verdicts
* [HIPAA BAA gate evaluation guide](/compliance/baa-gate-guide) — where the
  gate runs in the send path and how operators verify posture
* [HIPAA compliance controls](/compliance/hipaa) — the toggle, the
  roles-versus-surface matrix, the PHI audit row
* [HIPAA posture guide](/compliance/hipaa-posture-guide) — assemble the
  whole posture end to end for an auditor or buyer
* [HIPAA onboarding](/guides/hipaa-onboarding) — the ordered sequence from
  BAA to audit-ready
* [Audit log export](/compliance/audit-export) — queued, tamper-evident
  export of the audit trail for evidence requests


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.