function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
KMKKMK 

Apex code to create(add) custom fields to the object dynamically using Metadata API.

Hi All,

 

I am new to Saleforce development. Please can any one guide me to sample apex code to create(add) custom fields to custom or standard objects dynamically using Salesforce Metadata API in my development org.

 

Thanks in advance.

bob_buzzardbob_buzzard

You can't access the metadata API via Apex, I'm afraid.  Its a web service API used by external clients.

KMKKMK

Hi Bob, thanks for your response.

Can I use Metadata API in my Apex ?

or Is there any way to add customer fields to objects dynamically by programmatic with apex code using metadata API, with out using external clients

bob_buzzardbob_buzzard

Unfortunately the answer is no to both of these.  I can't find much in the way of ideas regarding this either.

KMKKMK

Hi Bob,

 

I just now started reading about Apex Callouts from the force.com Documentation. This seems to be a way to access external web services. There is an example describing how to use Amazon's Webservices after we get the WSDL from Amazon. 

 

Now my doubt is, can't I Use the Metadata API WSDL to generate Apex classes and then consume the web services in my Apex. If I cannot, what is the difference between the Metadata API WSDL and other External Web services WSDL. 

 

Thank you for your responce.



bob_buzzardbob_buzzard

You can't callout to any Salesforce web services from within Salesforce.  You can only callout to external web services.

AsterAster

Hi,

 

I understand that we cannot create objects/fields in SFDC either through programatic means using APEX or by using APIs in APEX code.

 

Is there an alternate means to create objects or fields in SFDC using APEX?

 

Aster

sreenathsreenath

Hi All,

            Not using API's, Can we create fields for the objects using apex programming dynamically. Any one had know about it please provide the code or suggestions to create fields.

 

Thanks

Sreenath

Prateek SengarPrateek Sengar

Hi,

You can try the following. Consume the Metadata api in apex. This will require a bit of tweaking in the Metadata api wsdl. Modify the generate apex code so that it could be saved. You might need to rename the update and delete method of the generated class to something else.

Once you have done that with few minor modification in the generated class you can use it to create fields and custom object programatically.

 

Regards

Prateek Sengar

Andy FawcettAndy Fawcett

I have been successful in getting parts of the Metadata API to work from Apex.

 

I've uploaded the code and a write up to Github, include pros and cons of this approach.

 

MetadataService.CustomObject customObject = new MetadataService.CustomObject();
customObject.fullName = 'Test__c';
customObject.label = 'Test';
customObject.pluralLabel = 'Tests';
customObject.nameField = new MetadataService.CustomField();
customObject.nameField.type_x = 'Text';
customObject.nameField.label = 'Test Record';
customObject.deploymentStatus = 'Deployed';
customObject.sharingModel = 'ReadWrite';
MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { customObject });

 

Enjoy!

 

https://github.com/financialforcedev/apex-mdapi

Bindhyachal Kumar SinghBindhyachal Kumar Singh

Hi AndylnTheCloud,

 

It is working fine and i also needed this...............

Thanks for sharing code for creating a new Custom field using metadata API.......

Great Job.......

Lee_CampbellLee_Campbell

@AndyInTheCloud

 

Is there some kind of update step missing from the code you posted, as there might be in a trigger, for example? I implemented the code and your MetadataService, almost verbatim and nothing happened to my object/field structure (coupled with the fact that I;m not really what the update and metadata labelled methods of your code do or how to utilise them...). Any help greatly appreciated.

 

Lee

Andy FawcettAndy Fawcett

The Metadata API from Salesforce is asyncronous, notice the return result from the 'create' operation. 

 

MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { customField });

 

See the note in the read me file about this.

 

"Most operations return AsyncResult which gives you an Id to call back on to determine the fate of your request. While this can be called, you will need to do this via AJAX, Apex Future or Apex Job. The deploy and retrieve samples utilise apex:actionPoller."

 

My hunch is that it is failing and you need to utilise one of the above approaches to find out why. If your in a Visualforce context its best to store the return AsyncResult in your controller then allow the page to rerender with an actionPoller on it, which will check the request for completion. Once done you can retrieve any error messages. 

 

Though it is doing a 'deploy' operation and not a 'create' operation, this example can be adapted to show the results of a 'create' operation. https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataDeployController.cls, see checkAsyncRequest method. The page then uses this to call it periodically, 

 

<apex:actionPoller action="{!checkAsyncRequest}" interval="5" rerender="form" rendered="{!NOT(ISNULL(AsyncResult))}"/>

Or if your just trying things out, you could execute the code via Anonymous Apex and output the AsyncResult ID to the debug log. Then a few moments latter issue the 'checkStatus' operation (again via Anonymous Apex) with the ID.

 

If in the meantime you want to share the code your running I can try to spot an obvious issues or problems.

 

 

 

Lee_CampbellLee_Campbell

That's all I needed, it's all slotting into place now, thanks!

Lee_CampbellLee_Campbell

Hi again Andy,

 

I'm actually still having a couple of issues in that my AsyncResult is essentially blank (numberofComponentsDeployed=null).

 

I'm trying to create custom fields based upon the names of some of my SFDC objects, triggered by user actions. So, I'm seeing a "Completed" Apex Job, using an @future class that calls on some of the methods/classes as per your metadata service, but the fields don't appear. My trigger, and the extra method I added to your MetadataService are below:

 

trigger PROC_CreateSettingsModule on Procurement_Settings__c (before update){
	
	List<Schema.ChildRelationship> objs = Procurement__c.sObjectType.getDescribe().getChildRelationships();
	List<String> objsList = new List<String>();	
	for(integer i = 0;i<objs.size();i++){
		if(string.valueof(objs[i].getChildSObject()).contains('__c')==true&&string.valueof(objs[i].getChildSObject())!=null){
			objsList.add(string.valueof(objs[i].getChildSObject()));//.getDescribe.getLabel());
		}
	}
	
	if(trigger.isUpdate){
		string currentID = UserInfo.getSessionId();
		for(Procurement_Settings__c newSettings:trigger.new){
			List<string> baseObjs = newSettings.BaseObjects__c.split('\n');
			for(integer j = 0;j<baseObjs.size();j++){
				if(baseObjs[j]==null||baseObjs[j]==''){
					baseObjs.remove(j);
				}
			}
			MetadataService.callRequest(baseObjs,currentID);
			
		}
	}
}

 

And the "callRequest" method:

 

@future(callout=true)
    public static void callRequest(List<string> baseObjs, string currentID){
            MetadataService.MetadataPort service = new MetadataService.MetadataPort();
            service.SessionHeader = new MetadataService.SessionHeader_element();
            service.SessionHeader.sessionId = currentID;
            
            for(integer j = 0;j<baseObjs.size();j++){
                MetadataService.CustomField dayFields = new MetadataService.CustomField();
                dayFields.description = 'Field in which users may enter the number of days it is assumed elapse between the submission deadline for '+baseObjs[j]+' and the date a demonstration is due';
                dayFields.type_x = 'Number';
                dayFields.scale = 0;
                dayFields.fullname = 'Procurement_Settings__c.'+baseObjs[j]+'_Days__c';
                dayFields.label = 'Assumed days between '+baseObjs[j]+ 'and demo';
                dayFields.length = 3;
                MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { dayFields });
            }
    }

 
I don't really see what I'm missing. Any tips?

 

Thanks,

Lee

Andy FawcettAndy Fawcett

Your still not checking the AsyncResult status, you need to do this via calling MetadataService.checkStatus as as per the Metadata API's documentation, see steps here. To do this you need to use actionPoller in Visualforce context or via Batch Apex job (@future is not suitable as you cannot creating a polling condition in this context) in other contexts, such as Apex Trigger in your case.

 

As I can see your working in a Apex Trigger context, I assume you don't have a Visualforce page or prefer not to create one for the user action being performed? This has prompted me to create a new sample showing how to achieve what your doing via Batch Apex, it will be a generate example for any Metadata component. And will information the user (by default) via email of any failures. However it will allow the caller to provide its own handler, for example to update your trigger record with any errors. I should have this done today, I'll follow up here.

 

In the meantime if you want to check your async results, simply dump the Async Result ID to the debug log and manually call via Execute Annoymous Apex from Force.com IDE or Developer Console the checkStatus method to retrieve the actual error causing your problem.

Andy FawcettAndy Fawcett

I've commited some expirmental code to call checkStatus from a Batch Apex context (as apposed to Visualforce actionPoller). You can find it here. I am sitll working on the test for it and would not consider it production at this stage. However i wanted to share with you to let you give it a try and give me some feedback. Here is an example of how to use it.

 

MetadataService.CustomField customField = new MetadataService.CustomField();
customField.fullName = 'Test__c.TestField__c';
customField.label = 'Test Field';
customField.type_x = 'Text';
customField.length = 42;
MetadataCreateJob.run(
	new List<MetadataCreateJob.Item> { new MetadataCreateJob.Item(customField) },
	new MetadataCreateJob.EmailNotificationMetadataAsyncCallback());		

 

As you can see you can provide your own Apex callback if you want, in this case I've wrriten a email handler to email the results to the user. If it works OK for you, I can show you how to write your own handler to pass to it, to update for example your trigger records with the result. If thats what you prefer.

Andy FawcettAndy Fawcett

Oh and make sure to update MetadataService.cls, I've made a change to that for Batch Apex context.

RishabhRishabh

Hi AndlntheCloud,

 

Can you write calback hander for below code so, when I get the result from that call back, I want to do next process.

 

MetadataCreateJob.run(
	new List<MetadataCreateJob.Item> { new MetadataCreateJob.Item(customField) },
	new MetadataCreateJob.EmailNotificationMetadataAsyncCallback());	

Thanks,

Rishabh Shah

Jai ChaturvediJai Chaturvedi

Hi AndyInTheCloud,

 

I have seen your post and its really useful. But i am not getting how to get that class in apex. it is giving  error.

 

Also read your post:

Generating a valid Apex MetadataService class
- Edit the WSDL
- Change the Port name from 'Metadata' to 'MetadataPort'
- As of Summer'13 (API 28) there was a small bug in the CustomField type definition, change the 'type' element definition to include a minOccurs="0" atttribute, as per the other elements in this type.
- Attempt to generate Apex from this WSDL
- Give it a better name if you want when prompted, e.g. MetadataService
- In earlier platform releases this would error, as update and delete are reserved words.
- It seems this has now been fixed and as of Summer'13 the Metadata API WSDL generates without errors!
- Open Eclipse (or your favourite editor)
- Open the class
- To be compatible with the samples here, edit the method name update_x to updateMetadata
- To be compatible with the samples here, edit the method name delete_x to deleteMetadata
- To be compatible with the samples here, edit the method name retrieve_x to retrieve
- Save the class
- Update the MetadataServiceText class
- Observe the uncovered items (new metadata operations, types added since last release)
- Add new code to cover operations and types
- See this for guidelines http://andyinthecloud.com/2013/05/11/code-coverage-for-wsdl2apex-generated-classes
- Making further edits to the Apex code
- Modify the end point to be dynamic
- public String endpoint_x = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/m/28.0';
- Make 'Metadata' inner class 'virtual'
- Make 'MetadataWithContent' inner class 'virtual'
- Review WSDL for types that extend 'tns:Metadata' and update related Apex classes to also extend Metadata
- Review WSDL for types that extend 'tns:MetadataWithContent' and update related Apex classes to also extend MetadataWithContent
- Apply the following to each class that extends Metadata, e.g. for CustomObject
Add the following at the top of the class
public String type = 'CustomObject';
public String fullName;
Add the following at the top of the private static members
private String[] type_att_info = new String[]{'xsi:type'};
private String[] fullName_type_info = new String[]{'fullName','http://www.w3.org/2001/XMLSchema','string','0','1','false'};
Add 'fullName' as the first item in the field_order_type_info String array, e.g.
private String[] field_order_type_info = new String[]{'fullName', 'actionOverrides' …. 'webLinks'};
- Apply the following to each class that extends MetadataWithContent, e.g. for ApexPage
Add the following after 'fullName'
public String content;
Add the following after 'fullName_type_info'
private String[] content_type_info = new String[]{'content','http://www.w3.org/2001/XMLSchema','base64Binary','0','1','false'};
Add 'content' after 'fullName' in the field_order_type_info String array, e.g.
private String[] field_order_type_info = new String[]{'fullName', 'content', 'apiVersion','description','label','packageVersions'};

 

But i am not geeting how to start. Can you please help me giving me basic steps to save that class in apex.

Please. It is much needed.

 

Prateek SengarPrateek Sengar
When you will import the wsdl in salesforce it will throw you an error. Just copy the code with error paste in a editor or notepad. Make the changes mentioned in my post. Paste it back in the window and try and save it. It should work

Thanks & Regards
Prateek Sengar

This e-mail and any files transmitted with it are for the sole use of the intended recipient(s) and may contain confidential and privileged information. If you are not the intended recipient(s), please reply to the sender and destroy all copies of the original message. Any unauthorized review, use, disclosure, dissemination, forwarding, printing or copying of this email, and/or any action taken in reliance on the contents of this e-mail is strictly prohibited and may be unlawful.
Andy FawcettAndy Fawcett

If it helps there is an API 28 version in Apex already of this Web Service in the GitHub repository (complete with test class). So you don't need to do the WSDL import process yourself. https://github.com/financialforcedev/apex-mdapi/tree/master/apex-mdapi/src/classes

Jai ChaturvediJai Chaturvedi
I picked the class from the URL you provided and trying to save latest code but getting following error:

[https://ap1.salesforce.com/img/approvals/stopsign_16x16.gif] Error: Compile Error: Invalid type: MetadataService.MetadataPort at line 48 column 9


Please help. I just want create field from apex class.

Thanks
Jack

________________________________
This message is for the designated recipient only and may contain privileged, proprietary, or otherwise confidential information. If you have received it in error, please notify the sender immediately and delete the original. Any other use of the e-mail by you is prohibited.

Where allowed by local law, electronic communications with Accenture and its affiliates, including e-mail and instant messaging (including content), may be scanned by our systems for the purposes of information security and assessment of internal compliance with Accenture policy.

______________________________________________________________________________________

www.accenture.com
Andy FawcettAndy Fawcett

Just to confirm you need as a minimum these two classes...

 

https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataService.cls

 

https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceTest.cls

 

Then take a look at the MetadataServiceExamples.cls for sample code.

 

You may also be interested in the MetadataCreateJob.cls.

Sachin C 15Sachin C 15
Hi Andy,

As explained in your blog and over here,I was successful in creating a custom object and associated custom fields, using metadata api through Apex, There is a strange issue, That I cannot access these fields, in any of the external tools like workbench, dataloader.io, Apex Data Loader as well, and thus cannot perform any data operations.

I believe, we need to add some more metadata properties, while creating a field,

Currently it is:
MetadataService.CustomField customField = new MetadataService.CustomField();
customField.fullName = 'Test__c.TestField__c';
customField.label = 'Test Field';
customField.type_x = 'Text';
customField.length = 42;
MetadataService.AsyncResult[] results =   service.create(new List<MetadataService.Metadata> { customField });

Could you or any one in the forum, could throw some light on this?

Also, Can we replicate entire functionality of creating an Object and Fields using REST?
Sachin C 15Sachin C 15
Issue was with permissions, as Object and Fields were created dynamically. 
Marking this as resolved :) :P Thank you everyone.
Fahad Khan 2Fahad Khan 2
Andy Fawcett you are such a love
sandeep reddy 37sandeep reddy 37
andymy requeir ment is i need to create document brows field how it works 
 
Puja Patil 13Puja Patil 13
Hi Andy Fawcett,

I used same code as you given , but getting error "Invalid type: MetadataService.MetadataPort"

Please help me to resolve this error.
It's urgent.
Nikhil Sharma 17Nikhil Sharma 17
Hi Andy,

Thanx in advance.
I need your help. Can we update the api name of a field using update metadata api?
Here is my Code:
I want to update the field api name from Mp_aadatetime__c to something else. I can change the other attributes like label, type but unable to change its api name.


MetadataService.MetadataPort service = MetadataServiceExamples.createService();
        MetadataService.CustomField customField = new MetadataService.CustomField();
        customField.fullName = 'Contact.Mp_aadatetime__c';
        customField.label='New Test 123 Field Label';
        customField.type_x = 'Text';
        customField.length = 62;
        List<MetadataService.SaveResult> results =
            service.updateMetadata(
                new MetadataService.Metadata[] { customField });

Regards
Nikhil Sharma
 
Ayub AnsariAyub Ansari
As a technical evolution, Salesforce has made very easy to create the field by using tooling api...
 
String objectapiname = 'GCP_Content_Item__c';
String fieldapiname = 'GCP_Country_Name';//without __c
String fieldlabel = 'GCP_Country_Name';
String fielddescription = 'GCP Country Name';
HttpRequest requestinside = createHttpRequest(, 'POST');
requestinside.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
requestinside.setHeader('Content-Type', 'application/json');
requestinside.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v41.0/tooling/sobjects/CustomField/');
requestinside.setMethod('POST');
String fieldDef = '{"Metadata" : ';
String metadef = '"type" : "Text","description" : "'+fielddescription+'", "inlineHelpText" : "","precision" : null,"label" : "'+fieldlabel+'","length" : 255,"required" : false'
fieldDef += '{'+metadef+'},';
fieldDef += '"FullName" : "'+objectapiname+'.'+fieldapiname+'__c"}';
system.debug(fieldDef);
requestinside.setBody(fieldDef);

HTTPResponse res = http.send(requestinside);
System.debug(res.getBody());

https://ayubansarii.wordpress.com/2018/03/24/apex-code-to-create-custom-field-programmatically/
Dennis H PalmerDennis H Palmer
Updated ^^^ to be a function and solves compilation errors.
public static string createField(string objectAPIName, string fieldAPIName, string fieldLabel, string fieldDescription) {
        HttpRequest request = new HttpRequest();
        request.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
        request.setHeader('Content-Type', 'application/json');
        request.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v41.0/tooling/sobjects/CustomField/');
        request.setMethod('POST');
        
        request.setBody('{"Metadata" : {"type" : "Text","description" : "'+fieldDescription+'", "inlineHelpText" : "","precision" : null,"label" : "'+fieldLabel+'","length" : 255,"required" : false}, "FullName" : "'+objectAPIName+'.'+fieldAPIName+'"}');
        
        Http http = new Http();
        HTTPResponse res = http.send(request);
        return res.getBody();
    }

 
Aayushi Agrawal 3Aayushi Agrawal 3
Hi Dennis

By any chance, is there any similar way to delete salesforce field ? I need to create a new field whenever there is new field at 3rd party system, similar way I need to delete field in Salesforce when a field is deleted at 3rd party.

I would like to have advice on this.

Thanks!!
Dennis H PalmerDennis H Palmer
Hey Aayushi,

I have not found a way to do this for deleting.  I've moved my solution to use the Metadata API instead.  I made it in a class called Tooling.  So far it has creating fields and deleting fields.  For this you must have the MetadataService class located here (https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataService.cls).
public class Tooling {
	public integer length;
	public integer precision;
	public integer relationshipOrder;
	public integer scale;
	public integer startingNumber;
	public integer visibleLines;
    
	public string defaultValue;
    public string deleteConstraint;
	public string description;
	public string displayFormat;
	public string externalDeveloperName;
	public string formula;
	public string formulaTreatBlanksAs;
	public string inlineHelpText;
	public string label;
	public string maskChar;
	public string maskType;
	public string referenceTo;
	public string relationshipLabel;
	public string relationshipName;
    public string summarizedField;
	public string summaryForeignKey;
	public string summaryOperation;
	public string fieldType;
    
    public boolean caseSensitive;
    public boolean displayLocationInDecimal;
    public boolean escapeMarkup;
	public boolean externalId;
    public boolean isFilteringDisabled;
    public boolean isNameField;
    public boolean isSortingDisabled;
    public boolean populateExistingRows;
    public boolean readOnlyProxy;
    public boolean reparentableMasterDetail;
    public boolean required;
    public boolean restrictedAdminField;
    public boolean stripMarkup;
    public boolean trackFeedHistory;
    public boolean trackHistory;
    public boolean unique;
    public boolean writeRequiresMasterRead;
    
	public MetadataService.FilterItem[] summaryFilterItems;
    
    private MetadataService.MetadataPort service = new MetadataService.MetadataPort();  
    
    public Tooling() {
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = UserInfo.getSessionId();
    }
    
    public MetadataService.SaveResult[] createField(string objectAPIName, string fieldAPIName) {
		string returnValue = '';
        
        List<MetadataService.Metadata> fields = new List<MetadataService.Metadata>();
        MetadataService.CustomField customField = new MetadataService.CustomField();
		customField.fullName = objectAPIName + '.' + fieldAPIName;

        // decimal fields.
        if (this.length != null) { customField.length = this.length; }
        if (this.precision != null) { customField.precision = this.precision; }
        if (this.relationshipOrder != null) { customField.relationshipOrder = this.relationshipOrder; }
        if (this.scale != null) { customField.scale = this.scale; }
        if (this.startingNumber != null) { customField.startingNumber = this.startingNumber; }
        if (this.visibleLines != null) { customField.visibleLines = this.visibleLines; }
        
        // string fields.
        if (this.defaultValue != null) { customField.defaultValue = this.defaultValue; }
        if (this.deleteConstraint != null) { customField.deleteConstraint = this.deleteConstraint; }
        if (this.description != null) { customField.description = this.description; }
        if (this.displayFormat != null) { customField.displayFormat = this.displayFormat; }
        if (this.externalDeveloperName != null) { customField.externalDeveloperName = this.externalDeveloperName; }
        if (this.formula != null) { customField.formula = this.formula; }
        if (this.formulaTreatBlanksAs != null) { customField.formulaTreatBlanksAs = this.formulaTreatBlanksAs; }
        if (this.inlineHelpText != null) { customField.inlineHelpText = this.inlineHelpText; }
        if (this.label != null) { customField.label = this.label; }
        if (this.maskChar != null) { customField.maskChar = this.maskChar; }
        if (this.maskType != null) { customField.maskType = this.maskType; }
        if (this.referenceTo != null) { customField.referenceTo = this.referenceTo; }
        if (this.relationshipLabel != null) { customField.relationshipLabel = this.relationshipLabel; }
        if (this.relationshipName != null) { customField.relationshipName = this.relationshipName; }
        if (this.summarizedField != null) { customField.summarizedField = this.summarizedField; }
        if (this.summaryForeignKey != null) { customField.summaryForeignKey = this.summaryForeignKey; }
        if (this.summaryOperation != null) { customField.summaryOperation = this.summaryOperation; }
        if (this.fieldType != null) { customField.type_x = this.fieldType; }
        
        // boolean fields
        if (this.caseSensitive != null) { customField.caseSensitive = this.caseSensitive; }
        if (this.displayLocationInDecimal != null) { customField.displayLocationInDecimal = this.displayLocationInDecimal; }
        if (this.escapeMarkup != null) { customField.escapeMarkup = this.escapeMarkup; }
        if (this.externalId != null) { customField.externalId = this.externalId; }
        if (this.isFilteringDisabled != null) { customField.isFilteringDisabled = this.isFilteringDisabled; }
        if (this.isNameField != null) { customField.isNameField = this.isNameField; }
        if (this.isSortingDisabled != null) { customField.isSortingDisabled = this.isSortingDisabled; }
        if (this.populateExistingRows != null) { customField.populateExistingRows = this.populateExistingRows; }
        if (this.reparentableMasterDetail != null) { customField.reparentableMasterDetail = this.reparentableMasterDetail; }
        if (this.required != null) { customField.required = this.required; }
        if (this.restrictedAdminField != null) { customField.restrictedAdminField = this.restrictedAdminField; }
        if (this.stripMarkup != null) { customField.stripMarkup = this.stripMarkup; }
        if (this.trackFeedHistory != null) { customField.trackFeedHistory = this.trackFeedHistory; }
        if (this.trackHistory != null) { customField.trackHistory = this.trackHistory; }
        if (this.unique != null) { customField.unique = this.unique; }
        if (this.writeRequiresMasterRead != null) { customField.writeRequiresMasterRead = this.writeRequiresMasterRead; }
        
        // object fields
        if (this.summaryFilterItems != null) { customField.summaryFilterItems = this.summaryFilterItems; }
        
        fields.add(customField);
        
        MetadataService.SaveResult[] results = service.createMetadata(fields);
        
        return results;
    }

    public string deleteField(string objectAPIName, string fieldAPIName) {
        string returnValue = '';
        
        List<MetadataService.Metadata> fields = new List<MetadataService.Metadata>();
        MetadataService.CustomField customField = new MetadataService.CustomField();
		customField.fullName = objectAPIName + '.' + fieldAPIName;
        fields.add(customField);
        String[] newStringDeletedFields = new String[] { customField.fullName }; 
        List<MetadataService.DeleteResult> results = service.deleteMetadata('CustomField', newStringDeletedFields); 
        
        // Iterate through each returned result
        for(MetadataService.DeleteResult dr : results) {
            if (!dr.success) {
                // Operation failed, so get all errors
                for(MetadataService.Error err : dr.errors) {
                    returnValue += err.statusCode  + ': ' + err.message + '<br /><br />';
                }
            }
        }
        
        return returnValue;
    }
}

 
Abdulla d 5Abdulla d 5
// to add custom fields to pageouts using meta data
public class UpdateContactPageLayout {
    public Metadata.Layout addLayoutItem() {
        
        // Retrieve Account layout and section 
        List<Metadata.Metadata> layoutsList  = Metadata.Operations.retrieve(Metadata.MetadataType.Layout, 
            new List<String> {'Contact-Contact Layout'});
        Metadata.Layout layoutMetadata  = (Metadata.Layout) layoutsList.get(0);
        Metadata.LayoutSection contactLayoutSection = null;
        List<Metadata.LayoutSection> layoutSections = layoutMetadata.layoutSections;
        for (Metadata.LayoutSection section : layoutSections) {
            
            if (section.label == 'Additional Information') {
                contactLayoutSection = section;
                break;
            }
        }
        
        // Add the field under Account info section in the left column
        List<Metadata.LayoutColumn> contactColumns  = contactLayoutSection.layoutColumns;     
        List<Metadata.LayoutItem> contactLayoutItems  = contactColumns .get(0).layoutItems;
        
        // Create a new layout item for the custom field
        Metadata.LayoutItem newField  = new Metadata.LayoutItem();
        newField .behavior = Metadata.UiBehavior.Edit;
        newField .field = 'AMAPI__Apex_MD_API_Twitter_name__c';
        contactLayoutItems.add(newField);
        
        return layoutMetadata ;
    }
}
Nitesh Kumar 186Nitesh Kumar 186
Hi KMK you no need to MetaDataApi for create dynamic . You can do it very simple an deasy way #tooling api
 
Kiran MaramKiran Maram
Hi Dennis
public static string createField(string objectAPIName, string fieldAPIName, string fieldLabel, string fieldDescription) {
        HttpRequest request = new HttpRequest();
        request.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
        request.setHeader('Content-Type', 'application/json');
        request.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v41.0/tooling/sobjects/CustomField/');
        request.setMethod('POST');
        
        request.setBody('{"Metadata" : {"type" : "Text","description" : "'+fieldDescription+'", "inlineHelpText" : "","precision" : null,"label" : "'+fieldLabel+'","length" : 255,"required" : false}, "FullName" : "'+objectAPIName+'.'+fieldAPIName+'"}');
        
        Http http = new Http();
        HTTPResponse res = http.send(request);
        return res.getBody();
    }

when I run this code am getting following error, please help me to fix

[{"message":"This session is not valid for use with the REST API","errorCode":"INVALID_SESSION_ID"}]
Dennis H PalmerDennis H Palmer
Hey Kiran,

There are a few things that could cause this error.  I would make sure your user has access to API.  I would also make sure you're logged in.  Where are you trying to run the code?  Class?  Trigger?  Visualforce Page?  Execute Anonymous window in dev console?
Kiran MaramKiran Maram
Hi Dennis, am running this code from Apex Class, the following is a complete class file
public class SampleAPIServiceCallouts {
    
    @AuraEnabled
    public static string addNewCustomField(string objectAPIName, string fieldAPIName, string fieldLabel, string fieldDescription) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
        //request.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionID());
		request.setHeader('Content-Type', 'application/json');
        String epURL = URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v41.0/tooling/sobjects/CustomField/';
        request.setEndpoint(epURL);
        request.setMethod('POST');
        
        String fieldDef = '{"Metadata" : ';
        String metadef = '"type" : "Text","description" : "'+fielddescription+'", "inlineHelpText" : "","precision" : null,"label" : "'+fieldlabel+'","length" : 255,"required" : false';
        fieldDef += '{'+metadef+'},';
        fieldDef += '"FullName" : "'+objectapiname+'.'+fieldapiname+'__c"}';
        system.debug(fieldDef);
        request.setBody(fieldDef);
        
        HTTPResponse res = http.send(request);
        return res.getBody();
    } 
   
}
And am running the above addNewCustomField method from Component Helper Class
 
Dennis H PalmerDennis H Palmer
You must make sure the profile you are logged in with has access to API (https://support.geckoboard.com/hc/en-us/articles/217148797-How-do-I-enable-API-access-in-Salesforce-).  
ADARSH SINGH 17ADARSH SINGH 17
Hi Dennis,

I have created custom fields under an object using the tooling API. but by default, there is no Field Level Permission assigned to any specific profile. So can you please let me know, is there any way to assign FLS while creating fields using tooling API?

Thanks,
Dennis H PalmerDennis H Palmer
Sure is.  
// add field permissions to all profiles.
for (integer i = 0; i < this.fieldProfiles.size(); i++) {
    MetadataService.ProfileFieldLevelSecurity fieldPermission = new MetadataService.ProfileFieldLevelSecurity();
    fieldPermission.field = customField.fullName;
    fieldPermission.editable = true;
    fieldPermission.readable = true;
    if (this.fieldProfiles[i].fieldPermissions == null) {
        this.fieldProfiles[i].fieldPermissions = new MetadataService.ProfileFieldLevelSecurity[] {fieldPermission};
    } else {
        this.fieldProfiles[i].fieldPermissions.add(fieldPermission);
    }
}
Debaranjan GhoshDebaranjan Ghosh
@Andy 
I tried to reuse your code.
But I am getting one error at last line of your code which says "Variable does not exists :service for the line 

MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { dayFields }); } }