Composite requests

From a Cloud API perspective, a composite request is a set of requests that are executed in a single InsuranceSuite bundle (which corresponds to a single database transaction).

  • A composite request can include one or more subrequests that create or modify data. Either all of the subrequests succeed, or none of them are executed. Each subrequest is a separate unit of work.
  • A composite request can also include one or more subselections that query for data.

Subrequests and subresponses are executed serially, in the order they are specified in the composite request payload. PolicyCenter then gathers the response to each subrequest and subselection and returns them in a single response payload. The responses to each subrequest and subselection appear in the same order as the original composite request.

Composite requests can make use of variables. This allows data created by the execution of one subrequest to be used by later subrequests.

Composite requests are limited to a maximum of 25 subrequests. Composite requests with more than 25 subrequests and/or subselections fail with a BadInputException.

Constructing composite request calls

The /composite endpoint

To create a composite request, use the /composite endpoint in the Composite API. (This is different than batches. Every API has its own /batch endpoint. But in all of Cloud API, there is only one /composite endpoint, and it is in the Composite API.)

The syntax for the composite request call is:

POST <applicationURL>/rest/composite/v1/composite

Sections of a composite request

A composite request can have up to two sections:

  • A requests section, which contains the subrequests that commit data.
  • A selections section, which contains the subselections that query for data. These are executed after the subrequests, and only if all the subrequests commit data successfully.

At a high level, the syntax for these sections is as follows:

{
  "requests": [
    {
      <subrequest 1>
    },
    {
      <subrequest 2>
    },
    ...
  ],
  "selections": [
    {
      <subselection 1>
    },
    {
      <subselection 2>
    },
    ...
  ]
}

The requests section

In the requests section, the only supported operations are POST, PATCH, and DELETE. This includes both POSTs that create data and POSTs that execute business actions (such as POST /assign).

The basic syntax for the requests section is shown below.

{
  "requests": [
    {
      "method": "<post/patch/delete>",
      "uri": "<path>",
      "body": {
        "data": {
          "attributes": {
            "<field1>": "<value1>",
            "<field2>": "<value2>",
            ...
            }
        }
      }
    }, 
    {
      <next subrequest>
    },
    ...
    {
      <final subrequest>
    }
  ]
}

For example, the following simple composite request creates two notes for activity xc:202.

POST <applicationURL>/rest/composite/v1/composite

{
  "requests": [   
    {
      "method": "post",
      "uri": "/common/v1/activities/xc:202/notes",
      "body": {
        "data": {
          "attributes": {
            "body": "Cloud API note #1."
          }
        }
      }
    },    
    {
      "method": "post",
      "uri": "/common/v1/activities/xc:202/notes",
      "body": {
        "data": {
          "attributes": {
            "body": "Cloud API note #2."
          }
        }
      }
    }
  ]
}

For the complete syntax that includes all composite request features, see Complete composite request syntax.

Using variables to share information across subrequests

Information from one subrequest can be used in later subrequests. You can do this through the use of composite variables.

Declaring variables

Composite variables are declared in a subrequest's vars section. Each variable has a name and path. The name is an arbitrary string. The path specifies a value from the subrequest's response payload as a JSON path expression.

For example, suppose a subrequest that creates an activity has the following:

      "vars": [
        {
          "name": "newActivityId",
          "path": "$.data.attributes.id"
        }
      ]

This creates a variable named newActivityId, which is set to the value of the data section's attributes section's id field (which would typically be the ID of the newly created activity).

Referencing variables

To reference a variable, use the following syntax:

${<varName>}

You can use variables anywhere in the body of a subrequest. The most common uses for variable values are:

  • In an attributes field
  • Within the path of a uri
  • As part of a query parameter

For example, suppose there is a subrequest that creates an activity, and it is followed by a subrequest that creates a note. The first subrequest creates a newActivityId variable as shown previously. The uri for the second subrequest is:

"uri": "/common/v1/activities/${newActivityId}/notes"

This would create the new note as a child of the first subrequest's activity.

The following is the complete code for the previous examples.

{
  "requests": [
    {
      "method": "post",
      "uri": "/account/v1/accounts/pc:34/activities",
      "body": {
        "data": {
          "attributes": {
              "activityPattern": "contact_insured",
              "subject": "Cloud API activity"
          }
        }
      },
      "vars": [
        {
          "name": "newActivityId",
          "path": "$.data.attributes.id"
        }
      ]
    },    
    {
      "method": "post",
      "uri": "/common/v1/activities/${newActivityId}/notes",
      "body": {
        "data": {
          "attributes": {
            "body": "Cloud API note #1."
          }
        }
      }
    }
  ]
}

Responses to the subrequests

The response to a composite request contains a responses section. This section contains one subresponse for each subrequest. Every subresponse has three sections:

  • A body section, which by default contains the default response data defined in the corresponding endpoint.
  • A headers section, which contains any custom headers.
  • A status field, which indicates the subresponse's status code.

For example, the following is the responses section and the first subresponse for a composite request whose first subrequest created an activity:

"responses": [
  {
    "body": {
      "data": {
        "attributes": {
          "activityPattern": "contact_insured",
          "activityType": {
            "code": "general",
            "name": "General"
            },
          "assignedByUser": {
            "displayName": "Andy Applegate",
            "id": "demo_sample:1",
            "type": "User",
            "uri": "/admin/v1/users/demo_sample:1"
          },
          ...
        },
        "checksum": "0",
        "links": {
          "assign": {
            "href": "/common/v1/activities/cc:403/assign",
            "methods": [
              "post"
            ]
          },
          ...
        }
      }
    },
    "headers": {
      "GW-Checksum": "0",
      "Location": "/common/v1/activities/xc:403"
    },
    "status": 201
  },

Fields whose values are generated when data is committed

The individual subresponses to each subrequest specify data that has technically not been committed yet. However, some fields contain values that are not generated until the data is committed.

When a subresponse includes a value that is generated as part of the commit, Cloud API makes effort to match the data that will be committed as closely as possible. For example, the composite request reserves ID values so that these IDs can be provided in subresponses and committed to the database.

But, there are some fields for which Cloud API cannot match the value. For example, the values for createTime and updateTime cannot be determined prior to the commit. Fields of this type are always omitted from a subrequest's subresponse. But, they can be retrieved through a subselection.

Suppressing subresponse details

In some cases, a given object may be modified by multiple subrequests. This makes the intermediate subresponses unnecessary, and those subresponses can increase the size of the composite response unnecessarily and make the composite response harder to parse.

You can simplify the composite response by suppressing the amount of information returned for one or more subrequests. To do this, include the following with each relevant subrequest:

"includeResponse": false

For example:

{
  "requests": [   
    {
      "method": "post",
      "uri": "/common/v1/activities/xc:202/notes",
      "body": {
        "data": {
          "attributes": {
            "body": "Cloud API note #1."
          }
        }
      },
      "includeResponse": false
    },
    ...    

The composite response still includes a subresponse for the subrequest. But instead of providing the endpoint's default response, the subresponse appears as:

{
    "responseIncluded": false
},

The responseIncluded field defaults to true. If you want a detailed response for a given subrequest, simply omit the responseIncluded reference.

Specifying which fields to return

For a POST or PATCH subrequest, you can also refine which fields are returned. To do this, use the fields query parameter. The syntax for this is:

{
  "requests": [
    {
      "method": "<post/patch>",
      "uri": "<path>",
      "body": {
        "data": {
          "attributes": {
            "<field1>": "<value1>",
            "<field2>": "<value2>",
            ...
            }
        }
      },
      "parameters" : {
        "fields" : "<value>"
      }
    }, 
    ...
  ]
}

For example, the following code snippet creates an activity. For the subresponse, it specifies to include only the activity's ID and the assigned user.

{
  "requests": [
    {
      "method": "post",
      "uri": "/account/v1/accounts/pc:34/activities",
      "body": {
        "data": {
          "attributes": {
              "activityPattern": "contact_insured",
              "subject": "Cloud API activity"
          }
        }
      },
      "parameters" : {
        "fields" : "id,assignedUser"
      }
    },
  ...  

The selections section

The selections section contains subselections that query for data. These are executed after the subselections in the requests section, and only if all the subrequests commit data successfully.

The basic syntax for the selections section is shown below. You do not need to specify a method for each subselection, as the only valid method in the selections section is GET.

  "selections": [
    {
      "uri": "<pathForFirstSubselection>"
    },
    {
      "uri": "<pathForSecondSubselection>"
    },
    ....
  ]

For example, the following code creates a new activity and a note for that activity. It then queries for the newly created activity.

{
  "requests": [
    {
      "method": "post",
      "uri": "/account/v1/accounts/pc:34/activities",
      "body": {
        "data": {
          "attributes": {
              "activityPattern": "contact_insured",
              "subject": "Cloud API activity"
          }
        }
      },
      "vars": [
        {
          "name": "newActivityId",
          "path": "$.data.attributes.id"
        }
      ]
    },    
    {
      "method": "post",
      "uri": "/common/v1/activities/${newActivityId}/notes",
      "body": {
        "data": {
          "attributes": {
            "body": "Cloud API note #1."
          }
        }
      }
    }
  ],
  "selections": [
    {
      "uri": "/common/v1/activities/${newActivityId}
    }
  ]
  }

For the complete syntax that includes all composite request features, see Complete composite request syntax.

Using query parameters in the selections section

You can use certain query parameters for each subselection. This includes:

  • fields
  • filter
  • includeTotal
  • pageOffset
  • pageSize
  • sort

Each subselection is independent from the others. You can use different query parameters for each subselection, and you can have some subselections with query parameters and others without query parameters.

The syntax for adding query parameters to a subselection is as follows:

  "selections": [
    {
      "uri": "<pathForFirstQuery>",
      "parameters" : {
        "fields" : "<value>",
        "filter" : [<value>],
        "includeTotal" : <value>,
        "pageOffset" : <value>,
        "pageSize" : <value>,
        "sort" : [<value>]
      }
    },
    ....
  ]

Note the following:

  • fields is specified as a single string of one or more fields, delimited by commas. The entire string is surrounded by quotes.
    • For example, "assignedUser,dueDate,priority,subject"
  • filter and sort are stringified arrays consisting of one or more expressions. Each individual expression is surrounded by quotes. The list of expressions is then surrounded by [ and ].
    • For example: ["dueDate:gt:2022-12-20","status:in:open,complete"]
  • includeTotal , pageOffset, and pageSize are either Boolean or integer values, and therefore do not use quotes.

For example, when querying for activities, to return only the assigned user, due date, priority and subject fields:

    {
      "uri": "/common/v1/activities",
      "parameters" : {
        "fields" : "assignedUser,dueDate,priority,subject"
      }

To return only open and complete activities with due dates after 2022-12-20:

    {
      "uri": "/common/v1/activities",
      "parameters" : {
        "filter" : ["dueDate:gt:2022-12-20","status:in:open,complete"]      }

To return activities based on multiple criteria:

    {
      "uri": "/common/v1/activities",
      "parameters" : {
        "fields" : "assignedUser,dueDate,priority,subject",
        "filter" : ["dueDate:gt:2022-12-20","status:in:open,complete"],
        "includeTotal" : true,
        "pageSize" : 5,
        "sort" : ["dueDate"]
      }

Composite requests that execute only queries

You can create a composite request that does not create or modify data and instead only queries for data. To do this, create a composite request with only a selections section and no requests section. In this case, the GETs in the selections section are always executed.

Responses to the selections subrequests

When a composite request contains a selections section, the response also contains a selections section. This section has the same structure as the responses section. It contains one subresponse for each subselection. Every subresponse has three sections:

  • A body section, which by default contains the default response data defined in the corresponding endpoint.
  • A headers section, which contains any custom headers.
  • A status field, which indicates the subresponse's status code.

Error handling

If any of the subrequests in the requests section fail, Cloud API does the following:

  • Does not commit any of the data
  • Does not execute any GETs in the selections section
  • Returns a 400 error

Cloud API also returns a response.

  • The response begins with the following: "requestFailed": true. This is to make it easy to identify that the composite request did not commit data.
  • For the initial subrequests that did not fail (if any), the response is returned.
    • This is either the response as specified by the associated endpoint (and any query parameters), or the "responseIncluded": false text.
    • The standard response can be useful for debugging purposes, as you can see the state of objects that succeeded before the subrequest that failed.
  • For the subrequest that failed, the error message is returned.
  • For the subrequests after the failed subrequest, the text "skipped": true is returned.
  • If a selections section was included, the text "skipped": true is returned for each subselection.

For example, the following is the response for a composite request with five subrequests and a set of queries. All subrequests have responseIncluded set to false. The third subrequest failed because the dueDate attribute was incorrectly spelled as ueDate.

{
  "requestFailed": true,
  "responses": [
    {
      "responseIncluded": false
    },
    {
      "responseIncluded": false
    },
    {
      "requestError": {
        "details": [
          {
            "message": "Schema definition 'ext.common.v1.common_ext-
                1.0#/definitions/Note' does not define any property
                named 'ueDate'",
            "properties": {
              "lineNumber": 1,
              "parameterLocation": "body",
              "parameterName": "body"
            }
          }
        ],
        "developerMessage": "The request parameters or body had issues. 
           See the details elements for exact details of the problems.",
        "errorCode": "gw.api.rest.exceptions.BadInputException",
        "status": 400,
        "userMessage": "The request parameters or body had issues"
      },
      "status": 400
    },
    {
      "skipped": true
    },
    {
      "skipped": true
    }
  ],
  "selections": [
    {
      "skipped": true
    }
  ]
}

If there is an error in the selections section only, the subrequests in the requests section will execute, the data will be committed, and the composite request will return with a 200 response code, indicating success. Cloud API also attempts to execute each subselection as best as possible.

Composite request limitations

Composite requests have the following general limitations:

  • The number of subrequests and subselections in a single composite request must be 25 or less.
  • The subrequests can make use of other endpoints that are part of Cloud API. However, they cannot make use of endpoints outside of Cloud API, such as custom endpoints created by an insurer.
  • You cannot include a subrequest that uses a content type other than application/json.
    • For example, you cannot work with document resources in composite requests, as documents use multipart/form-data.
  • There is no mechanism for iterating over a set of things.
    • For example, you cannot start with a list of elements and include related resources for each item in that list.

There are also specific business requirements where you cannot use a composite request. For example:

  • The only types of jobs you can create in a composite request are Submissions.
  • You can modify other job types (such as Policy Change or Renewal) only if the job has already been created outside of the composite request.
  • You cannot create new job versions.
  • You cannot quote a job unless that job is a Submission created in the same Composite API request.
  • You cannot bind-only or bind-and-issue in a composite request.

Many of the examples in the previous list pertain to situations where there must be an intermediate data commit, which composite requests do not allow by design. However, the previous list is not intended to be exhaustive. Refer to the section of the documentation that discusses each business requirement for more information on requirements or limitations related to composite requests.

Complete composite request syntax

The following is the complete syntax for a composite request:

{
  "requests": [
    {
      "method": "<post/patch/delete>",
      "uri": "<path>",
      "body": {
        "data": {
          "attributes": {
            "<field1>": "<value1>",
            "<field2>": "<value2>",
            ...
            }
        }
      },
      "parameters" : {
        "fields" : "<value>"
      },
      "vars": [
        {
          "name": "<name>",
          "path": "<path-starting-with-$>"
        },
        <next variable>,
        ...
      ],
      "includeResponse": false
    }, 
    {
      <next subrequest>
    },
    ...
    {
      <final subrequest>
    }
  ],
  "selections": [
    {
      "uri": "<pathForFirstQuery>",
      "parameters" : {
        "fields" : "<value>",
        "filter" : [<value>],
        "includeTotal" : <value>,
        "pageOffset" : <value>,
        "pageSize" : <value>,
        "sort" : [<value>]
      }
    },
    {
      <next subselection>
    },
    ...
    {
      <final subselection>
    }
  ]
}