# Integrate the PT365 HEP picker into this EMR You are working in an existing EMR codebase. Inspect its framework, authentication, persistence, testing conventions, and content-security policy before making changes. Implement the smallest native integration that follows the repository's existing patterns. ## PT365 configuration - Environment: sandbox - API base URL: https://www.physicaltherapy365.com/api/v1 - Client ID: - Allowlisted parent origin: - OpenAPI contract: https://www.physicaltherapy365.com/api/v1/openapi.json - Human integration guide: https://www.physicaltherapy365.com/hep/developers/guide The PT365 client secret will be supplied separately. Store it only in the application's server-side secret manager as PT365_CLIENT_SECRET. Never paste it into source code, browser JavaScript, logs, test fixtures, or this prompt. ## Non-negotiable privacy boundary Never send PT365 a patient name, patient/chart/encounter ID, diagnosis, note, date of birth, email, phone number, address, insurance data, or other patient context. Reject accidental patient-oriented fields before making the request. PT365 receives only an exact parent origin, an opaque clinician identifier, optional visual theme, and optional existing exercise selections. ## Required implementation 1. Add server-only environment configuration for PT365_CLIENT_ID, PT365_CLIENT_SECRET, and PT365_API_BASE_URL. 2. Add a server-side OAuth client-credentials helper. POST grant_type=client_credentials to /oauth/token using HTTP Basic authentication. Cache the access token until shortly before its one-hour expiry. 3. Add an authenticated EMR backend endpoint that creates a picker session by POSTing to /picker/sessions with catalog:read/picker:create access. Do not allow the browser to call PT365 with the client secret. 4. Derive a stable opaque clinician ID with a one-way keyed hash of the EMR's internal clinician ID. Do not send the raw internal ID. 5. Send this picker-session body: {"origin":"","clinicianId":"","theme":{"brandName":"","primaryColor":"#215C5C","backgroundColor":"#F5F8F8"}} 6. Open the returned data.url in a responsive modal iframe. Add https://www.physicaltherapy365.com to the application's Content-Security-Policy frame-src directive. 7. Listen for window message events. Accept events only when event.origin is exactly https://www.physicaltherapy365.com and event.source is the picker iframe's contentWindow. 8. Handle pt365.ready, pt365.selection.completed, and pt365.cancelled. Treat iframe load failures, session expiry, and malformed messages as local integration errors. On completion, validate and store payload.items in the EMR. 9. Preserve each returned exercise ID, revision, ordered prescription, clinician instruction, and full content snapshot. Never replace an existing assignment snapshot when newer catalog content appears. 10. When editing, create a new picker session with initialSelection built from the stored item prescriptions. Patient context and clinician instructions must not be included in the server request; instructions are entered in the picker and returned browser-to-browser. 11. Add clear loading, timeout, cancellation, and retry states. Never interrupt an already-open picker because of a licensing warning or temporary catalog-sync problem. 12. Add automated tests for server-only credentials, token caching, forbidden patient fields, origin validation, event-source validation, completion storage, cancellation, editing, and PT365 outage behavior. ## Browser completion contract The completion event is shaped as: { "type": "pt365.selection.completed", "payload": { "items": [ { "order": 1, "prescription": { "exerciseId": "ex_...", "revision": "...", "side": "left|right|bilateral|not_applicable", "sets": 3, "repetitions": 10, "holdSeconds": 5, "durationMinutes": null, "frequency": { "times": 1, "period": "day" }, "resistance": null, "restSeconds": null, "customDosage": null, "clinicianInstructions": "" }, "exercise": { "id": "ex_...", "revision": "..." } } ] } } Use the OpenAPI document as the source of truth for complete schemas. Preserve unknown response fields for forward compatibility, but do not send undocumented request fields. ## Definition of done - A clinician can open the sandbox picker, search the fixed 10 exercises, prescribe and reorder items, complete the picker, edit the stored program, and cancel safely. - No PT365 secret reaches the browser. - No patient or encounter data reaches PT365. - Origin and iframe-window validation are both enforced. - The exact returned revision and snapshot are stored with the assignment. - Relevant tests, type checks, and linters pass. - Document the files changed, environment variables required, and a manual sandbox test procedure. Proceed with the implementation. Ask for input only if the existing application lacks a server-side execution environment, authenticated backend route, persistent assignment storage, or iframe support.