Dynamic Validation Rule Builder

Create Salesforce Validation Rules using natural language prompts - powered by AgentForce + Apex SOAP API

Tool Overview

This implementation creates a dynamic validation rule generator that:

Key Features

  1. Natural Language Processing: Converts plain English to validation formulas
  2. SOAP API Integration: Direct deployment without manual steps
  3. Complex Logic Support: Handles AND/OR conditions, regex, date validations
  4. Error Handling: Comprehensive validation and error reporting
  5. Multi-Object Support: Works with any Salesforce object

Apex Controller Class

The main Apex class that handles validation rule creation via SOAP API:

/**
 * Apex class to create Validation Rules using a direct SOAP API callout.
 * This class is designed to be called from AgentForce with @InvocableMethod.
 */
public class ValidationRuleSOAPCreator {

    public class ValidationRuleRequest {
        @InvocableVariable(label='Validation Rule Name' 
                          description='API name for the validation rule (PascalCase, no spaces). Example: HoursWorked_NegativeCheck'
                          required=true)
        public String ValidationRuleName;
        
        @InvocableVariable(label='Validation Rule Formula' 
                          description='Salesforce formula that returns TRUE when validation fails. Example: Hours__c < 0'
                          required=true)
        public String ValidationRuleFormula;
        
        @InvocableVariable(label='Error Message' 
                          description='User-friendly error message displayed when validation fails. Example: Hours Worked cannot be a negative number.'
                          required=true)
        public String ErrorMessage;
        
        @InvocableVariable(label='Rule Description' 
                          description='Business description explaining what the rule enforces. Example: Validates that the Hours Worked field does not contain negative values.'
                          required=true)
        public String RuleDescription;
        
        @InvocableVariable(label='Error Display Field' 
                          description='API name of the field where error message should be displayed. Example: Hours__c'
                          required=true)
        public String errorDisplayField;
        
        @InvocableVariable(label='Object Name' 
                          description='API name of the Salesforce object for the validation rule. Example: Account, Contact, Opportunity'
                          required=true)
        public String objectName;
    }
    
    public class ValidationRuleResponse {
        @InvocableVariable(label='Success Status' 
                          description='Boolean indicating whether the validation rule was created successfully')
        public Boolean isSuccess;
        
        @InvocableVariable(label='Validation Rule ID' 
                          description='Full identifier of the created validation rule (ObjectName.RuleName)')
        public String validationRuleId;
        
        @InvocableVariable(label='Response Message' 
                          description='Success or failure message from the validation rule creation operation')
        public String message;
        
        @InvocableVariable(label='Error Details' 
                          description='List of detailed error messages if the operation failed')
        public List<String> errors;   
        
        public ValidationRuleResponse() {
            this.errors = new List<String>();
        }
    }
    
    // Static endpoint for SOAP API metadata service
    private static final String SOAP_ENDPOINT = URL.getOrgDomainUrl().toExternalForm() + '/services/Soap/m/59.0';
    
    /**
     * Invocable method that AgentForce will call
     * This accepts a list of requests and returns a list of responses
     * 
     * @param requests List of ValidationRuleRequest objects containing rule details
     * @return List of ValidationRuleResponse objects with creation results
     */
    @InvocableMethod(label='Create Validation Rule via SOAP' 
                     description='Creates a validation rule using SOAP API . Accepts natural language requirements and generates technical validation rules.')
    public static List<ValidationRuleResponse> createValidationRule(List<ValidationRuleRequest> requests) {
        List<ValidationRuleResponse> responses = new List<ValidationRuleResponse>();
        
        for (ValidationRuleRequest request : requests) {
            responses.add(createValidationRuleViaSOAP(request));
        }
        
        return responses;
    }
	
    /**
     * Main method to create validation rule via SOAP API
     */
    public static ValidationRuleResponse createValidationRuleViaSOAP(ValidationRuleRequest request) {
        ValidationRuleResponse response = new ValidationRuleResponse();
        
        try {
            // 1. Validate the request
            List<String> validationErrors = validateRequest(request);
            if (!validationErrors.isEmpty()) {
                response.isSuccess = false;
                response.errors = validationErrors;
                response.message = 'Request validation failed';
                return response;
            }
            
            // 2. Construct the SOAP XML Request Body
            String soapRequestBody = buildSOAPRequest(request);
            System.debug('soapRequestBody-------------'+soapRequestBody);
            debugSOAPRequest(soapRequestBody);

            // 3. Create and execute the HTTP Request
            HttpRequest httpReq = new HttpRequest();
            httpReq.setEndpoint(SOAP_ENDPOINT);
            httpReq.setMethod('POST');
            httpReq.setHeader('Content-Type', 'text/xml; charset=UTF-8');
            httpReq.setHeader('SOAPAction', 'create');
            httpReq.setBody(soapRequestBody);
            
            // 4. Get the current session ID for authentication
            String sessionId = loginToSalesforce();
            
            if (String.isBlank(sessionId)) {
                response.isSuccess = false;
                response.message = 'Could not obtain a valid session ID for API authentication.';
                return response;
            }
            
            // 5. Execute the callout
            Http http = new Http();
            HttpResponse httpRes = http.send(httpReq);

            // 6. Parse the SOAP Response
            if (httpRes.getStatusCode() == 200) {
                Dom.Document responseDoc = httpRes.getBodyDocument();
                Dom.XmlNode rootNode = responseDoc.getRootElement();
                
                // Check for SOAP Faults
                Boolean hasFault = false;
                for (Dom.XmlNode node : rootNode.getChildElements()) {
                    if (node.getName() == 'Body') {
                        for (Dom.XmlNode bodyNode : node.getChildElements()) {
                            if (bodyNode.getName() == 'Fault') {
                                hasFault = true;
                                for (Dom.XmlNode faultNode : bodyNode.getChildElements()) {
                                    if (faultNode.getName() == 'faultstring') {
                                        response.errors.add('SOAP Fault: ' + faultNode.getText());
                                    }
                                }
                            }
                        }
                    }
                }
                
                if (hasFault) {
                    response.isSuccess = false;
                    response.message = 'SOAP API call returned a fault.';
                } else {
                    response.isSuccess = true;
                    response.validationRuleId = request.objectName + '.' + request.ValidationRuleName;
                    response.message = 'Validation Rule submitted successfully via SOAP API.';
                }
                
            } else {
                response.isSuccess = false;
                response.message = 'HTTP Error: ' + httpRes.getStatusCode() + ' ' + httpRes.getStatus();
                response.errors.add('Response Body: ' + httpRes.getBody());
            }
            
        } catch (Exception e) {
            response.isSuccess = false;
            response.message = 'Exception occurred: ' + e.getMessage();
            response.errors.add(e.getStackTraceString());
        }
        
        return response;
    }
    
    /**
     * Builds the complete SOAP XML request envelope for the create() call.
     */
    private static String buildSOAPRequest(ValidationRuleRequest request) {
		String sessionId = loginToSalesforce();
        String soapRequest = 
            '<?xml version="1.0" encoding="UTF-8"?>' +
            '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" ' +
                              'xmlns:met="http://soap.sforce.com/2006/04/metadata" ' +
                              'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
                '<soapenv:Header>' +
                    '<met:SessionHeader>' +
                        '<met:sessionId>' + sessionId + '</met:sessionId>' +
                    '</met:SessionHeader>' +
                '</soapenv:Header>' +
                '<soapenv:Body>' +
                    '<met:create>' +
                        '<met:metadata xsi:type="met:ValidationRule">' +
                            '<fullName>' + escapeXml(request.objectName + '.' + request.ValidationRuleName) + '</fullName>' +
                            '<active>true</active>' +
                            '<description>' + escapeXml(request.RuleDescription) + '</description>' +
                            '<errorMessage>' + escapeXml(request.ErrorMessage) + '</errorMessage>' +
                            '<errorConditionFormula>' + escapeXml(request.ValidationRuleFormula) + '</errorConditionFormula>' +
                        '</met:metadata>' +
                    '</met:create>' +
                '</soapenv:Body>' +
            '</soapenv:Envelope>';
            
        return soapRequest;
    }
    
    /**
     * Debug method to log the SOAP request
     */
    private static void debugSOAPRequest(String soapRequest) {
        String debugRequest = soapRequest.length() > 1000 ? soapRequest.substring(0, 1000) + '...' : soapRequest;
        System.debug('SOAP Request: ' + debugRequest);
        
        if (soapRequest.contains('&') && !soapRequest.contains('&amp;')) {
            System.debug('WARNING: Unescaped ampersand found in request');
        }
    }
    
    /**
     * Gets session ID for authentication
     */
    private static String getSessionId() {
        return loginToSalesforce();
    }
    
    /**
     * Helper function to escape XML special characters
     */
    private static String escapeXml(String input) {
        if (input == null) return '';
        return input
            .replace('&', '&amp;')
            .replace('<', '&lt;')
            .replace('>', '&gt;')
            .replace('"', '&quot;')
            .replace('\'', '&apos;');
    }
    
    /**
     * Validate the incoming request
     */
    private static List<String> validateRequest(ValidationRuleRequest request) {
        List<String> errors = new List<String>();
        
        if (request == null) {
            errors.add('Request cannot be null');
            return errors;
        }
        
        if (String.isBlank(request.ValidationRuleName)) errors.add('ValidationRuleName is required');
        if (String.isBlank(request.ValidationRuleFormula)) errors.add('ValidationRuleFormula is required');
        if (String.isBlank(request.ErrorMessage)) errors.add('ErrorMessage is required');
        if (String.isBlank(request.objectName)) errors.add('objectName is required');
        if (String.isBlank(request.errorDisplayField)) errors.add('errorDisplayField is required');
        
        // Validate object exists
        if (String.isNotBlank(request.objectName)) {
            Map<String, Schema.SObjectType> globalDescribe = Schema.getGlobalDescribe();
            if (!globalDescribe.containsKey(request.objectName)) {
                errors.add('Object ' + request.objectName + ' does not exist');
            }
        }
        
        return errors;
    }
    
    /**
     * Check the status of an async metadata deployment
     * This can also be made invocable if needed
     */
    public static void checkDeploymentStatus(String asyncResultId) {
		String sessionId = loginToSalesforce();
        String soapRequest = 
            '<?xml version="1.0" encoding="UTF-8"?>' +
            '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" ' +
                              'xmlns:met="http://soap.sforce.com/2006/04/metadata">' +
                '<soapenv:Header>' +
                    '<met:SessionHeader>' +
                        '<met:sessionId>' + sessionId + '</met:sessionId>' +
                    '</met:SessionHeader>' +
                '</soapenv:Header>' +
                '<soapenv:Body>' +
                    '<met:checkStatus>' +
                        '<met:asyncProcessId>' + asyncResultId + '</met:asyncProcessId>' +
                    '</met:checkStatus>' +
                '</soapenv:Body>' +
            '</soapenv:Envelope>';
        
        HttpRequest httpReq = new HttpRequest();
        httpReq.setEndpoint(SOAP_ENDPOINT);
        httpReq.setMethod('POST');
        httpReq.setHeader('Content-Type', 'text/xml; charset=UTF-8');
        httpReq.setHeader('SOAPAction', 'checkStatus');
        httpReq.setBody(soapRequest);
        
        Http http = new Http();
        HttpResponse httpRes = http.send(httpReq);
        
        System.debug('Deployment Status Response: ' + httpRes.getBody());
    }
	
	public static String loginToSalesforce() {
		//You can follow other authentication mechanism like Oauth 2.0, here for example purpose I'm obtaining the session id using username and password with security token
       String username = 'Your_Org_Admin_User_Name';
        String passwordWithToken = 'Password+SecurityToken';//Example here Test@123 is password and  Sx1TfjcEgoCxv5Ax8VLI1xsfd is security token e.g: 'Test@123Sx1TfjcEgoCxv5Ax8VLI1xsfd';
		
        String soapEndpoint = 'https://login.salesforce.com/services/Soap/u/59.0';
        HttpRequest req = new HttpRequest();
        req.setEndpoint(soapEndpoint);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'text/xml; charset=UTF-8');
        req.setHeader('SOAPAction', 'login');

        // Construct the SOAP request body
        String soapBody = '<?xml version="1.0" encoding="UTF-8"?>' +
                          '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">' +
                          '    <env:Body>' +
                          '        <n1:login xmlns:n1="urn:partner.soap.sforce.com">' +
                          '            <n1:username>' + username + '</n1:username>' +
                          '            <n1:password>' + passwordWithToken + '</n1:password>' +
                          '        </n1:login>' +
                          '    </env:Body>' +
                          '</env:Envelope>';

        req.setBody(soapBody);

        Http http = new Http();
        HttpResponse res = new HttpResponse();
        String sessionId = '';

        try {
            res = http.send(req);
            String responseBody = res.getBody();
            System.debug('Login Response Body: ' + responseBody);

            if (res.getStatusCode() == 200) {
                // Extract sessionId from the XML response
                sessionId = extractSessionId(responseBody);
                System.debug('Session ID: ' + sessionId);
            } else {
                System.debug('Login Failed: ' + res.getStatusCode() + ' - ' + responseBody);
            }
        } catch (Exception e) {
            System.debug('Error during login: ' + e.getMessage());
        }

        return sessionId;
    }

    public static String extractSessionId(String responseXml) {
        Pattern sessionIdPattern = Pattern.compile('<sessionId>(.*?)</sessionId>');
        Matcher matcher = sessionIdPattern.matcher(responseXml);

        if (matcher.find()) {
            return matcher.group(1);
        }
        return '';
    }
}

Prompt Template Structure (Flex Type)

The template that guides the AI in generating validation formulas:

You are a Salesforce formula expert specializing in complex validation rules. Generate a validation formula for the following requirement.

Context:
- Object: {!$Input:ObjectName}
- Primary Field: {!$Input:FieldName}
- Related Fields: {!$Input:RelatedFields}
- Validation Requirement: {!$Input:UserDescription}
- Business Context: {!$Input:BusinessContext}

## Guidelines for Formula Generation:
1. The formula must return TRUE when the validation FAILS.
2. Handle null values safely using ISBLANK() or ISNULL().
3. Use correct data type functions (e.g., VALUE(), DATEVALUE(), TEXT()).
4. Support multi-field dependencies with AND(), OR(), NOT().
5. Implement regex checks when formatting rules are required.
6. Ensure formula is optimized and free from syntax errors.
7. Always consider edge cases (e.g., blank fields, invalid ranges, wrong formats).
8. Provide a clear and professional error message.
9. Provide a short business-friendly description for the rule.
10. Suggest a **Validation Rule Name** following Salesforce naming best practices:
  - PascalCase (e.g., `AccountNameLengthCheck`)
  - Avoid spaces or special characters
  - Short but descriptive
11. Always return two additional fields:
  - **errorDisplayField**: The API name of the field where the error will be displayed (default to Primary Field if not specified).
  - **objectName**: The Salesforce object API name where the rule will be created.

## Expected Output Mapping (JSON-like format):
{
 "ValidationRuleName": "<Suggested Validation Rule API name>",
 "ValidationRuleFormula": "<Generated Salesforce formula here>",
 "ErrorMessage": "<Human-readable error message to show end users>",
 "RuleDescription": "<Short description of what the rule enforces>",
 "errorDisplayField": "<API name of field where error is displayed>",
 "objectName": "<Object API name where rule is created>"
}

## Task:
Generate the Validation Rule Name, Formula, Error Message, Description, errorDisplayField, and objectName for:  {!$Input:UserDescription}

AgentForce Topic Implementation

Topic Configuration

Topic Name: Dynamic Validation Rule Creator

Description: Automatically generates and deploys Salesforce validation rules from natural language requirements using AI-powered formula generation and SOAP API deployment.

Scope: This topic handles the complete lifecycle of validation rule creation - from interpreting business requirements to deploying working validation rules in Salesforce.

Instructions for the Topic:

You are a Salesforce validation rule expert that helps users create and deploy validation rules automatically. 

Your role:
1. Accept natural language descriptions of validation requirements
2. Generate proper Salesforce validation formulas 
3. Deploy the validation rules directly to Salesforce via SOAP API
4. Provide clear feedback on success or failure

Always maintain data accuracy and follow Salesforce best practices for validation rule creation.

When users provide validation requirements, first generate the technical specifications, then deploy the rule automatically.

Action 1: Generate Validation Formula

Action Name: Generate Validation Rule Specifications

Action Type: Flex Prompt

Description: Converts natural language validation requirements into technical Salesforce validation rule specifications

Input Variables:

Note: When configuring the Flex prompt template, make sure to replace these input variable names with the actual variable references using the "Insert Resource" button in the prompt builder. Use the format {!$Input:VariableName} for each variable.

Action 2: Deploy Validation Rule

Action Name: Deploy Validation Rule to Salesforce

Action Type: Apex (Invocable Method)

Description: Takes the generated validation rule specifications and deploys them to Salesforce using SOAP API

Apex Class Reference:

Class: ValidationRuleSOAPCreator

Method: createValidationRule

Input Mapping:

Maps the output from Action 1 directly to the Apex method input parameters

Output Variables:

Implementation Notes

  1. Authentication: The example uses username/password authentication - consider using Named Credentials for production
  2. Error Handling: The implementation includes comprehensive error handling for various failure scenarios
  3. Security: Ensure proper field-level security and object permissions when deploying
  4. Governor Limits: Be mindful of callout limits when creating multiple validation rules
  5. Action Chaining: The topic uses two sequential actions where Action 2 receives output from Action 1
  6. Flex Prompt Type: Action 1 uses a Flex prompt for dynamic AI-powered formula generation

Example Output

{
  "ValidationRuleName": "ClosedWon_AmountDateCheck",
  "ValidationRuleFormula": "AND(TEXT(StageName) = \"Closed Won\", OR(ISBLANK(CloseDate), Amount <= 1000))",
  "ErrorMessage": "When Stage is Closed Won, Close Date is required and Amount must be greater than 1000.",
  "RuleDescription": "Ensures that Closed Won Opportunities have a Close Date and Amount > 1000.",
  "errorDisplayField": "CloseDate",
  "objectName": "Opportunity"
}

Enhancement Ideas