Subject linkage
Review exact patient, encounter, and provider identifier rules.
Encounter workflows
Implement the encounter checklist recommendations endpoint ChartHero calls for partner-recommended preparation items.
v1.0Checklist recommendations let ChartHero ask your adapter for encounter-specific preparation items that can be shown in the ChartHero encounter checklist. Use this adapter endpoint when your system has care gaps, visit-preparation tasks, risk prompts, quality measure reminders, or other workflow signals that should be considered during a specific ChartHero encounter.
This is a partner-implemented endpoint. ChartHero calls your server, sends the ChartHero patient, encounter, and authenticated actor context, and expects an ordered recommendation payload in return.
| Field | Value |
|---|---|
| Method | POST |
| Path | {base_url}/v1/checklist-recommendations/fetch |
| Auth | Authorization: <configured_authorization_header_value> (for example Bearer <token> or ApiKey <token>) |
| Contract version | X-ChartHero-Contract-Version: 1.0 |
| Timeouts | 2 seconds to connect; 5 seconds for read/write |
| Retries | Up to 2 total attempts; the second attempt is immediate |
| Max response | 1 MiB |
| Availability | Conditional: checklist_recommendations capability plus runtime workflow gating |
On this page:
POST {base_url}/v1/checklist-recommendations/fetch
base_url is the adapter base URL configured in ChartHero setup. If your
service is mounted under a prefix such as /integrations/charthero, include that
prefix in the configured base URL.
The /v1 path segment is the adapter endpoint namespace. The
X-ChartHero-Contract-Version header is the request and response schema version
for that namespace. These usually move independently: a future compatible
contract update can use the same /v1 path with a newer contract-version header,
while a future /v2 path would represent a larger endpoint-family change.
Validate the header against the contract versions your adapter supports.
Treat this endpoint as a synchronous, idempotent read. ChartHero is asking for the current recommendations for one ChartHero encounter, not starting a long-running import job or asking your system to mutate state.
The request is scoped to one patient, one encounter, and one authenticated ChartHero actor. Return only recommendations that are valid for that exact context.
ChartHero validates the full response body. Unknown fields, missing required arrays, unsupported item types, malformed timestamps, or invalid enum values make the response unusable. Prefer returning fewer well-formed recommendations over a large payload with partially valid records.
| Constraint | Behavior |
|---|---|
| Availability | ChartHero calls this endpoint only for adapter installations where the checklist_recommendations capability is enabled and the runtime workflow asks for partner checklist recommendations. |
| Timeout | ChartHero allows 2 seconds to connect and 5 seconds for the request read/write operation. |
| Retries | ChartHero can make up to 2 total attempts. The second attempt is immediate; ChartHero does not currently use Retry-After to delay it. |
| Retry triggers | ChartHero retries transport timeouts, transport errors, and standard JSON error responses classified as retryable. See the Errors table for partner error cases. |
| Response format | Return Content-Type: application/json with UTF-8 encoded JSON. |
| Response size | Responses larger than 1 MiB are not accepted. There are no separate published item, block, or task count caps. |
New conversation-derived task creation is off by default. In the default live checklist mode, ChartHero can update existing tasks but does not create new conversation-derived tasks. Installations that enable conversation task generation may create additional non-workflow tasks or blocks, but workflow blocks remain structurally protected.
ChartHero sends these headers with the request:
| Header | Required | Meaning |
|---|---|---|
Authorization |
Yes | The external_api credential configured for the adapter, sent verbatim — for example Bearer <token>, ApiKey <token>, or a bare token. |
X-ChartHero-Contract-Version |
Yes | Adapter contract version. For this contract, expect 1.0. |
X-ChartHero-Request-Id |
Yes | Opaque request identifier for logs and support correlation. |
Content-Type |
Yes | application/json. |
NOTE:
encounteris ChartHero's visit context. Your source system may call the same record an appointment, visit, encounter, or activity; theappointment_not_founderror code is explained in Errors.
{
"patient": {
"id": "22222222-2222-4222-8222-222222222222",
"external_ids": [
{
"source": "acme_care_prod",
"type": "patient_id",
"value": "pat-123"
}
]
},
"encounter": {
"id": "encounter-1",
"external_ids": [
{
"source": "acme_care_prod",
"type": "encounter_id",
"value": "encounter-1"
}
]
},
"actor": {
"actor_type": "user",
"actor_id": "11111111-1111-4111-8111-111111111111",
"email": "clinician@example.com",
"display_name": "Dr. Chris Smith",
"first_name": "Chris",
"last_name": "Smith",
"specialty": "Cardiology",
"governance_role": "member",
"functional_roles": ["provider"],
"active": true,
"external_ids": [
{
"source": "provider_acme_care_prod",
"type": "provider_id",
"value": "prov-456"
}
]
}
}
Request rules:
patient, encounter, and actor are required.patient.id, encounter.id, and actor.actor_id are ChartHero identifiers
for correlation and support. Use external_ids[] for source-system lookup
when your system needs partner-owned identifiers.external_ids[] may be empty. When present, each identifier uses
{source,type,value} and must match the identifier sources configured for the
adapter installation.acme_care_prod, while provider IDs use provider_acme_care_prod.encounter.external_ids[] carries the upstream visit, appointment, encounter,
or activity identifier when one is available.actor.external_ids[] may be
empty.NOTE: In required provider-linking mode, ChartHero fails closed before this endpoint is called when no valid provider link is available.
Actor field guarantees:
| Actor field | Required | Meaning |
|---|---|---|
actor_type |
Yes | Actor discriminator. Checklist recommendation requests currently send user. |
actor_id |
Yes | Stable ChartHero public actor identifier. |
active |
Yes | Whether the user actor is active in the ChartHero organization. |
external_ids |
No | Partner provider identifiers when provider linking exists. This may be empty. |
email, display_name, first_name, last_name, specialty |
No | Human-readable ChartHero user context when available. |
governance_role |
No | ChartHero organization governance role when available. |
functional_roles |
No | ChartHero organization role tokens when available. This may be empty. |
Authorize each request against the exact patient, encounter, and actor
ChartHero sent. Do not return recommendations for any other subject or actor.
WARNING: Do not log authorization header values, raw source payloads, unrelated clinical records, credentials, transcripts, or bulk checklist content.
WARNING: Do not put secrets, credentials, unrelated records, raw transcripts, or bulk clinical payloads in
source_metadataorprovenance.source_data.
The response hierarchy is intentionally narrow:
items[]
recommendation_set
blocks[]
tasks[]
subtasks[]
Each entry in items[] is a tagged union: type names the variant, and the
identically named sibling object holds that variant's payload. The only
published item variant is currently recommendation_set.
Use a recommendation set when several tasks were produced by the same upstream quality run, care-management engine, or partner workflow. Use blocks to group tasks the way a clinician should review them, such as diabetes review, annual wellness visit preparation, medication reconciliation, or administrative follow-up.
ChartHero ingests items[], blocks[], tasks[], and subtasks[] in the order
you send them. Workflow blocks receive stronger structural protection after
ingestion, while task groups are less-coupled groupings that ChartHero may
reorganize as the checklist evolves.
Use type: "workflow" when the block represents a stable partner-authored unit
of work. A workflow is treated as an ordered collection: ChartHero preserves the
workflow task order, tracks workflow-level status and provenance, and does not
insert unrelated tasks into the workflow block during checklist updates.
A workflow is not a sequential gate. ChartHero does not require the clinician to finish task 1 before task 2, and task status can change in the order the encounter actually unfolds. The ordering tells ChartHero and downstream systems how the partner intended the work to be reviewed.
Use type: "task_group" when the tasks are related but can stand on their own.
Task groups are less-coupled display groupings for agenda items, checks,
discussion topics, or follow-up work. ChartHero may prioritize, regroup, or
separate non-workflow tasks as the checklist evolves.
| Block type | When to use it |
|---|---|
workflow |
The tasks belong to a stable ordered process, such as annual wellness visit preparation, medication reconciliation, or a partner care-gap review that should remain intact. |
task_group |
The tasks are related suggestions, reminders, or agenda items that can be reviewed independently or reorganized by ChartHero. |
Use subtasks for granular checks inside a parent task. Do not use subtasks as a second independent recommendation list. If a child item can stand alone, make it a task.
Return 200 with items. Use an empty array when there is nothing to show.
No recommendations:
{
"items": []
}
A simple task group:
{
"items": [
{
"type": "recommendation_set",
"recommendation_set": {
"title": "Visit Preparation Suggestions",
"blocks": [
{
"type": "task_group",
"title": "Related Recommendations",
"tasks": [
{
"title": "Discuss eye exam"
}
]
}
]
}
}
]
}
A workflow with provenance:
{
"items": [
{
"type": "recommendation_set",
"recommendation_set": {
"title": "ACO Pre-Visit Plan",
"generated_at": "2026-06-30T14:00:00Z",
"blocks": [
{
"type": "workflow",
"title": "Diabetes Review",
"description": "Review open diabetes care gaps.",
"tasks": [
{
"title": "Address A1c gap",
"note": "Most recent A1c is outside the measure window.",
"rationale": "Open care gap from the partner quality engine.",
"category": "HEDIS",
"task_type": "CARE_GAP",
"priority": "HIGH",
"requires_action": true,
"subtasks": [
{
"title": "Order A1c if clinically appropriate",
"kind": "check"
}
],
"provenance": {
"type": "STANDARD",
"source_id": "gap-123",
"source_system": "OTHER",
"source_type": "CARE_GAP",
"captured_at": "2026-06-30T13:00:00Z",
"source_data": {
"format": "json",
"payload": {
"measure": "A1C_TESTING",
"source_record_id": "gap-123"
}
}
},
"source_metadata": {
"source_url": "https://partner.example/gaps/gap-123"
}
}
]
}
],
"source_metadata": {
"run_id": "quality-run-789"
}
}
}
]
}
Fields not listed as required are optional unless noted. Arrays are required when
listed as required, but they may be empty unless the table says otherwise. On
tasks and subtasks, only title is required; provenance, priority,
requires_action, category, task_type, note, and rationale are optional.
| Object | Required fields | Optional fields |
|---|---|---|
| Response | items |
None |
| Item | type, recommendation_set |
None |
| Recommendation set | title, blocks |
generated_at, provenance, source_metadata |
| Block | type, title, tasks |
description, provenance, source_metadata |
| Task | title |
note, rationale, task_type, kind, priority, requires_action, category, subtasks, provenance, source_metadata |
| Subtask | title |
note, rationale, task_type, kind, priority, requires_action, provenance, source_metadata |
| Provenance | source_id, source_system, source_type, captured_at |
type, source_data |
| Source data | format, payload |
None |
provenance itself is optional at every recommendation level. When you include
it, source_id, source_system, source_type, and captured_at are required.
| Field | Default |
|---|---|
provenance.type |
STANDARD |
task.kind, subtask.kind |
task |
task.requires_action, subtask.requires_action |
false |
task.subtasks |
[] |
| Field | Limit |
|---|---|
source_id |
500 characters |
title |
200 characters |
description, note, rationale |
2,000 characters |
Enum casing is significant. Send the exact casing shown below: response shape
discriminators such as recommendation_set, workflow, task_group, task,
and check are lowercase, while clinical classification fields such as
priority, task_type, category, and provenance.type use uppercase values.
| Field | Allowed values |
|---|---|
item.type |
recommendation_set |
block.type |
workflow, task_group |
actor.actor_type |
user |
actor.governance_role |
owner, admin, manager, member |
actor.functional_roles[] |
Adapter-visible role tokens. provider is common, but this is not a closed enum. |
task.kind, subtask.kind |
task, check |
task.priority, subtask.priority |
URGENT, HIGH, MEDIUM, LOW |
task.task_type, subtask.task_type |
CARE_GAP, PREVENTIVE, CHRONIC, ACUTE, DIAGNOSTIC, MEDICATION, REFERRAL, COUNSELING, ADMINISTRATIVE, OTHER |
task.category |
AWV, HEDIS, RAF_HCC, CHRONIC_CONDITIONS, PREVENTIVE, ACUTE, CARE_COORDINATION, ADMINISTRATIVE |
provenance.type |
STANDARD |
provenance.source_system |
CHARTHERO_USPSTF, CHARTHERO_HEDIS, CHARTHERO_AWV, CHARTHERO_TEMPLATE, ARCADIA, EPIC, CERNER, ATHENA, OTHER |
provenance.source_type |
USPSTF_RECOMMENDATION, HEDIS_MEASURE, CMS_AWV, CARE_GAP, EHR_ALERT, MANUAL_ENTRY, AI_DETECTED, WORKFLOW_TEMPLATE |
provenance.source_data.format |
json, text |
provenance.type currently allows only STANDARD; the field is reserved for
future provenance variants.
For source_data, use format: "json" when payload is a structured JSON
value such as an object or array. Use format: "text" when payload is a short
human-readable string.
Use provenance when a recommendation item corresponds to a stable upstream
source object. source_id, source_system, and source_type describe that
upstream origin. Recommendation import/dedupe behavior is not part of this v1
endpoint contract.
Provenance tells ChartHero and the clinician why an item exists.
source_metadata and provenance.source_data serve different purposes:
| Field | Where it appears | Use it for |
|---|---|---|
source_metadata |
Recommendation set, block, task, or subtask | Small partner-owned metadata that helps your system reconcile or debug a recommendation, such as a run ID, source URL, confidence score, or display hint. ChartHero treats it as opaque metadata. |
provenance.source_data |
Inside provenance |
The small source snippet or structured source fact that explains why the recommendation exists. |
See Security considerations for data that must not be included in either field.
Return the standard adapter error body with a stable error_code.
| Case | Status | error_code |
retryable |
|---|---|---|---|
| Request body is malformed or cannot be interpreted | 400 |
invalid_request |
false |
| Contract version is unsupported | 400 |
unsupported_contract_version |
false |
| Authorization header value is missing or invalid | 401 |
unauthorized |
false |
| Authorization header value is valid but not allowed to use this endpoint | 403 |
forbidden |
false |
| Patient cannot be found in your system | 404 |
patient_not_found |
false |
| Encounter or appointment cannot be found in your system | 404 |
appointment_not_found |
false |
| Patient and encounter identifiers do not belong together | 409 |
identifier_mismatch |
false |
| Endpoint is rate limited | 429 |
rate_limited |
true |
| Endpoint is temporarily unavailable | 503 |
temporarily_unavailable |
true |
Use appointment_not_found when your system cannot resolve the external visit
context carried in encounter.external_ids[], whether your source system calls
that record an appointment, visit, encounter, or activity. The request field is
named encounter because ChartHero is asking for recommendations for one
ChartHero encounter.
Example:
{
"error_code": "patient_not_found",
"message": "Patient was not found in the partner system.",
"retryable": false
}
Use retryable: true only when a later identical request is likely to succeed,
such as rate_limited or temporarily_unavailable. See
Request IDs and errors for the full error
body and request-correlation headers.
A minimal handler authenticates ChartHero's call, checks the contract version, logs the request ID, looks up the scoped patient and encounter, and returns recommendation sets in ChartHero's schema.
This example is illustrative. lookupPatient, lookupEncounter,
lookupRecommendations, and toChartHeroRecommendationSet represent your
adapter-specific lookup and schema-mapping code. Validate the request body,
handle temporary downstream failures with 5xx retryable errors, and transform
upstream data before returning it.
import express from 'express';
const app = express();
app.use(express.json());
// The full Authorization header value you issued to ChartHero,
// e.g. "Bearer abc123", "ApiKey abc123", or a bare token.
// Fail closed at startup: with no expected value, a request with no
// Authorization header would otherwise compare undefined === undefined.
const CHARTHERO_AUTHORIZATION = process.env.CHARTHERO_AUTHORIZATION;
if (!CHARTHERO_AUTHORIZATION) {
throw new Error('CHARTHERO_AUTHORIZATION must be set');
}
const SUPPORTED_CONTRACT_VERSION = '1.0';
app.post('/v1/checklist-recommendations/fetch', (req, res) => {
const requestId = req.get('x-charthero-request-id') ?? 'missing';
console.info('checklist recommendation request', { request_id: requestId });
// Production adapters should validate req.body before reading nested fields.
if (req.get('authorization') !== CHARTHERO_AUTHORIZATION) {
return res.status(401).json({
error_code: 'unauthorized',
message: 'Invalid authorization',
retryable: false,
});
}
if (req.get('x-charthero-contract-version') !== SUPPORTED_CONTRACT_VERSION) {
return res.status(400).json({
error_code: 'unsupported_contract_version',
message: 'Unsupported contract version',
retryable: false,
});
}
const patientExternalId = req.body.patient?.external_ids?.find(
(identifier) =>
identifier.source === 'acme_care_prod' && identifier.type === 'patient_id',
)?.value;
const encounterExternalId = req.body.encounter?.external_ids?.find(
(identifier) =>
identifier.source === 'acme_care_prod' && identifier.type === 'encounter_id',
)?.value;
const patient = lookupPatient({
chartheroId: req.body.patient?.id,
externalId: patientExternalId,
});
if (!patient) {
return res.status(404).json({
error_code: 'patient_not_found',
message: 'Patient was not found',
retryable: false,
});
}
const encounter = lookupEncounter({
chartheroId: req.body.encounter?.id,
externalId: encounterExternalId,
patient,
});
if (!encounter) {
return res.status(404).json({
error_code: 'appointment_not_found',
message: 'Encounter was not found',
retryable: false,
});
}
const recommendations = lookupRecommendations({
actor: req.body.actor,
encounter,
patient,
});
return res.status(200).json({
items: recommendations.map(toChartHeroRecommendationSet),
});
});
app.listen(8080);
To self-test your endpoint, send the same shape ChartHero sends. This curl uses a minimal actor payload; most actor profile fields are optional.
curl -sS -X POST https://ehr.example.com/integrations/charthero/v1/checklist-recommendations/fetch \
-H 'Authorization: Bearer <partner_issued_token>' \
-H 'X-ChartHero-Contract-Version: 1.0' \
-H 'X-ChartHero-Request-Id: req_test_checklist_recommendations' \
-H 'Content-Type: application/json' \
-d '{
"patient": {
"id": "22222222-2222-4222-8222-222222222222",
"external_ids": [
{ "source": "acme_care_prod", "type": "patient_id", "value": "pat-123" }
]
},
"encounter": {
"id": "encounter-1",
"external_ids": [
{ "source": "acme_care_prod", "type": "encounter_id", "value": "encounter-1" }
]
},
"actor": {
"actor_type": "user",
"actor_id": "11111111-1111-4111-8111-111111111111",
"email": "clinician@example.com",
"display_name": "Dr. Chris Smith",
"active": true,
"external_ids": []
}
}'
Log enough to debug a recommendation request without leaking unnecessary clinical data.
| Log field | Include |
|---|---|
request_id |
X-ChartHero-Request-Id |
endpoint |
checklist-recommendations/fetch |
outcome |
Status code and stable error code |
contract_version |
X-ChartHero-Contract-Version |
recommendation_count |
Number of returned top-level items |
timestamp |
Server-side receive or completion time |
See Security considerations for fields and payloads that must stay out of logs.
Review exact patient, encounter, and provider identifier rules.
Handle malformed requests, not-found cases, and retryable errors.
Inspect the generated recommendations operation.