Conversations
Conversations allows you to build multi-channel bi-directional messaging workflows. For example, you might create a Conversation where a provider messages with a patient through a web app, and the patient receives and responds to messages via SMS on their phone. Conversations can have several participants on either channel (SMS or chat). Learn more about use cases for Conversations.
Oystehr Conversations is built on top of Twilio Conversations (opens in a new tab). Chat-channel participants join the Conversation using the Twilio Conversations Client SDKs (opens in a new tab) for web, iOS, and Android. SMS-channel participants send and receive messages to a Twilio Phone number which proxies messages to and from the Conversation.
Conversations on FHIR
Conversations are integrated directly with the FHIR Service. When you create a Conversation, a FHIR Encounter (opens in a new tab) resource is created to document it. Use this encounter to store and retrieve any details that are relevant to your use case. For example you might want to store:
- Encounter.subject (opens in a new tab) — Typically a patient
- Encounter.appointment (opens in a new tab) — Reference an appointment that scheduled this Conversation
- Encounter.reason (opens in a new tab) — A list of medical concerns to be discussed in the Conversation
Linking SMS Participants to Conversations
The Conversations service will automatically record every message sent to a Conversation in a FHIR Communication
record. In order to connect SMS participants to their FHIR profiles, the participant must have a telecom
property with a system
of sms
and a value
that is a valid phone number, with country code. For example:
{
"resourceType": "Patient",
"telecom": [
{
"system": "sms",
"value": "+11231231234"
}
]
}
A Patient
may also have a valid telecom
entry in a contact
property. For example:
{
"resourceType": "Patient",
"contact": [
{
"telecom": [
{
"system": "sms",
"value": "+11231231234"
}
]
}
]
}
Chat participants are linked automatically.
Encounter virtualService
Conversation Encounter resources are just like any other FHIR Encounter you might create except in one thing. When your Create Conversation request creates the FHIR Encounter, its Encounter.virtualService (opens in a new tab) value is automatically set to store the ID of the Twilio Conversation that is created.
If your Project is using FHIR R4B, an extension is used to backport the virtualService
model from FHIR R5 into the Encounter because the R4B Encounter (opens in a new tab) resource does not support it:
{
"encounter": {
"resourceType": "Encounter",
"id": "032b5cef-0cb7-42b5-af8f-b4813581c14a",
"status": "in-progress",
"subject": {
"reference": "Patient/11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000"
},
"participant": [
{
"individual": {
"reference": "Practitioner/f1d01874-0631-4903-8b32-73b3299b3363",
"display": "Dr Adam Careful"
}
}
],
"extension": [
{
"url": "https://extensions.fhir.zapehr.com/encounter-virtual-service-pre-release",
"extension": [
{
"url": "channelType",
"valueCoding": {
"system": "https://fhir.zapehr.com/virtual-service-type",
"code": "twilio-conversations",
"display": "Twilio Conversations"
}
},
{
"url": "addressString",
"valueString": "CH4b95706dd9f344d38bec278d98ec0d58"
}
]
}
]
}
}
The valueString
in the extension's addressString
is the Twilio Conversation ID.
Encounter participants
Conversation Encounter resources track references to their subject (a Patient or Group of Patients) and their participants. On its face this should be a good match to telemedicine workflows. However, at the time the FHIR R4B specification was created, telemedicine was not as popular as it is today. In FHIR R4B, a Patient cannot be both the subject of an Encounter and a participant. Oystehr uses an extension to provide a space for additional participants.
Oystehr M2M accounts can also be added as participants to an Encounter by including their FHIR Device resource in the encounter-other-participants
extension.
If your Project is using FHIR R4B, use the encounter-other-participants
extension to add Patient or Device references to an Encounter:
{
"encounter": {
"resourceType": "Encounter",
"id": "032b5cef-0cb7-42b5-af8f-b4813581c14a",
"status": "in-progress",
"subject": {
"reference": "Patient/11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000"
},
"participant": [
{
"individual": {
"reference": "Practitioner/f1d01874-0631-4903-8b32-73b3299b3363",
"display": "Dr Adam Careful"
}
}
],
"extension": [
{
"url": "https://extensions.fhir.zapehr.com/encounter-other-participants",
"extension": [
{
"url": "https://fhir.zapehr.com/encounter-other-participant",
"extension": [
{
"url": "reference",
"valueReference": "Patient/11bf4b17-e0b8-42e0-8dcf-dc8c4aefc000"
}
]
}
]
}
]
}
}
Using Conversations
There are four steps to using Conversations:
You can also send messages into a Conversation with an API call using the Send Message endpoint.
Create a Conversation
Create Conversation API Reference (opens in a new tab)
import Oystehr from '@oystehr/sdk'
const oystehr = new Oystehr({
accessToken: "<your_access_token>",
});
const response = await oystehr.conversation.create({
encounter: {
resourceType: 'Encounter',
id: 'home',
text: {
status: 'generated',
div: '<div xmlns="http://www.w3.org/1999/xhtml">Encounter with patient @example who is at home</div>',
},
status: 'finished',
class: {
system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode',
code: 'HH',
display: 'home health',
},
period: {
start: '2015-01-17T16:00:00+10:00',
end: '2015-01-17T16:30:00+10:00',
},
location: [
{
location: {
reference: '#home',
display: "Client's home",
},
status: 'completed',
period: {
start: '2015-01-17T16:00:00+10:00',
end: '2015-01-17T16:30:00+10:00',
},
},
],
},
});
Create Conversation takes just one value in the request body, a FHIR Encounter resource JSON. The endpoint does a few things:
- Creates the FHIR Encounter
- Creates a Twilio Conversation
- Updates the FHIR Encounter to put the Twilio Conversation SID into
virtualService
as described here.
The created Twilio Conversation has no participants in it at this point.
Update FHIR Encounter
Check out the API Reference (opens in a new tab)
Now that the conversation is created, we will add participants to the FHIR Encounter. This example is for FHIR R4B, so the patient is placed into an extension and the provider is listed in the participant array. Adding the participants to the FHIR Encounter is required for the next step, where we will add both participants to the Twilio conversation.
For the sake of example, we are adding the participants to the FHIR Encounter in a separate step from creating it. This isn't a hard requirement; if you know the participants at the time of creating the Encounter, you can include them in the Encounter definition provided to Create Conversation.
import { Encounter } from 'fhir/r4b'; // npm install @types/fhir
import Oystehr from '@oystehr/sdk'
const oystehr = new Oystehr({
accessToken: "<your_access_token>",
});
const result = await oystehr.fhir.update<Encounter>({
resourceType: "Encounter",
id: "cf39e0ec-067a-4808-a46b-5014299e9906",
text: {
status: "generated",
div: "<div xmlns=\"http://www.w3.org/1999/xhtml\">Encounter with patient @example who is at home</div>"
},
subject: {
reference: "Patient/11bf4b17-e0b8-42e0-8dcf-dc8c4aefc000"
},
participant: [
{
individual: {
reference: "Practitioner/471ba2a1-a6a1-4cfa-b01e-c558c6863949",
}
}
],
status: "finished",
class: {
system: "http://terminology.hl7.org/CodeSystem/v3-ActCode",
code: "HH",
display: "home health"
},
period: {
start: "2015-01-17T16:00:00+10:00",
end: "2015-01-17T16:30:00+10:00"
},
location: [
{
location: {
reference: "#home",
display: "Client's home"
},
status: "completed",
period: {
start: "2015-01-17T16:00:00+10:00",
end: "2015-01-17T16:30:00+10:00"
}
}
],
extension: [
{
url: "https://extensions.fhir.zapehr.com/encounter-virtual-service-pre-release",
extension: [
{
url: "channelType",
valueCoding: {
system: "https://fhir.zapehr.com/virtual-service-type",
code: "twilio-conversations",
display: "Twilio Conversations"
}
},
{
url: "addressString",
valueString: "CH4b95706dd9f344d38bec278d98ec0d58"
}
]
},
{
url: "https://extensions.fhir.zapehr.com/encounter-other-participants",
extension: [
{
url: "https://fhir.zapehr.com/encounter-other-participant",
extension: [
{
url: "reference",
valueReference: {
reference: "Patient/11bf4b17-e0b8-42e0-8dcf-dc8c4aefc000"
}
}
]
}
]
}
]
});
Add Participants
Add Participants API Reference (opens in a new tab)
import Oystehr from '@oystehr/sdk'
const oystehr = new Oystehr({
accessToken: "<your_access_token>",
});
await oystehr.conversation.addParticipant({
conversationId: 'CH8277a50cb9cf44809c7da3be1b88723c',
encounterReference: 'Encounter/cf39e0ec-067a-4808-a46b-5014299e9906',
participants: [
{
participantReference: 'Practitioner/471ba2a1-a6a1-4cfa-b01e-c558c6863949',
channel: 'chat',
},
{
participantReference: "Patient/11bf4b17-e0b8-42e0-8dcf-dc8c4aefc000",
channel: "chat"
}
],
});
To add participants to a Conversation you specify two things in the request body:
encounterReference
— A reference to a FHIR Encounter that was created using Create Conversation.participants
— An array of participants to add to the Conversation each containing:participantReference
— A reference to a Patient, Practitioner, RelatedPerson, or Device FHIR resource. More below.channel
— Eitherchat
orsms
.phoneNumber
— A phone number for the participant only ifchannel
=sms
.
participantReference
There are two key details to understand about the participantReference
in order to successfully add your actors to the Conversation.
First, every participantReference
must be in the Encounter's participant list or its encounter-other-participants
extension.
Second, if the channel
is chat
, then the participant will join the Conversation using the Twilio Conversations SDK (opens in a new tab) and a token from the Oystehr Get Token endpoint. The participantReference
must be the FHIR profile of the Developer, User, or M2M Client that is going to make the request to the Get Token endpoint.
Get Token
Get Token API Reference (opens in a new tab)
In order to connect to a Conversation as a chat-channel participant, invoke the Get Token endpoint as the Developer, User, or M2M Client who needs to connect. This returns a Twilio Conversation JWT with their identity.
{
"jti": "SKc53238564cd4efaf8fd4098f2fecca9c-1698785068",
"grants": {
"identity": "ca02393f-b2bf-4142-bec9-abcdefg01234",
"chat": {
"service_sid": "IS5411229312bd4940a4cba4ecade7b1c8"
}
},
"iat": 1698785068,
"exp": 1698788668,
"iss": "SKc5323856eee4efaf7fd4098f2cebca9c",
"sub": "AC3d7e1066c7fb6c701a5d6085f7626f3c"
}
In order to connect to the Conversation with this token, the actor must have been added to the conversation.
Additional Conversations features
Send Message
Besides enabling chat-channel and SMS-channel participants to send messages, you can also send messages directly to a Conversation with an API call using the Send Message endpoint (opens in a new tab).
This is useful for sending automated messages into a Conversation. For example, you might use the send message endpoint to introduce participants in a newly made conversation, or to remind patients about upcoming appointments.
While this can also be done with Transactional SMS, it may fit your use-case better to utilize already-existing Conversations.
import Oystehr from '@oystehr/sdk'
const oystehr = new Oystehr({
accessToken: "<your_access_token>",
});
await oystehr.conversation.message({
conversationId,
message: 'Reminder, your appointment at Dentists R Us is scheduled for Friday 12/29/23 at 2:30 PM.',
});
Remove Participants
You can remove previously added participants from a Conversation with the Remove Participants endpoint (opens in a new tab).
import Oystehr from '@oystehr/sdk'
const oystehr = new Oystehr({
accessToken: "<your_access_token>",
});
await oystehr.conversation.removeParticipant({
conversationId: 'CH8277a50cb9cf44809c7da3be1b88723c',
encounterReference: 'Encounter/cf39e0ec-067a-4808-a46b-5014299e9906',
participantReference: 'Practitioner/471ba2a1-a6a1-4cfa-b01e-c558c6863949',
});
To remove participants from a Conversation you specify two things in the request body:
encounterReference
— A reference to a FHIR Encounter that was created using Create Conversation.participantReference
— A reference to a FHIR resource. See more aboutparticipantReference
.
Compliance
Oystehr maintains a BAA with Twilio (opens in a new tab) which is offered through its enterprise-tier plans. When building on Oystehr, your BAA with Oystehr (opens in a new tab) acts as a chain to Twilio and our other service providers, so what you build on Oystehr Conversations may be HIPAA compliant.
As stated in Oystehr's BAA, HIPAA compliance is a shared responsibility and building on Oystehr does not automatically make what you build HIPAA-compliant. You are responsible for doing your part to safeguard PHI when using Oystehr. In addition, if you make use of Twilio with tokens or credentials not vended by Oystehr, your usage is not covered by our BAA. Learn more about HIPAA Compliance and Oystehr.
The Messaging Service does not yet support FHIR R5 (opens in a new tab).