The CDA Exchange Module endpoint can be used to create, update, delete, and view CDA document templates.
Additionally, once a template has been stored, it can be used to generate CDA documents, Fhir Documents, and Fhir Compositions.
This section of the docs contains all the details for the REST endpoints available to use with this module. You can find a high level overview of the Smile CDR CDA Exchange Module here.
Upon successful creation, this method will return a 200 "ok" status with the contents of your new template in the response body.
To invoke:
POST http://localhost:9000/cda/{module_id}/template/{template_id}
Content-Type: application/json
{
"script": "string",
"params": [
"string"
],
"description": "string"
}
Required:
module_id
: The name of your CDA Exchange moduletemplate_id
: Desired name of your new cda templateOptional:
You may also add the following body parameter:
script
: The contents of your ES5 JavaScript script. Note, that this script must include a function with signature:
Composition generateCdaExchangeComposition(inputMap)
where the return is a Composition that was created using ResourceBuilder.build('Composition')
.
inputMap
is a JavaScript Object of input parameters that is your source of externally declared variable values.params
: The list of parameters your script accepts / expects. This field has no effect on the actual script but rather serves as a hint for potential users of the template.description
: A human readable description of the purpose and/or functionality of the included script. If any of your params are not self explanatory, this is a great place to explain their expectation / usage.Response:
Upon successful replacement/creation, this method will return a 200 "ok" status with the contents of your new template as the response body.
Can be used just like the POST method, but if the template_id
already exists, this route will update the fields of your template with whichever arguments are included in your json post body.
To invoke:
PUT http://localhost:9000/cda/{module_id}/template/{template_id}
Content-Type: application/json
{
"description": "a new description. params and script remain unchanged."
}
Required:
module_id
: The name of your CDA Exchange moduletemplate_id
: Desired name of your new cda templateOptional:
You may also add the following body parameters:
script
: The contents of your ES5 JavaScript script. Note, that this script must include a function with signature:
Composition generateCdaExchangeComposition(inputMap)
where the return is a Composition that was created using ResourceBuilder.build('Composition')
.
inputMap
is a JavaScript Object of input parameters that is your source of externally declared variable values.params
: The list of parameters your script accepts / expects. This field has no effect on the actual script but rather serves as a hint for potential users of the template.description
: A human readable description of the purpose and/or functionality of the included script. If any of your params are not self explanatory, this is a great place to explain their expectation / usage.Response:
If successful, the server will respond with a 201 with the contents of your new template as the response body (including unmodified fields).
The create method and JSON update method will only accept your script if you have escaped your new lines, tabs, and double quotes. You are likely creating your scripts with a text editor or JavaScript IDE and you may prefer to not have to convert your script to an escaped version.
This route takes your raw script and updates the "script" field of your CDA template. If the template_id
does not yet exist,
it creates a new template with your script with empty description
and params
fields which you can fill in with the JSON update method
To invoke:
PUT http://localhost:9000/cda/{module_id}/template/{template_id}/script
Content-Type: application/javascript
Body:
function generateCdaExchangeComposition(inputMap) {
var composition = ResourceBuilder.build('Composition');
composition.setType("ContinuityOfCareDocument");
composition.setSubject(inputMap['PATIENT']);
return composition;
}
To invoke:
GET http://localhost:9000/cda/{module_id}/template/{template_id}
Required:
module_id
: The name of your CDA Exchange moduleOptional:
template_id
: The name of the template to view. If omitted, then all templates are returned.The server will produce a response resembling the following:
if template_id is included:
{
"script" : "function generateCdaExchangeComposition(inputMap) {\n var composition = ResourceBuilder.build('Composition');\n return composition;\n}",
"params" : [],
"description" : "A very basic cda template script example"
}
if template_id is omitted:
{
"templates": [
{
"script" : "function generateCdaExchangeComposition(inputMap) {\n var composition = ResourceBuilder.build('Composition');\n return composition;\n}",
"params" : [],
"description" : "A very basic cda template script example",
"id": "example_template"
},
{
"script": "...",
"params": [],
"description": "...",
"id": "..."
}
],
"count": 2
}
To invoke:
DELETE http://localhost:9000/cda/{module_id}/template/{template_id}
Required:
module_id
: The name of your CDA Exchange moduletemplate_id
: The name of the template to deleteResponse:
If successful, the server will respond with a 200 and return the contents of your deleted template as the response body. The response body will be empty if there was no template with that id.
Warning! This API does not support Tenant partitioning security. Do not expose this to untrusted callers when using partitioning to enforce security boundaries.
This method's primary use is to generate CDA documents, but can also be used to generate and persist Fhir Compositions and Fhir Bundles of type Document. There are many param options, but they mostly have sensible defaults and so you will likely not need to explicitly set all of them.
To invoke:
POST http://localhost:9000/cda/{module_id}/template/{template_id}/apply
Content-Type: application/json
{
"inputParams": {
"exampleParam1": "Patient/1",
"exampleParam2": "30",
},
"persistComposition": true,
"persistFhirDocument": true,
"generateCda": true,
"documentTypeCode": "34133-9",
"obsRoot": "1.23.456.7.123456.1.2345.6789"
"deriveNarrative": true
"populateMissing": ["SEX_ASSIGNED_AT_BIRTH", "SMOKING_STATUS"],
"return": "OUTCOME",
}
Required:
module_id
: The name of your CDA Exchange moduletemplate_id
: The id of the template you wish to applyOptional body params:
inputParams
: The parameters that your generateCdaExchangeComposition
function expects in its inputMap
object.persistComposition
: defaults to false. If true, the Composition will be stored in {module_id}
's persistence module.persistFhirDocument
: defaults to false. If true, the Document Bundle will be stored in {module_id}
's persistence module.generateCda
: defaults to true. If false, no CDA document will be created.obsRoot
: On occasion, the CDA parser will require two unique ids when mapping from a single Fhir resource. The CDA parser generates a random id for one of them. If you provide an obsRoot
value, then the root
attribute for these random ids will be the one you specify instead of a randomly generated onedocumentTypeCode
: If your template does not set the Composition type to a C-CDA document type code, or if you wish to override the document type in the template / Composition, you can put a document type code here. For example, "34133-9"
will force a Continuity of Care document. This param is not necessary if you used composition.setType in your template script (as illustrated in the example below)deriveNarrative
: Defaults to false. If true, cda document section <text>
fields will be automatically populated with a table generated from its entries.medicationRequestCaption
and medicationAdministrationCaption
: The medications section can be comprised of both MedicationRequest
and MedicationAdministration
resources. If you are deriving the narrative from the entries, there will be two tables with captions "Medication Requests" and "Medication Administrations".
If you wish to override these default captions, you may do so by including a string key for either or both of these params.populateMissing
: There are some section entries that you may wish to include one of even if a corresponding resource is not found in the Composition.
By including an entry in the populateMissing
array, the entry will be created for you based off of an empty resource. Currently supported values for this array are:
"SEX_ASSIGNED_AT_BIRTH"
"SMOKING_STATUS"
return
: defaults to "OUTCOME"
. If "CDA_DOCUMENT"
and generateCda=true
, the response will have content type application/xml with only the generated CDA document.
Otherwise, the response will be json (example below).If the return type is json, the response will populate up to five fields:
If generateCda=true
:
cdaDocument
: A string of the xml cda documentIf persistComposition=true
:
composition
: A link to the Composition without the base url.If persistFhirDocument=true
:
fhirDocument
: A link to the Fhir Document without the base url.If the cda parser found any critical issues with the source Composition / Document:
cdaErrors
: An array of string error messages. These will not necessarily cause the document to fail to build, however you should verify the integrity of the output if there were any errors.If the cda parser found any moderate issues with the source Composition / Document:
cdaWarnings
: An array of string warning messages. These should never cause your document to fail to build and the output is likely to still match expectations.Example response (note that the cdaDocument will tend to be quite large):
{
"cdaDocument": "<ClinicalDocument xmlns=\"urn:hl7-org:v3\" xmlns:sdtc=\"urn:hl7-org:sdtc\" ...</ClinicalDocument>",
"composition": "Composition/1369/_history/1",
"fhirDocument": "Bundle/1370/_history/1",
"cdaWarnings": [
"Composition did not have a date. Used current timestamp as the authoring DateTime",
"UsRealmHeader: composition.identifier was missing. Generated random id"
],
"cdaErrors": [
"no Subject found in Composition.",
"No authors found in Composition"
]
}
If you already have a compatible Composition or Bundle that you wish to create your CDA from, there is no need to use a CDA template. Instead, use this route to generate the CDA directly from a Fhir resource.
To invoke:
POST http://localhost:9000/cda/{module_id}/{resource_type}/{resource_id}/apply
Content-Type: application/json
{
"inputParams": {
"exampleParam1": "Patient/1",
"exampleParam2": "30",
},
"persistFhirDocument": true,
"generateCda": true,
"documentTypeCode": "34133-9",
"obsRoot": "1.23.456.7.123456.1.2345.6789"
"deriveNarrative": true,
"populateMissing": ["SEX_ASSIGNED_AT_BIRTH", "SMOKING_STATUS"],
"return": "OUTCOME",
}
Required:
module_id
: The name of your CDA Exchange moduleresource_type
: Either Composition
or Bundle
depending on if your source is a Composition resource or Bundle of type Document.resource_id
: The id of the source resource. {resource_type}/{resource_id}
could look something like Composition/123
Optional body params:
The optional params are nearly identical to the Use / Apply CDA template route.
The only difference is that there is no persistComposition
option (and the persistFhirDocument
option will not have any effect if your source resource is a Bundle)
The response will be identical to the route above except there will never be a composition
field in it.
For a step by step tutorial on how to create a CDA template, please refer to the JavaScript Templates documentation.
For detailed documentation on what can be included in a CDA template, please refer to the JavaScript Execution Environment documentation.
Here is an example of a CDA template. To add it to your CDA module, you would have to use one of the POST or PUT routes at the top of this document.
Your post body would look like the following if you use the JSON POST method.
{
"description": "Subject is the only mandatory parameter and should be populated with a reference to a Patient such as 'Patient/123'. You can leave the other parameters blank and still generate a CDA document",
"script": "{the script as shown below, but with special characters escaped}"
}
You could alternatively PUT the script in a separate request
function generateCdaExchangeComposition(inputMap) {
// create the composition for our document
var composition = ResourceBuilder.build('Composition');
// setting the composition type auto-populates the known boilerplate for that C-CDA document type
composition.setType('ContinuityOfCareDocument');
var subject = inputMap['SUBJECT'];
composition.setSubject(subject);
// since we informally specified non-subject fields as optional
// in the description, we prevent errors by checking for their inclusion
if (inputMap['AUTHOR']) {
composition.setAuthor(inputMap['AUTHOR']);
}
if (inputMap['CUSTODIAN']) {
composition.setCustodian(inputMap['CUSTODIAN']);
}
if (inputMap['ENCOUNTER']) {
composition.setEncounter(inputMap['ENCOUNTER']);
}
// get current timeStamp for use in auto-generated IDs and timestamps
var currentDate = Date.now();
composition.setDate(currentDate);
// boilerplate fields for this document-generation template
composition.language = 'en-US';
composition.identifier.system = '1.23.456.7.890123.4.5678';
var splitSubject = subject.split('/');
if (splitSubject.length > 1) {
composition.identifier.value = subject.split('/')[1] + currentDate;
} else {
composition.identifier.value = subject + currentDate;
}
composition.title = 'My Organization - Continuity of Care Document';
var problemSection = composition.addSection('problem');
var problemList = Fhir
.search()
.forResource('Condition')
.where('subject', subject)
.asList();
problemSection.populate(problemList);
var medicationSection = composition.addSection('medication');
var medicationList = Fhir
.search()
.forResource('MedicationRequest')
.where('subject', subject)
.asList();
var medicationList2 = Fhir
.search()
.forResource('MedicationAdministration')
.where('subject', subject)
.asList();
medicationSection
.populate(medicationList)
.populate(medicationList2);
var resultSection = composition.addSection('result');
var resultList = Fhir
.search()
.forResource('DiagnosticReport')
.where('subject', subject)
.asList();
resultSection.populate(resultList);
// using JavaScript's native Date object
var thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
// this will be of the form 'ge2018-07-31'
var thirtyDaysAgoIsoString = 'ge' + thirtyDaysAgo.toISOString().slice(0,10);
// the date here ensures we only get vital signs from the past 30 days
// you could also create a parameter instead of
// hard coding 30 to make your script more versatile
var vitalSection = composition.addSection("vitalsign");
var vitalList = Fhir
.search()
.forResource('Observation')
.where('subject', subject)
.where('category', 'vital-signs')
.where('date', thirtyDaysAgoIsoString)
.asList();
vitalSection.populate(vitalList);
var allergySection = composition.addSection('allergyIntolerance');
var allergyList = Fhir
.search()
.forResource('AllergyIntolerance')
.where('patient', subject)
.asList();
allergySection.populate(allergyList);
var socialHistorySection = composition.addSection('socialHistory');
var historyList = Fhir
.search()
.forResource('Observation')
.where('subject', subject)
.where('category', 'social-history')
.asList();
socialHistorySection.populate(historyList);
return composition;
}