Andy in the Cloud

From BBC Basic to Force.com and beyond…

92 thoughts on “Apex Metadata API Q&A

  1. Thanks Andy. Again a great article. Does nee version solve the problem of “having the instance as remote site” before calling the metadata api?

    I need to create fieldsets on package install. I tried using MetaData API in the past, but it would not work unless there was remote site to same Salesforce instance to which the app is being installed, for e.g. na11.salesforce.com. This did not make sense, as it was invoking from same org.

    Do I still have to worry about this?

    • I’m afraid not, you still need to refer to my other blog on how to semi-automate this. But sadly no solution from package install script, best is a post install VF page.

      • Hi Andy,
        I’m trying to automate the creation of Remote Site Settings as well, and I was wondering if you could direct me to your blog entry that “semi-automates” this. My current approach is to exec a VF page, but alas, I still get the
        IO Exception: Unauthorized endpoint, please check Setup->Security->Remote site settings. endpoint = [mydomain].my.visualforce.com/services/Soap/m/42.0

        My next approach is to publish a REST API resource (which in turn calls the mdapi) that I will call from an action method in the VF Page. I’m hoping that since the REST call will be outside of the “.visualforce.com” context, the Winter 19 changes will allow the call without adding a Remote Site Setting.

        Thanks very much for any guidance,
        What you’ve done with this is amazing.

  2. Thanks Andrew. Can you throw in quick example on how I could retrieve an element, let’s say a FieldSet? I would like to create an element only if it does not exist already.

    • There are quite a few examples in the MetadataServiceExamples.cls class (in the repo also) have you checked that out? If so, and your need something more, please raise a GitHub issue and i’ll see what i can do for you! 🙂

  3. Thanks Andy. Can we write the metadata of a Standard Field using the MetaData API?. Particularly the help text of Standard Fields. This can be done by Standard Salesforce screens, but I am not able to figure out how to do it from the API layer

    • Yes you can do this, the help text is exposed as a member variable on the CustomField class. Take a look at the Custom Field examples in the MetadataServiceExamples.cls (also in the repo). I think there is one that updates a Picklist field, you should be able to see what needs doing from that. Let me know via a GitHub issue on the repo if your still needing some help after taking a look at this, and i’d be happy to help more! 🙂

  4. Andy, do you know if we can create custom formula fields via metadata? I looked the class and field types here: https://www.salesforce.com/us/developer/docs/api_meta/Content/customfield.htm

    I do not think it is possible. Can you please confirm?

  5. Thanks Andy. That worked like a charm. Let me know if you have happen to be in Austin area.. beer is on me.

    • Great news and thanks i will! 🙂

      • Declared victory too soon. While I am able to create custom field via Metadata, it turns out I cannot do FLS with such ease. I have to manually set FLS for all profiles. My uses case is simple, all profiles should have read access to it.

        I found this old thread on how you can set FLS using code, this is like killing fly with bazooka. Is there any better way to achieve it? Here is the link: http://salesforce.stackexchange.com/questions/34521/set-field-level-security-for-profile-using-meta-data-api

      • You don’t need to use deploy/retrieve you can use the CRUD methods. Have you seen this example? https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceExamples.cls#L154 You can also create a Permission Set in Apex code directly using DML and also assign it to your users, again in code. I’d personally recommend this over adapting profiles, but either approach here should do what you want. Let me know if you want any more details. 🙂

      • That was fast! I do not like to create permission set if I don;t have to. Thank you for the example, looks like it would still require to get all profiles in for loop and update FLS for each. Is there global setting where we can set same FLS for all profile at once?

      • Yes that would be a single shared Permission Set assigned to all your users. 😉

      • Made some progress but getting exception with below code if there more than 10 profiles. Hence I restricted to just system admin profiles, now I strange error message in debug log. Can you please tell me what am I missing here? Thank you.

        //Store all profile names in list variable
        list profileNames = new list();
        for(Profile profile : [SELECT Id, Name FROM Profile WHERE UserType=’Standard’ AND Name LIKE ‘System%’ ORDER BY Name]){
        profileNames.add(profile.Name);
        }

        //Get the field
        MetadataService.ProfileFieldLevelSecurity fieldSec = new MetadataService.ProfileFieldLevelSecurity();
        fieldSec.field = ‘custom_object__c.custom_field__c’;
        fieldSec.readable = true;
        fieldSec.editable = true;

        //Update FLS for each profile
        for(MetadataService.Metadata metadata : service.readMetadata(‘Profile’, profileNames).getRecords()){
        MetadataService.Profile profile = (MetadataService.Profile)metadata;
        profile.fieldPermissions = new MetadataService.ProfileFieldLevelSecurity[] {fieldSec};
        updateMetadata.add(profile);
        }

        //Call metadata service
        if(updateMetadata.size()>0 && UserInfo.getOrganizationId() != ’00Dd0000000cDqREAU’)
        service.updateMetadata(updateMetadata);

        // ERROR MESSAGE

        Web service callout failed: WebService returned a SOAP Fault: UNKNOWN_EXCEPTION: An unexpected error occurred. Please include this ErrorId if you contact support: 1896081984-142957 (-1781941849) faultcode=sf:UNKNOWN_EXCEPTION faultactor=

      • I’m not able to decode those types of errors, only Salesforce support can do that. As the message suggests you have to raise a support case and provide your org id and this error message. They usual then tell you the underlying error message or declare it a platform API bug (which is basically what it is, since it’s not giving you a friendly error message).

      • Sorry Andrew. I did not mean that. Only Salesforce can help debug such user-friendly messages. I was asking from logic standpoint. Do you see anything wrong as I have very limited knowledge working with metadata API.

  6. Hi Andrew,

    I just found your MetadataServices class today (the one here https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataService.cls). I copied the class, test class, and examples class in to my salesforce developer org (along with all the other classes just to be on the safe side) and created a remote site setting for https://na16.salesforce.com (the URL of my dev org). When I try to run any of your examples however (like the customObject example), I get the following error, “Method does not exist or incorrect signature: createService()”. What’s weird though, is that I can’t seem to find the code that calls this method in the MetadataServices class. My org is API 33.0, which I know is the Spring 15 version, but I can’t figure out why the createService() method seems to be missing from the class. Do you have any ideas on why it might be throwing that error or what I might be doing wrong? Any insight would be seriously appreciated.

    Thanks!

    • That’s odd, this method is only in MetadataServiceExamples.cls

      • Hi Andrew, how would you recommend that I adjust my execution of your examples? I’m admittedly a bit of a newb to this area and played with it for about 4 hours on Tuesday before finally throwing my hands up and reaching out to you. Just deleting that section of the example methods (the createService part) doesn’t seem to fix the problem (as it just throws a different error at that point). I’m not quite sure what I need to adjust. Thanks a bunch for your response btw.

      • No worries, can you post on the github repo as an issue with a sample of your code and I will try to help there. Thanks

      • Done. Thanks Andrew! Talk to you on there.

      • Is there any solution for this issue “Method does not exist or incorrect signature: createService()”?

      • Yes create the method, you can copy it from the MetadataServiceExamples.cls

  7. Hi Andy,

    The code at github helped me a lot and I’m able to achieve most of the required things via Metadata but stuck at sharing rule creation.

    I have created new MetadataService class using API version 33.0 specially to create sharing rule using Metadata API and when I try to create sharing rule, I receive either no error or unknown error from salesforce on callout.

    I posted my question on stack exchange:
    http://salesforce.stackexchange.com/questions/69977/issue-with-creating-sharing-rule-using-metadata-api-version-33-0

    If you could take a look and let know what I’m doing wrong.

  8. Hey Andrew,

    First of all, the APEX Metadata API you have created is very helpful and an amazing piece of code. Thank you for taking the time to do this.

    I am trying to see if I can use the MetadataService class you have created to deploy Metadata to other orgs that I have access to. I know it is possible to use APEX to deploy to other orgs as http://www.flosum.com/ already does this very well.

    However, I am trying to just understand how I would go about achieving this using the class you have created.

    Any help or tips you could give me would be extremely appreciated.

  9. Hi Andrew, I am trying to retrieve all the Page Layouts of an Object without having hard-coded Layout names in my MataData API calls from Apex but did find a way to achieve that. Are there any methods available in the API which can be used to pull all Layout names for an Object? Thanks you in Advance.

  10. Hi ALL

    i am getting below mentioned error when try to use META API apex class

    Compile error at line 15 column 41 Method does not exist or incorrect signature: [MetadataService.MetadataPort].create(List)

    i am using Eclipse 3.6 Force.com IDE.

    Regards
    Sumit

  11. Hi Andrew,

    I was unable to retrieve all page layouts in one org. I am using this code but response is null.

    // Read the Layout
    MetadataService.MetadataPort service = createService();
    system.debug( service.readMetadata(‘Layout’,
    new String[] { ‘*’ }).getRecords());

    Please suggest

    • You cannot pass wildcards to this method. You must either use listMetadata method and then call retrieveMetadata or use the retrieve method. You can see examples in the github repo and also don’t forget the salesforce Metadada api docs.

  12. Hi Andrew ,

    I am facing a weird issue regarding update layout. My code is working fine unless its an unmanaged packaged but as soon as I shift it into managed package it starts throwing unknown exception.
    Error like this: Web service callout failed: WebService returned a SOAP Fault: UNKNOWN_EXCEPTION: An unexpected error occurred. Please include this ErrorId if you contact support: 697030706-2552 (-407299373) faultcode=sf:UNKNOWN_EXCEPTION faultactor=3058

    and I am working with standard objects only although I tried adding namespace too but not working. Using code like this : MetadataService.Layout layout =
    (MetadataService.Layout) service.readMetadata(‘Layout’,
    new String[] { ‘Account-dragNdrop__Account-Account Layout’ }).getRecords()[0];
    handleSaveResults(
    service.updateMetadata(
    new MetadataService.Metadata[] { layout })[0]);

    Let me know what you think.

    • Layouts and managed packages are tricky for sure, worth scanning some of the github issues in the repo for tips, also check my faq blog post to see how to use developer workbench to confirm the name

  13. Hello Andy, Great Article, thanks. Is there a way of adding a VF page to a standard layout in lightning experience using the meta data api?

    • You should be able to retrieve the layout, edit it in memory to add the section and item for the page then update it. Take a look at the sample code in the repo and also retrieve via xml a layout to see how it’s setup and replicate in your code

  14. Hello Andy,

    I truly appreciate all your efforts in this area. I’ve been able to use your code as the basis for automating the creation of objects and fields in SF.

    I am puzzled by the “hard-coded” element of your MetadataServiceExamples.cls – is there a reason why you took this approach as opposed to designing the createObject method to be called with parameters? e.g. something akin to the following:

    public static void createObject( string objFullName
    , string objLabel
    , string objPluralLabel
    , string objNameFieldTypeX
    , string objNameFieldLabel
    , string objDescription
    , string objDeploymentStatus
    , string objSharingModel
    , boolean objEnableReports
    , boolean objEnableActivities
    , boolean objEnableHistory
    , boolean objEnableFeeds
    , boolean objEnableBSS // Allow Sharing, Allow Bulk API Access, and Allow Streaming API Access must be enabled together or disabled together
    , boolean objEnableDivisions
    , boolean objEnableEnhancedLookup
    )
    {
    MetadataService.MetadataPort service = createService();
    MetadataService.CustomObject customObject = new MetadataService.CustomObject();
    customObject.fullName = objFullName;
    customObject.label = objLabel;
    customObject.pluralLabel = objPluralLabel;
    customObject.nameField = new MetadataService.CustomField();
    customObject.nameField.type_x = objNameFieldTypeX;
    customObject.nameField.label = objNameFieldLabel;
    customObject.description = objDescription;
    customObject.deploymentStatus = objDeploymentStatus;
    customObject.sharingModel = objSharingModel;
    customObject.enableReports = objEnableReports;
    customObject.enableActivities = objEnableActivities;
    customObject.enableHistory = objEnableHistory;
    customObject.enableFeeds = objEnableFeeds;
    customObject.enableBulkApi = objEnableBSS;
    customObject.enableSharing = objEnableBSS;
    customObject.enableStreamingApi = objEnableBSS;
    customObject.enableDivisions = objEnableDivisions;
    customObject.enableEnhancedLookup = objEnableEnhancedLookup;

    List results =
    service.createMetadata(
    new MetadataService.Metadata[] { customObject });
    handleSaveResults(results[0]);
    }

    • This is by design, which is to mirror Salesforce Metadata API, the MetadataService class is a most system generated wrapper for.

  15. Hi Andy,

    Can we minimize the metadataservice class to have only required metadata helper classes?

    • Only by manually deleting things or copying over incrementally to a new class the things you need. It is an enhancement to provide a better way to do this.

  16. Great Article @Andy

  17. Hi Andy,

    Is it possible to fetch other org’s metadata?

  18. Not able to retrieve Apex class. Code which I am using this :

    MetadataService.MetadataPort service = MetadataServiceExamples.createService();
    system.debug(service.readMetadata(‘ApexClass’,new String[] {‘StringArrayTest’}).getrecords()[0]);

    Error I am getting is this :

    00:59:29:268 FATAL_ERROR System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: INVALID_TYPE: This type of metadata is not available for this organization faultcode=sf:INVALID_TYPE faultactor=

    • This is a Salesforce API restriction, nothing I can do about it. However see my other comment response for options.

  19. Can we retrieve apex class content ? I am not able to do so

    • Yes with the retrieve API, as per the Retrieve Demo in the GitHub repo. Or a better way is to just use SOQL on the ApexClass object (see soap developers guide), you can query the Body field. You can only read records on this object. To update code, you need to use the deploy metadata API, again there is a deploy example in the GirHub repo.

  20. Andy, can we retrieve Global Pick-list through this?

    • It was recently updated to the API version to support this. Though I have yet to physically try it. In theory it should I would say. Let me know how you get on. It would be great to add a sample to the metadataserviceexamples class. 🙂

      • Thanks for Reply, I tried it for Global Pick list by using this code :

        MetadataService.GlobalPickList report = (MetadataService.GlobalPickList) service.readMetadata(‘GlobalPickList’, new String[] { ‘TestGlobal’ }).getRecords()[0];

        But it throws error “Web service callout failed: WebService returned a SOAP Fault: null: type is not a valid metadata type for reading faultcode=sf:UNKNOWN_EXCEPTION faultactor= “.

        Can you please confirm me that we can’t access global pick-list values directly in Apex?

      • This looks like a restriction of the Metadata API. The docs don’t appear to indicate one way or another… https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_globalpicklist.htm

      • If you raise an issue on the GitHub site I will investigate further for you at some point

  21. Hi Andrew.

    This is was an amazing post. I tried to run it in my org and although i am able to get the results, they are not as expected. I am trying to get a list of Names of all the workflows which are present in my org, i use this code :

    public static void listMetadata()
    {
    MetadataService.MetadataPort service = createService();
    List queries = new List();
    MetadataService.ListMetadataQuery queryWorkflow = new MetadataService.ListMetadataQuery();
    queryWorkflow.type_x = ‘Workflow’;
    queries.add(queryWorkflow);
    //MetadataService.ListMetadataQuery queryValidationRule = new MetadataService.ListMetadataQuery();
    //queryValidationRule.type_x = ‘ValidationRule’;
    //queries.add(queryValidationRule);
    MetadataService.FileProperties[] fileProperties = service.listMetadata(queries, 25);
    for(MetadataService.FileProperties fileProperty : fileProperties)
    System.debug(‘********The Names are :’+fileProperty.fullName);
    }

    public static MetadataService.MetadataPort createService()
    {
    MetadataService.MetadataPort service = new MetadataService.MetadataPort();
    service.SessionHeader = new MetadataService.SessionHeader_element();
    service.SessionHeader.sessionId = UserInfo.getSessionId();
    return service;
    }
    listMetadata();

    But i do not get the names of the workflows, instead a list of objects. What am i doing wrong here?

  22. CustomField is always NULL.


    Can anyone suggest a resolution for this common code snippet below? The field exists and I can create/update using the Apex Metadata API just fine.

    // Read Custom Field
MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata(
’CustomField’, new String[] { ‘Account.CustomerPriority__c’ }
).getRecords()[0];

    Ultimately, I want to create pick list values and activate them on record types. I see I need to update all the existing pick list values otherwise they will be deactivated on the record type.

    • Hmmm I have a feeling you can only read by retrieving the object. Though I can only find a reference to the fact in the retrieve API topic not the readmetadata one.

  23. Hi Andy,
    Sorry, I posted this on the wrong thread before, and could really use your assistance with this wonderful api.
    Can you consider why I get nulls for all properties reading any object or element.
    Even the simple snippet below returns all nulls. Creating is PERFECT and error free!
    MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata(‘CustomField’, new String[] { ‘Lead.picklist__c’ }).getRecords()[0];

    Thank you

  24. Trying to add data to picklist and here is the error:

    Error is in expression ‘{!saveNew}’ in component in page dynamiclist: Class.MetadataService.MetadataPort.upsertMetadata: line 11599, column 1
    Class.DynamicListController.addPicklistValue: line 119, column 1

  25. Hi Andy,
    Your blog is great. It helps a lot to understand Metadata API’s. I have implemented this.
    I am having one question for you that how you generate that XML file because when I retrieve meta data it returns a value which is having some URL but not those nods.
    If possible please guide me with next step.
    Thanks in advance.

    • Sure. Have you seen the examples in the GitHub repository? There is a deploy and retrieve example, as well as lots of examples in the metadataserviceexamples,cls file. If you don’t get xml back in the response it could be a salesforce issue. Try running your code in Execute Annonymous and looking for the response in the debug log. Hope this helps! 👍🏻

  26. Hi Andrew,

    Thank you for the API, saves people’s time. I’m trying to retrieve picklist based on record types using MetadataService (MetadataService.RecordType.picklistValues.picklist.values). The code is given below:

    MetadataService.MetadataPort service = createService();
    MetadataService.RecordType recordType = (MetadataService.RecordType) service.readMetadata(‘RecordType’, new String[] {.}).getRecords()[0];

    for (MetadataService.RecordTypePicklistValue recordTypePicklist : recordtype.picklistValues) {
    if ( == recordTypePicklist.picklist) {
    // getting info on each value
    for (MetadataService.PicklistValue picklistValue : recordTypePicklist.values) {
    // printing the values
    system.debug (picklistValue.fullName + ‘ – isActive? = ‘ + picklistValue.isActive);
    }
    }
    }

    Here, the picklistValue.fullName field is populated but isActive fields shows only null even though it skips the deactivated ones. Can I assume that by default, mdapi only pulls active pickliat values for the given recordtype?
    Thank you in advance.

    • some code got auto-corrected. It is MetadataService.RecordType recordType = (MetadataService.RecordType) service.readMetadata(‘RecordType’, new String[] {objectName.recordTypeName}).getRecords()[0]; and
      if (picklstFieldName == recordTypePicklist.picklist) {

    • Yes, as this type is likely shared when defining picklist values, I would image you are correct it’s redundant in this context. I guess you could easily confirm this assertion by making some active and others inactive.

  27. Andrew Sir, I m creating an Application using Metadata API. Your work helped me a lot. But few of the problems I m not able to find are :
    1. Time and DateTime custom Field is not getting Created.

  28. This library is very helpful!

  29. public String getPackageXml()
    {
    return ” +
    ” +
    ” +
    ‘HelloWorld’ +
    ‘ApexClass’ +
    ” +
    ‘26.0’ +
    ”;
    }

    public String getHelloWorldMetadata()
    {
    return ” +
    ” +
    ‘28.0’ +
    ‘Active’ +
    ”;
    }

    public String getHelloWorld()
    {
    return ‘public class HelloWorld’ +
    ‘{‘ +
    ‘public static void helloWorld()’ +
    ‘{‘ +
    ‘System.debug(\’ Hello World\’);’ +
    ‘}’ +
    ‘}’;
    }

    I don’t see test class, isn’t necessary?
    Thanks Adelchi

  30. Hi, firstly let me congratulate for the immense job you did with metadataAPI. Second please explain why I can find a test class for helloworld class in here:

    It is not necessary?

    Thank you.

    Adelchi

  31. Great one Andy. I am planning to use this for one of our use case. I have seen the last commit is 2 years ago and wanted to confirm if this is still being maintained and can I use this with the latest Salesforce API version?

    • It’s not something I have had time to update sadly. If the version last updated suites your needs you can still go ahead or tweak as need arises. I do have thoughts on how to better auto generate this going forward – but that’s another product.

  32. Hi Andy,

    I’m not able to readMetadata() from a managed CustomObjectTranslation, it doesn’t return anything (gives null). I have no problem with standard objects or unmanaged custom objects.

    Also, I’m using the same fullname that appears when using the listMetadata() method. I’ve tried with and without namespace prefix and still got nothing. Is this a known limitation? Or maybe I’m doing something wrong?

    Example
    This works fine in unmanaged package
    MetadataService.CustomObjectTranslation customObjectTranslation = (MetadataService.CustomObjectTranslation)
    service.readMetadata(‘CustomObjectTranslation’, new String[] {‘myCustomobject__c-en_GB’}).getRecords()[0];

    This is not working in managed or in my scratch org
    MetadataService.CustomObjectTranslation customObjectTranslation = (MetadataService.CustomObjectTranslation)
    service.readMetadata(‘CustomObjectTranslation’, new String[] {‘prefiex__myCustomobject__c__c-en_GB’}).getRecords()[0];

    Can anyone help me with this?

    Thanks,
    Kumbresh

    • This maybe a bug in the metadata api itself – the wrapper is pretty thin without much if any logic – one tip you can try is run your code in developer console anonymous apex and you will find in the debug log the raw xml sent to the server for your request and also response. Sometimes it helps to see things this way to discover things. Additionally you can reproduce your problem generally outside of this wrapper you will have a much better conversation with Salesforce support or other community references that just understand the raw metadata api – then once you can find what the problem is – revisit via the wrapper to try again. Hope this helps – you are so far doing the correct things to get the fully qualified name via list operation. Note finally that there is also some good metadata api support in the sfdx cli and developer workbench tools that you can also use to explore and contrast. Hope all this helps – good luck!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s