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-messageoperation and theCommunicationsearch 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 case | Endpoint | Notes |
|---|---|---|
| Send or append a message | POST /v2/{organizationId}/fhir/R4/Patient/{patientId}/Communication/$new-patient-message | Body is a Parameters resource |
| Retrieve a conversation | GET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Communication | Filter by encounter or encounter.identifier |
Retrieve a conversation's Encounter | GET /v2/{organizationId}/fhir/R4/Patient/{patientId}/Encounter | type is required; add identifier to address it by conversation-identifier |
| Upload an attachment | POST /v2/{organizationId}/fhir/R4/Patient/{patientId}/Binary | Body is the raw file, not FHIR JSON |
| Download an attachment | GET /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.
| Parameter | Required | Repeatable | Description |
|---|---|---|---|
message | Yes | No | The message text, as a non-blank valueString. |
conversation-topic | No | No | The subject of the whole conversation, as a non-blank valueString. Defaults to "E-Consult". See The conversation's subject. |
topic | No | No | Deprecated. The former name of conversation-topic, still accepted. Use conversation-topic instead; when both are sent, conversation-topic wins. |
conversation-identifier | Yes | No | A valueIdentifier (system and value) that identifies the conversation. |
sent | No | No | A valueDateTime for when the message was sent. Defaults to the current time. |
attachment | No | Yes | A 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
Encounterresource stamped with thatconversation-identifier), and the message is the firstCommunicationin 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-topicsets the subject for that whole conversation. Omitted, the subject becomes"E-Consult". - On a message added to an existing conversation,
conversation-topicis 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:completedsubjectandsenderset to the patientrecipientset to the organizationencounterreferencing the conversation'sEncounter, which holds the conversation's subject (see The conversation's subject)payloadcontaining the submitted message ascontentString, followed by one entry per attachment ascontentAttachment
Attaching files
Attachments are not sent inline with the message. Upload each file first, then reference it from $new-patient-message:
-
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 returns201 Createdwith aLocationheader of the formBinary/{id}, and aBinaryresource in the body containingresourceType,id, andcontentType(datais omitted, since the caller already has the bytes it just sent).Set the
Content-Typeheader to the file's actual mime type (e.g.application/pdf,image/jpeg), not a generic value likeapplication/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 unsupportedContent-Typeis rejected outright with422. -
Call
$new-patient-message(to start or continue the conversation) with oneattachmentparameter per uploaded file, each avalueAttachmentwith:Field Required Description urlYes The Binary/{id}reference returned by the upload, e.g.Binary/3c1e.... Pass the{id}back exactly as returned.titleYes A label for the attachment, e.g. the original filename. creationNo A dateTimefor 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.
| Parameter | Meaning |
|---|---|
encounter | The logical ID of the conversation's Encounter, if known. |
encounter.type | The 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.identifier | The conversation-identifier used when sending the message, in system|value form. |
_count | Maximum number of results per page. |
page | Opaque 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.
| Parameter | Meaning |
|---|---|
type | Required. 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. |
identifier | The 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. |
_count | Maximum number of results per page. |
page | Opaque 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
| Status | Scenario |
|---|---|
403 | Client lacks the required scope or access to the requested patient context |
404 | An 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) |
422 | Missing/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 |
500 | The request was valid but could not be persisted |
What is supported and what is not
| Supported today | Not supported as a general REST pattern |
|---|---|
Starting a new conversation with $new-patient-message | Closing or archiving a conversation |
| Appending a message to an existing conversation | Generic create/update/delete for Communication or Encounter |
| Retrieving a conversation's messages by encounter or encounter identifier | Generic 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) |