PT
PT365Integration Guide

Complete integration documentation

From credentials to a working HEP picker.

This is the implementation contract for EMR teams and their coding assistants. The recommended picker integration requires a backend, an iframe-capable browser interface, and storage for the returned assignment snapshot.

Under 30 minutesNo patient contextProduction-compatible sandbox

Coding-assistant handoff

One prompt. Complete integration.

Replace the three angle-bracket placeholders, then paste this into Codex, Cursor, Claude Code, or your preferred repository-aware coding assistant.

Download prompt
# 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: <YOUR_PT365_CLIENT_ID>
- Allowlisted parent origin: <YOUR_EMR_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":"<YOUR_EMR_ORIGIN>","clinicianId":"<opaque-hash>","theme":{"brandName":"<EMR_NAME>","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.

01 · Before you start

Minimal EMR requirements

Server backend

A trusted server route or serverless function that can keep credentials secret and call HTTPS APIs.

Browser surface

An iframe-capable web interface with exact-origin and iframe-window postMessage validation.

Assignment storage

Persistent JSON storage for ordered prescriptions and the exact exercise content snapshot returned at completion.

Opaque clinician ID

A stable, non-reversible identifier used only for monthly-active licensing. Never send the raw internal user ID.

02 · Server only

Exchange credentials for an access token

Store PT365_CLIENT_ID and PT365_CLIENT_SECRET in the EMR's secret manager. Use HTTP Basic authentication and cache the returned bearer token until shortly before its one-hour expiration.

curl -u "$PT365_CLIENT_ID:$PT365_CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  https://www.physicaltherapy365.com/api/v1/oauth/token

Production and sandbox use the same schemas. Sandbox credentials are restricted to the ten designated exercises and cannot access production content.

03 · Recommended path

Create and embed a picker session

The EMR backend creates a 15-minute session. The origin must exactly equal an origin registered for the credential. The clinician identifier must be an opaque 8–128 character value.

curl -X POST \
  -H "Authorization: Bearer $PT365_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "origin": "https://sandbox.your-emr.com",
    "clinicianId": "opaque-clinician-7f21",
    "theme": {
      "brandName": "Your HEP",
      "primaryColor": "#215C5C",
      "backgroundColor": "#F5F8F8"
    }
  }' \
  https://www.physicaltherapy365.com/api/v1/picker/sessions

Render the returned data.url as the iframe source. Add https://www.physicaltherapy365.com to the EMR's Content-Security-Policy frame-src directive.

const iframe = document.querySelector("#pt365-picker");

window.addEventListener("message", (event) => {
  if (event.origin !== "https://www.physicaltherapy365.com") return;
  if (event.source !== iframe.contentWindow) return;

  if (event.data?.type === "pt365.selection.completed") {
    saveExactExerciseSnapshots(event.data.payload.items);
  }
});

04 · Medical-record durability

Store the returned snapshot unchanged

The completion payload contains ordered items. Each item has an order, editable prescription, and complete versioned exercise snapshot. Store all three together in the EMR assignment record.

Prescription

Side, sets, repetitions, hold, duration, frequency, resistance, rest, custom dosage, and clinician instructions.

Identity

Immutable opaque exercise ID and exact content revision used for this assignment.

Content

Name, description, steps, cue, taxonomy, defaults, and checksummed start/end media.

History

Never rewrite an old assignment when a new exercise revision is published or withdrawn.

05 · Existing programs

Editing uses a new short-lived session

Build initialSelection from the stored prescriptions and request another picker session. PT365 validates that every selected exercise and revision is available in the credential's licensed environment.

Clinician instructions are intentionally not accepted in the server-created session. They are entered in the picker browser and returned directly to the EMR, keeping patient-specific free text away from PT365 request infrastructure.

06 · Optional advanced integration

Build a licensed local catalog cache

GET/api/v1/exercises

Search or page the licensed catalog with stable opaque cursors.

GET/api/v1/exercises/{id}?revision=…

Retrieve current or historical immutable content.

GET/api/v1/taxonomies

Discover controlled filter values instead of hardcoding them.

GET/api/v1/catalog/changes?cursor=…

Apply ordered additions, updates, deprecations, and withdrawals.

Commit the next cursor only after successfully storing the entire page. Treat cursors as opaque. A withdrawal should prevent new assignment while preserving historical assignments and their stored snapshots.

07 · Optional notifications

Use signed webhooks to reduce sync delay

Create a webhook for exercise.published, exercise.updated, exercise.deprecated, and exercise.withdrawn. Store the returned signing secret when the endpoint is created; it is shown only once.

Verify X-PT365-Signature against the unmodified body using HMAC-SHA256 of <X-PT365-Timestamp>.<raw-body>. Reject stale timestamps, compare signatures in constant time, and deduplicate using X-PT365-Delivery. Return a successful response quickly and use the change feed as the recovery source. Non-2xx deliveries are retried up to eight times.

08 · No-PHI boundary

Information PT365 must never receive

Only the partner origin, opaque clinician identifier, visual theme, and structured exercise selections belong in a picker-session request. Unknown fields are rejected.

09 · Definition of done

Run the complete sandbox workflow

  1. Authenticate from the backend and confirm the token reports sandbox.
  2. Confirm catalog search exposes exactly ten exercises.
  3. Launch the picker from the registered origin.
  4. Search, preview, select, prescribe, reorder, and complete exercises using the keyboard.
  5. Validate the message origin and source, then persist the complete returned items.
  6. Reopen the saved program through initialSelection and edit it.
  7. Test invalid credentials, origin spoofing, expiry, cancellation, quota responses, and PT365 unavailability.
  8. Confirm logs and outbound requests contain no patient or encounter data.