Smile CDR v2024.05.PRE
On this page:

35.4.1CDA Exchange Endpoint

 
Deprecated
The CDA Exchange module has been deprecated, and will be completely rebuilt from the ground up in a future release. Smile Digital Health does not recommend using this module, and will drop support for it in an upcoming release. If you have CDA Exchange needs, please contact our support team.

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.

35.4.2Create CDA Template

 
This method requires the CREATE_CDA_TEMPLATE permission.

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"
}

35.4.2.1Parameters

Required:

  • module_id: The name of your CDA Exchange module
  • template_id: Desired name of your new cda template

Optional:
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.

35.4.3Update CDA Template

 
This method requires the CREATE_CDA_TEMPLATE and DELETE_CDA_TEMPLATE permissions

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."
}

35.4.3.1Parameters

Required:

  • module_id: The name of your CDA Exchange module
  • template_id: Desired name of your new cda template

Optional:
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).

35.4.4Update CDA Template script via text

 
This method requires the CREATE_CDA_TEMPLATE and DELETE_CDA_TEMPLATE permissions

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;
}

35.4.5View CDA Template(s)

 
This method requires the VIEW_CDA_TEMPLATE permission.

To invoke:

GET http://localhost:9000/cda/{module_id}/template/{template_id}

35.4.5.1Parameters

Required:

  • module_id: The name of your CDA Exchange module

Optional:

  • 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
}

35.4.6Delete CDA template

 
This method requires the DELETE_CDA_TEMPLATE permission.

To invoke:

DELETE http://localhost:9000/cda/{module_id}/template/{template_id}

35.4.6.1URL Parameters

Required:

  • module_id: The name of your CDA Exchange module
  • template_id: The name of the template to delete

Response:
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.

35.4.7Use / Apply CDA Template

 

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.

This method requires the USE_CDA_TEMPLATE permission.

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",
}

35.4.7.1Parameters

Required:

  • module_id: The name of your CDA Exchange module
  • template_id: The id of the template you wish to apply

Optional 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 one
  • documentTypeCode: 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 document

If 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"
  ]
}

35.4.8Create CDA from Composition/Bundle

 

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",
}

35.4.8.1Parameters

Required:

  • module_id: The name of your CDA Exchange module
  • resource_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.

35.4.9Example CDA Template

 

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;
}