Composite API

Purpose

To perform a composite API call, consisting of one or up to five sub-requests.

Request Details

Request URL

{api-domain}/crm/{version}/__composite_requests

Header

Authorization: Zoho-oauthtoken d92d4xxxxxxxxxxxxx15f52

Scope

ZohoCRM.composite_requests.CUSTOM
(and) Scope of the subrequest

Sample Request

Copiedcurl "https://www.zohoapis.com/crm/v7/__composite_requests"
-X POST
-H "Authorization: Zoho-oauthtoken 1000.03xxxxxxxxxxxxxxxxxa5317.dxxxxxxxxxxxxxxxxxfa"
-d "@newdata.json"
Request JSON
  • rollback_on_failboolean, optional

    Represents whether to rollback the composite API request if any of the sub-requests fail. The default value is false.

  • parallel_executionboolean, optional

    Represents whether to process all independent sub-requests (sub-requests with no dependencies/references) in parallel. The default value is true. If "rollback_on_fail" is true, then the default value of this key is false.

  • __composite_requestsJSON array, mandatory

    The JSON array containing the details of the sub-requests.

    • sub_request_id (string, optional) - The request ID for the corresponding sub-request. The accepted regex is [a-zA-Z0-9][a-zA-Z0-9_]*.
    • uri (string, mandatory) - The request URL of the corresponding sub-request. The accepted regex is /crm(/.*)?/v[0-9]+([.][0-9]+)?/.*.
    • method (string, mandatory) - The request method of the corresponding sub-request. The possible methods are GET, POST, PUT, PATCH, and DELETE.
    • params (JSON Object, optional) - The parameters for the corresponding sub-request.
    • body (JSON object, optional) - The request body of the corresponding sub-request.
    • headers (JSON object, optional) - Headers for the corresponding sub-request. Note that you cannot use the headers "authorization" or "x-crm-org" in this key.

Note

  • rollback_on_fail and parallel_execution keys cannot both be true, simultaneously. For such a request, the system throws the AMBIGUITY_DURING_PROCESSING error.
  • All Delete APIs (except Notification APIs) are only supported with ID support in the URL. Bulk delete option is not supported.
  • When rollback_on_fail is false:
    • Workflows, approvals, and other automation actions are triggered as intended.
    • One API credit is consumed.
  • When rollback_on_fail is true:
    • Workflows:
      • The automation actions are executed only when all the sub-requests are served and a rollback is not performed. If one or more sub-requests fail and a rollback happens, the automation actions are not triggered.
      • For example, consider the case where a workflow is triggered every time a lead is created with the company name starting with 'S'. In sub-request 1, a lead is created, whose Company name starts with 'S', and sub-request 2 edits the newly created record's company name so that it does not start with 'S'. Only after executing both the sub-requests the workflow will be executed. But in this case, the workflow will not be triggered because the newly created Lead's company name is already updated in sub-request 2, and the criterion for the workflow is not met. Consider another case, where in sub-request 1, a lead is created, whose Company name is 'Silicon Solutions', and sub-request 2 edits this newly created record's website. If none of the other sub-requests change the company name of this newly created record, after executing all the sub-requests successfully, the workflow will be triggered.
    • When a rollback happens, one credit will be used for the composite API.
    • When all the sub-requests are successful, two credits will be used for the composite API.
  • Each composite API consumes five concurrency credits, irrespective of the number of sub-requests.
  • Each composite API reduces the sub-concurrency by one, while the sub-requests consume their respective sub-concurrencies.

Allowed APIs

The following table gives the list of APIs allowed in a composite request. For these APIs, there are restrictions on the number of records that you can create, update, or delete in a composite request.

APINo. of records allowed
Get Org, Get Metadata, Convert a Lead, Get Layout Rules, Get Validation Rules, Get Custom Links, Get/Add/Update Roles, Get Profiles, Get Records' Count, Get/Update Blueprint, Get Variables/Variable Groups, Get Tags, Add/Remove Tags for a record, Get List of Attachments, Delete Photo, Get Currencies, Get Shared Record Details, Get Assignment Rules, Get/Add/Update Pipeline, Get Wizards, Get List of From Addresses, Delete Notifications,No restriction
Add/Update/Delete Users, Update/Delink Related Records, Add/Delete Variables, Create/Update/Delete Notes, Create/Update/Merge Tags, Add/Update Currency, Add/Update/PATCH Notifications, Create/Update/Upsert/Delete Records,1
Get Territories, Get Notes, Get Email/Inventory Templates, Get Notifications, Get/Search Records, Get Related Records, Get records through a COQL query, Get Deleted Records, Get Users25

Sample Input

Copied{
    "rollback_on_fail": true,
    "parallel_execution" : false,
    "__composite_requests": [
        {
            "sub_request_id":"1",
            "method":"POST",
            "uri":"/crm/v6/Leads",
            "headers":{},
            "body":
            {
                "data":[
                    {
                        "Last_Name":"Boyle"
                    }
                ]
            }
        },
        {
            "sub_request_id":"2",
            "method":"PUT",
            "uri":"/crm/v6/Leads/@{1:$.data[0].details.id}",
            "body":{
                "data":[
                    {
                        "Company":"ABC"
                    }
                ]
            }
        },
        {
            "sub_request_id":"3",
            "method":"GET",
            "params":{
                "fields":"Last_Name,Company,Email",
                "per_page":"3",
                "page":"1"
            },
            "uri":"/crm/v6/Leads/@{2:$.data[0].details.id}"
        },
        {
            "sub_request_id":"4",
            "method":"GET",
            "params":{
                "fields":"Last_Name, Modified_Time"
            },
            "headers":{
                "If-Modified-Since":"2020-08-22T12:30:00+05:30"
            },
            "uri":"/crm/v6/Leads"
        },
        {
            "sub_request_id":"5",
            "method":"GET",
            "params":{
                "fields":"Last_Name, Modified_Time"
            },
            "uri":"/crm/v6/Leads/554023000001736019"
        }
    ]
}

Possible Errors

  • API_NOT_SUPPORTEDHTTP 400

    You have invoked the API from an unsupported version
    Resolution: You can only use Composite API from V3.

  • INVALID_DATAHTTP 400

    You have specified an invalid value for either rollback_on_fail or parallel_execution
    Resolution: The possible values for these keys are either "true" or "false".

  • INVALID_DATAHTTP 400

    You have not specified a JSON array for the "composite_requests" key.
    Resolution: Input a valid JSON array in the key.

  • MANDATORY_NOT_FOUNDHTTP 400

    You have not specified the mandatory key "__composite_requests", or given a null value.
    Resolution: Input a valid JSON array in this key.

  • AMBIGUITY_DURING_PROCESSINGHTTP 400

    "rollback_on_fail" and "parallel_execution" keys cannot be true in the same request.
    Resolution: Specify the value of either of the keys as false, according to your requirement.

  • INVALID_DATAHTTP 400

    You have used more than 5 sub-requests.
    Resolution: You can only use a maximum of 5 sub-requests for a composite API.

Sample Response

Copied{
  "__composite_requests": [
    {
      "code": "SUCCESS",
      "details": {
        "response": {
          "headers": {
            "X-ACCESSTOKEN-RESET": "2022-02-12T23:43:25-11:00",
            "Content-Disposition": "attachment; filename=response.json",
            "content-type": "application/json;charset=utf-8",
            "clientVersion": "4751295",
            "clientsubVersion": "a27707afc2e1d00d9d9f62aa55194b04"
          },
          "status_code": 201,
          "body": {
            "data": [
              {
                "code": "SUCCESS",
                "details": {
                  "Modified_Time": "2022-02-12T22:43:32-11:00",
                  "Modified_By": {
                    "name": "Patricia Boyle",
                    "id": "554023000000235011"
                  },
                  "Created_Time": "2022-02-12T22:43:32-11:00",
                  "id": "554023000002365002",
                  "Created_By": {
                    "name": "Patricia Boyle",
                    "id": "554023000000235011"
                  }
                },
                "message": "record added",
                "status": "success"
              }
            ]
          }
        }
      },
      "message": "composite sub request executed successfully",
      "status": "success"
    },
    {
      "code": "SUCCESS",
      "details": {
        "response": {
          "headers": {
            "X-ACCESSTOKEN-RESET": "2022-02-12T23:43:25-11:00",
            "Content-Disposition": "attachment; filename=response.json",
            "content-type": "application/json;charset=utf-8",
            "clientVersion": "4751295",
            "clientsubVersion": "a27707afc2e1d00d9d9f62aa55194b04"
          },
          "status_code": 200,
          "body": {
            "data": [
              {
                "code": "SUCCESS",
                "details": {
                  "Modified_Time": "2022-02-12T22:43:32-11:00",
                  "Modified_By": {
                    "name": "Patricia Boyle",
                    "id": "554023000000235011"
                  },
                  "Created_Time": "2022-02-12T22:43:32-11:00",
                  "id": "554023000002365002",
                  "Created_By": {
                    "name": "Patricia Boyle",
                    "id": "554023000000235011"
                  }
                },
                "message": "record updated",
                "status": "success"
              }
            ]
          }
        }
      },
      "message": "composite sub request executed successfully",
      "status": "success"
    },
    {
      "code": "SUCCESS",
      "details": {
        "response": {
          "headers": {
            "X-ACCESSTOKEN-RESET": "2022-02-12T23:43:25-11:00",
            "Content-Disposition": "attachment; filename=response.json",
            "content-type": "application/json;charset=utf-8",
            "clientVersion": "4751295",
            "clientsubVersion": "a27707afc2e1d00d9d9f62aa55194b04"
          },
          "status_code": 200,
          "body": {
            "data": [
              {
                "Company": "ABC",
                "Email": null,
                "Last_Name": "Boyle",
                "id": "554023000002365002",
                "$canvas_id": null
              }
            ]
          }
        }
      },
      "message": "composite sub request executed successfully",
      "status": "success"
    },
    {
      "code": "SUCCESS",
      "details": {
        "response": {
          "headers": {
            "X-ACCESSTOKEN-RESET": "2022-02-12T23:43:25-11:00",
            "Content-Disposition": "attachment; filename=response.json",
            "content-type": "application/json;charset=utf-8",
            "clientVersion": "4751295",
            "clientsubVersion": "a27707afc2e1d00d9d9f62aa55194b04"
          },
          "status_code": 200,
          "body": {
            "data": [
              {
                "Last_Name": "Boyle",
                "id": "554023000002365002"
              },
              {
                "Last_Name": "Max",
                "id": "554023000001736019"
              },
              {
                "Last_Name": "Deborah Grogan",
                "id": "554023000001054002"
              },
              {
                "Last_Name": "Leads test",
                "id": "554023000000948001"
              }
            ],
            "info": {
              "per_page": 25,
              "count": 4,
              "page": 1,
              "more_records": false
            }
          }
        }
      },
      "message": "composite sub request executed successfully",
      "status": "success"
    },
    {
      "code": "SUCCESS",
      "details": {
        "response": {
          "headers": {
            "X-ACCESSTOKEN-RESET": "2022-02-12T23:43:25-11:00",
            "Content-Disposition": "attachment; filename=response.json",
            "content-type": "application/json;charset=utf-8",
            "clientVersion": "4751295",
            "clientsubVersion": "a27707afc2e1d00d9d9f62aa55194b04"
          },
          "status_code": 200,
          "body": {
            "data": [
              {
                "Last_Name": "Max",
                "id": "554023000001736019",
                "$canvas_id": null
              }
            ]
          }
        }
      },
      "message": "composite sub request executed successfully",
      "status": "success"
    }
  ]
}

Possible Errors For the sub-requests

  • MANDATORY_NOT_FOUND

    You have not specified the mandatory key in one of the sub-requests, the data type is not consistent with the field, or not compliant with the regex.
    Resolution: Input all manadatory values for every sub-request following the regex. Make sure the data type of the input matches with the field's.

  • INVALID_DATA

    You have specified an invalid sub_request_id
    Resolution: Specify a valid sub_request_id. The accepted regex is [a-zA-Z0-9][a-zA-Z0-9_]*.

  • INVALID_DATA

    You have specify an invalid value for one of the keys
    Resolution: Specify valid values for all the keys. Refer to the sample input section for more details.

  • DUPLICATE_DATA

    You have specified the same sub_request_id for more than one sub_request
    Resolution: Specify unique sub_request_id for all sub-requests.

  • NOT_ALLOWED

    You have specified a version higher than that of the composite API's for one or more sub-requests in their URLs
    Resolution: Specify a version equal to or less than that of the composite API for all the sub-requests.

  • NOT_SUPPORTED

    You have tried to invoke an API that is not supported by composite API
    Resolution: Refer to the supported APIs section above for the list of allowed APIs in a composite request.

  • LIMIT_EXCEEDED

    You have exceeded the limit in the sub-request
    Resolution: There are restrictions on the number of records that you can create, update, or delete in a composite request. Refer to the "Allowed APIs" section for the limits applicable to each API.

  • INVALID_REFERENCE

    You have given an improper reference in the request body
    Resolution: Refer to the notes section and sample input section for details on how to give a proper reference.

  • INVALID_REFERENCE

    The response used in the given reference does not have a proper value
    Resolution: Specify a valid reference.

  • INVALID_REFERENCE

    You have given a reference which is yet to be executed
    Resolution: This error is thrown for sequential execution ("parallel_execution" is false). Arrange the sub-requests so that the reference can be given properly.

  • INVALID_REFERENCE

    The given reference refers to the same sub-request as it is referred to in
    Resolution: Give a reference from another sub-request.

  • INVALID_REFERENCE

    The sub-request referred to in the reference has failed
    Resolution: This error is thrown for parallel execution ("parallel_execution" is true). You can only use successful sub-requests as references.

  • LOOPING_FOUND

    The sub-requests are referring to each other
    Resolution: This error is thrown for parallel execution ("parallel_execution" is true). Sub-requests cannot have references to each other. Specify the references such that there is no looping.

  • ROLLBACK_PERFORMED

    The sub-request was rolled back because another sub-request in the composite API has failed
    Resolution: This error is thrown only when "rollback_on_fail" is true. Make the necessary corrections so that the failed API can be successful.

  • PROCESSING_STOPPED

    The sub-request was rolled back because another sub-request in the composite API has failed
    Resolution: This error is thrown only when "rollback_on_fail" is true and there were unexecuted sub-requests when the transaction was rolled back. Make the necessary corrections so that the failed API can be successful.

  • REQUEST_TIMEOUT

    The execution time taken to complete all the sub-requests in parallel has exceeded the default timeout period of five minutes
    Resolution: Check the network for connectivity issues, or other reasons for execution time exceeding five minutes. The already triggered sub-request may be successfully executed in the back-end, even if this error is populated.

  • REQUEST_TIMEOUT

    This error is thrown when connection time-out occurs during NIO flow and CRM is yet to receive the request
    Resolution: This error is thrown for parallel execution ("parallel_execution" is true). Check the network for connectivity issues.

  • INTERNAL_ERROR

    Internal server error
    Resolution: Contact your system administrator or the support team.

Status Codes for a Composite Request

The following are the status codes in the response of a composite request.

  • When "rollback_on_fail" is true:
    • Status code is 400, when a rollback is performed (one or all the sub-requests failed).
    • Status code is 200, when a rollback is not performed.
  • When "rollback_on_fail" is false:
    • Status code is 400, when none of the sub-requests is triggered.
    • Status code is 207, when a few of the sub-requests are triggered.
    • Status code is 200, when all the sub-requests are triggered. Note that a composite request is considered a success when all the sub-requests are triggered, irrespective of the success or failure of each of these sub-requests.

Sample input when rollback_on_fail is "true"

Copied{
  "rollback_on_fail": true,
  "parallel_execution": false,
  "__composite_requests": [
    {
      "sub_request_id": "1",
      "method": "POST",
      "uri": "/crm/v6/Leads",
      "headers": {},
      "body": {
        "data": [
          {
            "Last_Name": "Sam"
          }
        ]
      }
    },
    {
      "sub_request_id": "2",
      "params": {
        "fields": "Last_Name,Company,Email",
        "per_page": "3",
        "page": "1",
        "ids": "@{1:$.data[0].details.id}"
      },
      "method": "GET",
      "uri": "/crm/v6/Leads"
    },
    {
      "sub_request_id": "3",
      "method": "GET",
      "uri": "/crm/v6/Leads",
      "params": {
        "fields": "Last_Name,Company,Email",
        "page": 1
      },
      "body": {
        "data": [
          {
            "Last_Name": "Sam"
          }
        ]
      }
    },
    {
      "sub_request_id": "4",
      "method": "POST",
      "body": {
        "select_query": "select Last_Name from Leads where Last_Name is not null limit 26"
      },
      "uri": "/crm/v6/coql"
    },
    {
      "sub_request_id": "5",
      "method": "GET",
      "params": {
        "fields": "Last_Name,Company,Email"
      },
      "uri": "crm/v6/Leads"
    }
  ]
}

Sample Response for a rollback

Copied{
  "__composite_requests": [
    {
      "code": "ROLLBACK_PERFORMED",
      "details": {
        "rollbacked_by_sub_request_index": 3
      },
      "message": "the transaction was rolled back since another sub request in the same transaction failed.",
      "status": "error"
    },
    {
      "code": "ROLLBACK_PERFORMED",
      "details": {
        "rollbacked_by_sub_request_index": 3
      },
      "message": "the transaction was rolled back since another sub request in the same transaction failed.",
      "status": "error"
    },
    {
      "code": "ROLLBACK_PERFORMED",
      "details": {
        "rollbacked_by_sub_request_index": 3
      },
      "message": "the transaction was rolled back since another sub request in the same transaction failed.",
      "status": "error"
    },
    {
      "code": "SUCCESS",
      "details": {
        "response": {
          "headers": {
            "X-ACCESSTOKEN-RESET": "2022-02-02T11:43:59+05:30",
            "X-Content-Type-Options": "nosniff",
            "Content-Disposition": "attachment; filename=response.json",
            "X-Download-Options": "noopen",
            "content-type": "application/json;charset=utf-8",
            "clientVersion": "4725537",
            "clientsubVersion": "1fbb2cd4aec39c18e65a4eebcff5a8cc"
          },
          "status_code": 400,
          "body": {
            "code": "LIMIT_EXCEEDED",
            "details": {
              "by": "limit",
              "limit": 25
            },
            "message": "limit exceeded",
            "status": "error"
          }
        }
      },
      "message": "composite sub request executed successfully",
      "status": "success"
    },
    {
      "code": "PROCESSING_STOPPED",
      "details": {
        "rollbacked_by_sub_request_index": 3
      },
      "message": "the transaction was rolled back since another sub request in the same transaction failed.",
      "status": "error"
    }
  ]
}