Patient Effect

The Patient effect enables the creation and updating of patient records within the Canvas system. This effect captures demographic information, contact details, and clinical associations necessary for patient registration and updates.

Attributes #

AttributeTypeDescriptionRequired
first_namestrPatient’s first nameYes
last_namestrPatient’s last nameYes
middle_namestr or NonePatient’s middle nameNo
birthdatedatetime.date or NonePatient’s date of birthNo
prefixstr or NoneName prefix (e.g., “Dr.”, “Mr.”)No
suffixstr or NoneName suffix (e.g., “Jr.”, “III”)No
sex_at_birthPersonSex or NonePatient’s sex assigned at birthNo
nicknamestr or NonePatient’s preferred name or nicknameNo
social_security_numberstr or NonePatient’s SSNNo
administrative_notestr or NoneAdministrative notes about the patientNo
clinical_notestr or NoneClinical notes about the patientNo
default_location_idstr or NoneID of patient’s default practice locationNo
default_provider_idstr or NoneID of patient’s default healthcare providerNo
activebool or NoneWhether the patient record is activeNo
deceasedbool or NoneWhether the patient is deceasedNo
deceased_datetimedatetime.datetime or NoneDate and time of patient’s deathNo
deceased_causestr or NoneCause of patient’s deathNo
deceased_commentstr or NoneAdditional comments about patient’s deathNo
biological_race_codeslist[str] or NoneCDC race codes describing the patient’s biological race (e.g., "2106-3")No
cultural_ethnicity_codeslist[str] or NoneCDC ethnicity codes describing the patient’s cultural ethnicity (e.g., "2186-5")No
previous_nameslist[str] or NoneList of patient’s previous namesNo
contact_pointslist[PatientContactPoint] or NonePatient’s contact informationNo
external_identifierslist[PatientExternalIdentifier] or NonePatient’s external identifiersNo
patient_idstr or NonePatient id. Required for updates. Optional on creation, where it must be a 32-character hex string (a UUID4 without hyphens) — see Supplying a patient id on creation.No
addresseslist[PatientAddress] or NonePatient’s addressesNo
preferred_pharmacieslist[PatientPreferredPharmacy] or NonePatient’s preferred pharmaciesNo
metadatalist[PatientMetadata] or NonePatient metadataNo

PatientContactPoint #

The PatientContactPoint dataclass represents various methods of contacting the patient.

Attributes #

AttributeTypeDescriptionRequired
systemContactPointSystemType of contact (e.g., phone, email)Yes
valuestrThe contact information value (e.g., phone number, email address)Yes
useContactPointUsePurpose of the contact point (e.g., home, work)Yes
rankintPriority order of contact methodsYes
has_consentbool or NoneWhether consent has been given to use this contact methodNo

PatientExternalIdentifier #

The PatientExternalIdentifier dataclass represents an external identifier (ID) associated with the patient. An example would be the unique patient ID for a third party system integrated with Canvas EMR.

Attributes #

AttributeTypeDescriptionRequired
systemstrURL of the system of origin for the external ID (e.g., http://hl7.org/fhir/sid/us-ssn)Yes
valuestrThe external ID or membership number/valueYes

PatientAddress #

The PatientAddress dataclass represents a patient’s address information.

Attributes #

AttributeTypeDescriptionRequired
line1strStreet address line 1Yes
line2str or NoneStreet address line 2No
citystrCity nameYes
state_codestrState code (e.g., “CA”, “NY”)Yes
postal_codestrPostal/ZIP codeYes
countrystrCountry codeYes
useAddressUseAddress type (e.g., home, work)Yes

PatientPreferredPharmacy #

The PatientPreferredPharmacy dataclass represents a patient’s preferred pharmacy, and if it’s their default pharmacy.

AttributeTypeDescriptionRequired
ncpdp_idstrThe ncpdp ID of the pharmacyYes
defaultboolTrue if it’s the default pharmacyYes

PatientMetadata #

The PatientMetadata dataclass represents a custom key-value pair for a patient.

AttributeTypeDescriptionRequired
keystrThe key of the metadataYes
valuestrThe value of the metadataYes

Implementation Details #

  • Creation: Creates new patient records. By default the server generates the patient id, but you may supply your own patient_id — see Supplying a patient id on creation
  • Updates: Updates existing patient records when patient_id is provided
  • Validates that referenced practice locations exist in the system
  • Verifies that referenced healthcare providers exist in the system
  • Structures contact information through the PatientContactPoint dataclass
  • Structures external identifier through the PatientExternalIdentifier dataclass
  • Structures address information through the PatientAddress dataclass
  • Structures metadata through the PatientMetadata dataclass

Example Usage #

from canvas_sdk.effects.patient import Patient, PatientContactPoint, PatientExternalIdentifier, PatientMetadata
from canvas_sdk.handlers.base import BaseHandler
from canvas_sdk.v1.data.common import ContactPointSystem, ContactPointUse, PersonSex
import datetime


class MyHandler(BaseHandler):
    def compute(self):
        patient = Patient(
            first_name="Jane",
            last_name="Doe",
            middle_name="Marie",
            birthdate=datetime.date(1980, 1, 15),
            sex_at_birth=PersonSex.SEX_FEMALE,
            nickname="Janie",
            default_location_id="location-uuid",
            default_provider_id="provider-uuid",
            contact_points=[
                PatientContactPoint(
                    system=ContactPointSystem.PHONE,
                    value="555-123-4567",
                    use=ContactPointUse.MOBILE,
                    rank=1,
                    has_consent=True
                ),
                PatientContactPoint(
                    system=ContactPointSystem.EMAIL,
                    value="jane.doe@example.com",
                    use=ContactPointUse.WORK,
                    rank=2,
                    has_consent=True
                )
            ],
            external_identifiers=[
                PatientExternalIdentifier(
                    system="http://www.aaa.com",
                    value="pat_id_123456"
                )
            ],
            metadata = [
                PatientMetadata(key="source", value="plugin"),
                PatientMetadata(key="created_on", value=datetime.datetime.now().isoformat())
            ]
        )

        return [patient.create()]

Supplying a patient id on creation #

By default, Canvas generates the patient id (patient_id) when you create a patient. You can supply your own instead by passing a 32-character hex string (a UUID4 with its hyphens removed) in the patient_id parameter of Patient. This lets your plugin generate the id up front and reuse it for follow-up, patient-scoped effects — such as notes or commands — in the same plugin execution, without reading the id back first. It works the same way Notes and Commands accept a pre-generated id.

A supplied id must be a well-formed patient id: a 32-character lowercase hex string, which is a UUID4 with its hyphens removed. Use generate_patient_id() to produce one rather than building the format by hand. An id in any other format — for example, a hyphenated or uppercase UUID — raises a validation error on create(), as does an id that already belongs to an existing patient. Since generate_patient_id() returns a fresh, well-formed id, it satisfies both requirements. If you omit patient_id, the server generates the id as before, so existing plugins are unaffected.

Because you generate the id up front, you can also return it to the caller from a SimpleAPI endpoint — so a client creating the patient gets the id back in the response instead of having to look it up afterward. This example authenticates with the APIKeyAuthMixin, which expects a simpleapi-api-key secret declared in your manifest:

from canvas_sdk.effects.patient import Patient, generate_patient_id
from canvas_sdk.effects.simple_api import JSONResponse, Response
from canvas_sdk.handlers.simple_api import APIKeyAuthMixin, SimpleAPIRoute


class CreatePatientAPI(APIKeyAuthMixin, SimpleAPIRoute):
    PATH = "/patients"

    def post(self) -> list[Response]:
        body = self.request.json()
        new_patient_id = generate_patient_id()

        patient = Patient(
            patient_id=new_patient_id,
            first_name=body["first_name"],
            last_name=body["last_name"],
        )

        # `new_patient_id` can be reused for follow-up patient-scoped effects in
        # the same execution, and is returned so the caller has it immediately
        # without a follow-up lookup.
        return [
            patient.create(),
            JSONResponse({"patient_id": new_patient_id}, status_code=201),
        ]

Patient Update Example #

from canvas_sdk.effects.patient import Patient, PatientAddress, PatientExternalIdentifier
from canvas_sdk.handlers.base import BaseHandler
from canvas_sdk.v1.data.common import AddressUse


class MyHandler(BaseHandler):
    def compute(self):
        # Update an existing patient
        updated_patient = Patient(
            patient_id="existing-patient-uuid",
            first_name="Jane",
            last_name="Smith",  # Changed last name
            addresses=[
                PatientAddress(
                    line1="456 Updated Street",
                    line2="Suite 200",
                    city="Updated City",
                    state_code="CA",
                    postal_code="90210",
                    country="US",
                    use=AddressUse.HOME
                )
            ],
            external_identifiers=[
                PatientExternalIdentifier(
                    system="http://www.updated-system.com",
                    value="new_patient_id_789"
                )
            ]
        )

        return [updated_patient.update()]

Marking a Patient as Inactive or Deceased #

from canvas_sdk.effects.patient import Patient
from canvas_sdk.handlers.base import BaseHandler
import datetime


class MyHandler(BaseHandler):
    def compute(self):
        # Mark a patient as inactive
        inactive_patient = Patient(
            patient_id="existing-patient-uuid",
            active=False
        )

        return [inactive_patient.update()]


class DeceasedPatientHandler(BaseHandler):
    def compute(self):
        # Record a patient's death
        deceased_patient = Patient(
            patient_id="existing-patient-uuid",
            deceased=True,
            deceased_datetime=datetime.datetime(2025, 3, 14, 12, 0, 0),
            deceased_cause="Natural causes",
            deceased_comment="Pronounced at home."
        )

        return [deceased_patient.update()]

Setting Race and Ethnicity #

biological_race_codes and cultural_ethnicity_codes each accept a list of code strings drawn from the CDC Race and Ethnicity CodeSystem (CDCREC) — the same code set used by the FHIR Patient API. You can set both fields when creating or updating a patient, and you can supply more than one code per field.

Canvas recognizes the full CDCREC code set — both the OMB top-level categories below and the more specific detailed codes that roll up to them (for example, the race code 2108-9 “European” rolls up to 2106-3 “White”, and the ethnicity code 2148-5 “Mexican” rolls up to 2135-2 “Hispanic or Latino”). The categories below are the most common values; see the CodeSystem for the complete list of detailed codes.

Race (biological_race_codes) — OMB top-level categories:

CodeDescription
1002-5American Indian or Alaska Native
2028-9Asian
2054-5Black or African American
2076-8Native Hawaiian or Other Pacific Islander
2106-3White
2131-1Other Race

Ethnicity (cultural_ethnicity_codes) — OMB top-level categories:

CodeDescription
2135-2Hispanic or Latino
2186-5Not Hispanic or Latino
from canvas_sdk.effects.patient import Patient
from canvas_sdk.handlers.base import BaseHandler


class MyHandler(BaseHandler):
    def compute(self):
        patient = Patient(
            patient_id="existing-patient-uuid",
            biological_race_codes=["2106-3"],      # White
            cultural_ethnicity_codes=["2186-5"]    # Not Hispanic or Latino
        )

        return [patient.update()]

Validation #

The effect performs validation before execution to ensure data integrity:

  1. Required Fields:
    • For creation: Validates that mandatory fields like first_name and last_name are provided
    • For updates: Requires patient_id to be provided and verifies the patient exists in the database
  2. Referenced Entity Validation: Confirms that any referenced entities exist in the system:
    • Verifies that the specified default practice location exists
    • Ensures that the specified default provider exists
  3. Data Format Validation: Ensures that provided values conform to expected formats:
    • Date fields must be valid dates
    • Enumerated types like PersonSex, ContactPointSystem, and ContactPointUse must contain valid values
    • On creation, if patient_id is supplied it must be a well-formed patient id (a 32-character hex string); otherwise validation raises
    • On creation, a supplied patient_id must not already belong to an existing patient; a duplicate id raises a validation error
  4. Update-Specific Validation:
    • Validates that the patient exists before attempting updates