ChartHeroAI

DocsDeveloper Docs

Launch

Launch Context

Implement the launch context endpoint ChartHero calls to verify and hydrate launched patient or appointment context.

v1.0

This page is the field-level reference for the launch-context callback — the full request, every response field, and all denial cases. For how this callback fits the end-to-end launch, start with Launch Flow.

Endpoint

ChartHero calls launch context after it validates the launch URL and resolves the adapter installation.

Text
POST {base_url}/v1/launch/context

This endpoint receives the opaque launch state from the encrypted link, verifies it in your system, and returns the patient-bearing context ChartHero should use.

Route label: Required for first launch.

Call behavior

ChartHero makes a single attempt at this callback — there is no automatic retry for launch context. The timeouts are bounded per phase: up to 2 seconds to establish the connection and up to 5 seconds to read your response. So resolve launch_state and respond within a few seconds; a timeout fails the launch closed. Size the endpoint for a synchronous lookup, not a long-running job. Returning retryable: true or 503 temporarily_unavailable from this callback is informational only — ChartHero does not retry launch context regardless.

Because there is no retry, launch_id is for your own deduplication, tracing, and replay-safe logging — not for retry-safety. Returning the same context for a repeated launch_id is good practice, but ChartHero will not call this endpoint twice for one launch.

Request

JSON
{
  "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"
      }
    ]
  },
  "launch_state": {
    "appointment_id": "apt-789",
    "correlation_id": "partner-click-123"
  },
  "launch_id": "01JZ123456789ABCDEFGHJKLM"
}

Rules:

  1. launch_state is the JSON object from the encrypted launch payload. Treat it as adapter-owned state and resolve it using your system's own authorization and workflow rules.
  2. actor is the authenticated ChartHero user for this callback. ChartHero sends it whenever the launch is valid and an authenticated actor is resolved. In required provider-linking mode, a valid callback requires a durable mapped provider link; otherwise ChartHero rejects the launch before calling this endpoint.
  3. actor.actor_type, actor.actor_id, actor.display_name, actor.first_name, actor.last_name, actor.active, and actor.external_ids[] use the same field names and semantics as public External API actor resources where those concepts overlap. Adapter launch-context calls currently send only actor_type: "user".
  4. actor.actor_id is the ChartHero public user id. actor.email, name, specialty, governance role, and functional roles are ChartHero user and membership context for the launching organization.
  5. launch_id is the validated launch jti, included for deduplication, tracing, and replay-safe partner logging. ChartHero does not retry this callback, so it is not needed for retry-safety.
  6. Do not rely on ChartHero to interpret launch_state. Return one validated launch subject after your adapter verifies and hydrates the launch: encounter with required encounter.patient for encounter or appointment launches, or top-level patient for patient-only launches.
  7. Patient, encounter, actor, and provider identifiers use {source,type,value}. Actor and provider identifiers use the enabled provider identifier source; the currently published provider helper contract uses type: "provider_id".
  8. source is required even when the adapter has one default source. type is the identifier kind, such as patient_id, mrn, or appointment_id. The source and type you return must exactly match an enabled external identifier binding for that subject — unrecognized sources or types are rejected.
  9. A single response subject may include multiple external IDs. It must not contain conflicting values for the same resolved source/type tuple.

The callback actor is not the raw top-level actor object from the encrypted launch URL. ChartHero first validates the encrypted actor evidence against the authenticated ChartHero user. If validation succeeds, this request sends enriched ChartHero actor context plus any mapped partner provider identifiers. In optional or disabled provider-linking modes, actor.external_ids is an empty array when no mapped provider identifiers are available. In required provider-linking mode, missing mapped provider identifiers produce launch_mapping_missing before this callback is sent.

Success response

Minimal top-level patient response for patient-only launch flows:

JSON
{
  "patient": {
    "external_ids": [
      {
        "source": "acme_care_prod",
        "type": "patient_id",
        "value": "pat-123"
      }
    ]
  }
}

Encounter response with embedded patient:

JSON
{
  "encounter": {
    "external_ids": [
      {
        "source": "acme_care_prod",
        "type": "appointment_id",
        "value": "apt-789"
      }
    ],
    "patient": {
      "external_ids": [
        {
          "source": "acme_care_prod",
          "type": "patient_id",
          "value": "pat-123"
        }
      ]
    },
    "start_at": "2026-06-01T14:00:00Z"
  }
}

Response with optional demographics, encounter, and setup defaults:

JSON
{
  "encounter": {
    "external_ids": [
      {
        "source": "acme_care_prod",
        "type": "appointment_id",
        "value": "apt-789"
      }
    ],
    "patient": {
      "external_ids": [
        {
          "source": "acme_care_prod",
          "type": "patient_id",
          "value": "pat-123"
        }
      ],
      "first_name": "Jane",
      "middle_name": null,
      "last_name": "Doe",
      "suffix": null,
      "dob": "1970-01-01",
      "sex": "female"
    },
    "participants": [
      {
        "actor_type": "provider",
        "role": "scheduled_provider",
        "provider": {
          "external_ids": [
            {
              "source": "provider_acme_care_prod",
              "type": "provider_id",
              "value": "prov-456"
            }
          ],
          "email": "clinician@example.com",
          "display_name": "Dr. Chris Smith"
        }
      }
    ],
    "start_at": "2026-06-01T14:00:00Z",
    "end_at": "2026-06-01T14:30:00Z"
  },
  "setup_defaults": {
    "mode": "phone_call",
    "template_id": "00000000-0000-0000-0000-000000000013",
    "phone_call": {
      "to_e164": "+15551234567"
    }
  }
}

Rules:

  1. Return exactly one top-level subject: encounter for an encounter or appointment launch, or patient for a patient-only launch.
  2. patient and encounter are mutually exclusive at the top level. Do not return both in the same success response.
  3. Return encounter when your adapter resolves encounter or appointment state, and include the patient as required encounter.patient. Return top-level patient when the launch is not tied to an encounter. ChartHero creates a new local encounter for each newly minted patient-only launch; internal resolution retries use the launch ID to avoid creating duplicates.
  4. encounter.start_at and encounter.end_at must be timezone-aware datetimes. When end_at is present, it must not be before start_at.
  5. setup_defaults is optional. Omit it or return null when there are no effective defaults.
  6. If setup_defaults is an object, it must include at least one of mode, template_id, or phone_call. First-level null values are treated as omitted; include at least one non-null value when you want ChartHero to create setup.
  7. mode must be one of dictation, ambient, or phone_call. Only phone_call uses the phone_call object: if setup_defaults.phone_call is non-null and mode is supplied, mode must be phone_call. If mode is omitted or null while phone_call is present, ChartHero treats the defaults as phone-call defaults.
  8. If setup_defaults.phone_call is non-null, phone_call.to_e164 is required.
  9. setup_defaults.template_id must be a ChartHero core template id provisioned as selectable for the launching user. ChartHero provides valid ids during adapter onboarding; omit template_id when no provisioned template should be selected.
  10. Invalid setup_defaults makes the launch-context response invalid; ChartHero does not ignore malformed defaults.

Denials

Return the standard adapter error body with a stable error_code. ChartHero recognizes these four denial cases for this endpoint:

Case Status error_code
Supplied actor cannot access the resolved patient or encounter 403 permission_denied
Launch state cannot resolve to the expected patient 404 patient_not_found
Launch state identifies an upstream appointment that cannot resolve 404 appointment_not_found
Resolved patient and encounter do not belong together 409 identifier_mismatch

These four status + error_code pairs are the only ones ChartHero maps to a specific denial reason. Any other response — including a malformed request body you reject with 400 — is treated as a generic failure that fails the launch closed, not as a recognized denial. Use the four codes above when you want ChartHero to attribute the specific reason.

See Request IDs and errors for the full error body shape and request-correlation headers.

Example: receive and respond

A minimal handler authenticates ChartHero's call, resolves your opaque launch_state, and answers in ChartHero's schema.

JS
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');
}

app.post('/v1/launch/context', (req, res) => {
  // 1. Authenticate ChartHero's call with the authorization header value you issued.
  if (req.get('authorization') !== CHARTHERO_AUTHORIZATION) {
    return res
      .status(401)
      .json({ error_code: 'unauthorized', message: 'Invalid authorization', retryable: false });
  }

  // 2. Resolve your opaque launch_state against your own system.
  const appointment = lookupAppointment(req.body.launch_state.appointment_id); // your code
  if (!appointment) {
    return res
      .status(404)
      .json({ error_code: 'appointment_not_found', message: 'Unknown appointment', retryable: false });
  }

  // 3. Return encounter with embedded patient in ChartHero's schema.
  return res.json({
    encounter: {
      external_ids: [{ source: 'acme_care_prod', type: 'appointment_id', value: appointment.id }],
      patient: {
        external_ids: [{ source: 'acme_care_prod', type: 'patient_id', value: appointment.patientId }],
      },
      start_at: appointment.startAt,
    },
  });
});

app.listen(8080);

To self-test before activation, fire the exact shape ChartHero will POST at your own endpoint:

cURL
curl -sS -X POST https://ehr.example.com/integrations/charthero/v1/launch/context \
  -H 'Authorization: Bearer <partner_issued_token>' \
  -H 'X-ChartHero-Contract-Version: 1.0' \
  -H 'Content-Type: application/json' \
  -d '{
    "actor": { "actor_type": "user", "actor_id": "11111111-1111-4111-8111-111111111111", "email": "clinician@example.com", "active": true, "external_ids": [] },
    "launch_state": { "appointment_id": "apt-789", "correlation_id": "partner-click-123" },
    "launch_id": "01JZ123456789ABCDEFGHJKLM"
  }'

Next steps

ChartHeroAI

Developer documentation for ChartHero APIs, adapters, and integrations.

HIPAA-aligned safeguards

BAA available

ChartHero AI LLC