This guide shows you how to manage patients in JupyterHealth Exchange using either the web-based Console (Method 1) or the REST API (Method 2).
Introduction¶
Core Patient Operations¶
Advanced Topics¶
Reference¶
Introduction¶
Prerequisites¶
Note: The JupyterHealth Exchange Console is a web-based Single Page Application (SPA) that provides a user-friendly interface for managing patients, studies, and health data.
For Console Access:
- Practitioner account with login credentials 
- Member or manager role in an organization 
- Patient’s demographic information 
For REST API Access:
- OAuth2 access token 
- Member or manager role in an organization 
- HTTP client (curl, Python, JavaScript, etc.) 
Choose the method that best fits your workflow.
Understanding Patient Attributes¶
In JupyterHealth Exchange, patients:
- Have a user account for authentication 
- Belong to one or more organizations 
- Can be enrolled in multiple studies 
- Must consent to data sharing for each study 
- Own and control their health data 
Reference: jupyterhealth-exchange/core/models.py
Add New Patient¶
Method 1: Using JupyterHealth Exchange Console¶
The JupyterHealth Exchange Console provides a web-based interface for practitioners to manage patients interactively.
Prerequisites¶
- Practitioner account in JupyterHealth Exchange 
- Member or manager role in an organization 
- Patient’s demographic information 
- Access to JupyterHealth Exchange web portal 
Step 1: Access the Console¶
- Navigate to the JupyterHealth Exchange web portal: - https://your-jhe-instance.com/portal/
- Log in with your practitioner credentials - Enter your email address 
- Enter your password 
- You’ll be authenticated via OAuth2/OIDC 
- After successful login, you’ll be redirected to the Organizations page 
 
Step 2: Navigate to Patients¶
- In the console navigation, click the Patients tab 
- At the top left, select your Organization from the dropdown 
- Optionally, filter by Study using the second dropdown (or select “All Studies”) 
You’ll see a table of existing patients in the selected organization/study.
Step 3: Create New Patient¶
- Click the “Add Patient...” button (green button, top left area) 
- A modal dialog opens titled “Create Patient” 
- Option 1: Check if patient already exists - Enter the patient’s email in the “Patient E-mail” field 
- Click “Lookup” button 
- If patient exists in another organization: - Patient details will appear 
- You can add them to your organization (skip to Step 5) 
 
- If patient doesn’t exist: - Continue with patient creation below 
 
 
- Create new patient by filling in the form: - E-mail (required): - john.smith@example.com- This will be the patient’s login email 
- Automatically filled if you used lookup 
 
- External Identifier: Medical record number or other ID (e.g., - MRN-12345)
- Family Name (required): - Smith
- Given Name (required): - John
- DOB (required): - 1980-01-01(YYYY-MM-DD format)
- Cell: - 555-1234(optional phone number)
 
- Click “Create” button 
- The patient is created and added to the selected organization 
- The modal closes and the patient appears in the patient table 
Step 4: View Patient Details¶
After creating the patient, you can view their full details:
- Find the patient in the table 
- Click the “eye” icon (View/Read button) in the leftmost column 
- A modal opens showing: - Patient ID and demographic information 
- Organizations: List of organizations this patient belongs to 
- Studies Pending Response: Studies awaiting patient consent 
- Studies Responded To: Studies with consent status (checkmarks for consented) 
 
Step 5: Generate Invitation Link¶
This is a key step - the patient needs an invitation link to connect their mobile app and start uploading health data.
- With the patient details modal open (from Step 4): 
- Scroll down to the “Generate Invitation Link” section 
- Configure email option: - Check “Send link by email” to automatically email the link to the patient 
- Or uncheck it to manually copy and send the link yourself 
 
- Click the “Generate Invitation Link” button (green) 
- The invitation link appears in the text area. 
- Send Link to Patient Option A: Email sent automatically - If “Send link by email” was checked, the patient receives an email with the invitation link 
- Email template includes instructions for installing the CommonHealth app 
 - Option B: Copy link manually - Click “Copy to Clipboard” button 
- Send the link to the patient via: - Email 
- SMS 
- Patient portal 
- Printed QR code 
- In-person instruction 
 
 
Link Components:
- App package ID: Links to CommonHealth app in app store 
- Exchange hostname: Your JupyterHealth Exchange instance 
- Authorization code: One-time code for patient authentication Example: - https://play.google.com/store/apps/details?id=org.thecommonsproject.android.phr.dev&referrer=cloud_sharing=jhe.yourdomain.com|LhS05iR1rOnpS4JWfP6GeVUIhaRcRh
Step 6: Enroll Patient in Study (Optional)¶
To enroll one or more patients in a study:
- Navigate to the Patients section 
- Select the organization from the “Organization” dropdown at the top 
- Check the checkbox(es) next to the patient(s) you want to enroll 
- Click the “Add Patient(s) to Study...” button (blue button) 
- You’ll be taken to the Studies page where you can select which study to add the patients to 
- Find the target study and click to add the selected patients 
- All selected patients are enrolled in the study 
Notes:
- You can enroll a single patient or multiple patients at once using this method 
- Patient and study must be in the same organization 
Step 7: Verify Patient Setup¶
After completing the above steps, verify the patient is properly configured:
- Click the “eye” icon to view patient details 
- Check that: - ✅ Patient belongs to correct organization(s) 
- ✅ Patient is enrolled in desired study (appears in “Studies Pending Response”) 
- ✅ Invitation link has been generated and sent 
 
- Patient should receive the invitation email (if email option was selected) 
- Patient can now install the CommonHealth app and authenticate 
Console Tips¶
- Pagination: Use the page selector and “per page” dropdown to navigate large patient lists 
- Filtering: Use Organization and Study dropdowns to narrow down the patient list 
- Checkboxes: Select multiple patients for bulk operations 
- Action Icons: - Eye icon = View patient details 
- Pencil icon = Edit patient information 
- Trash icon = Delete patient from organization 
 
- Keyboard shortcuts: Press Enter in page input field to jump to that page 
Method 2: Using REST API¶
The REST API enables programmatic patient management, ideal for bulk imports and automated workflows.
Prerequisites¶
- OAuth2 access token (practitioner authentication) 
- Member or manager role in an organization 
- HTTP client (curl, Postman, or programming language) 
Step 1: Authenticate¶
Obtain an access token as a practitioner. See Using the REST API for the full authentication flow.
ACCESS_TOKEN="your-access-token-here"Step 2: Create Patient via API¶
curl -X POST https://your-jhe-instance.com/api/v1/patients \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "telecomEmail": "john.smith@example.com",
    "nameFamily": "Smith",
    "nameGiven": "John",
    "birthDate": "1980-01-01",
    "telecomPhone": "555-1234",
    "organizationId": 1
  }'Fields:
- telecomEmail(required): Patient’s email address (used for authentication)
- nameFamily(required): Last name
- nameGiven(required): First name
- birthDate(required): Date of birth in YYYY-MM-DD format
- telecomPhone(optional): Phone number
- organizationId(required): Your organization ID
Response:
{
  "id": 10001,
  "jheUserId": 5,
  "identifier": null,
  "nameFamily": "Smith",
  "nameGiven": "John",
  "birthDate": "1980-01-01",
  "telecomPhone": "555-1234",
  "telecomEmail": "john.smith@example.com",
  "organizationId": 1
}Note the patient ID (e.g., 10001) for subsequent operations.
Reference: jupyterhealth-exchange/core/views/patient.py
Step 3: Generate Invitation Link¶
Create an invitation link for the patient to connect their mobile app:
curl https://your-jhe-instance.com/api/v1/patients/10001/invitation_link \
  -H "Authorization: Bearer $ACCESS_TOKEN"Response:
{
  "invitationLink": "https://play.google.com/store/apps/details?id=org.thecommonsproject.android.phr.dev&referrer=cloud_sharing=jhe.yourdomain.com|LhS05iR1rOnpS4JWfP6GeVUIhaRcRh"
}Link Components:
- App package ID: - org.thecommonsproject.android.phr.dev
- Exchange hostname: - jhe.yourdomain.com
- Authorization code: - LhS05iR1rOnpS4JWfP6GeVUIhaRcRh
Reference: jupyterhealth-exchange/core/models.py and jupyterhealth-exchange/core/views/patient.py
Step 4: Send Invitation to Patient¶
Option 1: Automatic Email¶
curl "https://your-jhe-instance.com/api/v1/patients/10001/invitation_link?send_email=true" \
  -H "Authorization: Bearer $ACCESS_TOKEN"This sends an email to the patient’s registered email address with the invitation link.
Reference: jupyterhealth-exchange/core/views/patient.py
Option 2: Manual Distribution¶
Send the invitation link via:
- Email 
- SMS 
- Patient portal 
- Printed QR code 
- In-person instruction 
Example email template:
Subject: Join [Study Name] - Connect Your Health Data
Dear John,
You've been invited to participate in our health study. To get started:
1. Click this link on your mobile device: [INVITATION_LINK]
2. Install the CommonHealth app (if not already installed)
3. Follow the instructions to connect your health devices
4. Review and provide consent for data sharing
If you have questions, contact us at research@example.com.
Thank you,
[Your Organization Name]Add Existing Patient to Organization¶
If a patient already exists in the Exchange but not in your organization, you can add them using either method.
Method 1: Using JupyterHealth Exchange Console¶
- Navigate to Patients tab 
- Click “Add Patient...” button 
- In the modal, enter the patient’s email in the “Patient E-mail” field 
- Click “Lookup” button 
- If the patient exists in another organization: - Patient details will appear 
- An alert shows: “This Patient already exists. Click the Update button below to add this Patient to the Organization” 
 
- Click “Update” button 
- Patient is added to your organization 
Method 2: Using REST API¶
Step 1: Search for Patient¶
curl "https://your-jhe-instance.com/api/v1/patients/global_lookup?email=john.smith@example.com" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Response:
[
  {
    "id": 10001,
    "nameFamily": "Smith",
    "nameGiven": "John",
    "birthDate": "1980-01-01",
    "telecomEmail": "john.smith@example.com"
  }
]Note: This searches across all organizations in the Exchange.
Reference: jupyterhealth-exchange/core/views/patient.py
Step 2: Add to Your Organization¶
curl -X PATCH "https://your-jhe-instance.com/api/v1/patients/10001/global_add_organization?organization_id=1" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Response:
{
  "id": 10001,
  "nameFamily": "Smith",
  "nameGiven": "John",
  "organizationId": 1
}Reference: jupyterhealth-exchange/core/views/patient.py
Enroll Patient in Study¶
You can enroll patients in studies using either the Console or REST API.
Method 1: Using JupyterHealth Exchange Console¶
- Navigate to Patients tab 
- Select your Organization from the dropdown 
- Select the Study from the dropdown (not “All Studies”) 
- Check the checkbox(es) next to the patient(s) you want to enroll 
- Click “Add Patient(s) to Study...” button (blue button) 
- Confirm the enrollment 
- Patient(s) are enrolled in the selected study 
Bulk enrollment: You can select multiple patients and enroll them all at once.
Note: Patient and study must be in the same organization.
Method 2: Using REST API¶
Step 1: List Available Studies¶
curl "https://your-jhe-instance.com/api/v1/studies?organization_id=1" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Response:
{
  "results": [
    {
      "id": 10001,
      "name": "Diabetes Management Study",
      "description": "Monitoring blood glucose levels",
      "scopeRequests": [
        {
          "codingCode": "omh:blood-glucose:4.0",
          "text": "Blood Glucose"
        }
      ]
    }
  ]
}Step 2: Enroll Patient¶
curl -X POST https://your-jhe-instance.com/api/v1/studies/10001/patients \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "patient_ids": [10001]
  }'Note: Patient and study must be in the same organization.
Reference: jupyterhealth-exchange/core/views/study.py
Step 3: Verify Enrollment¶
curl "https://your-jhe-instance.com/api/v1/patients?organization_id=1&study_id=10001" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Response should include the newly enrolled patient.
Manage Patient Consent¶
Patient consent can be viewed through the Console. To grant or revoke consent on behalf of a patient, use the REST API.
Method 1: View Patient Consent Using JupyterHealth Exchange Console¶
- Navigate to Patients tab 
- Find the patient and click the “eye” icon (View button) 
- In the patient details modal, scroll down to view: - Studies Pending Response: Studies awaiting patient’s consent decision - Shows which data types (scopes) are pending 
 
- Studies Responded To: Studies with consent decisions - Checkmark icon (✓) = Patient consented to this data type 
- X icon = Patient declined this data type 
 
 
Note: Consent status in the Console is read-only. Patients typically grant consent via their mobile app. To grant or revoke consent on behalf of a patient (with appropriate authorization), use the REST API.
Method 2: View Consent Status via REST API¶
curl https://your-jhe-instance.com/api/v1/patients/10001/consents \
  -H "Authorization: Bearer $ACCESS_TOKEN"Response:
{
  "patient": {
    "id": 10001,
    "nameFamily": "Smith",
    "nameGiven": "John",
    "birthDate": "1980-01-01"
  },
  "consolidatedConsentedScopes": [
    {
      "id": 1,
      "codingCode": "omh:blood-glucose:4.0",
      "text": "Blood Glucose"
    }
  ],
  "studiesPendingConsent": [
    {
      "id": 10002,
      "name": "Cardiac Health Study",
      "pendingScopeConsents": [
        {
          "code": {
            "codingCode": "omh:blood-pressure:4.0",
            "text": "Blood Pressure"
          },
          "consented": null,
          "consentedTime": null
        }
      ]
    }
  ],
  "studies": [
    {
      "id": 10001,
      "name": "Diabetes Management Study",
      "scopeConsents": [
        {
          "code": {
            "codingCode": "omh:blood-glucose:4.0"
          },
          "consented": true,
          "consentedTime": "2024-05-01T10:30:00Z"
        }
      ]
    }
  ]
}Key Fields:
- consolidatedConsentedScopes: All data types patient has consented to (across all studies)
- studiesPendingConsent: Studies awaiting patient’s consent decision
- studies: Studies patient has consented to or declined
Reference: jupyterhealth-exchange/core/views/patient.py
Grant Consent (Practitioner on Behalf of Patient)¶
Practitioners with member or manager roles can grant consent on behalf of patients using the REST API.
Important Notes:
- Patients typically grant consent themselves via their mobile app 
- Practitioners should only grant consent with explicit patient authorization 
- This functionality is not yet available in the Console UI - must use REST API 
Prerequisites:
- OAuth2 access token with practitioner authentication 
- Member or manager role in the patient’s organization (viewer role cannot grant consent) 
- Patient must be enrolled in the study 
curl -X POST https://your-jhe-instance.com/api/v1/patients/10001/consents \
  -H "Authorization: Bearer $PRACTITIONER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "studyScopeConsents": [{
      "studyId": 10001,
      "scopeConsents": [{
        "codingSystem": "https://w3id.org/openmhealth",
        "codingCode": "omh:blood-glucose:4.0",
        "consented": true
      }]
    }]
  }'Reference: jupyterhealth-exchange/core/views/patient.py and authorization check at lines 198-200
Revoke Consent (Practitioner on Behalf of Patient)¶
Practitioners with member or manager roles can revoke consent on behalf of patients using the REST API.
Important Notes:
- Patients typically revoke consent themselves via their mobile app 
- Practitioners should only revoke consent with explicit patient authorization 
- This functionality is not yet available in the Console UI - must use REST API 
To revoke consent, set consented: false:
curl -X POST https://your-jhe-instance.com/api/v1/patients/10001/consents \
  -H "Authorization: Bearer $PRACTITIONER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "studyScopeConsents": [{
      "studyId": 10001,
      "scopeConsents": [{
        "codingSystem": "https://w3id.org/openmhealth",
        "codingCode": "omh:blood-glucose:4.0",
        "consented": false
      }]
    }]
  }'Effect: Patient’s existing data remains in the Exchange, but no new data of this type will be collected from the patient’s devices.
Update Patient Information¶
You can update patient demographics using either the Console or REST API.
Method 1: Using JupyterHealth Exchange Console¶
- Navigate to Patients tab 
- Find the patient in the table 
- Click the “pencil” icon (Edit/Update button) 
- In the modal, update the demographic fields: - External Identifier 
- Family Name 
- Given Name 
- DOB 
- Cell phone 
 
- Click “Update” button 
- Patient information is updated 
Note: Email address is read-only in the Console and cannot be changed through the UI. Changing email requires REST API access.
Method 2: Using REST API¶
curl -X PATCH https://your-jhe-instance.com/api/v1/patients/10001 \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "telecomPhone": "555-5678",
    "nameGiven": "Jonathan"
  }'Updatable Fields:
- nameFamily
- nameGiven
- birthDate
- telecomPhone
- telecomEmail
- identifier
Note: Email address is used for authentication. Changing it requires patient to re-authenticate.
Remove Patient from Study¶
You can remove patients from studies using either the Console or REST API.
Method 1: Using JupyterHealth Exchange Console¶
- Navigate to Patients tab 
- Select the Organization from the dropdown 
- Select the Study from the dropdown (the study you want to remove patients from) 
- Check the checkbox(es) next to the patient(s) you want to remove 
- Click “Remove Patient(s) from Study” button (orange/warning button) 
- Confirm the removal 
- Patient(s) are removed from the study 
Bulk removal: You can select multiple patients and remove them all at once.
Method 2: Using REST API¶
curl -X DELETE https://your-jhe-instance.com/api/v1/studies/10001/patients \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "patient_ids": [10001]
  }'Effect:
- Removes patient from study 
- Deletes consent records for this study 
- Existing observation data remains (not deleted) 
Reference: jupyterhealth-exchange/core/views/study.py
Remove Patient from Organization¶
You can remove patients from organizations using either the Console or REST API.
Method 1: Using JupyterHealth Exchange Console¶
- Navigate to Patients tab 
- Select the Organization from the dropdown 
- Ensure “All Studies” is selected (to see all patients in organization) 
- Find the patient in the table 
- Click the “trash” icon (Delete button) 
- A confirmation modal appears: “Are you sure you want to delete this entire record?” 
- Click “Delete” button to confirm 
- Patient is removed from the selected organization 
Warning:
- This removes the patient from the selected organization 
- Patient is also removed from all studies in that organization 
- If the patient has no other organizations, this will delete all observations and the patient record entirely 
Method 2: Using REST API¶
curl -X DELETE "https://your-jhe-instance.com/api/v1/patients/10001?organization_id=1" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Effect:
- Removes patient from organization 
- Removes from all studies in that organization 
- Deletes all consent records for organization’s studies 
- If patient has no other organizations: deletes all observations and patient record 
Reference: jupyterhealth-exchange/core/views/patient.py
View Patient’s Data¶
You can view patient data using either the Console or REST API.
Method 1: Using JupyterHealth Exchange Console¶
- Navigate to Observations tab in the Console 
- Select the Organization from the dropdown 
- Select the Study from the dropdown (optional) 
- Use filters to narrow the data: - Patient: Select specific patient or view all 
- Data Type: Filter by codeable concept (e.g., blood-glucose, steps) 
- Date Range: Filter by time period 
 
- The observations table displays: - Observation ID 
- Patient name 
- Data type (codeable concept) 
- Data source (device/wearable) 
- Status 
- Created date/time 
 
- Click on an observation row to view full details including OMH data payload 
- Use pagination controls to navigate large datasets 
Tips:
- Export data via REST API for analysis (see below) 
- Use date filters to focus on specific time periods 
- Filter by data source to see data from specific devices 
Method 2: Using REST API¶
List Observations¶
curl "https://your-jhe-instance.com/api/v1/observations?organization_id=1&study_id=10001&patient_id=10001" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Response:
{
  "count": 250,
  "next": "...",
  "previous": null,
  "results": [
    {
      "id": 1,
      "subjectPatient": 10001,
      "patientNameDisplay": "Smith, John",
      "codeableConcept": 1,
      "codingCode": "omh:blood-glucose:4.0",
      "dataSource": 70001,
      "status": "final",
      "created": "2024-05-02T08:30:00Z"
    }
  ]
}Reference: jupyterhealth-exchange/core/models.py
Export Patient Data (FHIR)¶
curl "https://your-jhe-instance.com/fhir/r5/Observation?patient=10001&patient._has:Group:member:_id=10001" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Returns FHIR Bundle with all authorized observations for the patient.
Reference: See “Fetching and Exporting Data” guide for details.
Bulk Patient Import¶
For importing multiple patients at once, use the REST API with a script:
import requests
import csv
BASE_URL = "https://your-jhe-instance.com"
ACCESS_TOKEN = "your-access-token"
ORGANIZATION_ID = 1
def create_patient(email, family_name, given_name, birth_date, phone=None):
    response = requests.post(
        f"{BASE_URL}/api/v1/patients",
        headers={
            "Authorization": f"Bearer {ACCESS_TOKEN}",
            "Content-Type": "application/json",
        },
        json={
            "telecomEmail": email,
            "nameFamily": family_name,
            "nameGiven": given_name,
            "birthDate": birth_date,
            "telecomPhone": phone,
            "organizationId": ORGANIZATION_ID,
        },
    )
    if response.status_code == 201:
        patient = response.json()
        print(f"Created patient {patient['id']}: {given_name} {family_name}")
        return patient
    else:
        print(f"Error creating {given_name} {family_name}: {response.text}")
        return None
def generate_invitation(patient_id):
    response = requests.get(
        f"{BASE_URL}/api/v1/patients/{patient_id}/invitation_link?send_email=true",
        headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
    )
    if response.status_code == 200:
        data = response.json()
        print(f"  Invitation sent: {data['invitationLink']}")
    else:
        print(f"  Error generating invitation: {response.text}")
# Read from CSV
with open("patients.csv", "r") as f:
    reader = csv.DictReader(f)
    for row in reader:
        patient = create_patient(
            email=row["email"],
            family_name=row["last_name"],
            given_name=row["first_name"],
            birth_date=row["dob"],
            phone=row.get("phone"),
        )
        if patient:
            generate_invitation(patient["id"])
print("Bulk import complete!")CSV Format (patients.csv):
email,last_name,first_name,dob,phone
john.smith@example.com,Smith,John,1980-01-01,555-1234
jane.doe@example.com,Doe,Jane,1975-05-15,555-5678Common Issues¶
Patient Already Exists¶
Error: “User with this email already exists”
Cause: A patient with this email already has an account.
Solution: Use global lookup to find the patient and add them to your organization:
# Find patient
curl "https://your-jhe-instance.com/api/v1/patients/global_lookup?email=john.smith@example.com" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
# Add to organization
curl -X PATCH "https://your-jhe-instance.com/api/v1/patients/10001/global_add_organization?organization_id=1" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Reference: jupyterhealth-exchange/core/views/patient.py
Cannot Enroll Patient in Study¶
Error: “Patient and study must share the same organization”
Cause: Patient is in organization A but study is in organization B.
Solution: Ensure both patient and study are in the same organization:
# Check patient's organization
curl https://your-jhe-instance.com/api/v1/patients/10001 \
  -H "Authorization: Bearer $ACCESS_TOKEN"
# Check study's organization
curl "https://your-jhe-instance.com/api/v1/studies?organization_id=1" \
  -H "Authorization: Bearer $ACCESS_TOKEN"Reference: jupyterhealth-exchange/core/views/study.py
Invitation Link Doesn’t Work¶
Issue: Patient clicks link but app doesn’t open or connection fails.
Common Causes:
- Link expired: Authorization codes expire after 2 weeks - Solution: Generate new invitation link 
 
- Wrong app package: Link targets wrong environment (dev vs prod) - Solution: Verify - CH_INVITATION_LINK_PREFIXin- .env
 
- App not installed: Patient doesn’t have CommonHealth app - Solution: Link should redirect to app store first 
 
- Network issue: Can’t reach Exchange server - Solution: Verify Exchange URL is accessible 
 
Reference: jupyterhealth-exchange/jhe/settings.py and jupyterhealth-exchange/core/models.py
Permission Denied¶
Error: 403 Forbidden when creating or viewing patient
Cause: User doesn’t have patient.manage_for_organization permission.
Solution: Verify your role in the organization:
curl https://your-jhe-instance.com/api/v1/organizations \
  -H "Authorization: Bearer $ACCESS_TOKEN"Your role must be manager or member, not viewer.
Reference: jupyterhealth-exchange/core/permissions.py and view permission check at jupyterhealth-exchange/core/views/patient.py
Best Practices¶
Patient Onboarding¶
- Verify information: Double-check email address and demographics before creating patient 
- Immediate invitation: Generate and send invitation link right after creating patient 
- Follow-up: Contact patients who don’t connect within 48 hours 
- Test first: Create a test patient account to verify the onboarding flow 
Consent Management¶
- Clear communication: Explain what data will be collected and why 
- Study-specific consent: Patients consent per study, not globally 
- Allow withdrawal: Make it easy for patients to revoke consent 
- Document consent: Keep records of when patients consented 
Data Privacy¶
- Minimum necessary: Only collect data types required for your study 
- Role-based access: Ensure only authorized practitioners can view patient data 
- Audit access: Regularly review who has accessed patient records 
- De-identify for analysis: Use de-identified datasets when possible 
Communication¶
- Multi-channel: Provide invitation links via multiple channels (email, SMS, portal) 
- Technical support: Have a help desk for patients with connection issues 
- Status updates: Regularly check which patients are actively uploading data 
- Engagement: Reach out to patients who stop uploading data 
Example: Complete Patient Onboarding via REST API¶
# Set variables
BASE_URL="https://your-jhe-instance.com"
ACCESS_TOKEN="your-access-token"
ORG_ID=1
STUDY_ID=10001
# 1. Create patient
PATIENT=$(curl -X POST "$BASE_URL/api/v1/patients" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "telecomEmail": "john.smith@example.com",
    "nameFamily": "Smith",
    "nameGiven": "John",
    "birthDate": "1980-01-01",
    "telecomPhone": "555-1234",
    "organizationId": '$ORG_ID'
  }')
PATIENT_ID=$(echo $PATIENT | jq -r '.id')
echo "Created patient ID: $PATIENT_ID"
# 2. Enroll in study
curl -X POST "$BASE_URL/api/v1/studies/$STUDY_ID/patients" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"patient_ids": ['$PATIENT_ID']}'
echo "Enrolled in study $STUDY_ID"
# 3. Generate and send invitation
INVITATION=$(curl "$BASE_URL/api/v1/patients/$PATIENT_ID/invitation_link?send_email=true" \
  -H "Authorization: Bearer $ACCESS_TOKEN")
echo "Invitation sent:"
echo $INVITATION | jq -r '.invitationLink'
# 4. Verify patient status
curl "$BASE_URL/api/v1/patients/$PATIENT_ID/consents" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq
echo "Patient onboarding complete!"Choosing Between JupyterHealth Exchange Console and REST API¶
When to Use JupyterHealth Exchange Console¶
✅ Best for:
- Adding individual patients interactively 
- Practitioners who prefer web-based interfaces 
- Viewing and managing patient data visually 
- Generating invitation links with email integration 
- Monitoring patient consent status 
- Quick testing and prototyping 
- Training and demonstrations 
- Day-to-day patient management tasks 
❌ Not ideal for:
- Bulk patient imports (>20 patients) 
- Automated workflows 
- Integration with external systems (EHR, etc.) 
- Scheduled or triggered operations 
- Building custom applications 
Key Features:
- Full patient lifecycle management (create, view, update, delete) 
- One-click invitation link generation with email 
- Visual consent status tracking 
- Study enrollment with multi-select 
- Real-time observation viewing 
- Organization and study filtering 
- No programming required 
When to Use REST API¶
✅ Best for:
- Bulk patient imports from CSV or database 
- Automated patient enrollment workflows 
- Integration with EHR systems or other applications 
- Building custom UIs or mobile apps 
- Scheduled or triggered patient creation 
- Data export and analysis (CSV, DataFrame) 
- Programmatic consent management 
- Continuous integration/deployment pipelines 
❌ Not ideal for:
- One-off patient additions 
- Users unfamiliar with APIs or command line 
- Quick interactive patient management 
- Exploratory data viewing 
Key Features:
- Batch operations (create 100s of patients at once) 
- Scriptable workflows (Python, JavaScript, shell) 
- Webhook integration 
- Custom business logic 
- Advanced filtering and querying 
- FHIR R5 API compliance 
Hybrid Approach (Recommended)¶
Most organizations use both methods for different purposes:
Typical Workflow:
- Initial Setup (Console): - Create organizations via web portal 
- Create studies with scope requests 
- Configure data sources 
- Add initial test patients 
 
- Bulk Import (REST API): - Import existing patients from EHR system 
- Enroll patients in studies programmatically 
- Generate and send invitation links in batch 
 
- Daily Operations (Console): - Add new patients one-by-one as they enroll 
- View consent status and follow up with patients 
- Monitor data collection in real-time 
- Generate invitation links for individual patients 
- Troubleshoot patient issues 
 
- Analysis and Reporting (REST API): - Export observation data for analysis 
- Generate reports via scripts 
- Integrate with data warehouses 
- Automated quality checks