Troubleshooting: HIPAA enable blocked (403)
When you toggle HIPAA mode in the dashboard (Settings → Compliance → HIPAA) or callPUT /api/v1/settings/hipaa, the request is refused and
the workspace stays in its previous posture.
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.
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):- Attest that PHI is in scope —
POST /api/v1/compliance/baa/requiremoves anot_requiredorganization topendingso the execute step opens. This starts the flow; it does not enable HIPAA mode.
- Execute the BAA —
POST /api/v1/compliance/baa/executerecords the agreement with a type-the-name click-wrap e-signature. Thetyped_attestationmust exactly matchsigner_name. On success the organization is stampedexecutedwith the signer, version, and date.
GET /api/v1/compliance/baa/template. The dashboard’s Compliance →
BAA pane walks this same flow.
- Retry the enable — once
GET /api/v1/compliance/baa/readsexecuted, enable succeeds:
Edge cases
- Re-check the state between steps. A 403 means
baa_statusis notexecuted— re-readGET /api/v1/compliance/baa/after each call instead of assuming the flow advanced. Therequirestep must land beforeexecuteopens; anexecuteonnot_requireddoes not skip it. - An
expiredBAA blocks again. Executed BAAs carry a one-year term; oncedays_until_expiryreaches zero the state moves toexpiredand the enable gate re-opens the flow. Re-execute withPOST /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 abaa_statusvalue, the enable and send gates read that canonical state and ignore the mirror — asigned: truewrite there does not unblock the toggle. UsePOST /api/v1/compliance/baa/executeinstead.
- A prior
baa_statusvalue locks the legacy row out. If your organization already carries anybaa_statusvalue (evennot_required), the skip-ahead shortcut is closed by design — mirror writes are ignored once canonical state exists. If you cannot move pastnot_requiredand support tooling wrote the mirror in the past, run the canonicalrequire→executesequence; it supersedes the mirror.
Related pages
- HIPAA compliance — the full BAA lifecycle, roles matrix, and PHI audit log
- BAA — the HIPAA send gate — the per-recipient send-time verdict the same rules govern
- Troubleshooting: HIPAA_BAA_REQUIRED — the campaign-launch refusal (422), not this enable gate
- Compliance API endpoints — every
/compliance/baaand HIPAA endpoint with role requirements