Call to External Service

There is a need to be able to call out for data in an external system. A couple of examples:

  • enrich a claimLine with a value such as a Service Category, that is derived externally .

  • distribute a claim level amount between its claim lines for Coordination of Benefits.

The call to an external service can be called anywhere in dynamic logic and from each type of dynamic logic. It is a synchronous call to an external webservice and follows patterns for other synchronous calls, like a time-out. The schema is owned by the external service. The XML message that complies with the schema definition is constructed in the dynamic logic.

Note: the callout functionality described here is not to be confused with the functionality of claim callout rules. Claim callout rules also allow for asynchronous callouts. Also, callouts as a consequence of claim callout rules are tracked in the claim status history. Refer to the text "Claim Callouts" in the implementation guide for process rules.

Concepts

Using MarkupBuilder

Using the groovy MarkupBuilder allows you to construct the XML message in a way that follows the structure of the XML message.The root element and the attributes of the root element are specified when invoking the MarkupBuilder

def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.RootElement(attribute1: 'value1'){}
writer.toString()

yields

<RootElement attribute1='value1' />

See also the use cases at the end.

For invoking the web service, a utility class is used: ohiWebService.

This class provides the following functionality:

  • wrapping the XML message in a SOAP envelope

  • delivering the message to the endpoint as configured in the configuration file

  • extracting the XML response from the envelope

  • logging requests/responses

  • raising an error when the response is not received within the timeout

The following features are not supported for Web Services that are called from Dynamic Logic:

  • Payload Validation: the payload of a message is not validated before sending it to the external web service. The payload that is returned is also not validated.

  • Availability Tracking: OHI Claims does not keep track of the availability of web services that are called from Dynamic Logic.

  • WS-Security: calling web services that require WS-Security features is not supported.

  • Asynchronous services: only synchronous web services are supported.

Endpoints

The endpoint used by ohiWebService are configured in the OHI Claims properties file. Since the call is synchronous the service uses one endpoint per Service. for Example: ServiceCategory.

ohi.ServiceCategory.endpoint.request

http://machine.domain:port/application_name/ServiceCategoryRequestService

This endpoint is used by ohiWebService for the service with the name "ServiceCategory"

// ohiWebService uses the value of the ohi.ServiceCategory.endpoint.request property in the OHI Claims properties file

def ws = new OhiWebService("ServiceCategory")

Error handling

No response

If no response is received within the timeout, ohiWebService will raise the following exception:

OHI-FL-WSCL-002: Could not invoke the external service <ServiceName>, exception is : <Exception Message>

If this exception is not handled by a try catch block then this will cause
  • a message to be written to the log

  • the processing of the claim to be stopped.

Invalid response

An invalid response is received then this will become apparent in the handling of the response, because in the object tree that corresponds with the structure of the response message, the presumed properties are not available.

Use cases

Calculate the Service Category for a claim line.

This example shows how the service category for one claim line can be determined externally.

Sample XML request:

<ServiceCategoryRequest version="4">
  <procedureCode>P100</procedureCode>
  <location>L42</location>
</ServiceCategoryRequest >

Sample XML response:

<ServiceCategoryResponse>
  <source>dft</source>
  <serviceCategory>SC56</serviceCategory>
</ServiceCategoryResponse>

Dynamic Logic:

Typically, this would be embedded in an End Benefits Derivation Rule at Claim Line Level

def ws = new OhiWebService("ServiceCategory")

def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.ServiceCategoryRequest(version:4){
  procedureCode( claimLine.procedure.code )
  location( claimLine.locationProvider.code )
}

def resp = ws.invoke(writer.toString())

def result = new XmlSlurper().parseText(resp)

Calculate the Service Category for all claim lines in a claim.

This example shows how the service category for all the claim lines in one claim can be determined externally in one call out. Note that each request in the message has an code that is used to correlate the response with the corresponding claim line.

Sample XML request:

<ServiceCategoryRequest version='4'>
  <requests>
    <request code='560'>
      <procedureCode>P100</procedureCode>
      <location>L40</location>
    </request>
    <request code='561'>
      <procedureCode>P101</procedureCode>
      <location>L41</location>
    </request>
  </requests>
</ServiceCategoryRequest >

Sample XML response:

<ServiceCategoryResponse>
  <responses>
    <response code='560'>
      <serviceCategory>SC56</serviceCategory>
    </response>
    <response code='561'>
      <serviceCategory>SC77</serviceCategory>
    </response>
  </responses>
</ServiceCategoryResponse>

Dynamic Logic:

Typically, this would also be embedded in an End Benefits Derivation Rule at Claim Level

def ws = new OhiWebService("ServiceCategory")

def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.ServiceCategoryRequest(version:4){
  requests {
    claim.claimLineList.each { claimLine ->
      request(code: claimLine.code) {
        procedureCode( claimLine.procedure.code )
        location( claimLine.locationProvider.code )
      }
    }
  }
}

def resp = ws.invoke(writer.toString())

def result = new XmlSlurper().parseText(resp)
// Update each claimLine by finding the corresponding response in the XML
claim.claimLineList.each { claimLine ->
  def response = result.responses.response.find{
    it.@code.text() == claimLine.code.toString() }
  assert response.size() == 1, 'No response found for ' + claimLine.code
  claimLine.serviceCategory = response.@serviceCategory.toString()
}

Distribute claim amount over claim lines

This example shows how the amount that another insurance company paid (dynamic field OICPaid at the claim level) is distributed over the claim lines.

Dynamic Logic:

Typically, this would be embedded in a Pre Benefits Derivation Rule at Claim Level.

Per claim line it allocates a share of the OIC paid at the claim level to the line until the OIC paid is out. To the lines that are allocated a part of the Claim level OIC paid the message with the code '1234' is attached.

if (claim.OICPaid > 0 {
def OICPaidLeft = claim.OICPaid

claim.claimLineList.sort{it.sequence}.each{claimLine ->
                 if (OICPaidLeft > 0)
                 {claimLine.OICPaid = Math.min(claimLine.allowedAmount.amount, OICPaidLeft);
                  OICPaidLeft = OICPaidLeft - claimLine.allowedAmount.amount
                  claimLine.addMessage('1234') // Add a message with the code '1234'
                 }
                 else
                 {claimLine.OICPaid = 0
                 }
               }
}