Canvas FHIR
The Canvas SDK FHIR client provides a simple interface for interacting with the Canvas FHIR API, supporting CRUD operations on FHIR resources such as Coverages, DocumentReferences, AllergyIntolerances, and more. It handles OAuth client credentials authentication and token caching automatically.
Requirements #
- Canvas FHIR Client ID: An OAuth client ID for your Canvas environment
- Canvas FHIR Client Secret: The corresponding OAuth client secret
These credentials should be stored as plugin secrets and grant access to the Canvas FHIR API for your environment.
Imports #
The Canvas FHIR client is included in the Canvas SDK. Import the client:
from canvas_sdk.clients.canvas_fhir import CanvasFhir
Initialize the Client #
# Declare these secrets in the CANVAS_MANIFEST.json and set the values on the
# plugin configuration page.
client_id = self.secrets["CANVAS_FHIR_CLIENT_ID"]
client_secret = self.secrets["CANVAS_FHIR_CLIENT_SECRET"]
client = CanvasFhir(client_id, client_secret)
On initialization, the client will:
- Authenticate using the OAuth client credentials flow against your Canvas environment’s token endpoint.
- Cache the access token using the plugin cache system, keyed by
client_id, with automatic expiration. - Determine the FHIR API base URL from the environment’s
CUSTOMER_IDENTIFIERsetting (e.g.,https://fumage-{CUSTOMER_IDENTIFIER}.canvasmedical.com).
CanvasFhir #
The main class for interacting with the Canvas FHIR API.
Constructor #
CanvasFhir(client_id: str, client_secret: str)
| Parameter | Type | Description |
|---|---|---|
client_id | str | OAuth client ID for the Canvas API |
client_secret | str | OAuth client secret |
Methods #
search(resource_type: str, parameters: dict) -> dict #
Search for FHIR resources matching the given parameters.
# Search for a patient's allergy intolerances
results = client.search("AllergyIntolerance", {"patient": "Patient/abc123"})
for entry in results.get("entry", []):
resource = entry["resource"]
print(f"Allergy: {resource['code']['coding'][0]['display']}")
| Parameter | Type | Description |
|---|---|---|
resource_type | str | FHIR resource type (e.g., Patient, Coverage) |
parameters | dict | Search parameters as key-value pairs |
Returns: FHIR Bundle dict containing matching resources.
Raises: requests.HTTPError if the API returns an error status code.
read(resource_type: str, resource_id: str) -> dict #
Read a single FHIR resource by its ID.
# Read a specific resource by ID
allergy = client.read("AllergyIntolerance", "allergy-id-123")
print(f"Status: {allergy['clinicalStatus']['coding'][0]['code']}")
| Parameter | Type | Description |
|---|---|---|
resource_type | str | FHIR resource type |
resource_id | str | ID of the resource to read |
Returns: FHIR resource dict.
Raises: requests.HTTPError if the API returns an error status code.
create(resource_type: str, data: dict) -> dict #
Create a new FHIR resource.
# Create a new Coverage resource
coverage = client.create("Coverage", {
"resourceType": "Coverage",
"status": "active",
"beneficiary": {"reference": "Patient/abc123"},
"payor": [{"reference": "Organization/org-456"}],
})
print(f"Created Coverage: {coverage['id']}")
| Parameter | Type | Description |
|---|---|---|
resource_type | str | FHIR resource type |
data | dict | FHIR resource data to create |
Returns: Created FHIR resource dict (including server-assigned id).
Raises: requests.HTTPError if the API returns an error status code.
update(resource_type: str, resource_id: str, data: dict) -> dict #
Update an existing FHIR resource.
# Update an existing resource
updated = client.update("Coverage", "coverage-id-789", {
"resourceType": "Coverage",
"id": "coverage-id-789",
"status": "cancelled",
"beneficiary": {"reference": "Patient/abc123"},
"payor": [{"reference": "Organization/org-456"}],
})
print(f"Updated Coverage status: {updated['status']}")
| Parameter | Type | Description |
|---|---|---|
resource_type | str | FHIR resource type |
resource_id | str | ID of the resource to update |
data | dict | Complete FHIR resource data |
Returns: Updated FHIR resource dict.
Raises: requests.HTTPError if the API returns an error status code.
Authentication #
The client uses the OAuth 2.0 client credentials flow to authenticate with the Canvas API. Token management is handled automatically:
- On first use, the client exchanges the
client_idandclient_secretfor an access token via the Canvas token endpoint. - The token is cached using the plugin cache system with the key
canvas_fhir_credentials_{client_id}. - The cached token expires 60 seconds before the actual token expiration to avoid using stale credentials.
- Subsequent requests reuse the cached token until it expires.
Error Handling #
The Canvas FHIR client uses raise_for_status() on all HTTP responses, which raises requests.HTTPError for non-successful status codes.
from requests import HTTPError
try:
result = client.read("Patient", "nonexistent-id")
except HTTPError as e:
print(f"HTTP {e.response.status_code}: {e.response.text}")
Complete Plugin Example #
Here’s a complete example of using the Canvas FHIR client in an ActionButton handler:
from canvas_sdk.clients.canvas_fhir import CanvasFhir
from canvas_sdk.effects import Effect
from canvas_sdk.handlers.action_button import ActionButton
from logger import log
class FhirRequestHandler(ActionButton):
"""Handler that queries the FHIR API when a button is clicked."""
BUTTON_TITLE = "Trigger FHIR Request"
BUTTON_KEY = "TRIGGER_FHIR_REQUEST"
BUTTON_LOCATION = ActionButton.ButtonLocation.CHART_SUMMARY_ALLERGIES_SECTION
def handle(self) -> list[Effect]:
"""Handle the button click."""
client_id = self.secrets["CANVAS_FHIR_CLIENT_ID"]
client_secret = self.secrets["CANVAS_FHIR_CLIENT_SECRET"]
patient_id = self.event.target.id
client = CanvasFhir(client_id, client_secret)
# Search for the patient's allergy intolerances
search_response = client.search(
"AllergyIntolerance",
{"patient": f"Patient/{patient_id}"},
)
log.info(f"Search: {search_response}")
# Read the first result
first_entry = search_response["entry"][0]["resource"]
read_response = client.read("AllergyIntolerance", first_entry["id"])
log.info(f"Read: {read_response}")
return []
Additional Resources #