Documentation/api/AUTHORIZATION.md
Complete guide to OpenEMR API scopes, permissions, and access control.
OpenEMR implements SMART on FHIR v2.2.0 scopes with granular permissions, allowing fine-grained control over API access. Scopes define:
.cruds syntax.read/.write scopes supportedSMART v2.2.0 Format (Recommended):
<context>/<Resource>.<permissions>[?<query>]
Components:
<context>: patient, user, or system<Resource>: FHIR resource type (e.g., Patient, Observation)<permissions>: One or more permission flags (.cruds)<query>: Optional query string for granular filteringExamples:
patient/Patient.rs # Read and search patients
user/Observation.cruds # Full access to observations
system/Patient.rs # System-level patient read/search
patient/Condition.rs?category=... # Granular scope with filter
SMART v2.2.0 introduces granular permission flags:
| Flag | Permission | Description | HTTP REST Operation |
|---|---|---|---|
c | Create | Create new resources | POST |
r | Read | Read individual resources by ID | GET |
u | Update | Update existing resources | POST,PUT,PATCH |
d | Delete | Delete resources | DELETE |
s | Search | Search for resources | GET |
Combine flags to specify multiple permissions. Flags must be in order: cruds
✅ Valid Examples:
patient/Patient.r # Read only
patient/Patient.rs # Read and search
patient/Patient.cr # Create and read
patient/Patient.cru # Create, read, update
patient/Patient.crud # Create, read, update, delete
patient/Patient.cruds # All permissions
patient/Observation.rs # Read and search observations
patient/Observation.cud # Create, update, delete (no read/search)
user/Condition.rus # Read, update, search
❌ Invalid Examples:
patient/Patient.sr # Wrong order (should be .rs)
patient/Patient.duc # Wrong order (should be .cud)
patient/Patient.rsc # Wrong order (should be .crs)
patient/Patient.xyz # Invalid flags
| Scope | Use Case |
|---|---|
.r | Read individual resources only (no search) |
.rs | Read-only access with search |
.cud | Write operations without read access |
.cruds | Full access to resource type |
.rus | Read, update, and search (common for updates) |
.crs | Create, read, and search (common for data entry) |
Scopes are contextualized by prefix:
patient/ - Patient ContextExamples:
patient/Patient.rs
patient/Observation.rs
patient/MedicationRequest.rs
Who uses this:
user/ - User ContextExamples:
user/Patient.rs
user/Observation.cruds
user/Practitioner.rus
Who uses this:
system/ - System ContextExamples:
system/Patient.rs
system/Patient.$export
system/Group.$export
Who uses this:
New in SMART v2.2.0: Restrict access to resource subsets using query parameters.
<context>/<Resource>.<permissions>?<parameter>=<value>
Filter conditions by category:
| Scope | Description |
|---|---|
patient/Condition.rs?category=http://hl7.org/fhir/us/core/CodeSystem/condition-category|health-concern | Health concerns only |
patient/Condition.rs?category=http://terminology.hl7.org/CodeSystem/condition-category|encounter-diagnosis | Encounter diagnoses only |
patient/Condition.rs?category=http://terminology.hl7.org/CodeSystem/condition-category|problem-list-item | Problem list items only |
Example Use Cases:
Request Example:
# Register with granular Condition scope
curl -X POST https://localhost:9300/oauth2/default/registration \
-H 'Content-Type: application/json' \
--data '{
"scope": "openid patient/Patient.rs patient/Condition.rs?category=http://hl7.org/fhir/us/core/CodeSystem/condition-category|health-concern"
}'
Filter observations by category:
| Scope | Description |
|---|---|
patient/Observation.rs?category=http://hl7.org/fhir/us/core/CodeSystem/us-core-category|sdoh | Social determinants of health |
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|social-history | Social history (smoking, etc.) |
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory | Lab results only |
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|survey | Survey/assessment results |
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs | Vital signs only |
Example Use Cases:
Request Example:
# Vital signs tracking app
curl -X POST https://localhost:9300/oauth2/default/registration \
-H 'Content-Type: application/json' \
--data '{
"scope": "openid patient/Patient.rs patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs"
}'
Filter documents by category:
| Scope | Description |
|---|---|
patient/DocumentReference.rs?category=http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category|clinical-note | Clinical notes only |
Example Use Cases:
Request Example:
# Clinical notes app
curl -X POST https://localhost:9300/oauth2/default/registration \
-H 'Content-Type: application/json' \
--data '{
"scope": "openid patient/Patient.rs patient/DocumentReference.rs?category=http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category|clinical-note"
}'
Important: Granular scopes restrict access. If you request:
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs
You will only be able to access vital signs observations. You cannot access lab results or other observation types.
To access multiple categories, request separate scopes:
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory
OpenEMR maintains backward compatibility with SMART on FHIR v1 scopes.
| V1 Scope | V2 Equivalent | Permissions Granted |
|---|---|---|
patient/Patient.read | patient/Patient.rs | Read + Search |
patient/Patient.write | patient/Patient.cud | Create + Update + Delete |
user/Observation.read | user/Observation.rs | Read + Search |
user/Observation.write | user/Observation.cud | Create + Update + Delete |
system/Patient.read | system/Patient.rs | Read + Search |
V1 scopes are still accepted during registration and authorization:
{
"scope": "openid patient/Patient.read patient/Observation.read"
}
What you receive:
{
"scope": "openid patient/Patient.read patient/Observation.read"
}
For new applications:
.cruds)For existing applications:
read/write) continue to workread/write)Breaking changes:
Certain scopes are required or strongly recommended for all applications:
| Scope | Required For | Description |
|---|---|---|
openid | All OAuth2 apps | OpenID Connect authentication |
fhirUser | SMART apps | Identifies the authorized user |
online_access | Session-based apps | Indicates online access pattern |
| Scope | When Required | Description |
|---|---|---|
offline_access | Apps needing refresh tokens | Enables refresh token issuance |
launch | EHR launch apps | Indicates EHR launch capability |
launch/patient | Apps needing patient context | Receive patient ID in token |
At least one API type scope is required:
| Scope | API Access |
|---|---|
api:fhir | FHIR API (/fhir/ endpoints) |
api:oemr | Standard API (/api/ endpoints) |
api:port | Patient Portal API (/portal/ endpoints) |
Example minimal scope request:
openid api:fhir patient/Patient.rs
Access to FHIR R4 endpoints (/fhir/). Requires api:fhir base scope plus resource-specific scopes.
Access to a single patient's data. Patient ID provided in token response.
patient/AllergyIntolerance.rs
patient/Appointment.rs
patient/CarePlan.rs
patient/CareTeam.rs
patient/Condition.rs
patient/Coverage.rs
patient/Device.rs
patient/DiagnosticReport.rs
patient/DocumentReference.rs
patient/Encounter.rs
patient/Goal.rs
patient/Immunization.rs
patient/MedicationRequest.rs
patient/Medication.rs
patient/Observation.rs
patient/Patient.rs
patient/Procedure.rs
patient/Provenance.rs # Read/search only
patient/MedicationDispense.rs # Pharmacy dispensing records
patient/RelatedPerson.rs # Patient relationships
patient/ServiceRequest.rs # Lab/procedure orders
patient/Specimen.rs # Laboratory specimens
patient/Binary.rs # Binary data (read-only)
patient/Location.rs # Facility locations
patient/Organization.rs # Healthcare organizations
patient/Person.rs # Person records
patient/Practitioner.rs # Healthcare providers
patient/PractitionerRole.rs # Provider roles
patient/DocumentReference.$docref # Generate CCD documents
Access to data the authenticated user is authorized to see (multiple patients).
user/AllergyIntolerance.rs
user/CarePlan.rs
user/CareTeam.rs
user/Condition.rs
user/Coverage.rs
user/Device.rs
user/DiagnosticReport.rs
user/DocumentReference.rs
user/Encounter.rs
user/Goal.rs
user/Immunization.rs
user/MedicationRequest.rs
user/Medication.rs
user/Observation.rs
user/Patient.rs # Access to multiple patients
user/Procedure.rs
user/Provenance.rs
user/MedicationDispense.rs
user/RelatedPerson.rs
user/ServiceRequest.rs
user/Specimen.rs
user/Binary.rs
user/Location.rs
user/Organization.rs
user/Person.rs
user/Practitioner.rs
user/PractitionerRole.rs
user/DocumentReference.$docref
Unrestricted access to all data. Used for backend services and bulk exports.
system/AllergyIntolerance.rs
system/CarePlan.rs
system/CareTeam.rs
system/Condition.rs
system/Coverage.rs
system/Device.rs
system/DiagnosticReport.rs
system/DocumentReference.rs
system/Encounter.rs
system/Goal.rs
system/Immunization.rs
system/MedicationRequest.rs
system/Medication.rs
system/Observation.rs
system/Patient.rs
system/Procedure.rs
system/Provenance.rs
system/MedicationDispense.rs
system/RelatedPerson.rs
system/ServiceRequest.rs
system/Specimen.rs
system/Binary.rs
system/Group.rs
system/Location.rs
system/Organization.rs
system/Person.rs
system/Practitioner.rs
system/PractitionerRole.rs
Required for bulk FHIR exports:
system/Patient.$export # Patient-level export
system/Group.$export # Group-level export
system/*.$export # System-level export
system/*.$bulkdata-status # Check export status
system/Binary.read # Download export files
Complete bulk export scope set:
system/Group.$export system/*.$bulkdata-status system/Binary.read
See FHIR API - Bulk Exports for details.
patient/DocumentReference.$docref
user/DocumentReference.$docref
system/DocumentReference.$docref
Access to OpenEMR REST API (/api/ endpoints). Requires api:oemr base scope plus resource-specific scopes.
user/allergy.cruds
user/appointment.cruds
user/dental_issue.cruds
user/document.crs
user/drug.rs
user/encounter.crus
user/employer.s
user/facility.crus
user/immunization.rs
user/insurance.crus
user/insurance_company.crus
user/insurance_type.s
user/list.r
user/medical_problem.cruds
user/medication.cruds
user/message.cud
user/patient.crus
user/practitioner.crus
user/prescription.rs
user/procedure.rs
user/product.s
user/soap_note.crus
user/surgery.cruds
user/transaction.cuds
user/vital.crus
Note: Standard API uses different permission syntax than FHIR but follows the same .cruds pattern.
EXPERIMENTAL - Access to patient portal endpoints (/portal/).
patient/encounter.rs
patient/patient.s
patient/appointment.rs
Enable Patient Portal API: Administration → Config → Connectors → "Enable OpenEMR Patient Portal REST API (EXPERIMENTAL)"
Used with EHR Launch to receive context information:
launch # EHR launch capability
launch/patient # When launching outside EHR, ask for a patient to be selected at launch time
See AUTHENTICATION.md - EHR Launch for details.
openid # OpenID Connect (required)
fhirUser # Practitioner/Patient identity
profile # User profile information
email # User email address
online_access # Session-based access
offline_access # Refresh token access
Administrators can revoke API access at multiple levels.
Disable an entire client application, preventing all tokens from working.
Steps:
Effect:
When to use:
Revoke a specific user's authorization for a client without affecting other users.
Steps:
Effect:
When to use:
Revoke individual access or refresh tokens.
Steps:
Steps:
Verify revocation:
Token Information Displayed:
Effect:
When to use:
Revoke all tokens for security incidents:
✅ Request minimum necessary scopes
# Good - specific permissions
patient/Patient.rs patient/Observation.rs
# Avoid - overly broad
patient/*.cruds
✅ Use granular scopes when possible
# Good - specific data subset
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs
# Less secure - all observations
patient/Observation.rs
✅ Use appropriate context
# Good - patient app uses patient context
patient/Patient.rs
# Wrong - patient app requesting system context
system/Patient.rs
✅ Document scope requirements
/**
* Required scopes:
* - openid
* - offline_access
* - patient/Patient.rs
* - patient/Observation.rs
* - patient/MedicationRequest.rs
*/
const requiredScopes = [
'openid',
'offline_access',
'patient/Patient.rs',
'patient/Observation.rs',
'patient/MedicationRequest.rs'
];
✅ Handle scope changes gracefully
// Check granted scopes
const grantedScopes = tokenResponse.scope.split(' ');
if (!grantedScopes.includes('patient/Observation.rs')) {
console.warn('Observation access not granted');
// Disable observation features
}
✅ Request scopes progressively
// Start with minimal scopes
const initialScopes = ['openid', 'patient/Patient.rs'];
// Request additional scopes when needed
const additionalScopes = ['patient/Observation.rs'];
✅ Inform users about data access
✅ Follow principle of least privilege
✅ Regular scope audits
For apps that must comply with ONC Cures Update:
✅ Patient standalone apps
patient/* scopes onlyuser/* or system/* scopes✅ Provider/system apps
user/* or system/* scopesApp Description: Mobile app for patients to track blood pressure and weight.
Required Scopes:
openid
offline_access
patient/Patient.rs
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs
Scope Justification:
openid: Authenticationoffline_access: Refresh tokens for long-term usepatient/Patient.rs: Read patient demographicsRegistration:
curl -X POST https://localhost:9300/oauth2/default/registration \
-H 'Content-Type: application/json' \
--data '{
"application_type": "public",
"client_name": "Vital Signs Tracker",
"redirect_uris": ["com.example.vitals://callback"],
"scope": "openid offline_access patient/Patient.rs patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs"
}'
App Description: Provider-facing app for documenting encounters.
Required Scopes:
openid
fhirUser
launch
launch/patient
user/Patient.rs
user/Encounter.cruds
user/Condition.crs
user/Observation.crs
user/DocumentReference.crs
Scope Justification:
launch + launch/patient: EHR launch with contextfhirUser: Identify the provideruser/Patient.rs: Read patient demographicsuser/Encounter.cruds: Full encounter managementuser/Condition.crs: Create, read, search conditionsuser/Observation.crs: Create, read, search observationsuser/DocumentReference.crs: Create clinical notesNo delete permission - documentation is preserved.
App Description: Patient app to view lab results only.
Required Scopes:
openid
patient/Patient.rs
patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory
patient/DiagnosticReport.rs
patient/Practitioner.rs
Scope Justification:
DiagnosticReport.rs: Lab report metadataPractitioner.rs: Ordering provider informationApp Description: Backend service for analyzing patient populations.
Required Scopes:
system/Patient.rs
system/Condition.rs
system/Observation.rs
system/MedicationRequest.rs
system/Encounter.rs
Authentication: Client credentials grant with JWKS
Scope Justification:
system/* context: Access to all patientsApp Description: Analytics platform exporting all patient data.
Required Scopes:
system/*.$export
system/*.$bulkdata-status
system/Binary.read
Authentication: Client credentials grant (required)
Scope Justification:
$export scope: Initiate bulk export$bulkdata-status: Check export progressBinary.read: Download NDJSON filesApp Description: Pharmacy integration for medication dispensing.
Required Scopes:
openid
fhirUser
user/Patient.rs
user/MedicationRequest.rs
user/Medication.rs
user/MedicationDispense.cruds
user/Practitioner.rs
Scope Justification:
MedicationDispense resource for recording dispensingMedicationRequest.rs: View ordersMedication.rs: Medication detailsApp Description: Patient app for tracking health concerns specifically.
Required Scopes:
openid
offline_access
patient/Patient.rs
patient/Condition.rs?category=http://hl7.org/fhir/us/core/CodeSystem/condition-category|health-concern
patient/Goal.rs
Scope Justification:
Goal.rs: Related goals for health concernsApp Description: Research study collecting survey data.
Required Scopes:
openid
patient/Patient.rs
patient/Observation.crs?category=http://terminology.hl7.org/CodeSystem/observation-category|survey
Scope Justification:
App Description: Existing app using V1 scopes.
Requested Scopes (V1):
openid
patient/Patient.read
patient/Observation.read
patient/Condition.write
patient/Patient.rs
patient/Observation.rs
patient/Condition.cud
Granted Scopes (V2):
openid
patient/Patient.read
patient/Observation.read
patient/Condition.write
patient/Patient.rs
patient/Observation.rs
patient/Condition.cud
Behavior:
.read mapped to V2 .rs.write mapped to V2 .cudNext Steps:
Support:
This documentation represents the collective knowledge and contributions of the OpenEMR open-source community. The content is based on:
The organization, structure, and presentation of this documentation was enhanced using Claude AI (Anthropic) to:
All technical accuracy is maintained from the original community-authored documentation.
OpenEMR is an open-source project. To contribute to this documentation:
Last Updated: November 2025 License: GPL v3
For complete documentation, see Documentation/api/