Phone Dial Configuration Effect

The PhoneDialConfiguration effect turns the phone numbers in a patient chart into clickable links — the click-to-dial affordance. It lets a user click to dial a number, or hand it to a softphone, directly from the chart. Return it in response to the PHONE_DIAL__GET_CONFIGURATION event, which Canvas fires as a chart loads its phone numbers.

Import the effect and its enums from the submodule:

from canvas_sdk.effects.phone_dial_configuration import (
    PhoneDialClickHandling,
    PhoneDialConfiguration,
    PhoneDialSection,
)

How it works #

As a patient chart loads phone numbers, Canvas fires PHONE_DIAL__GET_CONFIGURATION. A handler subscribed to the event returns one or more PhoneDialConfiguration effects, each naming the chart sections it makes clickable and how a click on those sections is handled. If no plugin returns a configuration, every number renders as plain text, except the patient header’s primary number when your instance has Dialing Service settings configured. See Dialing Service settings below.

A single effect carries one click_handling mode that applies to every section it lists. To handle sections differently — some dialed by the device, some by your plugin — return one effect per handling mode.

A click is either dialed by the device or handed to your plugin, depending on the click_handling mode the effect carries.

Two categories of numbers are never affected by this configuration: sections you do not list render as plain text, and fax numbers are always plain text regardless of configuration.

When more than one plugin responds, Canvas merges their configurations:

  • A section is clickable if any plugin lists it.
  • A section is plugin-driven if any plugin asks for PLUGIN on it.
  • Among the remaining configurations, the first one that names a dial_label supplies the label.

Dialing Service settings #

Click-to-dial also exists as an instance setting, independent of any plugin. When both DIALING_LABEL and DIALING_URL_TEMPLATE are set under Dialing Service, the patient header’s primary number becomes clickable on its own, dialed by the device and labeled with DIALING_LABEL. Those settings reach only the patient header, so no other section is affected by them.

Event payload #

PropertyValueDescription
event.target.idstr (UUID)The id of the Patient whose chart is loading phone numbers.
event.actoruserThe logged-in user viewing the chart, when available.
event.context{}Empty — no additional context is provided.

Attributes #

FieldTypeDefaultDescription
clickable_sectionslist[PhoneDialSection]RequiredThe chart sections whose numbers become clickable. Provide at least one — an empty list is rejected.
click_handlingPhoneDialClickHandlingDEVICEHow a click on the listed sections is handled.
dial_labelstr | NoneNoneA display affordance only. When dial_label is set, the section shows the number plus a “Dial number with <label>” button. When it isn’t, the number itself is the link. Where the call actually goes is still the click handler’s decision, not the configuration’s.

PhoneDialSection #

The chart sections you can make clickable:

MemberValue
PhoneDialSection.PATIENT"patient"
PhoneDialSection.CONTACT"contact"
PhoneDialSection.EXTERNAL_CARE_TEAM"external_care_team"

PhoneDialClickHandling #

How a click is handled:

MemberValueWhat a click does
PhoneDialClickHandling.DEVICE"device"Opens a tel: link that the device’s phone app handles. Your plugin does nothing beyond declaring the section.
PhoneDialClickHandling.PLUGIN"plugin"Opens nothing locally. Your handler decides what happens, using the PHONE_NUMBER_CLICKED event.

PHONE_NUMBER_CLICKED fires under either mode, so a plugin can observe clicks even on device-dialed sections.

Example #

This handler makes three sections clickable with different handling: contact numbers go to your plugin behind a “Dial number with Zoom” button, patient numbers go to your plugin as a plain link, and external-care-team numbers are dialed by the device.

from canvas_sdk.effects import Effect
from canvas_sdk.effects.phone_dial_configuration import (
    PhoneDialClickHandling,
    PhoneDialConfiguration,
    PhoneDialSection,
)
from canvas_sdk.events import EventType
from canvas_sdk.handlers.base import BaseHandler


class ConfigurePhoneDialing(BaseHandler):
    RESPONDS_TO = EventType.Name(EventType.PHONE_DIAL__GET_CONFIGURATION)

    def compute(self) -> list[Effect]:
        return [
            PhoneDialConfiguration(
                clickable_sections=[PhoneDialSection.CONTACT],
                click_handling=PhoneDialClickHandling.PLUGIN,
                dial_label="Zoom",
            ).apply(),
            PhoneDialConfiguration(
                clickable_sections=[PhoneDialSection.PATIENT],
                click_handling=PhoneDialClickHandling.PLUGIN,
            ).apply(),
            PhoneDialConfiguration(
                clickable_sections=[PhoneDialSection.EXTERNAL_CARE_TEAM],
                click_handling=PhoneDialClickHandling.DEVICE,
            ).apply(),
        ]

Dial a plugin-handled section #

A PLUGIN section does not dial anything on its own — the click only fires PHONE_NUMBER_CLICKED. To send the number to a softphone, subscribe to that event and return a Redirect effect that navigates to the softphone’s dial URL. PHONE_NUMBER_CLICKED carries the clicking user as event.actor, so the redirect has a browser to navigate — see Event Actor. The click event’s context carries the number as phone_number and the chart section it came from as source, which is one of patient, contact or external_care_team, matching PhoneDialSection. Read it with .get("source") rather than indexing, since the key is left out when no section is sent.

This handler reads the base of your softphone’s dial URL from a plugin secret named ZOOM_DIAL_URL_BASE, rather than hard-coding it, then composes the dial URL with an f-string. Set that secret to the value from your Zoom Phone account.

from urllib.parse import quote

from canvas_sdk.effects import Effect
from canvas_sdk.effects.redirect import RedirectEffect
from canvas_sdk.events import EventType
from canvas_sdk.handlers.base import BaseHandler


class DialClickedNumber(BaseHandler):
    RESPONDS_TO = EventType.Name(EventType.PHONE_NUMBER_CLICKED)

    def compute(self) -> list[Effect]:
        zoom_dial_url_base = self.secrets["ZOOM_DIAL_URL_BASE"]
        phone_number = self.event.context["phone_number"]
        # source names the chart section the click came from, e.g. "contact".
        source = self.event.context["source"]
        return [
            RedirectEffect(
                url=f"{zoom_dial_url_base}?number={quote(phone_number)}",
                target=RedirectEffect.TargetType.SAME_TAB,
            ).apply()
        ]

Declare both keys under variables in your plugin’s CANVAS_MANIFEST.json so an admin can set them — an undeclared variable has nowhere to receive a value, so the secret read and the redirect both fail silently:

{
  "variables": [
    { "name": "REDIRECT_ALLOWLIST_EXTERNAL" },
    { "name": "ZOOM_DIAL_URL_BASE" }
  ]
}

Add zoomus:// (or your softphone’s scheme and host) to REDIRECT_ALLOWLIST_EXTERNAL so the redirect is permitted. See Redirect for how the allowlist is declared and set.