Skip to content

Add note_lock_webhook plugin - #412

Open
hardylt wants to merge 1 commit into
Medical-Software-Foundation:mainfrom
hardylt:note_lock_webhook
Open

Add note_lock_webhook plugin#412
hardylt wants to merge 1 commit into
Medical-Software-Foundation:mainfrom
hardylt:note_lock_webhook

Conversation

@hardylt

@hardylt hardylt commented Jul 20, 2026

Copy link
Copy Markdown

What this plugin does

note_lock_webhook watches note state changes in Canvas and POSTs to an external endpoint the moment a note is signed (state == "SGN").

The payload is deliberately minimal:

{
  "state": "SGN",
  "note_id": "<note id>",
  "patient_id": "<patient id>"
}

Those two identifiers are enough to make follow-up calls to the Canvas FHIR API for the full note, the encounter, or the patient chart — so the webhook stays fast and carries no PHI beyond identifiers.

Why

A signed note is the point at which clinical documentation becomes final, and it's the natural trigger for downstream work: billing and coding review, care-coordination handoffs, quality reporting, warehouse sync, patient follow-up.

Without a push signal, external systems have to poll the FHIR API on a timer and diff results to notice a note was signed — wasteful when nothing changed, and slow exactly when latency matters. This inverts that.

Implementation notes

  • Subscribes to NOTE_STATE_CHANGE_EVENT_UPDATED. Canvas emits this for every transition; the protocol returns early on anything that isn't SGN, so it's inert for the majority of note events.
  • WEBHOOK_URL (required) and AUTH_TOKEN (optional bearer) are plugin secrets, following the convention in task_webhook_notification. Nothing needs editing in the source to install it.
  • The trigger state is a named SIGNED_STATE constant, so pointing it at LKD or another state is a one-line change.
  • A non-2xx response is logged as an error; there is no retry. The README notes that endpoints needing delivery guarantees should enqueue and acknowledge quickly.
  • The protocol returns no effects — it does not modify anything in Canvas.

Structure

Follows the billing-dashboard layout: README.md, LICENSE (MIT), pyproject.toml, a note_lock_webhook/ code folder with CANVAS_MANIFEST.json inside it, and tests/.

Tests

11 tests covering payload shape, target URL, bearer header present/absent, error logging on non-2xx, and no-request behavior across NEW / LKD / ULK / DEL / None.

uv run pytest tests/

One thing worth a maintainer's eye: I could not install canvas_sdk in my dev environment, so I verified these against stub modules rather than the real SDK. The branching logic and test wiring are exercised, but a run against the actual SDK would be a useful check.

🤖 Generated with Claude Code

Watches note state changes and POSTs the note id and patient id to an
external endpoint when a note is signed (state == "SGN"). The payload is
deliberately minimal: the two identifiers are enough to make follow-up
calls to the Canvas FHIR API for whatever else a downstream system needs.

The webhook URL and an optional bearer token are read from plugin
secrets (WEBHOOK_URL, AUTH_TOKEN), following the convention used by
task_webhook_notification.

Non-signed state changes return early without making a request, so the
plugin is inert for the majority of note events.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hardylt

hardylt commented Jul 20, 2026

Copy link
Copy Markdown
Author

@JessicaHerbert

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant