# athenahealth API Technical Specification — DRAFT

**Status:** DRAFT v3 — Ready for review
**Last Updated:** 2026-05-05
**Prepared by:** Kinometric Development Team

---

## General Information

| Field | Value |
|-------|-------|
| **athenahealth Client Name** | Kinometric / Flight Systems Inc. |
| **athenahealth Practice ID** | 1187801 (Preview) — _[TODO: Production practice ID]_ |
| **Primary API Developer Name** | Charlie Rogers |
| **Primary API Developer Contact** | charlie.rogers@flightsystems.net |
| **Developer Portal Email (Preview)** | charlie.rogers@flightsystems.net |
| **Developer Portal Email (Production)** | dev@kinometric.com |
| **Vendors involved** | Kinometric — in-house development, no third-party vendors |

---

## Application Security Questions

### How/Where will you be storing your key and secret? Is it encrypted at rest?

All athenaOne API communication is server-side only (PHP). The client_secret is never transmitted to or accessible by end-user devices, browsers, or the mobile application. The OAuth2 client_credentials grant ensures the secret remains exclusively on the application server.

Server-side storage protections:

- **File permissions:** 640 (owner read/write, group read for www-data, no world access)
- **Git exclusion:** File is in `.gitignore` and never committed to version control
- **Server access:** Only the application server process (www-data) and system administrator have access
- **Network isolation:** The configuration file is not served by the web server (blocked by `.htaccess` and Apache configuration)
- **Token caching:** OAuth2 access tokens are cached in a temp file with 0600 permissions and auto-expire. The client_secret is only used for token refresh, not stored in memory beyond the token request.
- **No client exposure:** The mobile app never sees, stores, or transmits the client_secret. All API calls to athenaOne are proxied through the PHP server.

### Who will have access to your application?

- **Providers** — Authenticated medical providers who conduct balance assessments. Each provider is assigned to one or more practices and a department within each practice, and can only access patients within their active practice.
- **Administrators** — Practice administrators who manage users, patients, and review test results. Administrators can upload documents to athenaOne.
- **No patient access** — Patients do not have direct access to the application. All interactions with athenaOne are provider/admin-initiated.
- **No third-party vendor access** — The application is developed and maintained in-house by Kinometric / Flight Systems Inc.

Access is controlled by:
- Username + password authentication (bcrypt hashed, HIPAA-compliant password policy: 8+ chars, uppercase, lowercase, number, cannot match username)
- Ephemeral 64-character session tokens (regenerated per login, stored in-memory only on the mobile app — not persisted to SharedPreferences or disk)
- 15-minute idle session timeout (server-side, destroys session and clears cookies)
- Optional biometric re-authentication uses Flutter Secure Storage (Android Keystore / iOS Keychain) for credential storage
- Multi-practice patient isolation (`practice_filter.php` enforces data segregation across 31 API endpoints). Each practice is a separate PHI boundary with its own patients, test results, and questionnaire data.
- Users can belong to multiple practices via a many-to-many membership table, each with a role and department assignment
- Role-based access per practice: admin (full access), clinician (standard clinical workflow), readonly (view-only), superadmin (`is_admin=true`, cross-practice access for system administrators)
- Department-level access gate on athenaOne patient lookups: server verifies the patient's department is in the practice's allowed departments before returning data
- No patient data is cached on-device — patient lists are fetched fresh from the server on each screen load

### Does your application keep error logs for tracking and resolving technical issues?

Yes. The application maintains comprehensive logging with HIPAA-compliant rotation:

| Log File | Purpose | Retention |
|----------|---------|-----------|
| `logs/athena_proxy_error.log` | Athena API errors (sanitized, no PHI) | Archive at 2MB, compressed to .gz, 6-year retention |
| `logs/athena_send.log` | Document upload tracking | Archive at 2MB, compressed to .gz, 6-year retention |
| `logs/athena_hipaa_audit.log` | HIPAA access audit (who, what, when, IP) | Persistent, JSON-formatted, 6-year retention |
| `test/logs/auth_debug.log` | Authentication attempts | Archive at 2MB, compressed to .gz, 6-year retention |
| `logs/upload_csv.log` | CSV data upload tracking | Archive at 2MB, compressed to .gz, 6-year retention |
| `logs/auth.log` | Login authentication | Archive at 2MB, compressed to .gz, 6-year retention |
| `logs/archives/*.gz` | Compressed rotated log archives | Encrypted backup, 6-year offsite retention |

**Log rotation:** A centralized log utility (`log_utils.php`) provides HIPAA-compliant rotation for all PHP endpoints. When any log file exceeds 2MB, it is compressed to a dated `.gz` archive in `logs/archives/`. No log data is ever deleted or truncated. Archives are included in the nightly encrypted backup (restic AES-256, client-side encryption) and transferred offsite via WireGuard VPN to append-only NAS storage, with B2 Object Lock for PHI. Retained for 6 years per HIPAA.

**PHI protection in logs:** Audit logs record only IDs (user_id, patient_id, document_id), action types, timestamps, and IP addresses. Patient names, clinical data, and questionnaire answers are never logged.

### Will your application update or add information directly into the patient's chart?

**Yes** — The application uploads PDF clinical documents (balance test results) to the patient's chart in athenaOne.

- **Who makes changes:** Authenticated providers and administrators initiate the upload from the Kinometric mobile app or web dashboard.
- **Review process:** The provider conducts the balance test, reviews the scored results and AI analysis, then explicitly chooses to upload the PDF report to athenaOne. There is no automatic or unreviewed upload. The provider must verify the patient match (Kinometric vs athenaOne records displayed side-by-side) before uploading.
- **Document type:** `CLINICALDOCUMENT` subclass, uploaded via the clinical document API with `autoclose=true`.
- **No other chart modifications:** The application does not modify existing chart entries, prescriptions, appointments, or any other clinical data. It only adds new clinical documents.

### How will you match users to the correct patient or provider?

Patient matching supports two methods, both requiring user confirmation before any document upload:

**Method 1: Direct ID Lookup** (primary method for returning patients)
- The athenaOne Patient ID (`athenapatientid`) is stored in the Kinometric database as `realpatientid` in the patients table.
- Before uploading, the system verifies the patient exists in both systems by querying `GET /v1/{practiceId}/patients/{athenaPatientId}`.
- The web dashboard displays both the Kinometric and athenaOne patient records side-by-side for manual confirmation.

**Method 2: Name + Department Search** (for new patient intake)
- Provider searches for a patient by last name, first name, and department via `GET /v1/{practiceId}/patients?lastname={name}&departmentid={dept}`.
- Results are displayed for the provider to select the correct match. Client-side DOB filtering is available for additional verification.
- After selection, the athenaOne patient ID is stored in the Kinometric database as `realpatientid`, linking the records for future lookups via Method 1.
- **Department access gate:** The server verifies the patient's `departmentid` is within the practice's allowed departments before returning demographics. Denied lookups are logged in the HIPAA audit log.

**Upload safety:** Regardless of which method was used to identify the patient, document uploads always require:
1. A confirmed athenaOne patient ID link (`realpatientid` in Kinometric DB)
2. Explicit upload action by the provider (button click, not automatic)
3. HIPAA audit log entry for every upload attempt

### How are you safeguarding PHI?

**In Transit:**
- All communication with athenaOne APIs uses TLS 1.2/1.3 (HTTPS)
- All communication between the mobile app and Kinometric server uses TLS 1.2/1.3
- OAuth2 tokens transmitted only over HTTPS

**At Rest (Server):**
- Database credentials and API secrets stored with 640 file permissions (owner rw, group r for www-data)
- PostgreSQL database on localhost only (not network-exposed, bound to 127.0.0.1)
- Patient balance test CSV data stored with 640 permissions (owner + group read only)
- Temporary PDF files created during upload are immediately deleted after transmission (`unlink()`)
- OAuth2 token cache files have 0600 permissions
- Nightly encrypted backups (restic AES-256, client-side) with offsite replication via WireGuard VPN to append-only NAS + B2 Object Lock for PHI

**At Rest (Mobile App):**
- No patient data cached on-device — fetched fresh from server each time
- Session token held in-memory only (Dart FFAppState), not written to disk or SharedPreferences
- PDF reports generated entirely in memory (`pw.Document()` -> `Uint8List`), never written to filesystem
- If biometric login enabled, credentials stored in Android Keystore / iOS Keychain via FlutterSecureStorage
- No SQLite, Hive, or file-based patient data storage exists in the app

**Access Controls:**
- Multi-practice patient isolation (`practice_filter.php`) prevents cross-practice data access. Every query that touches PHI (patients, test results, questionnaires) is scoped to the user's active practice via `practice_id` foreign keys on all PHI tables.
- Users belong to practices via `user_practices` table with roles (admin, clinician, readonly) and department assignments
- Each practice can have its own athenaOne credentials (practice_id, client_id, client_secret) stored in the `practices` table, enabling per-practice EMR integration
- Department access gate on athenaOne patient lookups verifies patient's department is in the practice's allowed departments
- Ephemeral authentication tokens (64-char hex, regenerated per login)
- Password policy: minimum 8 characters, uppercase, lowercase, number required, cannot match username
- bcrypt password hashing with unique salt per user
- 15-minute idle session timeout (server-side)
- UFW firewall restricting server access to SSH, HTTP, HTTPS only

**Audit Trail:**
- HIPAA-compliant access audit log for all athenaOne API interactions
- JSON-formatted entries with timestamp, user_id, action, IP address, resource accessed
- Security incident logging and retrieval
- No PHI in log entries — only IDs and action types

**Application Security:**
- Input validation on all API parameters (numeric IDs enforced, resources whitelisted)
- Sanitized error messages (no API internals, credentials, or stack traces exposed to clients)
- Path traversal prevention on file uploads
- Base64 validation on document data

---

## Workflow Description

### Primary Workflow: Web Dashboard Upload

**User:** Provider or Administrator (via web browser)

**Step-by-step workflow:**

1. **Provider conducts balance test** using the Kinometric mobile app (Flutter/Android) on a tablet or phone. The patient stands on one leg while the device records accelerometer data at 100Hz for 10 seconds. Four test types are performed:
   - Left Balance Eyes Open (LBEO)
   - Right Balance Eyes Open (RBEO)
   - Left Balance Eyes Closed (LBEC)
   - Right Balance Eyes Closed (RBEC)

2. **App scores the test locally** using the built-in scoring engine (BalanceScorer.dart / BalanceScorer.java). Scores range 0-10 with risk levels (Critical, High, Moderate, Low). PDF reports are generated entirely in memory — no PHI written to device storage.

3. **App uploads CSV sensor data and results** to the Kinometric server via `POST /back_upload_csv.php`. The server stores the data and optionally runs AI analysis.

4. **Provider logs into the Kinometric web dashboard** at https://kinometric.com

5. **Provider navigates to the Athena tab** and either:
   - Enters the athenaOne patient ID directly for verification, or
   - Searches by patient name and department to find the patient
   - System queries athenaOne for patient demographics
   - System displays both Kinometric and athenaOne patient records side-by-side for verification
   - Department access gate ensures the patient belongs to an allowed department

6. **Provider selects the target department** from the department browser (optional — defaults to the user's assigned department)

7. **Provider uploads the test result PDF** to athenaOne. This triggers:
   - Server authenticates with athenaOne via OAuth2 (client_credentials)
   - Server uploads PDF as a clinical document via `POST /documents/clinicaldocument`
   - Server returns confirmation with the new document ID
   - HIPAA audit log records the upload (user, patient ID, department, timestamp)

8. **Document appears in athenaOne** as a clinical document in the patient's chart, viewable by the provider in athenaNet.

### Secondary Workflow: Mobile App Patient Lookup & Import

**User:** Provider (via Kinometric mobile app on tablet/phone)

**Step-by-step workflow:**

Before running a balance test on a new patient, the provider can look up or import a patient from athenaOne:

1. Provider selects their department (set once, persisted in app preferences — only changes when the user explicitly re-selects)
2. Provider searches by last name, first name, and optionally DOB via the Kinometric server
3. Or looks up a specific patient by athenaOne ID
4. Server proxies the search to athenaOne — the mobile app never communicates directly with athenaOne
5. Provider selects the correct match from the results
6. App imports the patient demographics (name, DOB, sex) into the Kinometric database with the athenaOne patient ID stored as `realpatientid`
7. Patient is now linked for future balance tests and athenaOne uploads

### Tertiary Workflow: Mobile App Direct Upload

**User:** Provider (via Kinometric mobile app)

**Step-by-step workflow:**

After scoring a balance test, the provider taps "Upload to Athena" in the mobile app:

1. Checks that the patient has an athenaOne patient ID (`realPatientid`). If not, shows error: "No athenaOne patient ID linked to this patient."
2. Shows confirmation dialog: "Upload balance test results to athenaOne for {patient name}?"
3. Generates PDF in memory using the v3 PDF generator (no PHI written to device storage)
4. Base64 encodes the PDF and POSTs to the Kinometric server
5. Server proxies the upload to athenaOne via OAuth2 (client_credentials) — the mobile app never communicates directly with athenaOne
6. Shows success snackbar with document ID, or error message on failure

---

## Workflow Graphic

```
┌─────────────────┐     CSV + Scores      ┌──────────────────────┐
│  Kinometric      │ ──────────────────────▶│  Kinometric Server   │
│  Mobile App      │                        │  (PHP + PostgreSQL)  │
│  (Flutter)       │     Confirmation       │                      │
│                  │ ◀──────────────────────│  - Stores test data  │
│  - Balance test  │                        │  - Scores & analyzes │
│  - Local scoring │     Patient Lookup     │  - Generates reports │
│  - In-memory PDF │ ──────────────────────▶│  - Patient import    │
│  - NO PHI on disk│     Demographics       │                      │
│                  │ ◀──────────────────────│                      │
└─────────────────┘                        └──────────┬───────────┘
                                                       │
┌─────────────────┐     Upload PDF          │ OAuth2 server-to-server
│  Web Dashboard   │ ──────────────────────▶│ (client_credentials)
│  (Browser)       │                        │ Secret NEVER leaves server
│                  │     Confirmation       │
│  - Verify patient│ ◀─────────────────────┘│
│  - Search by name│                        ▼
│  - Select dept   │              ┌──────────────────────┐
│  - Upload to     │              │  athenaOne API        │
│    athenaOne     │              │  (athenahealth)       │
└─────────────────┘              │                      │
                                  │  Endpoints used:     │
                                  │  - GET /patients     │
                                  │  - GET /patients?... │
                                  │  - GET /departments  │
                                  │  - POST /documents   │
                                  └──────────────────────┘

Data flow:
  Mobile App ──▶ Server: CSV sensor data, test scores, patient lookup requests
  Server ──▶ athenaOne: Patient search, demographics, PDF upload (server-side only, via OAuth2)
  Server ◀── athenaOne: Patient demographics, document ID confirmation
  Web Dashboard ──▶ Server: Upload request, patient verification
  Server ──▶ athenaOne: PDF clinical document (server-side only)
  Server ◀── athenaOne: Document ID confirmation

Multi-practice isolation:
  Practice A ──▶ Server ──▶ athenaOne (Practice A credentials)
  Practice B ──▶ Server ──▶ athenaOne (Practice B credentials)
  Practice C ──▶ Server ──▶ No athenaOne (standalone mode, non-Athena patients only)
```

---

## Use Cases

### Use Case #1: Upload Balance Test Results as Clinical Document

_Primary use case. Triggered by provider after completing a balance assessment. Estimated 50-200 uploads per day across all locations._

| # | Action | Cadence | API Endpoint |
|---|--------|---------|--------------|
| 1 | Authenticate with athenaOne (OAuth2 client_credentials) | Per session, cached ~1 hour | `POST /oauth2/v1/token` |
| 2 | Upload PDF balance test report as clinical document | Per test upload (50-200/day) | `POST /v1/{practiceId}/patients/{patientId}/documents/clinicaldocument` |

**Parameters sent with upload:**
- `departmentid` — Target department (from user's department assignment or explicit selection)
- `internalnote` — "Kinometric Balance Test Results - {Patient Name} - {Date}"
- `documentsubclass` — `CLINICALDOCUMENT`
- `autoclose` — `true`
- `attachmentcontents` — PDF file (multipart upload)

**Expected response:** `{"clinicaldocumentid": <id>}`

---

### Use Case #2: Patient Verification (by ID)

_Provider verifies that the Kinometric patient record matches the athenaOne patient before uploading._

| # | Action | Cadence | API Endpoint |
|---|--------|---------|--------------|
| 1 | Authenticate with athenaOne (cached token) | Reuses cached token | `POST /oauth2/v1/token` |
| 2 | Retrieve patient demographics by athenaOne patient ID | On-demand, ~10-50/day | `GET /v1/{practiceId}/patients/{patientId}` |
| 3 | Verify patient's department is in practice's allowed departments | Same request cycle | `GET /v1/{practiceId}/departments` |

**Matching logic:** Exact match on athenaOne patient ID (stored as `realpatientid` in Kinometric database). Department access gate verifies the patient's department is allowed for the practice. Denied lookups are HIPAA audit-logged.

---

### Use Case #3: Patient Search by Name (New Patient Intake)

_Provider searches athenaOne by patient name and department to find and import a new patient into Kinometric._

| # | Action | Cadence | API Endpoint |
|---|--------|---------|--------------|
| 1 | Authenticate with athenaOne (cached token) | Reuses cached token | `POST /oauth2/v1/token` |
| 2 | Search patients by last name and department | On-demand, ~50-100/day | `GET /v1/{practiceId}/patients?lastname={name}&departmentid={dept}` |
| 3 | Retrieve full demographics for selected patient | After user selects match | `GET /v1/{practiceId}/patients/{patientId}` |

**Flow:** Provider enters last name (required), first name (optional), and department. Server returns matching patients. Provider selects the correct match. Demographics are imported into the Kinometric database. DOB filtering is performed client-side (athenaOne search API does not support DOB as a filter parameter).

---

### Use Case #4: Department Browsing

_Admin or provider browses available departments to select the correct upload target or set their default department._

| # | Action | Cadence | API Endpoint |
|---|--------|---------|--------------|
| 1 | Authenticate with athenaOne (cached token) | Reuses cached token | `POST /oauth2/v1/token` |
| 2 | Retrieve list of departments (paginated, up to 1000) | On-demand, rare (~1/week) | `GET /v1/{practiceId}/departments?limit=100&offset={n}` |

---

---

## Complete API Endpoint Summary

| Endpoint | Method | Purpose | Frequency |
|----------|--------|---------|-----------|
| `/oauth2/v1/token` | POST | OAuth2 authentication (client_credentials) | Per session, cached ~1hr |
| `/v1/{pid}/patients/{id}` | GET | Patient verification / demographics | On-demand, ~50-150/day |
| `/v1/{pid}/patients?lastname=...` | GET | Patient search by name | On-demand, ~50-100/day |
| `/v1/{pid}/departments` | GET | Browse/verify departments | On-demand, ~10-50/day |
| `/v1/{pid}/patients/{id}/documents/clinicaldocument` | POST | **Upload PDF test results** | Per test, ~50-200/day |

**Estimated total API calls per day:** 200-500 (well within standard rate limits)

**No prohibited operations:**
- No bulk data pulls (single-patient queries only, scoped by department)
- No data imports from legacy systems
- No SSO into athenaNet
- No reading or modification of existing chart data (only adds new clinical documents)
- No appointment queries

---

## Compliance & Security Summary

| Category | Implementation |
|----------|---------------|
| **HIPAA Administrative Safeguards** | Access controls, workforce training, security officer designated, incident response procedures documented |
| **HIPAA Physical Safeguards** | Server hosted in secure data center (Linode), workstation security policies in place |
| **HIPAA Technical Safeguards** | Encryption (TLS 1.2/1.3), access controls (practice + department isolation), audit logging, integrity controls (input validation), 15-min session timeout |
| **Authentication** | OAuth2 client_credentials (2-legged), no user tokens involved |
| **Authorization** | Server-side only — no client-side API calls to athenaOne |
| **Data Minimization** | Only uploads PDF reports and reads patient demographics for verification. No clinical chart data is read from athenaOne. |
| **Audit Trail** | JSON-formatted HIPAA audit log for all API interactions, security incident logging |
| **Error Handling** | Sanitized errors to client; detailed errors logged server-side only |
| **Secret Management** | athena client_secret encrypted at rest via sodium_crypto_secretbox (XSalsa20-Poly1305), key stored separately at /etc/kinometric/ with 640 root:www-data permissions. Config file excluded from git, never exposed to clients. |
| **Backup** | Nightly automated backups via restic (AES-256, client-side encryption) to two targets: local encrypted repo (7-day retention) + off-site append-only NAS via WireGuard VPN (14d/8w/24m/7yr retention). B2 Object Lock offsite for PHI. Failure alerts via email. PostgreSQL dumped in custom format (pg_dump -Fc). Backup runner monitored via systemd timer with email-on-failure to dev@kinometric.com via Microsoft Graph API. |

---

## Multi-Practice Data Model

The application supports multiple practices (clinics/organizations) as separate PHI isolation boundaries. Each practice can have its own athenaOne EMR integration credentials and department structure.

### Database Schema

```
practices
├── id (PK)
├── name                    — Practice display name
├── slug                    — URL-safe identifier
├── address, city, state,   — Practice address
│   zip, phone
├── athena_practice_id      — athenaOne practice ID (unique per practice)
├── athena_client_id        — OAuth2 client ID for this practice
├── athena_client_secret    — OAuth2 client secret for this practice
├── athena_base_url         — API base URL (sandbox vs production)
├── athena_default_dept     — Default department for uploads
├── is_active               — Soft-disable practices
└── created_at, updated_at  — Timestamps

departments
├── id (PK)
├── name                    — Department display name
├── practice_id (FK)        — Which practice this department belongs to
├── athena_department_id    — Corresponding athenaOne department ID
├── patient_facing_name     — Name shown to patients
├── is_active               — Soft-disable departments
└── created_at              — Timestamp

user_practices
├── id (PK)
├── user_id (FK → users)    — Which user
├── practice_id (FK → practices) — Which practice
├── role                    — 'admin', 'clinician', or 'readonly'
├── is_default              — User's default practice on login
├── department_id (FK → departments) — User's assigned department in this practice
└── created_at              — Timestamp

users
├── user_id (PK)
├── username, password       — Authentication (bcrypt)
├── encryptionkey           — Ephemeral 64-char session token
├── display_name            — Provider name for attribution (e.g., "Dr. Smith")
├── email                   — Contact email
├── is_admin                — Superadmin flag (bypasses practice isolation)
└── secret_2fa, is_2fa_on   — Two-factor authentication (infrastructure ready)

patients
├── practice_id (FK → practices, NOT NULL)  — Patient belongs to one practice
├── is_sandbox (BOOLEAN, NOT NULL, DEFAULT false) — Sandbox test patients

test_results
├── practice_id (FK → practices, NOT NULL)  — Test scoped to one practice
├── providername            — Auto-populated from user's display_name
├── providerlocation        — Auto-populated from practice name + department name

questions
├── practice_id (FK → practices, NOT NULL)  — Questionnaire scoped to one practice
```

### Per-Practice Athena Integration

Each practice row stores its own athenaOne API credentials. When a document upload or patient verification is requested, the server:

1. Resolves the user's active practice from the request (`active_practice_id` parameter, session, or default membership)
2. Loads athenaOne credentials from the `practices` table for that practice
3. Authenticates with athenaOne using the practice-specific `client_id` / `client_secret`
4. Makes API calls using the practice-specific `athena_practice_id` in the URL path
5. Caches OAuth2 tokens per-practice (separate cache files per practice_id)

If a practice has no athenaOne credentials configured, the system falls back to the global `athena_config.json` configuration file. This allows gradual onboarding of practices to athenaOne.

### Department Access Gate

When a patient is looked up by ID via the web dashboard, the server performs a department verification:
1. Fetches the practice's departments from athenaOne
2. Checks if the patient's `departmentid` is in the list of allowed departments
3. If denied, returns an error and logs a `get_athena_patient_denied` entry in the HIPAA audit log
4. If allowed, returns the full patient demographics

This prevents providers from accessing patients outside their practice's department scope.

### Non-Athena Patients

Not all patients require athenaOne integration. Patients without an `athena_patient_id` exist only in the Kinometric database. They use the same balance test, scoring, and reporting workflows — the only difference is that PDF uploads to athenaOne are unavailable. This supports:

- **Test/demo patients** during training and onboarding (flagged with `is_sandbox=true`)
- **Practices without EMR integration** that use Kinometric standalone
- **Gradual EMR rollout** where some patients are linked to athenaOne and others are not yet

### Provider Attribution

When test results are saved, the system auto-populates provider information:
- `providername` — from the authenticated user's `display_name` (falls back to `username`)
- `providerlocation` — from the user's active practice name + department name (e.g., "Main Practice — Physical Therapy")
- On update, existing provider attribution is preserved (COALESCE prevents overwriting the original clinician)

---

## TODO Before Submission

- [ ] Fill in client/practice name (confirm with Steve — PASS? Kinometric? Both?)
- [ ] Register dev@kinometric.com on Developer Portal for production
- [ ] Create polished workflow graphic (box-and-arrow, not ASCII — prompt in docs/athena_workflow_graphic_prompt.md)
- [ ] Complete Tech Spec form (copy content into athena's .docx template)
- [ ] Review with athenahealth contact
- [ ] Complete Solution Validation testing
- [ ] Obtain production Practice ID (after validation)
- [ ] Obtain production client_id and client_secret (after validation)
- [ ] Obtain Go-Live Authorization signature from practice (after validation)
- [x] Preview environment updated to 1187801 (2026-05-05)
- [x] Scoped to 4 endpoints only (patient search, patient get, departments, document upload)
- [x] Encryption-at-rest for client_secret — sodium_crypto_secretbox, key at /etc/kinometric/athena_key (2026-05-05)
- [x] dev@kinometric.com alias created (2026-05-05)
- [x] Multi-practice data model documented
- [x] Non-Athena patient support documented
- [x] Department access gate documented
- [x] Log retention updated to 6-year archive rotation
- [x] Provider attribution documented
- [x] Session timeout documented
- [x] Sandbox patient support documented

---

## Go-Live Authorization

_[Not to be completed until Solution Validation is passed]_

By: ___________________________________________
Print Name: ___________________________________
Position: _____________________________________
Date: _________________________________________
