I have been working with Salesforce past few weeks for Punchydata—a product that I am developing. It doesn’t take long to realize that it’s not a pleasant API to work with. The documentation is sparse and throughly lacking examples. A good majority of things is only achievable via SOAP and XML. Now with a better understanding of how the API works, I can do the next best thing—blog about it. So, I’ll be writing a few posts on how to do something in Salesforce.
This one is about creating Custom Fields.
Note: I am assuming you have figured out the oAuth part of Salesforce, and know how to get an access token.
Custom fields in Salesforce allow you to store unique business data. For our app, Punchydata, that means lead source information like campaign, channel, and source. Technically, to create the custom fields, what I’ll be using is the SOAP API, but the interface is REST. You can use an abstraction layer over the SOAP XML (for eg, node has node-soap), but in my personal experience, that’s unnecesarry. It’s easier to just store the XML in raw text files.
So, how do you create a SOAP request with REST in Salesforce? To do that, you’ll be sending a request something like this: (I recommend picking up either Insomnia or Postman to test your requests).
URL: https://na1.salesforce.com/services/Soap/m/39.0
Headers:
- Content-Type: text/xml
- SOAPAction: "" (Salesforce mandates this header, although you can just have two double quotes to denote an empty action)
The body of the request would be the actual SOAP Envelope. Example:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:SessionHeader xmlns:h="http://soap.sforce.com/2006/04/metadata"
xmlns="http://soap.sforce.com/2006/04/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<sessionId>__ACCESS_TOKEN__</sessionId>
</h:SessionHeader>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
...
</s:Body>
</s:Envelope>
Creating Custom Fields
To create custom fields, we will be using Salesforce’s Metadata API. This API is meant “to retrieve, deploy, create, update or delete customization information, such as custom object definitions and page layouts.”
The SOAP Envelope for creating a custom field would look something like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:SessionHeader xmlns:h="http://soap.sforce.com/2006/04/metadata"
xmlns="http://soap.sforce.com/2006/04/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<sessionId>__SESSION_TOKEN__</sessionId>
</h:SessionHeader>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<createMetadata xmlns="http://soap.sforce.com/2006/04/metadata">
<metadata xsi:type="CustomField">
<fullName>Lead.channel__c</fullName>
<label>User Channel</label>
<type>Text</type>
<length>60</length>
</metadata>
</createMetadata>
</s:Body>
</s:Envelope>
I won’t dive into how SOAP XML works and what all the elements and attributes mean. Let’s just head to the body of envelope (s:Body tag).
The envelope is creating a custom field named channel__c
inside the “Lead” object with the label “User Channel.” It’s a text field of length 60. You can check all the available types Salesforce offers here.
Before using that envelope, you have to replace ”SESSION_TOKEN” with a valid access token.
That’s basically how our envelope would look like. So, if I have to create a custom field inside the Opportunity object that tracks the number of email follow-ups, what would the request look like?
URL: https://na1.salesforce.com/services/Soap/m/39.0
Headers:
- Content-Type: text/xml
- SOAPAction: ""
Body:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:SessionHeader xmlns:h="http://soap.sforce.com/2006/04/metadata"
xmlns="http://soap.sforce.com/2006/04/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<sessionId>__SESSION_TOKEN__</sessionId>
</h:SessionHeader>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<createMetadata xmlns="http://soap.sforce.com/2006/04/metadata">
<metadata xsi:type="CustomField">
<fullName>Opportunity.follow_up__c</fullName>
<label>Number of Follow Ups</label>
<type>Number</type>
</metadata>
</createMetadata>
</s:Body>
</s:Envelope>
If the request succeeds, you’ll get a response something like this:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns="http://soap.sforce.com/2006/04/metadata">
<soapenv:Body>
<createMetadataResponse>
<result>
<fullName>Opportunity.follow_up__c</fullName>
<success>true</success>
</result>
</createMetadataResponse>
</soapenv:Body>
</soapenv:Envelope>
Creating Multiple Fields
You can also create multiple fields in a single request by concatenating custom fields objects. For eg, you can have the following XML to create two follow up fields.
<createMetadata xmlns="http://soap.sforce.com/2006/04/metadata">
<metadata xsi:type="CustomField">
<fullName>Opportunity.follow_up__c</fullName>
<label>Number of Follow Ups</label>
<type>Number</type>
</metadata>
<metadata xsi:type="CustomField">
<fullName>Opportunity.follow_up_1__c</fullName>
<label>Number of Follow Ups 1</label>
<type>Number</type>
</metadata>
</createMetadata>
But there’s a caveat here. You can create maximum of ten fields at once. If you have more than 10, you would have to break down the request into two or more.
So, that’s how you create custom fields in Salesforce. Two important things though. First, to show the field in the UI, you would have to edit the page layout and add the field in it (Setup > Object Manager > [Your Object] > Page Layouts > [Page Layout to Edit]). Second, to make the field accessible you have to set permissions to make it editable by the appropriate role.
I’ll be writing posts on how to do both of these tasks with API, too.