Tips and FAQ

Salesforce Sales Cloud - How to Track the Field Change History for Different Resources

To retrieve the change history for a certain resource, use the Salesforce /query endpoint. This is an undocumented API on the Salesforce Sales Cloud element that lets the user pass a SOQL query directly to Salesforce.

Prior to making the request itself, there are some settings to be configured in the Salesforce UI to enable the field history tracking. The steps to be taken are reflected in the following Salesforce article for standard objects: https://help.salesforce.com/articleView?id=tracking_field_history_for_standard_objects.htm&type=5 and in this one for custom objects: https://help.salesforce.com/articleView?id=tracking_field_history_for_custom_objects.htm&type=5.

After configuring the field history tracking for the desired fields, the request can be made in Postman or any other tool using the /query endpoint mentioned above. It should have the structure as presented below, with a q(query) parameter having an SOQL statement as its value and the selected fields (id, NewValue, OldValue, Field) depending on what resource the user is going to query. We would suggest checking the documentation in order to correctly provide the field names in the query: https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_accounthistory.htm (this is for the AccountHistory resource, but searching the page for the word "history" will provide all the resources for which we can track the history of the fields).

The GET request example: 

curl -X GET \
  'https://{staging OR api}.cloud-elements.{com OR co.uk}/elements/api-v2/query?q=SELECT id, NewValue, OldValue, Field from AccountHistory' \
  -H 'Authorization: User xxxxxxxxxxx, Organization xxxxxxxxxxx, Element xxxxxxxxxxx' \

The response payload has the following structure: 

{
        "Field": "TextName",
        "NewValue": "Alex Entity",
        "IsDeleted": false,
        "Id": "xxxxxxxxx",
        "OldValue": "Alex Str"
 }

More information about the query endpoint can be found here.

Salesforce Sales Cloud - Request Limits Exceeded

Salesforce has API request and concurrent request limits based on the type of Organization and the Salesforce Edition. After these requests limits have been exceeded, the following error is thrown: ConcurrentPerOrgLongTxn Limit exceeded, REQUEST_LIMIT_EXCEEDED with error code : 403. For most of the scenarios, Cloud Elements displays Provider Message and Error Code as-is which is received from the Vendor.

For more information on the request limits and different error codes we can refer to the below links:

https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm

https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/errorcodes.htm

Salesforce Sales Cloud: GET /PATCH by externalId Functionality

We have recently enhanced the swagger of the Salesforce Sales Cloud element with the generic GET /{objectName}/fields/{name}/{externalID} and PATCH /{objectName}/fields/{name}/{externalID} endpoints. These can be used to retrieve or update, respectively, the desired records based on an externalId.

An external ID is a custom field that has the “External ID” attribute, meaning that it contains unique record identifiers from a system outside of Salesforce. Only the fields with the following data types can be external Ids: Number, Text, Email.

Image_2019-11-11_at_3.15.43_PM.png

Using the GET /{objectName}/fields/{name}/{externalID}, as mentioned above, the customers can retrieve records with a specific external ID. 

Example usage for retrieving a TEST_ID__c record using an external ID(accounts is the object name, the externalID field name is TEST_ID__c and the externalID field value is 589632):

curl -X GET 
"https://api.cloud-elements.com/elements/api-v2/accounts/fields/TEST_ID__c/589632" 
-H "accept: application/json" 
-H "Authorization: User XXXXXX, Organization XXXXXX, ElementXXXXXX"

The response payload has the following structure:

{
  "LastModifiedDate": "2019-11-11T13:11:05.000+0000",
  "IsDeleted": false,
  "LastViewedDate": "2019-11-11T13:11:05.000+0000",
  "LastReferencedDate": "2019-11-11T13:11:05.000+0000",
  "Name": "TestName",
  "SystemModstamp": "2019-11-11T13:11:05.000+0000",
  "Type": "Prospect",
  "CleanStatus": "Pending",
  "CreatedById": "XXXXXXXXXXXX",
  "OwnerId": "XXXXXXXXXXXX",
  "CreatedDate": "2019-11-11T13:11:05.000+0000",
  "RecordTypeId": "0121r000000eDEfAAM",
  "attributes": {
    "type": "Account",
    "url": "/services/data/v46.0/sobjects/Account/XXXXXXXXXXXX"
  },
  "PhotoUrl": "/services/images/photo/XXXXXXXXXXXX",
  "Id": "XXXXXXXXXXXX",
  "TEST_ID__c": "589632",
  "LastModifiedById": "XXXXXXXXXXXX"
}

The PATCH /{objectName}/fields/{name}/{externalID} endpoint can be used to create records or update existing records (upsert) based on the value of a specified external ID field.

  • If the specified value doesn't exist, a new record is created.
  • If a record does exist with that value, the field values specified in the request body are updated.

An example of a successful PATCH by externalId request(accounts is the object name, the externalID field name is TEST_ID__c and the externalID field value is 589632): 

curl -X PATCH 
"https://api.cloud-elements.com/elements/api-v2/accounts/fields/TEST_ID__c/589632" 
-H "accept: application/json" 
-H "Authorization: User XXXXXX, Organization XXXXXX, Element XXXXXX" 
-H "Content-Type: application/json" 
-d "{ \"Name\": \"TestName\"}"

The response payload has the following structure:

{
  "id": "XXXXXXX",
  "success": true,
  "created": true*
}

* In case the record existed but has been updated, the created parameter value is set to false. 

 

Salesforce Sales Cloud - Accounts and Permissions

If you have different levels of permissions configured for users within your Salesforce application, those permissions will be inherited and remain unchanged through the OAuth flow as the Salesforce Sales Cloud element is integrated.

Salesforce has multiple types of accounts and editions. Only these allow you to use the API:

  • Enterprise Edition
  • Unlimited Edition
  • Developer Edition
  • Performance Edition

Visit https://help.salesforce.com/HTViewSolution?id=000005140&language=en_US for more information.

Salesforce Sales Cloud - API Versions

After you connect an instance to Salesforce, Cloud Elements remains up-to-date with the latest versions of REST APIs.

Salesforce Sales Cloud - Custom Objects in Salesforce Sales Cloud

We support your custom objects and fields in the Salesforce Sales Cloud element. Use the following endpoints:

  • GET /objects to return all custom objects.
  • GET /{objectName} to interact with the custom APIs.
  • GET /objects/{objectName}/metadata endpoint to expose metadata for fields associated with your custom object.

Salesforce Sales Cloud - Does GET /objects return all available objects in SFDC?

Currently, theGET /objects endpoint returns only the objects that the authenticated user has Read/Write permissions for.

Salesforce Sales Cloud - How can I ensure that the oauth flow redirects to my sandbox for Salesforce?

You can add a siteAddress parameter to the GET /sfdc/oauth/url. For example:

https://api.cloud-elements.com/elements/api-v2/elements/sfdc/oauth/url?apiKey=<key>&apiSecret=<secret>&callbackUrl=https://console.cloud-elements.com/elements/jsp/home.jsp&siteAddress=https://test.salesforce.com>

Salesforce Sales Cloud - How to Test Revoking a Salesforce OAuth Token and Re-Authenticating Your Instances Through API

In this article, we will discuss how, if you are setting up a framework to re-authenticate instances of Salesforce Sales Cloud (SFDC), you can know how you can test that your process was successful. We will look into how to revoke an SFDC OAuth token and we will also go over a scenario that you can run through to test and ensure that your re-authentication process through API call is successful.

1. The first step is to access your SFDC account and create an OAuth/Connected App that has scopes of both 'full' and 'refresh_token, offline_access'. It is also important to pay attention to the 'Refresh Token Policy' that you set on your Connect App because that can impact your ability to re-authenticate with this described process as well.


2. Next authenticate an instance through API. The required calls and payloads are already discussed in the developer docs.


3. Access your Cloud Elements account, locate your new SFDC instance, and copy the auth header (user/org/element tokens) from the instance. For instance you can copy the full Authorization header from any endpoint after selecting your instance.

4. Now revoke the user's OAuth token either through the SFDC interface or through an API call to SFDC directly.

5. a) Revoke through the SFDC website by accessing: Setup -> Manage Apps -> Connected Apps OAuth Usage -> User Count.

Then simply click the 'Revoke' option for the desired user token you want to revoke.

5. b) Or you can use Postman or another tool to make an API call to GET /instances with your full Cloud Elements auth header including the element token, and retrieve the current oauth.user.token value.

Then make a POST to this URL to revoke the OAuth token for this instance. The Bearer in this case is equivalent to your user's OAuth token. 

- https://{SFDC-Instance}.salesforce.com/services/oauth2/revoke?token=#####

- https://help.salesforce.com/articleView?id=remoteaccess_revoke_token.htm&type=5



7. Confirm that the SFDC instance is no longer authenticated.

8. Make an API call to GET /oauth/url just as you did when you initially provisioned your instance of SFDC.

9. Access the OAuth URL generated, login to SFDC, and retrieve the provider code.

10. Make an API call to PATCH /instances with the full Cloud Elements auth header retrieved previously, blank oauth.user.refresh_token and oauth.user.token values, and the providerData.code value in the body.

11. Confirm a successful response, and verify that the SFDC instance was successfully re-authenticated.

If you are interested in seeing this process demo'ed through a quick video you can find that here:

Note that there can also be scenarios where re-authentication through API must be made via a PUT /instances containing the full instance configuration as opposed to a PATCH /instances. This can be necessary if the token has expired or if the OAuth token is revoked through alternate means than what was discussed in this article.

Salesforce Sales Cloud - How do I attach a file to an opportunity in Salesforce using the Cloud Elements API?

To do this, use the POST /{objectName}/{objectId}/attachments API where the objectName would be Opportunity and the objectID would be the ID of the opportunity you would like to attach it to. To retrieve the attachments related to an opportunity you can use the GET /{objectName}/{objectId}/attachments API. 

Salesforce Sales Cloud - How do I post to cases in Salesforce?

You should be able to useGET /{objectName}and POST /{objectName} to access the case objects in Salesforce Sales Cloud.

Salesforce Sales Cloud - Is it possible to create custom objects for Salesforce via Cloud Elements?

Unfortunately, our API does not currently support the creation of custom objects in Salesforce Sales Cloud. 

Salesforce Sales Cloud - Is there an implementation of the Salesforce Query API?

There is an undocumented API on the Salesforce Sales Cloud element that lets you pass an SOQL query directly to Salesforce. CEQL (Cloud Elements Query Language) supports, for the most part, the most basic functionality of SQL. Typically something like this:

select * from Account where Name = 'TestAccount'

Salesforce actually supports much more than this. Such as aggregate functions like count(Id) and even INNER JOIN and OUTER JOIN.

See this documentation for what salesforce supports: https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm

On Cloud Elements, you can use this extra querying functionality through the GET /query api.
The syntax is as follows:

GET /query?q=SELECT count(Id) FROM Contact WHERE name = 'test'&page=1&pageSize=200

GET /query?q=SELECT Amount, Id, Name, (SELECT Quantity, ListPrice, PricebookEntry.UnitPrice, PricebookEntry.Name FROM OpportunityLineItems) FROM Opportunity

This API is undocumented and only available on the Salesforce Sales Cloud element.

The SFDC element also supports a raw query endpoint (GET /raw-query) that can be used when nested select statements are required. Similar to the /query resource, this endpoint is not in the API Docs, but is accessible when you make API calls against SFDC element instances. An example of using this endpoint is:

/hubs/crm/raw-query?q=select%20id%20from%20Account%20where%20OwnerId%20in%20(select%20User.Id%20from%20User%20where%20User.Id%20=%20'005f4111002Nmt1XXX')

Salesforce Sales Cloud - Maximum URL Length

Salesforce allows for a large number of custom fields. However, they have a maximum URL length when calling their API. The Salesforce element, by default queries against all available fields in each object. For example, when calling the standard GET /contacts URL the element, by default, queries against all available (standard and custom) fields in the contacts object. This can lead to the URL becoming quite long and reach the limit for the request URL. If your response URL is longer than 16,088 characters, you will get a 'Request Header Fields Too Large  StatusCode=431'. If it reaches 16,410 characters, you will get 'URI Too Long  StatusCode=414'.

If you have a large number of custom fields, you should use the GET /query resource and narrow down which fields are being returned.. For example:

GET /query?q=select%20<Attribute>%20from%20<table>%20where%20<attribute>%3D%27<search_term>%27

Salesforce Sales Cloud - Only Mapped Fields are Returned in Transformations

When using transformations with Salesforce Sales Cloud, only the mapped fields are returned. Salesforce requires all fields that need to be returned to be included in the request. To avoid issues with accounts that have a large number of custom fields, the transformation functionality is designed to only request the mapped fields. If a field needs to be included, or custom javascript needs to be written for a certain field, be sure to include it within your mapped fields.

Salesforce Sales Cloud - Pagination

Salesforce allows paging only up to 2000 records. If you need to go above 2000 records, you should use the orderBy function and select a unique parameter such as “Id ascending”. However, the recommended approach is to use the bulk APIs

Salesforce Sales Cloud - Querying Numeric Fields

When querying for numeric field values in Salesforce Sales Cloud, there is an important aspect of the vendor functionality to keep in mind. If you attempt to query for a field value that is technically larger than type integer can store then you will receive an error "For input string" and no records are returned.

The reason for this error is that in Salesforce queries an integer cannot be bigger than 2^31-1 (2,147,483,647); Salesforce queries (SOQL) interpret field values up to 2^31-1 as 'numbers', but values from 2^31 and greater are regarded as strings. This is an aspect of Salesforce query functionality, but fortunately there is a workaround that you can use. To get Salesforce to accept your query for these large field values, you must append .0 to the end of the value, which defines the value as type decimal, which can be up to 2^63-1, and your query will be interpreted by SOQL as a number instead of a string.

For example: When querying numeric field values in a SFDC endpoint you should use a query such as `where customField__TicketID__c=7010965773.0` when the field value is greater than 2^31-1.

Salesforce Sales Cloud - Retrieving Relational Objects Through Bulk

There is a way to allow the retrieval of relational object fields through bulk. When posting a bulk job through the /bulk/query endpoint, you can pass the following in the metadata body {"expandChildObjects": true}

Note: that this functionality only works for custom objects as you cannot add nested objects to standard objects.

Salesforce Sales Cloud - Salesforce Sandbox Environment

The Salesforce sandbox environment has a different base URL from the production environment. Instead of https://login.salesforce.com/, you log in to https://test.salesforce.com/. When you authenticate a Salesforce instance, you must pass a siteAddress query parameter with the sandbox URL when getting the redirect URL. See Authenticate Through API.

Salesforce Sales Cloud - Steps to Re-Authenticate a SalesForce instance with the API

As you may already know, you can re-authenticate Salesforce from Cloud Elements 2.0. However, you can also do this programmatically through API calls. Follow the steps below. 

1.  Make a GET request to /elements/sfdc/oauth/url.

curl -X GET \	
'https://api.cloud-elements.com/elements/api-v2/elements/sfdc/oauth/url?apiKey=<your_api_key>&apiSecret=<your_api_secret>&callbackUrl=<your_callback_url>'

2. Navigate to this URL to get the code as you would for the OAuth 2.0 flow.

3. Get the current instance configuration.

curl -X GET \
https://api.cloud-elements.com/elements/api-v2/instances/<instance_id> \
-H 'Authorization: User <Your_user_secret>, Organization <Your_organization_secret>' \
-H 'accept: application/json'

4. Take the body of the response from step 3 and delete the oauth.user.token and oauth.user.refresh_token

5. In the same body add a "providerData" object that contains the code.

"providerData": {
       "code": "xxxxxxxxxxx"
}

6. Now use the following endpoint with the body from the response in step 4 and 5.

PUT https://api.cloud-elements.com/elements/api-v2/api-v2/instances/{instanceId}?reAuthenticate=true

7. Make sure that in Step 6 you pass the correct authorization headers and OAuth token.

Salesforce Sales Cloud - The 'event.objects' Polling Framework

The event.objects configuration key represents the old polling framework for Salesforce Sales Cloud. Although not deprecated, its configuration is rigid and Cloud Elements has moved away from supporting this framework. However, you are still more than welcome to utilize this functionality.

How Does it Work? 

Through the Authentication UI, there's an optional field labeled "Objects to Monitor for Changes" where you can indicate the resources to start monitoring.

Ensure that the resources match the provider names exactly. You can specify multiple resources through a comma-delimited list such as `Contact, Account`.

By specifying these resources, the event.objects polling framework auto-generates the query URL used to poll against the service provider. For example, placing Contact into this field generates the following URL:

Contact|/hubs/crm/Contact?where=LastModifiedDate>='${date}'&orderBy=Id&includeDeleted=true||LastModifiedDate|yyyy-MM-dd'T'HH:mm:ss.SSSZ

Changing the Objects to Monitor

This polling framework, although rigid, is capable of supporting dynamic changes to the objects it monitors.

The following workflow illustrates the best practice in creating a dynamic poller using our old polling framework:

  1. PATCH event.vendor.type with {"propertyValue":“polling”}
  2. PATCH event.objects to {"propertyValue":“Contact”}
  3. PATCH event.notification.enabled to {"propertyValue":"true"}
  4. PATCH event.objects with either {"propertyValue":“Account”} or {"propertyValue":“”}

This will preserve the functionality of the event.poller.urls, as well as the query URLs throughout any dynamic resource monitoring.

Salesforce Sales Cloud - Troubleshooting 401 Errors

Salesforce returns 401 errors when a Salesforce Sales Cloud session expires. Expired sessions are commonly caused by the following:

  • The user revoked access rights.
  • The session expired. Access tokens have a limited lifetime specified by the session timeout in Salesforce.
  • The instance is using the Username-Password OAuth Authentication method which does not allow the use of refresh tokens. For sessions that don’t expire, you may wish to switch to token based authentication for your instances.
  • Too many uses of the same credentials to authenticate. Currently, Salesforce has a limit of 5 concurrent sessions. A sixth session could invalidate a previous instance.

The most common solution is to re-authenticate the instance. If the expired instance belongs to your customer, they will be prompted to enter their credentials the next time they attempt to use the element. 

To re-authenticate via the user interface:

  1. Navigate the instance.
  2. Hover over your instance and click “Edit”.
  3. In the lower-right corner of the next page, click “RE-AUTHENTICATE” which will allow you to re-authenticate your instance using the Salesforce login page.

 To re-authenticate using the API:

  1. Navigate to your instance's custom OAuth url (this is found by making a GET request to https://api.cloud-elements.com/elements/api-v2/elements/23/oauth/url?apiKey={yourApiKey}&apiSecret={yourApiSecret}&callbackUrl={yourCallbackUrl})
  2.  Enter your Salesforce login credentials. You should then receive a new code which can be used to re-authenticate your instance.

For more information regarding the OAuth process for Salesforce Sales Cloud, see the documentation here.

Salesforce Sales Cloud - Using Virtual Data Resources with Polling

You have created your virtual data resource and followed all the instructions given in our Salesforce Sales Cloud - Events / Polling and Why Would a Polling Job Fail to Return Events and still, somehow the polling for Salesforce Sales Cloud using virtual data resources is not working. Rest assured that we have seen this issue before, and we will show here all the steps you need to take in order to make sure you overcome this setback.

When you create a new instance of Salesforce with a polling configuration, one of the required fields is "Object to Monitor for Changes", essentially that is the object that we will be monitoring when a record for that Object is created, updated or deleted (i.e. Contacts, Accounts, etc...). What you may not see right away, is that an Event Poller URL is automatically generated after the instance is created and an"Object to Monitor for Changes" is provided, and this URL requires that the virtual data resource yield to some key properties in order for it to work properly with our polling engine. Here is what the Event Poller URL looks like - you can check that out by going into the instance of Salesforce Sales Cloud you have created, and scrolling down to Event Poller URL.

 

EventPollerURL.png

So let us give you a little bit more background on why the Event Poller URL is important and relevant here:

1. It sets the default value of the yielding property, in the case of Salesforce Sales Cloud, the "LastModifiedDate" property.

2. It requires deleted records to be included, hence the need to yield to the "IsDeleted" property.

3. And last, but just as important, it requires the created property to be included, so Salesforce recognizes a newly created record. 

But enough said, here are the three required properties when creating a virtual data resource and using it for polling within the Salesforce Sales Cloud element:

- "CreatedDate"

- "LastModifiedDate"

- "isDeleted"

Once you have those 3 properties mapped within your virtual data resource as well as all the other properties you would like to have included, you should be able to use the virtual data resource in the polling configuration of your Salesforce Sales Cloud element.

Salesforce Sales Cloud - What causes the "API_DISABLED_FOR_ORG - The REST API is not enabled for this organization" error in Salesforce?

This error means that the Edition of Salesforce you are trying to connect to does not have access to the API. There are multiple editions of Salesforce, the group edition for example does not have access to the API. For more information on the available editions, please see Salesforce Documentation.

Salesforce Sales Cloud - What Causes Invalid Session ID Messages?

There is a setting in Salesforce that may lead to issues where calls return “INVALID_SESSION_ID – Session expired or invalid”. If this is occurring, clear the the Session Setting Lock sessions to the IP address from which they originated check box.