Skip to main content

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.
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.
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.
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. 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. 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 scopePOST /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.
  1. Execute the BAAPOST /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.
Review the agreement before signing with GET /api/v1/compliance/baa/template. The dashboard’s Compliance → BAA pane walks this same flow.
  1. Retry the enable — once GET /api/v1/compliance/baa/ reads executed, enable succeeds:
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.
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 requireexecute sequence; it supersedes the mirror.