Stream: implementers
Topic: SMS and Twilio
Grey Faulkenberry (Jun 15 2021 at 16:55):
I've been looking through the other threads about SMS messages, and if I just missed the answer to my question, my apologies.
I think my work flow is going to be something like this:
- Provider orders a survey for a patient (magic happens! - resources created, questionnaire identified, etc)
- A Communication resource representing an SMS to the patient is created, the payload is a short message and a link to the survey
- When the Communication is first created, its status is 'preparation'
- A listener of some sort (FHIR Subscription, pub/sub) alerts a Twilio microservice that there is a new SMS communication
- The microservice gets the Communication payload (updates the resource's status to in-progress) and attempts to send the SMS, and updating the Communication resource as appropriate
Does that seem like the right approach?
Josh Mandel (Jun 15 2021 at 17:07):
This seems like a good workflow in general. One question is whether you might want to capture (2) as a CommunicationRequest
with status active
(meaning: ready to be acted upon), and in (5) create the Communication
(with .basedOn
referencing the request) to capture the fact that the communiaction has been sent.
Josh Mandel (Jun 15 2021 at 17:09):
Based on my best reading of these resources, I think the "Request" best captures your intended semantics in (2), which is "this request is ready to be acted upon".
Josh Mandel (Jun 15 2021 at 17:11):
(BTW, I really like this idea. I sketched out a design but never started working on something similar a couple of years back. If you have any inclination to build the microservice -- whether polling- or subscription-based -- as an open-source standalone component, I'd certainly cheer you on!)
Grey Faulkenberry (Jun 15 2021 at 17:40):
Thanks! I'll look into that. And everything I've done so far is open-source, so I'll see what I can do! (I haven't used Subscriptions much yet, so that may be the main roadblock).
Josh Mandel (Jun 15 2021 at 18:43):
Realistically today I'd do it via polling, and leave Subscriptions as an optimization for later (we're in the process of revamping Subscriptions for FHIR R5, along with a back-port to R4B, but this is still work in progress).
Grey Faulkenberry (Jun 16 2021 at 12:54):
So I came up with another question as I was tinkering with this. Specifically, how do I represent SMS as a medium? I don't think any of the participation modes do a great job at capturing it (https://www.hl7.org/fhir/v3/ParticipationMode/vs.html). I guess it's technically typewritten, but I don't feel like that's what we're going for.
Josh Mandel (Jun 16 2021 at 20:16):
Agreed! The medium
is optional, and that valueset is only an example, so no huge surprise there but I agree we ought to include it. Will you submit a Jira tracker for this?
Josh Mandel (Jun 16 2021 at 20:21):
As a stopgap I might just use
"medium": [{
"coding": [{
"system": "http://hl7.org/fhir/contact-point-system",
"code": "sms"
}]
}]
from http://build.fhir.org/codesystem-contact-point-system.html#contact-point-system-sms -- which would make the job of looking up the right ContactPoint in Patient.telecom
pretty straightforward.
Josh Mandel (Jun 16 2021 at 20:23):
^^ Edited to fix Patient.telecom
, which I wrote accidentally as ".contact" at first.
Grey Faulkenberry (Jun 16 2021 at 21:07):
So it's not ready for production, but it does work: https://github.com/MayJuun/fhir_twilio
Josh Mandel (Jun 16 2021 at 21:09):
I'm watching the files disappear as I browse GH ;-))
Grey Faulkenberry (Jun 16 2021 at 21:12):
You do need the Dart SDK though, although I made it specifically to run in a Docker container.
Grey Faulkenberry (Jun 16 2021 at 21:12):
Yeah, accidentally uploaded a credentials file the first time
Grey Faulkenberry (Jun 16 2021 at 21:13):
Should be fixed now
Grey Faulkenberry (Jun 16 2021 at 21:13):
Right now, as long as you put in your own credentials, you can post a ContactRequest to the PublicHapi server, and this will check it, and text you whatever the payload is, and change the status of the ContactRequest to complete.
Grey Faulkenberry (Jun 16 2021 at 21:15):
Not fancy, obviously, but its functional. And when I get time I'll make it cleaner, add functionality, etc. But just in case you wanted to take a look.
Josh Mandel (Jun 16 2021 at 21:20):
Nice! My first experience reading Dart. BTW I'm mulling over the "right" way to "claim a Request from a work queue", e.g. to ensure here that somebody else (like, another task runner in your cluster) hasn't already grabbed this one. I've asked a question in #workflow > Claiming a request, to work on it to see if there's something I'm missing.
Grey Faulkenberry (Jun 16 2021 at 21:21):
It's the only language I know pretty well (obviously it looks similar to anything else object oriented, but this is the only one I really work in).
Josh Mandel (Jun 16 2021 at 21:22):
As a stopgap or a hack, it'd be possible to
PUT /CommunicationRequest/:id
If-Match: W/"prev-version"
{
// .copyWith(status: Code('on-hold'));
}
and only proceeding if this works.
Grey Faulkenberry (Jun 16 2021 at 21:22):
And yeah, that's definitely something that would need to be solved. Plus the ensuring that the request is for an SMS, etc.
Grey Faulkenberry (Jun 16 2021 at 21:23):
If you have any interest in Dart/Flutter, I can certainly suggest some useful resources (and/or go on a long monologue about why I've enjoyed using them). :joy:
Vassil Peytchev (Jun 17 2021 at 00:36):
Task is the ressource geared towards "claiming". If you replace (or add in addition to) CommunicationRequest with Task, you can "claim" the Task by becoming the owner.
Josh Mandel (Jun 17 2021 at 00:42):
Makes sense, but still: how does the Request status state machine address the "is claimed" state?
Vassil Peytchev (Jun 17 2021 at 00:46):
No Task.owner - no claim. Also, businessStatus (+ statusReason)
Josh Mandel (Jun 17 2021 at 01:41):
But you can't know that from the Request status, and you can't know it atomically. Would you agree @Vassil Peytchev that a new Request status like in-progress
would help fill a meaningful gap in the current state machine?
Josh Mandel (Jun 17 2021 at 01:42):
Or is there really no gap, and I just have mistaken expectation?
Vassil Peytchev (Jun 17 2021 at 02:18):
Sorry for the drive-by posting, I need to expand a little on this.
In the original scenario we start with a provider order. Usually a provider order is a ServiceRequest, but here it is probably more appropriate to be a ComunicationRequest. In any case, the resource that follows the Request pattern is just a record of the order, and (on purpose) is decoupled from the fulfillment of the request. This is reflected in the Request pattern's state machine, where the order is active or complete.
To get the request fulfilled, you create a Task resource, with Task.focus pointing to the order - in this case the CommunicationRequest. The state machine for Task.status is more involved and includes the capability to "negotiate" with the fulfiller. A very basic use of Task.status is setting it to requested
and when the micrtoservice "claims" it, updates it to accepted
or even directly to in-progress
.
The microservice would be subscribed to/polling on a Task (can be based on Task.focus being a reference to a CommunicationRequest, or Task.code being a specific code, and Task.status being requested
)
Once the SMS is sent, Task.output would reference the Communication resource, and the status would be completed
.
With this fairly straight-forward workflow, Task.status seems to be sufficient. If there were more complicated workflows, you can add more nuances via task.businessStatus and create any state machine based on the triplet of status, businessStatus, and statusReason. Getting an owner assigned is another way to claim a Task, if there is a reason to distinguish the Task being claimed from being accepted or being in progress.
Josh Mandel (Jun 17 2021 at 02:34):
This all makes sense, but Task feels like a big hammer, and also the Request.status *does" have a state machine that gets updated at various points during this workflow -- it's not like the Request represented some immutable "order as initially placed". As such, why not have a status like in-progress
on the Request.status state machine?
Vassil Peytchev (Jun 17 2021 at 03:03):
You could avoid the request, and have the Task represent both the request and the fulfillment. If the main point of the workflow is how the microservice will know there is something to do, it does it, and leaves a record of the fulfillment, you can start with Task - there is no need for a CommunicationRequest, unless there is a need for the permanent record of an official authorization to also be exposed as a FHIR resource.
As to why the Request pattern does no allow recording of the fulfillment - my guess is that the record of the fulfillment is in many cases on a different server than the record of the request. From the point of view of a pure FHIR client and a single server, that might be overkill, but I think the benefits of not having too many ways to do the same thing outweigh the hammer...
Josh Mandel (Jun 17 2021 at 13:33):
Thanks again for the feedback here. I'd definitely try to avoid having to touch two resources in this workflow. Either Task alone or CommunicationRequest alone seems okay.
That said, I'll file a jira issue about the Request status state machine, because forcing a request to jump from "ready to be acted on" to "all done" seems a bit coarse grained (to be clear, I think it's fine if that jump is a common pattern in practice, but it seems very strange not to be able to express a state in between).
Josh Mandel (Jun 17 2021 at 14:35):
Submitted FHIR-32948
Lloyd McKenzie (Jun 24 2021 at 01:48):
In general, asking a Patient to complete a Questionnaire should be a Task, not a CommunicationRequest. CommunicationRequest allows you to say "please share data". It's not really intended to be used for "please perform some activity, then send me the result". The SDC implementation guide defines a profile for what the Task should look like here: http://build.fhir.org/ig/HL7/sdc/StructureDefinition-sdc-task.html (and will soon have some additional guidance that talks about how to use it).
Lloyd McKenzie (Jun 24 2021 at 01:48):
So my vote is for Task alone.
Lloyd McKenzie (Jun 24 2021 at 01:50):
Request resources can't be claimed. A Task soliciting the fulfillment some or all of a Request can be claimed. Without a Task, the Request itself isn't actionable. It's like a paper prescription or lab requisition sitting in the patient's pocket. No pills are counted and no blood is drawn until/unless the patient gives the piece of paper to a pharmacy/lab and says "please action". (And even then, for the drug prescription, they only typically action one fill, not the whole order.)
Josh Mandel (Jun 24 2021 at 03:09):
The semantics of the communication request are "please send this sms" ; it's not about "a Task to complete a questionnaire" -- or at least, I wouldn't want to limit it to that.
Josh Mandel (Jun 24 2021 at 03:10):
I'm struggling to understand what a real life use case for Communication Request would be, otherwise.
Josh Mandel (Jun 24 2021 at 03:12):
Reading through the resolution on FHIR-32948 I'm thoroughly puzzled. Saying, "it makes sense to describe a request as ready for action and done but nothing in between" just seems like a gaping logical hole in the state machine. Logically, something happens in between those states.
Josh Mandel (Jun 24 2021 at 03:17):
In any case @Grey Faulkenberry you should certainly take Lloyd's advice on this since he's the expert. On this particular subject I am just a detractor yelling into the wind :-)
Brendan Keeler (Jun 24 2021 at 16:57):
Brendan Keeler (Jun 24 2021 at 16:57):
@Josh Mandel ^^
Lloyd McKenzie (Jun 25 2021 at 16:27):
A Request is just an authorization. By itself, it doesn't drive action. It just sits there until the author decides (based on other events in the world) that it's done, expired, or otherwise needs to end. If you want to know what's happening with a Request, you can look at things that reference the Request, but the Request itself doesn't change. The base rule is "fillers can't edit the placer Request".
If you think about the real world, if a clinician authors a non-directed prescription or referral, no one comes back and edits the record in the ordering system to subsequently 'assign' that order once it gets sent to a specific pharmacy or a dispatch center assigns the referral to a particular care agency. To see what Observations have been created against a ServiceRequest, you query the Observations. The ServiceRequest won't point to them. That ensures that we don't need to update 2 resources when an Event resource comes into play. The same principle holds when updating status. Also "in progress" isn't super-useful because "in-progress for what?" One order could simultaneously be in progress with 3 different fillers, each satisfying a different part. Task allows us to manage this because we can have a separate Task for each 'chunk'. Task is a special resource in that it's actually expected to be editable by both placer and filler systems.
Josh Mandel (Jun 25 2021 at 18:19):
It doesn't matter whether the "filler" or the "requester" is updating the status; a Request can be actively in progress.
Josh Mandel (Jun 25 2021 at 18:20):
Just because you can envision scenarios where you wouldn't want to mark it as such isn't a good reason to prevent anyone from marking it as such.
Lloyd McKenzie (Jun 25 2021 at 21:01):
Fundamentally, it's not part of the Request model because it's not a status of the authorization - it's a status of the activity happening that's authorized. We have a deep history of conflating "authorization" status and "execution" status and that causes grief. In FHIR, we've tried to clearly delineate them to avoid that confusion/grief. A Request can't have a status of 'accepted' or 'rejected'. Because regardless of whether a particular filler agrees to fulfill an order, it remains an active order - and in some workflow processes, an order that was rejected by one filler might later be satisfied by another. The notion of "in progress" is a similar status in that it deals with decisions and actions of the fulfiller, not with the state of the authorization.
John Silva (Jun 25 2021 at 21:14):
Does the FHIR Workflow pattern describe this (or describe this adequately enough)?
https://www.hl7.org/fhir/workflow.html
Lloyd McKenzie (Jun 25 2021 at 21:27):
It tries. Whether it's adequate or not, I don't know. Change requests suggesting improvement are more than welcome
John Silva (Jun 25 2021 at 21:48):
I'm not sure if it's adequate or not; maybe those who are asking the questions about the Communication workflow can answer better.
I also noticed that this page might be helpful too (referenced by workflow page): https://www.hl7.org/fhir/workflow-communications.html
Last updated: Apr 12 2022 at 19:14 UTC