> ## 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.

# Troubleshooting: HIPAA enable blocked (403) until the BAA is executed

> Fix the 403 Forbidden on PUT /settings/hipaa — HIPAA mode stays off until your Business Associate Agreement moves from pending to executed. Covers the baa_status lifecycle, the execute flow, and the legacy BAA mirror.

# Troubleshooting: HIPAA enable blocked (403)

When you toggle HIPAA mode in the dashboard (**Settings → Compliance →
HIPAA**) or call `PUT /api/v1/settings/hipaa`, the request is refused and
the workspace stays in its previous posture.

```json theme={null}
{
  "error": {
    "status": 403,
    "message": "Forbidden"
  }
}
```

This means your organization's `baa_status` is not `executed`. HIPAA mode
is BAA-gated: the toggle only accepts `{ "enabled": true }` once the
Business Associate Agreement is signed and in term. Nothing else (API-key
scopes, workspace plan, dashboard permissions) causes this refusal — read
your BAA state first, then execute the agreement. The full concept lives
under [HIPAA compliance](/compliance/hipaa).

<Note>
  BAA status is a **tenant-owned** control: you attest that PHI is in
  scope, you execute the agreement, and Orbit honors that attestation. It
  never designates your compliance posture on its own.
</Note>

Do not conflate this with `HIPAA_BAA_REQUIRED` **422** refusals on a
campaign launch — that is the PHI-adjacent audience precheck covered by
[Troubleshooting: HIPAA\_BAA\_REQUIRED](/troubleshooting/phi-audience-baa-required).
This page is the **enable-time gate** on the HIPAA toggle itself.

***

## Cause: read your `baa_status`

Check the current state with `GET /api/v1/compliance/baa/` (owner or
admin). It returns `baa_status`, the signer details, and
`days_until_expiry`.

| `baa_status`   | Meaning                                                              | Enable gate                        |
| -------------- | -------------------------------------------------------------------- | ---------------------------------- |
| `not_required` | Your organization has attested that no PHI is in scope (the default) | Blocked — the flow has not started |
| `pending`      | PHI is in scope; the BAA is awaiting execution                       | Blocked                            |
| `executed`     | The BAA is signed and within its one-year term                       | **Opens**                          |
| `expired`      | An executed BAA has passed its one-year term                         | Blocked — re-execute               |

While the state is anything but `executed`, `PUT /api/v1/settings/hipaa`
returns 403 and the dashboard toggle rejects the same way.

***

## Resolution: execute the BAA, then retry the enable

Work through these in order (owner role at every step):

1. **Attest that PHI is in scope** — `POST /api/v1/compliance/baa/require`
   moves a `not_required` organization to `pending` so the execute step
   opens. This starts the flow; it does not enable HIPAA mode.

```bash theme={null}
POST /api/v1/compliance/baa/require
{
  "reason": "We began storing patient appointment reminders that contain PHI."
}
```

2. **Execute the BAA** — `POST /api/v1/compliance/baa/execute` records the
   agreement with a type-the-name click-wrap e-signature. The
   `typed_attestation` must exactly match `signer_name`. On success the
   organization is stamped `executed` with the signer, version, and date.

```bash theme={null}
POST /api/v1/compliance/baa/execute
{
  "signer_name": "Jane Roe",
  "signer_email": "jane@example.com",
  "typed_attestation": "Jane Roe"
}
```

Review the agreement before signing with
`GET /api/v1/compliance/baa/template`. The dashboard's **Compliance →
BAA** pane walks this same flow.

3. **Retry the enable** — once `GET /api/v1/compliance/baa/` reads
   `executed`, enable succeeds:

```bash theme={null}
PUT /api/v1/settings/hipaa
{
  "enabled": true
}
```

Enabling tightens the workspace posture, so no re-authentication challenge
is needed. (Only **disabling** HIPAA mode requires the re-auth flow.)

***

## Edge cases

* **Re-check the state between steps.** A 403 means `baa_status` is not
  `executed` — re-read `GET /api/v1/compliance/baa/` after each call
  instead of assuming the flow advanced. The `require` step must land
  before `execute` opens; an `execute` on `not_required` does not skip it.
* **An `expired` BAA blocks again.** Executed BAAs carry a one-year term;
  once `days_until_expiry` reaches zero the state moves to `expired` and
  the enable gate re-opens the flow. Re-execute with
  `POST /api/v1/compliance/baa/execute`, then retry.
* **The legacy mirror does not satisfy the gate.**
  `PUT /api/v1/settings/hipaa/baa` (body `{ signed, signed_at,
  document_url }`) is a JSONB status mirror kept as a fallback for
  pre-migration tenants. Once an organization has a `baa_status` value,
  the enable and send gates read that canonical state and ignore the
  mirror — a `signed: true` write there does **not** unblock the toggle.
  Use `POST /api/v1/compliance/baa/execute` instead.

```bash theme={null}
PUT /api/v1/settings/hipaa/baa
{
  "signed": true,
  "signed_at": "2026-04-01T00:00:00Z",
  "document_url": "https://storage.devotel.io/baa/org_abc123.pdf"
}
```

That call returning success tells you nothing about the gate; check the
canonical state to be sure.

* **A prior `baa_status` value locks the legacy row out.** If your
  organization already carries any `baa_status` value (even
  `not_required`), the skip-ahead shortcut is closed by design — mirror
  writes are ignored once canonical state exists. If you cannot move past
  `not_required` and support tooling wrote the mirror in the past, run the
  canonical `require` → `execute` sequence; it supersedes the mirror.

***

## Related pages

* [HIPAA compliance](/compliance/hipaa) — the full BAA lifecycle, roles
  matrix, and PHI audit log
* [BAA — the HIPAA send gate](/compliance/send-gates#baa-the-hipaa-send-gate) —
  the per-recipient send-time verdict the same rules govern
* [Troubleshooting: HIPAA\_BAA\_REQUIRED](/troubleshooting/phi-audience-baa-required) —
  the campaign-launch refusal (422), not this enable gate
* [Compliance API endpoints](/api-reference/endpoints/compliance) — every
  `/compliance/baa` and HIPAA endpoint with role requirements
