Most EHRs are customized through configuration screens and external integrations. Canvas Medical takes a different route: it lets developers write Python plugins that run inside the EHR itself, react to events in the chart and change what clinicians see. For organizations that want an EHR shaped around their own care model, that is a significant difference.
This guide explains the Canvas SDK from Canvas's own documentation: the plugin model, handlers and effects, the manifest, the CLI and deployment workflow, and the performance rules every plugin author needs to follow. It is written for engineering teams evaluating Canvas or starting their first plugin.
What is the Canvas Medical SDK?
Building on Canvas Medical?
Agnotic builds custom EHR software, including plugins and integrations for programmable EHR platforms.
The Canvas Medical SDK is a Python toolkit for building plugins that run inside a Canvas EHR instance. Plugins listen for events, such as a task being created or a note changing state, read data from the instance and return effects that change the chart or the user interface. Canvas runs plugin code in a sandbox on the instance.
This puts custom logic next to the data instead of in a separate service that calls an API. A plugin can add a banner when a patient meets a care gap, create a task after a visit or add a custom command to the note, all without the clinician leaving the chart.
How does the event and effect model work?
The model is simple: something happens in Canvas, the platform emits an event, your handler runs, and it returns a list of effects for Canvas to apply. Handlers never change data directly; they describe the changes they want as effects.
- Events: Things that happen in the instance, such as tasks being created or updated, or note and command lifecycle changes.
- Handlers: Python classes that declare which events they respond to and contain the code to run.
- Effects: Typed instructions returned by a handler, such as creating a task, adding a banner alert or sending an HTTP request.
- Data module: Read access to instance data from inside a handler, so the plugin can look up what it needs.
This separation is helpful for review and testing. A handler is a function from an event and some data to a list of effects, which is easy to unit test and easy for a reviewer to reason about. It also gives Canvas control over how changes are applied, which keeps plugin behavior consistent with the rest of the EHR.
How do you write a Canvas handler?
You write a handler by subclassing BaseHandler, setting the RESPONDS_TO constant to one or more event types and implementing compute(). Canvas's documentation states that compute() must return a list of effects, which can be empty.
- RESPONDS_TO: One event type, or a list when the handler should run for several events.
- compute(): The method Canvas calls when a matching event fires.
- self.event, self.target and self.context: Details of the event and the record it concerns.
- self.secrets: Configuration values for the plugin, such as API keys.
- self.environment: Information about the running instance.
Canvas notes that all handler types inherit from BaseHandler, and many custom handlers will too. More specialized handler types exist for things like action buttons and custom applications, but they follow the same pattern of reacting and returning effects.
Why must Canvas handlers stay fast?
Handlers must stay fast because they run synchronously on a bounded pool of plugin workers shared across the whole Canvas instance. Canvas's documentation warns that if enough handlers block at once, the pool is exhausted and providers can find the entire application unresponsive, not just the plugin.
- External calls: Blocking HTTP calls, including calls to large language model APIs, are the most common cause of instance-wide slowdowns.
- Timeouts and retries: Canvas gives the example that a 300 second timeout retried several times can pin one worker for over ten minutes.
- Database queries: Unbounded queries and N+1 patterns slow handlers as data grows, so fetch only what is needed.
- Heavy computation: Long synchronous processing has the same effect as a slow network call.
Canvas provides two tools for this. Chaining set_async() on an effect makes the platform run it later as a deferred task with optional delay and retries, and the HTTP request effect lets the platform make outbound calls on your behalf. Canvas notes that set_async() defers effects, not compute(), so a raw blocking call inside compute() still holds the worker.
For a plugin that must call an external service, read the answer and then act, Canvas suggests splitting the work: a thin handler emits an async effect to a SimpleAPI callback route in the plugin, and that route does the slow work off the event path.
What goes in the Canvas manifest?
Every plugin has a CANVAS_MANIFEST.json file that declares what the plugin contains and needs. The manifest must sit inside the plugin package, and Canvas validates it before a plugin can be deployed.
- Components: The handlers, applications, commands and questionnaires the plugin provides.
- Secrets: The names of configuration values the plugin expects, set per instance.
- Plugin details: Identifying information such as the plugin's name and version.
- Deprecated parts: Canvas has deprecated the older protocols component in favor of handlers.
Treat the manifest as the plugin's contract with the instance. Reviewers should be able to read it and understand what the plugin reacts to and what it adds to the user interface before reading any code.
How do you deploy plugins with the Canvas CLI?
You deploy with the Canvas CLI, installed with pip install canvas on Python 3.11, 3.12 or 3.13. Developers sign in with canvas login, create a plugin with canvas init and ship it with canvas deploy, which pushes the plugin to Canvas Platform and deploys it to the named instances.
- canvas login: Opens a browser sign-in and lists the organizations you belong to with their plugin prefixes.
- canvas init: Creates a plugin package named with your organization's prefix and registers it with Canvas Platform.
- canvas validate: Checks the manifest and confirms that all handlers load in the sandbox.
- canvas deploy: Pushes the plugin and deploys it to one or more instances, such as a staging instance first.
- Service accounts: CI pipelines sign in with a service account token in the CANVAS_PLATFORM_TOKEN environment variable.
Canvas has deprecated the older approach of instance credentials in credentials.ini and the canvas install command. Its documentation states that support for them ends on 14 December 2026, so teams still using them should move to canvas login and canvas deploy.
How do Canvas plugins handle secrets and configuration?
Plugins read configuration through self.secrets, and the values are set per instance rather than written into code. The manifest names the secrets a plugin expects, and the CLI's config commands set or unset the values for each instance.
Canvas's CLI documentation adds a detail that matters for teams. For plugins managed through Canvas Platform, a deploy installs the platform's revision and replaces the plugin's variables with the values Canvas Platform stores. A value changed directly on an instance will therefore be overwritten at the next deploy, so keep the platform as the single place where configuration is set.
Use separate values for staging and production, rotate keys on a schedule, and never log secret values from a handler. Treat any API key a plugin holds with the same care as a key in a backend service.
How should teams structure Canvas plugin development?
Teams should treat plugins like any production service: source control, code review, automated tests and staged deployments. A plugin runs inside the EHR, so a bug can affect every clinician on the instance.
- One purpose per plugin: Small plugins are easier to review, test and roll back.
- Unit tests: Test compute() with sample events and data, and check the effects returned.
- Staging first: Deploy to a staging instance and test with clinicians before production.
- CI deployment: Use a service account so deployments do not depend on one person's login.
- Monitoring: Watch plugin logs and handler timing after each release.
Clinical review belongs in the process too. A plugin that adds an alert or creates tasks changes clinical work, so a clinician should approve the behavior, wording and frequency before it reaches production, and again after the first weeks of real use.
What are good first use cases for Canvas plugins?
Good first use cases are small, visible and low risk: workflow automation and decision support that reacts to events and adds information without changing clinical data on its own.
- Care gap banners: Show an alert when a patient is due for a screening.
- Task automation: Create a follow-up task when a note with a certain visit type is signed.
- Custom commands: Add a structured command for documentation specific to your care model.
- Outbound notifications: Send an async HTTP request to another system when an event occurs.
- Questionnaires: Package intake or screening forms with the plugin.
Start with one use case, measure whether it changes behavior, and expand from there. The aim is a set of plugins that each do one job well, rather than one large plugin that is hard to change or hand over to a new team.
How Agnotic can help
Agnotic Technologies builds custom EHR software, including plugins and integrations for programmable platforms such as Canvas. We design handler logic with your clinical team, keep slow work off the synchronous path, set up CI deployment with service accounts and connect Canvas to the other systems in your stack.
For organizations deciding between platforms, we help compare a programmable EHR like Canvas with headless and custom approaches, based on your care model and team. For teams already on Canvas, we review existing plugins for slow handlers, blocking calls and missing tests, and help move deployments from the deprecated install path to Canvas Platform before support ends.
Plan your Canvas build
See our custom EHR development services, or browse the EHR integration hub.
Related reading on Agnotic
Read about custom EHR development, headless EHRs, the Oystehr platform and EHR customization.


