SFDC Stop - Always the latest about Salesforce


Full Tutorial Series with videos, free apps, live sessions, salesforce consulting and much more.


Telegram logo   Join our Telegram Channel

Tuesday, 18 October 2022

Getting started with Composite API in Salesforce

Hello Trailblazers,


In this post we're going to learn about Composite Resources in salesforce. Composite resources can improve your application's performance by minimizing round trips between client and salesforce (server). Let's try to learn by an example.


Get account details with related contacts and opportunities from Salesforce

Let's consider this small requirement: You need to sync account records along with the related contacts and opportunities from Salesforce to an external system. Let's say you're syncing one account at a time using the account's id. You may think of a solution which consist of 3 steps:

  1. Get the account record using the account id by making a callout to standard Rest API
  2. Get the contact records related to the account record by making another callout to the standard Rest API
  3. Get the opportunity records related to the account record by making another callout to the standard Rest API

Therefore, you got your account record along with the related contacts and opportunities in 3 Rest API calls to salesforce. There can be other alternative approaches as well depending upon the use case and requirement. The goal here is to know about what's provided to us out of the box by salesforce and how can we leverage it.

Composite API in Action - Get Records

What if I tell you: You can fetch account, related contacts and related opportunities as well, all in a single API callout without writing any custom code? Yes that's possible using the Composite API. Let's see how:

I am not covering the client Authentication part (how to get access token from salesforce) in this blog as we've a full blog dedicated to it: How to connect to Salesforce with Postman?

Composite API can execute a series of REST API requests (like: the 3 requests for our use case, which we're planning to execute one by one) in a single POST request. It can also retrieve a list of other composite resources with a GET request.

You can send multiple REST API requests together using composite api and the thing to note here is: The output of one request can be used as the input to a subsequent request. All requests in a composite call are called subrequests and all subrequests are executed in the context of the same user who's calling the API. The good thing here is: All the requests mentioned in the Composite API are sent together and they count as a single call towards your API limits.

Let's solve our use case now. We need to get Account record along with it's related Contacts and Opportunities and we only have the account id that we can pass to the request.

This is my account record in salesforce along with the related records:


As you can see above, the account's name is Sample Account and under that we have 2 Contact records: Sample Contact 1 and Sample Contact 2. Similarly, we have 2 Opportunity records as well: Sample Opportunity 1 and Sample Opportunity 2. We're going to fetch this whole information using composite API. Let's have a look at the request below:
{
    "compositeRequest": [
        {
            "method": "GET",
            "url": "/services/data/v55.0/sobjects/Account/0016D00000fHjSLQA0",
            "referenceId": "refAccount"
        },
        {
            "method": "GET",
            "url": "/services/data/v55.0/sobjects/Account/@{refAccount.Id}/Contacts",
            "referenceId": "refContacts"
        },
        {
            "method": "GET",
            "url": "/services/data/v55.0/sobjects/Account/@{refAccount.Id}/Opportunities",
            "referenceId": "refOpportunities"
        }
    ]
}
As you can see above, under the compositeRequest array, I've specified various requests that I want to make to salesforce. Below are the 3 requests I've specified:

  1. Get Account record using record id: Here, I've specified record id of the account record in the request.

  2. Get Contacts related to the current account: Here, you can also specify the account id itself in the request body but I've referenced the response of previous request and used it as an input here. Our previous request will return the account record in the request body which I'm referring as refAccount. That record will have an Id key in the response body with the value as record id of the account. So, I've specified @{refAccount.Id} in the URL of the second request where we're fetching the Contacts related to the account. We've specified refContacts as the reference id for this API response.

  3. Get Opportunities related to the current account: This is exact similar to the 2nd request, the only difference is - instead of getting contacts, here we're getting the Opportunities related to the current account record. The reference id for this request's response is refOpportunities.

This is how my request looks like in postman:

You can make a POST request and the URL for your API will be in this format: https://<my-domain-name>.my.salesforce.com/services/data/vXX.X/composite

Remember to add the Authorization header to the request with Bearer<space><access token> as the value, that we got while doing authorization. Let's have a look at the Authorization header below:

As you click on Send you'll get the response similar to what's shown below:

I am also sharing the full response below for your reference:
{
    "compositeResponse": [
        {
            "body": {
                "attributes": {
                    "type": "Account",
                    "url": "/services/data/v55.0/sobjects/Account/0016D00000fHjSLQA0"
                },
                "Id": "0016D00000fHjSLQA0",
                "IsDeleted": false,
                "MasterRecordId": null,
                "Name": "Sample Account",
                "Type": null,
                "ParentId": null,
                "BillingStreet": null,
                "BillingCity": null,
                "BillingState": null,
                "BillingPostalCode": null,
                "BillingCountry": null,
                "BillingLatitude": null,
                "BillingLongitude": null,
                "BillingGeocodeAccuracy": null,
                "BillingAddress": null,
                "ShippingStreet": null,
                "ShippingCity": null,
                "ShippingState": null,
                "ShippingPostalCode": null,
                "ShippingCountry": null,
                "ShippingLatitude": null,
                "ShippingLongitude": null,
                "ShippingGeocodeAccuracy": null,
                "ShippingAddress": null,
                "Phone": null,
                "Fax": null,
                "AccountNumber": null,
                "Website": null,
                "PhotoUrl": "/services/images/photo/0016D00000fHjSLQA0",
                "Sic": null,
                "Industry": null,
                "AnnualRevenue": null,
                "NumberOfEmployees": null,
                "Ownership": null,
                "TickerSymbol": null,
                "Description": null,
                "Rating": null,
                "Site": null,
                "OwnerId": "0056D000005v9kXQAQ",
                "CreatedDate": "2022-10-15T08:29:16.000+0000",
                "CreatedById": "0056D000005v9kXQAQ",
                "LastModifiedDate": "2022-10-15T08:29:16.000+0000",
                "LastModifiedById": "0056D000005v9kXQAQ",
                "SystemModstamp": "2022-10-15T08:29:16.000+0000",
                "LastActivityDate": null,
                "LastViewedDate": "2022-10-16T07:55:17.000+0000",
                "LastReferencedDate": "2022-10-16T07:55:17.000+0000",
                "Jigsaw": null,
                "JigsawCompanyId": null,
                "CleanStatus": "Pending",
                "AccountSource": null,
                "DunsNumber": null,
                "Tradestyle": null,
                "NaicsCode": null,
                "NaicsDesc": null,
                "YearStarted": null,
                "SicDesc": null,
                "DandbCompanyId": null,
                "OperatingHoursId": null
            },
            "httpHeaders": {
                "ETag": "\"gTI7lF2oYMlDxM+gTW1a62nzqxfxxihRqAygUNh9DPs=\"",
                "Last-Modified": "Sat, 15 Oct 2022 08:29:16 GMT"
            },
            "httpStatusCode": 200,
            "referenceId": "refAccount"
        },
        {
            "body": {
                "totalSize": 2,
                "done": true,
                "records": [
                    {
                        "attributes": {
                            "type": "Contact",
                            "url": "/services/data/v55.0/sobjects/Contact/0036D00000UAXTNQA5"
                        },
                        "Id": "0036D00000UAXTNQA5",
                        "IsDeleted": false,
                        "MasterRecordId": null,
                        "AccountId": "0016D00000fHjSLQA0",
                        "LastName": "Contact 1",
                        "FirstName": "Sample",
                        "Salutation": "Mr.",
                        "Name": "Sample Contact 1",
                        "OtherStreet": null,
                        "OtherCity": null,
                        "OtherState": null,
                        "OtherPostalCode": null,
                        "OtherCountry": null,
                        "OtherLatitude": null,
                        "OtherLongitude": null,
                        "OtherGeocodeAccuracy": null,
                        "OtherAddress": null,
                        "MailingStreet": null,
                        "MailingCity": null,
                        "MailingState": null,
                        "MailingPostalCode": null,
                        "MailingCountry": null,
                        "MailingLatitude": null,
                        "MailingLongitude": null,
                        "MailingGeocodeAccuracy": null,
                        "MailingAddress": null,
                        "Phone": null,
                        "Fax": null,
                        "MobilePhone": null,
                        "HomePhone": null,
                        "OtherPhone": null,
                        "AssistantPhone": null,
                        "ReportsToId": null,
                        "Email": null,
                        "Title": null,
                        "Department": null,
                        "AssistantName": null,
                        "LeadSource": null,
                        "Birthdate": null,
                        "Description": null,
                        "OwnerId": "0056D000005v9kXQAQ",
                        "CreatedDate": "2022-10-15T08:29:16.000+0000",
                        "CreatedById": "0056D000005v9kXQAQ",
                        "LastModifiedDate": "2022-10-16T07:46:44.000+0000",
                        "LastModifiedById": "0056D000005v9kXQAQ",
                        "SystemModstamp": "2022-10-16T07:46:44.000+0000",
                        "LastActivityDate": null,
                        "LastCURequestDate": null,
                        "LastCUUpdateDate": null,
                        "LastViewedDate": "2022-10-16T07:46:44.000+0000",
                        "LastReferencedDate": "2022-10-16T07:46:44.000+0000",
                        "EmailBouncedReason": null,
                        "EmailBouncedDate": null,
                        "IsEmailBounced": false,
                        "PhotoUrl": "/services/images/photo/0036D00000UAXTNQA5",
                        "Jigsaw": null,
                        "JigsawContactId": null,
                        "CleanStatus": "Pending",
                        "IndividualId": null
                    },
                    {
                        "attributes": {
                            "type": "Contact",
                            "url": "/services/data/v55.0/sobjects/Contact/0036D00000ULNcUQAX"
                        },
                        "Id": "0036D00000ULNcUQAX",
                        "IsDeleted": false,
                        "MasterRecordId": null,
                        "AccountId": "0016D00000fHjSLQA0",
                        "LastName": "Contact 2",
                        "FirstName": "Sample",
                        "Salutation": "Ms.",
                        "Name": "Sample Contact 2",
                        "OtherStreet": null,
                        "OtherCity": null,
                        "OtherState": null,
                        "OtherPostalCode": null,
                        "OtherCountry": null,
                        "OtherLatitude": null,
                        "OtherLongitude": null,
                        "OtherGeocodeAccuracy": null,
                        "OtherAddress": null,
                        "MailingStreet": null,
                        "MailingCity": null,
                        "MailingState": null,
                        "MailingPostalCode": null,
                        "MailingCountry": null,
                        "MailingLatitude": null,
                        "MailingLongitude": null,
                        "MailingGeocodeAccuracy": null,
                        "MailingAddress": null,
                        "Phone": null,
                        "Fax": null,
                        "MobilePhone": null,
                        "HomePhone": null,
                        "OtherPhone": null,
                        "AssistantPhone": null,
                        "ReportsToId": null,
                        "Email": null,
                        "Title": null,
                        "Department": null,
                        "AssistantName": null,
                        "LeadSource": null,
                        "Birthdate": null,
                        "Description": null,
                        "OwnerId": "0056D000005v9kXQAQ",
                        "CreatedDate": "2022-10-16T07:46:25.000+0000",
                        "CreatedById": "0056D000005v9kXQAQ",
                        "LastModifiedDate": "2022-10-16T07:46:25.000+0000",
                        "LastModifiedById": "0056D000005v9kXQAQ",
                        "SystemModstamp": "2022-10-16T07:46:25.000+0000",
                        "LastActivityDate": null,
                        "LastCURequestDate": null,
                        "LastCUUpdateDate": null,
                        "LastViewedDate": "2022-10-16T07:46:26.000+0000",
                        "LastReferencedDate": "2022-10-16T07:46:26.000+0000",
                        "EmailBouncedReason": null,
                        "EmailBouncedDate": null,
                        "IsEmailBounced": false,
                        "PhotoUrl": "/services/images/photo/0036D00000ULNcUQAX",
                        "Jigsaw": null,
                        "JigsawContactId": null,
                        "CleanStatus": "Pending",
                        "IndividualId": null
                    }
                ]
            },
            "httpHeaders": {},
            "httpStatusCode": 200,
            "referenceId": "refContacts"
        },
        {
            "body": {
                "totalSize": 2,
                "done": true,
                "records": [
                    {
                        "attributes": {
                            "type": "Opportunity",
                            "url": "/services/data/v55.0/sobjects/Opportunity/0066D000005z3tpQAA"
                        },
                        "Id": "0066D000005z3tpQAA",
                        "IsDeleted": false,
                        "AccountId": "0016D00000fHjSLQA0",
                        "IsPrivate": false,
                        "Name": "Sample Opportunity 1",
                        "Description": null,
                        "StageName": "Prospecting",
                        "Amount": null,
                        "Probability": 10.0,
                        "ExpectedRevenue": null,
                        "TotalOpportunityQuantity": null,
                        "CloseDate": "2022-10-17",
                        "Type": null,
                        "NextStep": null,
                        "LeadSource": null,
                        "IsClosed": false,
                        "IsWon": false,
                        "ForecastCategory": "Pipeline",
                        "ForecastCategoryName": "Pipeline",
                        "CampaignId": null,
                        "HasOpportunityLineItem": false,
                        "Pricebook2Id": null,
                        "OwnerId": "0056D000005v9kXQAQ",
                        "CreatedDate": "2022-10-16T07:29:30.000+0000",
                        "CreatedById": "0056D000005v9kXQAQ",
                        "LastModifiedDate": "2022-10-16T07:47:09.000+0000",
                        "LastModifiedById": "0056D000005v9kXQAQ",
                        "SystemModstamp": "2022-10-16T07:47:09.000+0000",
                        "LastActivityDate": null,
                        "PushCount": 0,
                        "LastStageChangeDate": null,
                        "FiscalQuarter": 4,
                        "FiscalYear": 2022,
                        "Fiscal": "2022 4",
                        "ContactId": null,
                        "LastViewedDate": "2022-10-16T07:47:09.000+0000",
                        "LastReferencedDate": "2022-10-16T07:47:09.000+0000",
                        "HasOpenActivity": false,
                        "HasOverdueTask": false,
                        "LastAmountChangedHistoryId": null,
                        "LastCloseDateChangedHistoryId": null
                    },
                    {
                        "attributes": {
                            "type": "Opportunity",
                            "url": "/services/data/v55.0/sobjects/Opportunity/0066D000005z3wPQAQ"
                        },
                        "Id": "0066D000005z3wPQAQ",
                        "IsDeleted": false,
                        "AccountId": "0016D00000fHjSLQA0",
                        "IsPrivate": false,
                        "Name": "Sample Opportunity 2",
                        "Description": null,
                        "StageName": "Prospecting",
                        "Amount": null,
                        "Probability": 10.0,
                        "ExpectedRevenue": null,
                        "TotalOpportunityQuantity": null,
                        "CloseDate": "2022-10-17",
                        "Type": null,
                        "NextStep": null,
                        "LeadSource": null,
                        "IsClosed": false,
                        "IsWon": false,
                        "ForecastCategory": "Pipeline",
                        "ForecastCategoryName": "Pipeline",
                        "CampaignId": null,
                        "HasOpportunityLineItem": false,
                        "Pricebook2Id": null,
                        "OwnerId": "0056D000005v9kXQAQ",
                        "CreatedDate": "2022-10-16T07:47:01.000+0000",
                        "CreatedById": "0056D000005v9kXQAQ",
                        "LastModifiedDate": "2022-10-16T07:47:01.000+0000",
                        "LastModifiedById": "0056D000005v9kXQAQ",
                        "SystemModstamp": "2022-10-16T07:47:01.000+0000",
                        "LastActivityDate": null,
                        "PushCount": 0,
                        "LastStageChangeDate": null,
                        "FiscalQuarter": 4,
                        "FiscalYear": 2022,
                        "Fiscal": "2022 4",
                        "ContactId": null,
                        "LastViewedDate": "2022-10-16T07:47:01.000+0000",
                        "LastReferencedDate": "2022-10-16T07:47:01.000+0000",
                        "HasOpenActivity": false,
                        "HasOverdueTask": false,
                        "LastAmountChangedHistoryId": null,
                        "LastCloseDateChangedHistoryId": null
                    }
                ]
            },
            "httpHeaders": {},
            "httpStatusCode": 200,
            "referenceId": "refOpportunities"
        }
    ]
}
Notice that in the compositeResponse array, we have 3 objects:

The first object is the response from callout to the URL mentioned in the first object of compositeRequest array, in our request body, which is returning the account record. This API response has a body attribute which corresponds to our account record referred by refAccount as shown below:
The second object is the response from URL mentioned in the second object of our compositeRequest array, in our request body, which is returning the contact records related to this account. As you can see below, there are 2 contact records linked to the current account:

We're referring to the body of this response using refContacts. Similarly, the third object is the response from the URL mentioned in the third object of our compositeRequest array, in our request body, which is returning the opportunities related to this account. You can see a similar response as above for opportunities below:
We're referring to the body of this response as refOpportunities.

So, this is how you can make a simple callout to Composite API to get your account, it's related contacts as well as it's related opportunities in one go!

Till now, we talked about how we can get the parent record and it's child records together using composite resources. You may ask: What if I have a lookup field and I want to get that related record details as well? - You can easily do that using composite API!

For example: Le'ts say I want to get the account owner's information as well i.e. the user record related to the account using composite API while I am fetching the account record. To do that, I can just add the below object to my compositeRequest array in the request body:
{
    "method": "GET",
    "url": "/services/data/v55.0/sobjects/User/@{refAccount.OwnerId}",
    "referenceId": "refUser"
}
As you can see above, I am referring to the OwnerId of my account record using @{refAccount.OwnerId} and I am getting the details from the User object using this OwnerId. The overall request is shown below:
I am referring to the body of user API output i.e. the user record as refUser. In the response also, we'll get the user details at the end as shown below:
So, all you need to understand here is: How to refer the previous response variables in the subsequent requests to fetch related records? Once you understood this, you can easily use composite API for your use case. You can easily reference the previous API response body using the reference id you have specified in the request.

Here we had 4 subrequests in a single composite API callout. We can have a maximum of 25 subrequests in a single call. Out of these 25, 5 requests can be related to query operations or sObject collections.

Creating Records using Composite API

Before marking this blog post as complete. I would like to show you one example of creating related records using composite API. This time we're going to create one account record, two opportunities and two contacts related to it using composite api. Let's have a look at the request body below:
{
    "compositeRequest": [
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Account",
            "referenceId": "refAccount",
            "body": {
                "Name": "My Sample Account"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Contact",
            "referenceId": "refContact1",
            "body": {
                "FirstName": "My Sample",
                "LastName": "Contact 1",
                "AccountId": "@{refAccount.id}"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Contact",
            "referenceId": "refContact2",
            "body": {
                "FirstName": "My Sample",
                "LastName": "Contact 2",
                "AccountId": "@{refAccount.id}"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Opportunity",
            "referenceId": "refOpportunity1",
            "body": {
                "Name": "My Sample Opportunity 1",
                "AccountId": "@{refAccount.id}",
                "ContactId": "@{refContact1.id}",
                "StageName": "Prospecting",
                "CloseDate": "2022-10-20"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Opportunity",
            "referenceId": "refOpportunity2",
            "body": {
                "Name": "My Sample Opportunity 2",
                "AccountId": "@{refAccount.id}",
                "ContactId": "@{refContact2.id}",
                "StageName": "Qualification",
                "CloseDate": "2022-10-20"
            }
        }
    ]
}
As you can see above, this time, the method for each request mentioned in the compositeRequest array is POST because we're creating records here. We also have a body in each request with the data we want to store in the record we're creating. First of all we created an account record with the name My Sample Account referred by refAccount, then we created two contact records: My Sample Contact 1 referred by refContact1 and My Sample Contact 2 referred by refContact2 under that account by mentioning the AccountId as @{refAccount.id}

Finally, we created two opportunity records as well named: My Sample Opportunity 1 referred by refOpportunity1 and My Sample Opportunity 2 referred by refOpportunity2. Notice that both the opportunity records are linked to the same account using: @{refAccount.id} as the AccountId but different contacts as the first one is referring to @{refContact1.id} as the ContactId, however the second one is using @{refContact2.id} as the ContactId. Value for StageName is also different for both the opportunity records.

Below is the response for this API callout:
{
    "compositeResponse": [
        {
            "body": {
                "id": "0016D00000fSq4HQAS",
                "success": true,
                "errors": []
            },
            "httpHeaders": {
                "Location": "/services/data/v55.0/sobjects/Account/0016D00000fSq4HQAS"
            },
            "httpStatusCode": 201,
            "referenceId": "refAccount"
        },
        {
            "body": {
                "id": "0036D00000ULNmoQAH",
                "success": true,
                "errors": []
            },
            "httpHeaders": {
                "Location": "/services/data/v55.0/sobjects/Contact/0036D00000ULNmoQAH"
            },
            "httpStatusCode": 201,
            "referenceId": "refContact1"
        },
        {
            "body": {
                "id": "0036D00000ULNmtQAH",
                "success": true,
                "errors": []
            },
            "httpHeaders": {
                "Location": "/services/data/v55.0/sobjects/Contact/0036D00000ULNmtQAH"
            },
            "httpStatusCode": 201,
            "referenceId": "refContact2"
        },
        {
            "body": {
                "id": "0066D000005z4LPQAY",
                "success": true,
                "errors": []
            },
            "httpHeaders": {
                "Location": "/services/data/v55.0/sobjects/Opportunity/0066D000005z4LPQAY"
            },
            "httpStatusCode": 201,
            "referenceId": "refOpportunity1"
        },
        {
            "body": {
                "id": "0066D000005z4LQQAY",
                "success": true,
                "errors": []
            },
            "httpHeaders": {
                "Location": "/services/data/v55.0/sobjects/Opportunity/0066D000005z4LQQAY"
            },
            "httpStatusCode": 201,
            "referenceId": "refOpportunity2"
        }
    ]
}
Let's verify our results in our salesforce org as well!

We have an account record named My Sample Account with two contacts and two opportunities linked to it as shown below:
Notice the contact and opportunity names, the stage and close date of the opportunity records, they're the exact same as we mentioned in the request. You can open the opportunities and verify that both My Sample Opportunity 1 and My Sample Opportunity 2 are linked with My Sample Contact 1 and My Sample Contact 2 respectively as shown below:


That's how we can Create Related Records using Composite API. Till now, we're making a POST request to the composite API. There are some other composite resources as well, that we can use and we can get a list of those by making a GET request to the composite API as shown below:
I think the information we learned today should be enough for you to get started with composite API. If you want to learn about other resources that are mentioned in the response above, let me know and I'll create new blog post(s) for the same.

Bonus Content: All or None

Thank you for reading and reaching to the end of the tutorial (almost). I hope you learned something new! Before we close this post, I want to talk about one most important flag in the composite request - the allOrNone flag. If we mark this flag as true, the whole composite request will rollback if any of the subsequent API request fail. So you can now make sure that you create all the records with correct relationships or none of them.

Considering our above example of Account, Contacts and Opportunities creation, let's mark the allOrNone flag as true in the request body as shown below:
Notice that I removed the StageName value from the second opportunity record and it's present in the first opportunity record (highlighted above). As StageName is a required field, the opportunity record creation will fail and it should rollback the whole request as we've specified the allOrNone parameter as true. Just to make it more clear, allOrNone parameter is not a part of the compositeRequest array, it's a separate key in the request body as shown below:
I am also sharing the whole request body below for your reference:
{
    "compositeRequest": [
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Account",
            "referenceId": "refAccount",
            "body": {
                "Name": "My Sample Account"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Contact",
            "referenceId": "refContact1",
            "body": {
                "FirstName": "My Sample",
                "LastName": "Contact 1",
                "AccountId": "@{refAccount.id}"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Contact",
            "referenceId": "refContact2",
            "body": {
                "FirstName": "My Sample",
                "LastName": "Contact 2",
                "AccountId": "@{refAccount.id}"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Opportunity",
            "referenceId": "refOpportunity1",
            "body": {
                "Name": "My Sample Opportunity 1",
                "AccountId": "@{refAccount.id}",
                "ContactId": "@{refContact1.id}",
                "StageName": "Prospecting",
                "CloseDate": "2022-10-20"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v55.0/sobjects/Opportunity",
            "referenceId": "refOpportunity2",
            "body": {
                "Name": "My Sample Opportunity 2",
                "AccountId": "@{refAccount.id}",
                "ContactId": "@{refContact2.id}",
                "CloseDate": "2022-10-20"
            }
        }
    ],
    "allOrNone": true
}
The response of this request is also provided below:
{
    "compositeResponse": [
        {
            "body": [
                {
                    "errorCode": "PROCESSING_HALTED",
                    "message": "The transaction was rolled back since another operation in the same transaction failed."
                }
            ],
            "httpHeaders": {},
            "httpStatusCode": 400,
            "referenceId": "refAccount"
        },
        {
            "body": [
                {
                    "errorCode": "PROCESSING_HALTED",
                    "message": "The transaction was rolled back since another operation in the same transaction failed."
                }
            ],
            "httpHeaders": {},
            "httpStatusCode": 400,
            "referenceId": "refContact1"
        },
        {
            "body": [
                {
                    "errorCode": "PROCESSING_HALTED",
                    "message": "The transaction was rolled back since another operation in the same transaction failed."
                }
            ],
            "httpHeaders": {},
            "httpStatusCode": 400,
            "referenceId": "refContact2"
        },
        {
            "body": [
                {
                    "errorCode": "PROCESSING_HALTED",
                    "message": "The transaction was rolled back since another operation in the same transaction failed."
                }
            ],
            "httpHeaders": {},
            "httpStatusCode": 400,
            "referenceId": "refOpportunity1"
        },
        {
            "body": [
                {
                    "message": "Required fields are missing: [StageName]",
                    "errorCode": "REQUIRED_FIELD_MISSING",
                    "fields": [
                        "StageName"
                    ]
                }
            ],
            "httpHeaders": {},
            "httpStatusCode": 400,
            "referenceId": "refOpportunity2"
        }
    ]
}
As you can see above, the account request, subsequent contact requests, as well as the request to create first opportunity record all were rolled back with errorCode as PROCESSING_HALTED because the 2nd opportunity record creation failed because of a required field that we removed: StageName. I am sharing the postman screenshot below as well for reference:
Notice that the status code of the response for main request is 200 whereas for subsequent requests it's 400. This is because the composite request was still successful even when the subsequent calls failed because there was no error encountered in the composite api callout (as a whole). The correct error code and messages are provided for each subsequent requests in the response body.

That's all for this tutorial. I hope you liked it. Let me know your feedback in the comments down below.

Happy Trailblazing!!

No comments:

Post a Comment