Skip to main content

Patient Messaging

For product owners and partners: Use this page to understand how patient-practice chat messages are exchanged through the MediKIT API.

For architects and developers: Use this page to implement the $new-patient-message operation and the Communication search shape used to retrieve a conversation.

Patient messages (chats) are modeled as a series of FHIR R4 Communication resources, grouped under a single Encounter resource that represents the conversation.

What this endpoint is for

Use patient messaging when your product needs to:

  • let a patient send a message to a practice through a patient portal or app
  • add a follow-up message to an existing conversation
  • retrieve the message history of a conversation for display in a chat view

Endpoint map

Use caseEndpointNotes
Send or append a messagePOST /v2/{organizationId}/fhir/R4/Patient/{patientId}/Communication/$new-patient-messageBody is a Parameters resource
Retrieve a conversationGET /v2/{organizationId}/fhir/R4/Patient/{patientId}/CommunicationFilter by encounter or encounter.identifier
Retrieve a conversation's EncounterGET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Encountertype is required; add identifier to address it by conversation-identifier
Upload an attachmentPOST /v2/{organizationId}/fhir/R4/Patient/{patientId}/BinaryBody is the raw file, not FHIR JSON
Download an attachmentGET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Binary/{id}Returns the raw file, not FHIR JSON

Required access

Required scopes

  • communication.new-patient-message (to send or append a message)
  • communication.read (to retrieve a conversation)
  • binary.create (to upload an attachment)
  • binary.read (to download an attachment)

Sending a message

The request body is a Parameters resource with the following top-level parts. No other top-level parameter names are accepted: an unrecognized parameter name is rejected.

ParameterRequiredRepeatableDescription
messageYesNoThe message text, as a non-blank valueString.
conversation-topicNoNoThe subject of the whole conversation, as a non-blank valueString. Defaults to "E-Consult". See The conversation's subject.
topicNoNoDeprecated. The former name of conversation-topic, still accepted. Use conversation-topic instead; when both are sent, conversation-topic wins.
conversation-identifierYesNoA valueIdentifier (system and value) that identifies the conversation.
sentNoNoA valueDateTime for when the message was sent. Defaults to the current time.
attachmentNoYesA valueAttachment referencing a file uploaded beforehand via POST .../Binary (see Attaching files).

The conversation-identifier determines whether the message starts a new conversation or continues one:

  • If an existing conversation already carries that conversation-identifier, the message is added to it.
  • Otherwise, a new conversation is created (a new Encounter resource stamped with that conversation-identifier), and the message is the first Communication in it.

Retracted conversations

A practice can retract a conversation, which marks its Encounter as entered-in-error. The practice stops seeing that conversation at that point, so nothing more can be added to it: sending a message under its conversation-identifier returns 410 Gone and stores nothing.

A retracted conversation's conversation-identifier stays bound to it and cannot be reused. To keep messaging the patient after a retraction, start a new conversation under a new conversation-identifier. Retrying the same one always returns 410.

The conversation's subject

conversation-topic is a property of the conversation as a whole, not of the individual message, and it is fixed when the conversation is created:

  • On the message that starts a conversation, conversation-topic sets the subject for that whole conversation. Omitted, the subject becomes "E-Consult".
  • On a message added to an existing conversation, conversation-topic is accepted but ignored. The message is filed under the subject the conversation already has, so you can keep sending it on every message without it changing anything.

A conversation's subject cannot be changed once it has been created. Start a new conversation, under a new conversation-identifier, for a new subject.

The subject is stored on the conversation itself, that is, on its Encounter, as reasonCode[0].text, and not on the individual messages. To read it, follow the encounter reference on any Communication in the conversation and retrieve that resource:

GET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Encounter/{id}

You can also reach it straight from the conversation-identifier you sent, without knowing its logical ID, by searching Encounter on that identifier (see Retrieving a conversation's Encounter).

Request example: starting a new conversation

{
"resourceType": "Parameters",
"parameter": [
{
"name": "message",
"valueString": "Hello, I have a question about my medication."
},
{
"name": "conversation-topic",
"valueString": "Question about my medication"
},
{
"name": "conversation-identifier",
"valueIdentifier": {
"system": "https://your-system.example.org/conversations",
"value": "b3f1a6f0-3e9a-4b8a-9b7a-2f6a7a2f6a7a"
}
}
]
}

Request example: adding to an existing conversation

{
"resourceType": "Parameters",
"parameter": [
{
"name": "message",
"valueString": "Thank you, that answers my question."
},
{
"name": "conversation-identifier",
"valueIdentifier": {
"system": "https://your-system.example.org/conversations",
"value": "b3f1a6f0-3e9a-4b8a-9b7a-2f6a7a2f6a7a"
}
},
{
"name": "sent",
"valueDateTime": "2026-07-14T10:32:00+02:00"
}
]
}

Response shape

A successful response returns the created FHIR R4 Communication resource. It has:

  • status: completed
  • subject and sender set to the patient
  • recipient set to the organization
  • encounter referencing the conversation's Encounter, which holds the conversation's subject (see The conversation's subject)
  • payload containing the submitted message as contentString, followed by one entry per attachment as contentAttachment

Attaching files

Attachments are not sent inline with the message. Upload each file first, then reference it from $new-patient-message:

  1. POST /v2/{organizationId}/fhir/R4/Patient/{patientId}/Binary: the request body is the file's raw bytes (not wrapped in FHIR JSON). A successful upload returns 201 Created with a Location header of the form Binary/{id}, and a Binary resource in the body containing resourceType, id, and contentType (data is omitted, since the caller already has the bytes it just sent).

    Set the Content-Type header to the file's actual mime type (e.g. application/pdf, image/jpeg), not a generic value like application/octet-stream: it's stored as-is and returned unchanged on download, so an incorrect value here means downstream consumers of the attachment (including the file itself, when opened later) see the wrong type. A missing or unsupported Content-Type is rejected outright with 422.

  2. Call $new-patient-message (to start or continue the conversation) with one attachment parameter per uploaded file, each a valueAttachment with:

    FieldRequiredDescription
    urlYesThe Binary/{id} reference returned by the upload, e.g. Binary/3c1e.... Pass the {id} back exactly as returned.
    titleYesA label for the attachment, e.g. the original filename.
    creationNoA dateTime for when the file was created.

Files can be uploaded in any order, and in parallel: there is no dependency between them or on the conversation existing yet. Only after all uploads succeed should $new-patient-message be called once with all attachment references; if $new-patient-message is never called, the uploaded files are simply never attached to any message.

Request example: message with one attachment

{
"resourceType": "Parameters",
"parameter": [
{
"name": "message",
"valueString": "I've attached a photo of the rash."
},
{
"name": "conversation-identifier",
"valueIdentifier": {
"system": "https://your-system.example.org/conversations",
"value": "b3f1a6f0-3e9a-4b8a-9b7a-2f6a7a2f6a7a"
}
},
{
"name": "attachment",
"valueAttachment": {
"url": "Binary/3c1e6f0a-9b7a-4b8a-9e9a-2f6a7a2f6a7a",
"title": "rash.jpg"
}
}
]
}

Request size limit

Each attachment may be at most 10MB, and a single $new-patient-message request may reference at most 10 attachments. Exceeding the per-attachment size limit is rejected by the POST .../Binary upload itself (422); exceeding the per-request attachment count is rejected by $new-patient-message (422).

Retrieving an attachment

GET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Binary/{id} returns the raw, previously-uploaded file content, not wrapped in FHIR JSON. Content-Type matches what was set at upload time, and the response carries Content-Disposition: attachment, so a browser following the URL downloads the file rather than rendering it in the page. The {id} used here is the same one returned in a Communication.payload[].valueAttachment.url (as Binary/{id}) from either the $new-patient-message response or a Communication search/byId response: the connector resolves that relative reference to an absolute URL on the current domain, so clients don't need to construct it manually.

Retrieving a conversation

Search Communication resources for the patient, filtered to a specific conversation. Only patient messages are returned: messages belonging to any other kind of encounter are never included, and reading one by its logical ID responds 404.

ParameterMeaning
encounterThe logical ID of the conversation's Encounter, if known.
encounter.typeThe encounter type. https://referentiemodel.nhg.org/tabellen/nhg-tabel-14-contactwijze|12 is the only accepted value, and results are already restricted to it, so this parameter is optional and never widens or narrows what you get back.
encounter.identifierThe conversation-identifier used when sending the message, in system|value form.
_countMaximum number of results per page.
pageOpaque pagination cursor - follow the next/previous link on a returned Bundle instead of setting this directly.
GET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Communication?encounter.identifier=https://your-system.example.org/conversations|b3f1a6f0-3e9a-4b8a-9b7a-2f6a7a2f6a7a

Response shape

Successful responses return a FHIR R4 Bundle (type: searchset) containing the conversation's Communication resources, ordered as stored.

Retrieving a conversation's Encounter

Search Encounter resources for the patient to get conversations themselves rather than their messages. This is the way to read a conversation's subject when you hold its conversation-identifier but not its logical ID.

ParameterMeaning
typeRequired. The encounter type, in system|code form. https://referentiemodel.nhg.org/tabellen/nhg-tabel-14-contactwijze|12 is the only accepted value, so results are patient message conversations.
identifierThe conversation-identifier used when sending the message, in system|value form. The system must be one your client is authorized for; a value without a system is not accepted.
_countMaximum number of results per page.
pageOpaque pagination cursor - follow the next/previous link on a returned Bundle instead of setting this directly.

Add identifier to address a single conversation:

GET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Encounter?type=https://referentiemodel.nhg.org/tabellen/nhg-tabel-14-contactwijze|12&identifier=https://your-system.example.org/conversations|b3f1a6f0-3e9a-4b8a-9b7a-2f6a7a2f6a7a

Leave identifier off to get the patient's conversations. Results come back in pages, so follow the next link on the returned Bundle to walk a longer set.

GET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Encounter?type=https://referentiemodel.nhg.org/tabellen/nhg-tabel-14-contactwijze|12

Error behavior

StatusScenario
403Client lacks the required scope or access to the requested patient context
404An attachment on $new-patient-message references a Binary/{id} that was not uploaded for this organization and patient, or GET .../Binary/{id} for an unknown attachment
410$new-patient-message addressed a conversation that has been retracted (see Retracted conversations)
422Missing/unrecognized parameters, an encounter.type other than https://referentiemodel.nhg.org/tabellen/nhg-tabel-14-contactwijze|12, a blank message, a blank or repeated conversation-topic or topic, a conversation-identifier missing system/value, an attachment missing url/title, more than 10 attachments in one request, a Binary upload missing or with an unsupported Content-Type, or a Binary upload exceeding 10MB
500The request was valid but could not be persisted

What is supported and what is not

Supported todayNot supported as a general REST pattern
Starting a new conversation with $new-patient-messageClosing or archiving a conversation
Appending a message to an existing conversationGeneric create/update/delete for Communication or Encounter
Retrieving a conversation's messages by encounter or encounter identifierGeneric create/update/delete for Binary outside the patient message attachment flow
Retrieving communications that are not patient messages
Attaching files to a message (upload via Binary, then reference from $new-patient-message)