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

# Business Associate Agreement (BAA) flow

> Execute your HIPAA Business Associate Agreement with Devotel: attest PHI scope, preview the template, sign with a type-the-name e-signature, and download the executed copy.

# Business Associate Agreement (BAA)

Organizations that send, store, or process Protected Health Information (PHI) through Devotel Orbit need a Business Associate Agreement on file. The platform enforces that the BAA route exists before HIPAA mode can be enabled: when PHI enters scope, the send-time gate rejects traffic with `HIPAA_BAA_REQUIRED` until an executed BAA is recorded.

This guide covers the full lifecycle: the canonical `baa_status` states, how the six `/api/v1/compliance/baa` endpoints fit together, which role can call which endpoint, what changes once a BAA is executed, and how to revert an executed agreement back to the platform default.

> This is a **tenant-owned HIPAA control**: you decide whether PHI is in scope, execute the agreement deliberately, and re-execute it before the annual term expires. Devotel supplies the e-sign pipeline — template rendering, typed-signature capture, immutable audit anchoring, and the stored executed PDF — but the legal determination that PHI is in scope is yours.

***

## States of `baa_status`

Your organization is always in one of four states, reported by `GET /api/v1/compliance/baa`:

| State          | Meaning                                                                                                                                        |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `not_required` | The organization has attested (or defaulted) that no PHI is in scope. This is the default for every new organization.                          |
| `pending`      | PHI is in scope (`hipaa_required = true`) and the BAA is awaiting execution. The execute form is available from this state.                    |
| `executed`     | A BAA has been signed and is within its one-year term. This is the only state that satisfies the HIPAA-enable and PHI-send gates.              |
| `expired`      | An executed BAA has passed its one-year term. PHI sends gate again until you re-execute. Re-execution becomes available 60 days before expiry. |

The response also carries the signer details and the countdown to expiry:

```json theme={null}
{
  "baa_status": "executed",
  "baa_executed_at": "2026-08-10T14:22:31.410Z",
  "baa_template_version": "v1",
  "baa_signer_name": "Jane Roe",
  "baa_signer_email": "jane@example.com",
  "baa_pdf_gcs_url": "gs://…/baa/org_…/baa_….pdf",
  "hipaa_required": true,
  "expires_at": "2027-08-10T14:22:31.410Z",
  "days_until_expiry": 342
}
```

When `hipaa_required` flips on while the status is still `not_required`, the read endpoint transitions the organization to `pending` automatically, so the execute step opens without a separate call.

***

## Why the flow starts with an attestation

The BAA flow exists because HIPAA applies to *use*, not to accounts. The platform does not assume every workspace handles PHI — the organization first attests that PHI is in scope, which raises the `hipaa_required` flag and moves the state to `pending`. That attestation is what opens the execute step; execution then completes the agreement. This ordering closes a circular dependency: HIPAA mode cannot be enabled without an executed BAA, but the dashboard also needed a way to *start* the BAA before HIPAA mode existed.

Both `require` (PHI is in scope) and `decline` (no PHI in scope) write a `compliance.baa.*` audit-chain row naming the actor, so the attestation itself is a recorded legal event — not a throwaway setting toggle.

***

## The endpoint flow

All routes live under `/api/v1/compliance/baa` and require an authenticated session. The six operations below are the complete lifecycle; the dashboard's **Settings → Compliance → BAA** page drives exactly these endpoints.

### 1. Read the current state

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/compliance/baa" \
  -H "Authorization: Bearer sk_live_..."
```

Any `owner` or `admin` can read. Use this first — it tells you whether the organization needs to attest, execute, re-execute, or download.

### 2. Preview the template

Before signing, review the finalized agreement text. `GET /api/v1/compliance/baa/template` returns the template rendered with your organization's legal name already filled in. Execution-time fields (timestamps, document reference) appear as readable markers rather than raw placeholders, and the signer fields are blanks the dashboard fills live as you type.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/compliance/baa/template?version=v1" \
  -H "Authorization: Bearer sk_live_..."
```

Response:

```json theme={null}
{
  "version": "v1",
  "covered_entity_name": "Acme Health Ltd",
  "format": "markdown",
  "body": "# Business Associate Agreement\n\nThis Business Associate Agreement..."
}
```

### 3. Attest that PHI is in scope

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/compliance/baa/require" \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "reason": "We began sending patient appointment reminders that contain PHI." }'
```

This flips `hipaa_required = true` and moves a `not_required` organization to `pending`. It opens the execute flow — it does **not** enable HIPAA mode. The optional `reason` (up to 500 characters) is recorded on the audit row.

### 4. Execute with a type-the-name e-signature

Execution is **owner-only** — a click-wrap signature binds the organization, so it is not a developer-tier action. The signer re-types their legal name into `typed_attestation`, and the server requires it to match `signer_name` exactly; a mismatch is rejected with a `400`, which also blocks blank-form auto-submits.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/compliance/baa/execute" \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "signer_name": "Jane Roe",
    "signer_email": "jane@example.com",
    "typed_attestation": "Jane Roe"
  }'
```

| Field               | Rule                                                            |
| ------------------- | --------------------------------------------------------------- |
| `signer_name`       | The signer's legal name (2–200 characters).                     |
| `signer_email`      | A valid email address.                                          |
| `typed_attestation` | Must **exactly match** `signer_name`. A mismatch returns `400`. |
| `template_version`  | Optional. Defaults to the current canonical version.            |

On success the server:

1. Renders the template with the signer details, execution timestamps, and a generated document reference
2. Stores the rendered document as the canonical executed PDF
3. Stamps the organization `executed` with the signer, template version, and execution timestamp, and records the expiry (execution plus the standard one-year term)
4. Writes a `compliance.baa.executed` entry to the audit log along with the signature method (`type_the_name`) — the audit entry is the legal evidence of attestation, and the stored PDF is the canonical document

The response returns the new state plus the document reference:

```json theme={null}
{
  "baa_status": "executed",
  "baa_executed_at": "2026-08-24T09:41:12.008Z",
  "baa_template_version": "v1",
  "baa_signer_name": "Jane Roe",
  "baa_signer_email": "jane@example.com",
  "baa_id": "baa_9f2k…",
  "expires_at": "2027-08-24T09:41:12.008Z",
  "days_until_expiry": 365,
  "hipaa_required": true
}
```

Execution is rate-limited to a handful of requests per minute; it is a deliberate legal act, not a scripted loop. (For the click-wrap legal basis, see [Voice signatures](/compliance/voice-signatures).)

### 5. Download the executed copy

Once a BAA is on file, any `owner` or `admin` can fetch it for your records, a customer's audit, or a regulator:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/compliance/baa/download" \
  -H "Authorization: Bearer sk_live_..."
```

The response carries a download URL valid for **24 hours**:

```json theme={null}
{
  "url": "https://storage.googleapis.com/…/baa/org_…/baa_….pdf?X-Goog-Signature=…",
  "expires_in_seconds": 86400
}
```

Share the URL within that window or download the file yourself and archive it. If no BAA has been executed yet, the endpoint returns `404`.

### 6. Revert to the platform default

Reverting removes the on-file agreement and returns the organization to `not_required`. It is **owner-only** and is only callable on an executed or expired BAA — and only after HIPAA mode has been disabled, so an active HIPAA workspace cannot silently unwind its own evidence.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/compliance/baa/revert" \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Organization no longer processes PHI; returning to default posture." }'
```

The executed BAA's audit history and stored PDF are **preserved** — revert removes the active state, it does not erase the evidence. Use this when PHI genuinely leaves scope, or to reset a workspace to a clean baseline; use [decline](#decline-no-phi-in-scope) instead when the "no PHI" attestation changes.

### Decline: attesting no PHI is in scope

`POST /api/v1/compliance/baa/decline` (owner or admin, optional `reason`) records that PHI is not in scope and lifts the send-time gate once an organization was opted in. It refuses to touch an on-file BAA — a decline cannot dismantle an executed agreement; that is what `revert` is for. Because `require` and `decline` are symmetric attestation toggles, an admin who declines can restore the requirement later if PHI comes back into scope.

***

## Roles and the audit chain

The read, preview, download, and attestation endpoints accept `owner` or `admin`. The two acts that bind or unwind a legal agreement — `execute` and `revert` — are `owner` only.

| Action                                                     | `owner` | `admin` | `developer` / `viewer` / `billing` |
| ---------------------------------------------------------- | :-----: | :-----: | :--------------------------------: |
| Read BAA state                                             |   Yes   |   Yes   |                 No                 |
| Preview the template                                       |   Yes   |   Yes   |                 No                 |
| Download the executed copy                                 |   Yes   |   Yes   |                 No                 |
| Attest PHI in scope (`require`) / not in scope (`decline`) |   Yes   |   Yes   |                 No                 |
| Execute the BAA                                            |   Yes   |    No   |                 No                 |
| Revert to default                                          |   Yes   |    No   |                 No                 |

Every write appends a `compliance.baa.*` entry to the organization's audit log — `compliance.baa.hipaa_required` on require, `compliance.baa.declined` on decline, `compliance.baa.executed` on execute, `compliance.baa.reverted` on revert — carrying the actor, the reason, and (on execution) the template version and signature method. That append-only chain, not the current status field, is the legal evidence of attestation. You can inspect it from the dashboard under [Settings → Audit log](/guides/audit-log).

***

## What changes once a BAA is executed

Executing the BAA does two things:

1. **Lifts the PHI send gate.** While `hipaa_required` is true and no in-term BAA is on file, outbound sends that touch PHI are rejected with `422 HIPAA_BAA_REQUIRED`. An executed BAA removes that rejection. (The gate's verdict and fail-closed behaviour are documented under [Send gates](/compliance/send-gates#baa-the-hipaa-send-gate).)
2. **Unblocks HIPAA mode.** Enabling HIPAA mode requires `baa_status = "executed"`; attempting it before execution returns `403`. Once HIPAA mode is on, the controls described in [HIPAA compliance controls](/compliance/hipaa) — PHI access logging, data retention, and the rest — apply to the workspace.

What it does **not** change: executing a BAA does not by itself enable HIPAA mode, does not determine whether your processing is lawful, and does not replace your own HIPAA program. The agreement records the platform's obligations to you as a business associate; deciding that PHI is in scope, designating PHI-adjacent audiences, and configuring retention remain tenant-owned. For how the pieces fit together, see [HIPAA onboarding](/guides/hipaa-onboarding) and [HIPAA compliance controls](/compliance/hipaa).

***

## Dashboard flow

The same lifecycle is available without touching the API at **Settings → Compliance → BAA**:

1. **Status card** — shows the current `baa_status`, the execution date, the signer, and a re-execute banner when the term is within 60 days of expiry
2. **Template preview** — the rendered agreement with your organization's name filled in
3. **Attestation form** — the signer's name and email plus the type-the-name signature field, shown to owners when the state is `pending`
4. **Download** — a link to the executed copy once executed, with a fresh 24-hour URL on each request

If PHI has not been attested yet, the page presents a "Start handling PHI" call-to-action that submits the `require` attestation and immediately opens the execute pane — mirroring the API flow above.

***

## FAQ

**How long does an executed BAA last?**
One year from execution. The state response carries `expires_at` and `days_until_expiry`; within 60 days of expiry the dashboard shows a re-execute banner. Past expiry the status reads `expired` and the PHI send gate closes again until you re-execute with the same flow.

**Can an admin execute the BAA to unblock sends?**
No — execution (and revert) is owner-only because it binds the organization. An admin *can* mark PHI as required or declined, read the state, preview the template, and download the executed copy.

**What is the difference between `decline` and `revert`?**
`decline` records that no PHI is in scope and lifts the send gate; it refuses to touch an executed BAA. `revert` removes an executed or expired agreement entirely, returning the organization to `not_required` while preserving its audit history and stored PDF. Both leave audit-chain entries.

**Do the endpoints accept a legacy JSONB status mirror?**
The `/api/v1/compliance/baa` flow is the canonical path. The older `PUT /api/v1/settings/hipaa/baa` mirror (documented under [HIPAA compliance controls](/compliance/hipaa)) is a fallback for pre-migration tenants only; once an organization has a `baa_status` value, the gates read the canonical column and ignore the mirror.

***

*Last updated: September 2026*
*For questions about the BAA, contact: [compliance@devotel.io](mailto:compliance@devotel.io)*
