We are currently using the Apex Class with Process.Plugin that is refered here in the article https://developer.salesforce.com/blogs/developer-relations/2012/03/visual-workflow-converting-leads-with-an-apex-plugin.html

// Converts a lead as a step in a Visual Workflow process
global class VWFConvertLead implements Process.Plugin {
    // This method runs when the step is reached in a Flow
    global Process.PluginResult invoke(Process.PluginRequest request) {
        // set up variables to store input parameters from the Flow
        String leadID = (String) request.inputParameters.get('LeadID');
        String contactID = (String) request.inputParameters.get('ContactID');
        String accountID = 'XXXXXXXXXXXXX'; /*New Contact Account by Default*/ /*(String) request.inputParameters.get('AccountID');*/
        String convertedStatus = (String) request.inputParameters.get('ConvertedStatus');
        Boolean overWriteLeadSource = false; /*(Boolean) request.inputParameters.get('OverwriteLeadSource');*/
        Boolean createOpportunity = false; /*(Boolean) request.inputParameters.get('CreateOpportunity');*/
        String opportunityName = ''; /*(String) request.inputParameters.get('ContactID');*/ 
        Boolean sendEmailToOwner = false; /*(Boolean) request.inputParameters.get('SendEmailToOwner');   */
        String leadCompany = (String) request.inputParameters.get('LeadCompany');
        Map<String,Object> result = new Map<String,Object>();
        result = convertLead(leadID, contactID, accountID, convertedStatus, overWriteLeadSource, createOpportunity, opportunityName, sendEmailToOwner, leadCompany);
        return new Process.PluginResult(result); 

    global Process.PluginDescribeResult describe() {
        // Set up plugin metadata
        Process.PluginDescribeResult result = new Process.PluginDescribeResult();
        result.description = 'The LeadConvert Flow Plug-in converts a lead into an account and contact, as well as (optionally) an opportunity.';
        result.tag = 'Lead Management';
        result.inputParameters = new List<Process.PluginDescribeResult.InputParameter>{
            // Lead ID (mandatory)
            new Process.PluginDescribeResult.InputParameter('LeadID', 
                    Process.PluginDescribeResult.ParameterType.STRING, true),
            // Account Id (optional)
            new Process.PluginDescribeResult.InputParameter('AccountID', 
                    Process.PluginDescribeResult.ParameterType.STRING, false),
            // Contact ID (optional)
            new Process.PluginDescribeResult.InputParameter('ContactID', 
                    Process.PluginDescribeResult.ParameterType.STRING, false),            
            // Status to use once converted
            new Process.PluginDescribeResult.InputParameter('ConvertedStatus', 
                    Process.PluginDescribeResult.ParameterType.STRING, true),
            new Process.PluginDescribeResult.InputParameter('OpportunityName', 
                    Process.PluginDescribeResult.ParameterType.STRING, false),
            new Process.PluginDescribeResult.InputParameter('OverwriteLeadSource', 
                    Process.PluginDescribeResult.ParameterType.BOOLEAN, false),
            new Process.PluginDescribeResult.InputParameter('CreateOpportunity', 
                    Process.PluginDescribeResult.ParameterType.BOOLEAN, false),
            new Process.PluginDescribeResult.InputParameter('SendEmailToOwner', 
                    Process.PluginDescribeResult.ParameterType.BOOLEAN, false),
            new Process.PluginDescribeResult.InputParameter('leadCompany', 
                    Process.PluginDescribeResult.ParameterType.STRING, false)

        // Create a list that stores *output* parameters sent *to* your flow.
        result.outputParameters = new List<Process.PluginDescribeResult.OutputParameter>{
            // Account ID of the converted lead
            new Process.PluginDescribeResult.OutputParameter('AccountID', 
            // Contact ID of the converted lead
            new Process.PluginDescribeResult.OutputParameter('ContactID', 
            // Opportunity ID of the converted lead
            new Process.PluginDescribeResult.OutputParameter('OpportunityID', 
            // Company Name of the lead before converted
            new Process.PluginDescribeResult.OutputParameter('LeadCompanyName', 

        return result;
    public Map<String,String> convertLead (
                               String leadID,
                               String contactID,
                               String accountID,
                               String convertedStatus,
                               Boolean overWriteLeadSource,
                               Boolean createOpportunity,
                               String opportunityName,
                               Boolean sendEmailToOwner,
                               String leadCompany
        ) {
        Map<String,String> result = new Map<String,String>();
        if (leadId == null) throw new ConvertLeadPluginException('Lead Id cannot be null');
        // check for multiple leads with the same ID
        Lead[] leads = [Select Id, FirstName, LastName, Company From Lead where Id = :leadID];
        if (leads.size() > 0) {
            Lead l = leads[0];
            // CheckAccount = true, checkContact = false
            if (accountID == null && l.Company != null) {
                Account[] accounts = [Select Id, Name FROM Account where Name = :l.Company LIMIT 1];
                if (accounts.size() > 0) {
                    accountId = accounts[0].id;
            // perform the lead conversion
            Database.LeadConvert lc = new Database.LeadConvert();
            if (sendEmailToOwner != null) lc.setSendNotificationEmail(sendEmailToOwner);
            if (accountId != null && accountId.length() > 0) lc.setAccountId(accountId);
            if (contactId != null && contactId.length() > 0) lc.setContactId(contactId);
            /*if (createOpportunity) {
            Database.LeadConvertResult lcr = Database.convertLead(lc, true);
            if (lcr.isSuccess()) {
                /*Contact newContact = [SELECT Id, Company_Name_from_Form_Webinar__c FROM Contact WHERE Id =: lcr.getContactID()];
                newContact.Company_Name_from_Form_Webinar__c = leadCompany;
                update newContact;*/
                result.put('AccountID', lcr.getAccountId());
                result.put('ContactID', lcr.getContactId());
                result.put('LeadCompanyName', leadCompany);
                /*if (createOpportunity) {
                    result.put('OpportunityID', lcr.getOpportunityId());
            else {
                String error = lcr.getErrors()[0].getMessage();
                throw new ConvertLeadPluginException(error);
        else { 
            throw new ConvertLeadPluginException('No leads found with Id : "' + leadId + '"');
        return result;
        // Utility exception class
        class ConvertLeadPluginException extends Exception {}

Looks like with Spring 21 that the Make Flows Respect Access Modifiers for Legacy Apex Action we need to replace all the Process.Plugin to the InvocableMethod annotation. Also I can see in the flow that calls this action throws error

enter image description here

Can anyone help how can we convert the existing the apex to use @InvocableMethod. Any recommendations if there is any replacement of this action

1 Answer 1


First, you need a wrapper class that describes your input and output parameters, much like the original describe method:

global class ConvertLeadRequest {
  @InvocableVariable(required=true) global Id LeadID;
  @InvocableVariable Id AccountID;
  // etc //
global class ConvertLeadResult {
  // Whatever you need here //

Then, you need your main class:

global class ConvertLeadAction {
  global class ConvertLeadRequest {
    // from above example //
  global class ConvertLeadResult {
    // from above example //
  @InvocableMethod(label='Convert Lead Action')
  global static List<ConvertLeadResult> convertLeads(List<ConvertLeadRequest> requests) {
    // Handle input, convert leads, and then return output //

Note that I basically just borrowed this from the documentation, which has a literal example of doing what you're trying to do.

I do not recommend throwing an exception, though; you must return an appropriate error message or something. This is required because these methods are bulkified; multiple requests can be triggered at once (e.g. during bulk data loads), so your code needs to be prepared to handle this situation.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .