Salesforce Destination Support - Segment vs RudderStack

By Max Werner, published: 2021-11-17

It's easy to think that because CDP 1 and CDP 2 both support the same destination, they would behave the same, but that is very much not the case. Let's take a look at how Twilio Segment and RudderStack both handle the Salesforce destination in their own special ways.

The Two Contenders: Twilio Segment and RudderStack

When our clients evaluate CDPs the consideration of whether or not a given CDP supports the tool they use is generally just a checkbox question: We use Salesforce. Do both CDPs support Salesforce? - Yes. Ok, moving on.

However, even though both Segment and RudderStack support pushing data to the SalesForce Cloud (SFDC), and both of them authenticate against SFDC using the same username / password / security token mechanism, that’s pretty much where the similarities end.

Identifying Leads

The primary use case for the SFDC destination is most often to push lead or contact data into SFDC, be that create new leads or update fields on existing records. So let’s see how each CDP handles this. Both Segment and RudderStack handle this essentially the same way:

From the Segment docs:

When you call identify, Segment checks to see if this Lead exists based on the email trait. If it does, Segment updates the Lead with the traits you’ve passed in your identify call, otherwise Segment creates a Salesforce Lead.

From the RudderStack docs:

When the identify method is called, RudderStack checks if the lead already exists using the email property. If yes, the lead is updated with the traits passed in the identify call. If no lead exists, a new lead is created in Salesforce.

Updating Other Objects (Accounts, Opportunities, Custom Objects)

This is where the two CDPs start to diverge quite heavily, very much in sync with their own philosophies. Let’s take a look at a few common scenarios:

  • Updating Account data
  • Updating Opportunities
  • Updating Custom Objects

As you might expect from Segment, the account object is mapped to the group call. As long as you include a name trait in your group call, you’re off to the races. It will use the groupId to map to the AccountNumber field in the SFDC Account object and go from there. Anything else, Segment does not support. In their words:

Creating Other Resources

To reduce the complexity of the API, the Salesforce destination intentionally only supports creating leads using the identify call.

To create resources of other types, such as Accounts or custom objects, Segment recommends that you integrate with Salesforce directly

RudderStack on the other hand takes their warehouse first approach to the extreme here. And as per usual with RudderStack it leads to more setup work but gives you a ton more flexibility in return. They document this here.


  userId: '123456',
  traits: {
    FirstName: "John",
    LastName: "Gibbs",
    Email: "[email protected]"
  context: {
    externalId: [
        type: "Salesforce-Contact",
        id: "sf-contact-id"

In the example above, RudderStack updates the Contact object in Salesforce with id as sf-contact-id and send the traits object to Salesforce.

By default, RudderStack creates a Lead object in Salesforce and maps the traits to it, as mentioned above. For other objects, RudderStack does not modify the traits; they are sent to Salesforce as it is.

What this means in practice is that you can use RudderStack to insert or update ANY object in SFDC. Their docs could use some more elaboration though, so I’ll do my best on describing how to do it here:

Update the Accounts Object

As you can see above the context.externalId array needs to include the object you want to update. For accounts that would be type: "Salesforce-Account". If you provide an id key in the same object, RudderStack will update the record, if you omit it, RudderStack will create it. Now the ID here is not the AccountNumber field like Segment uses but rather the Salesforce internal record ID (which you can see for example in the URL when you are inside SFDC and view the record).

Updating Other Objects

Simply put, as long as you include the object name in that context.externalId array like {type: "Salesforce-Opportunity", …} you can insert/update any object. The same rule as accounts applies here too. If you want to update an object you need to have the internal record ID. So how do you do that?

Getting the SFDC Internal Record ID

RudderStack is proudly warehouse-first. That means they expect you to have your warehouse serve as your nerve center. So if you need the record IDs you’ll need to ETL your SFDC data into your warehouse. RudderStack offers a service they call “Cloud Extract” to do just that, but of course you are free to use any tool that does this operation for you like FiveTran or StitchData.

Once you have your SFDC data in your warehouse it is just a bit of SQL magic to join the two things together. As an example with the account object, let’s say you map your groupId to the object’s AccountNumber field, now you can easily join that in your warehouse on the groupId and retrieve the SFDC Record ID.

RudderStack Trade Offs - Flexibility vs Ease of Use

While it is not exactly hard to get that record ID into your warehouse, it does make it a lot harder to update non-lead objects from your event stream. With segment your group calls can be hooked up directly. In RudderStack you’d need to build some mechanism to search for a groupId and retrieve the SFDC ID if it exists. That mechanism can live in a transformation attached to your SFDC destination but it does involve a lot more setup.

Speaking of transformations, you can use them to change your event stream’s JSON payload to anything you need. Where Segment maps track calls to SFDC custom actions in a fairly rigid way, you can use RudderStack to change a random track call to update an opportunity. For example track("demo-form-submitted") could create an opportunity object for you with very little work.

Summary and Conclusion

As you can see, RudderStack’s and Segment’s core philosophies “CDP for developers” and “turn-key solution” shine through like a beacon here. If your use case is extremely standard, Segment’s integration is just that: “turn-key”. Once authenticated, it will just work. RudderStack on the other hand requires a lot more data engineering support to setup but if you heavily rely on multiple object types and relations in SFDC, it will give you the ability to do more or less anything in SFDC. Of course that also means it is a lot more breakable. For example, if you give a badly formatted timestamp string to RudderStack to push to a Date or DateTime field in SFDC, the entire call will fail, Segment will work behind the scenes to get at least the other stuff through.

So in a nutshell, just because both CDPs “support” Salesforce as a destination, doesn’t mean they work the same. Shameless plug here: That is why having a CDP consultant like OA on hand to plan your CDP purchase and data infrastructure process can save you so much pain. Just imagine buying one CDP over the other and realizing that it can only do 20% of what you need it to for a given destination. In the end you can, generally speaking, force it and make either CDP do what you want it to do, but the amount of engineering time and proverbial duct tape will differ greatly.