Academy
⌘K
Getting Started
  • Platform Overview
  • AWS Marketplace Setup
  • Quickstart Guide
Connections
  • Overview
Workflow Builder
  • Overview
  • Nodes Catalog
Helix AI Assistant
  • Overview
Mapping Engine
  • Overview
Workflow Library
  • Overview
  • HL7
  • FHIR
  • Insurance
  • Agentic
  • Mapping
Security & Infrastructure
  • Architecture
  • HIPAA Compliance
  • Performance & Scalability
← Back to ConnectHealth
Get Started
Back to Nodes
CDS Hooks0 inputs → 2 outputs

CDS Hooks In

msg (object)CDS HooksCDS Hooks InOutput 1payload.hook (string)payload.hookIn… (string)payload.context (object)Output 2payload.feedback (array)ch.cds.hook (string)ch.cds.connect… (string)Data flowProperty

Inbound trigger for a CDS Hooks invocation from the EHR — fires when the EHR calls this connection's invocation URL for the selected hook (patient-view, order-select or order-sign).

Details

This node registers with the global CDS Hooks Router on deploy. The EHR's request is authenticated by validating its signed JWT against the connection's trusted JWKS URL before this node ever sees it — invalid/unsigned requests are rejected upstream and never reach the flow.

A hook can only be bound to one CDS Hooks In node per connection, workspace-wide (not just within this flow). Already-claimed hooks show disabled in the dropdown above; deploying a second node for an already-claimed hook fails with an error status instead of silently overriding the first.

The Invocation URL field shows the exact URL to give the EHR when registering this CDS Hooks service — the CDS Hooks spec's standard convention (/api/integrations/<connectionId>/ cds-services/<hookId>), derived from the connection and hook only, never from this flow or node, so it doesn't change if the node is moved. A spec-compliant client auto-derives this same URL from discovery; EHRs that need the URL registered by hand are given this exact string.

Outputs

payload.hook string
The CDS Hooks hook id, e.g. "patient-view".
payload.hookInstance string
A UUID unique to this invocation, generated by the EHR.
payload.context object
Hook-specific context, filled in by the EHR on every request (the editor lists the fields for the selected hook). Always userId and patientId; encounterId when there's an encounter; draftOrders on order-select/order-sign and selections on order-select. Context vs prefetch: context is ids the EHR always sends; prefetch is full FHIR resources the EHR fetches for you only if it's configured on the EHR side.
payload.prefetch object
FHIR resources the EHR attached to the request, keyed per the prefetch templates this connection advertises in discovery — e.g. payload.prefetch.patient (the Patient in context). The EHR may omit any key, so always handle it being missing.
payload.fhirServer string
Base URL of the EHR's FHIR server. Use it to fetch anything prefetch didn't cover.
payload.fhirAuthorization object
EHR-issued OAuth token for fhirServer (access_token, token_type, expires_in, scope, subject), scoped to this invocation. Only present when the EHR sends it. A secret — never log it or send it anywhere but fhirServer.
payload.extension object
The EHR's vendor extensions, passed through as sent (see EHR notes below).
ch.cds.hook / ch.cds.hookInstance / ch.cds.connectionId string
Convenience shortcuts mirroring the payload fields above, plus the connection id.
ch.cds.triggerAction string
Which of the EHR's own workflow triggers fired this hook, when the EHR says so. Some EHRs reuse one hook for several of their triggers, so branch on this when one flow serves several.
ch.cds.responder function
Internal. Completes the pending HTTP response for the CDS Hooks Response node. Do not modify or use directly.

Output 2 — Feedback

Fires when the EHR reports what the clinician did with a card this hook returned (sent to the Invocation URL + /feedback). msg.payload.feedback is an array of { card, outcome: "accepted" | "overridden", outcomeTimestamp, acceptedSuggestions?, overrideReasons? }, where card is the card's uuid. Don't wire it to a CDS Hooks Response node — the EHR has already been answered. overrideReasons.userComment is free text and may contain PHI.

Typical Flow

┌──────────────┐   ┌──────────────┐   ┌──────────────────────┐
│ CDS Hooks In │──▶│  Your Logic  │──▶│  CDS Hooks Response   │
└──────────────┘   └──────────────┘   └──────────────────────┘
    

Best Practices

  • Always wire a CDS Hooks Response node at the end — even to return an empty card list.
  • Keep flow logic fast — the EHR expects a synchronous reply; a flow that takes longer than 8 seconds gets an empty response sent on its behalf.
  • Never log payload.context/payload.prefetch directly — they may contain PHI. Use ids/references in debug/audit output, not full resource content.
  • Never log or forward payload.fhirAuthorization — it is a live EHR access token.
  • Prefer payload.prefetch over a FHIR call; fall back to fhirServer + fhirAuthorization (or the connection's own FHIR Request node) only when a key is missing.

EHR notes

Epic
Doesn't read the discovery URL: its analysts register the Invocation URL on your client ID and enter the prefetch template on the service's OurPractice Advisory record. ch.cds.triggerAction comes from its bpa-trigger-action extension — e.g. "60" open patient chart, "6" enter problem, "18" enter order, "23" sign orders.