ICustomerManagementPOST Apex Interface

The ICustomerManagementPOST interface enables customization of POST operations using Apex lifecycle hooks. Implementations can validate and enrich creation payloads, apply additional business logic, and modify the final response structure, ensuring that customer-creation workflows remain flexible maintaining TMF-compliant behavior.

This interface supports the following hooks.

These hooks are not used for the Customer Management API. If implemented, the method may be invoked, but its return values are ignored.

  • configureDefaultValidations
  • customiseGraphQLQuery

Note

Creation Lifecycle Use Cases and Hook Mapping 

The following table lists common use cases for POST operation.

Use CaseHook(s)Description
Example Scenarios
Benefit
Enforce Business RulesapplyCustomValidationsValidates customer creation requests before processing to ensure they comply with business rules, data quality constraints, and organizational policies. Prevents invalid or duplicate customer records from entering the system.• Block creation of customers with duplicate names or identifiers• Reject requests with invalid customer types or lifecycle statuses• Enforce mandatory business validations (e.g., email/phone standards)Prevents invalid or low-quality customer records from being created, improving data integrity
Auto-Populate FieldscustomiseMutationPayloadAutomatically enriches or computes field values during customer creation. Ensures consistent and accurate population of derived or dependent fields before data is persisted.• Generate customer display name from first/last name• Auto-calculate classification, segment, or service level• Populate geo-encoding or internal routing codesReduces manual data entry, enforces consistency, and improves data accuracy
Audit TrailhandlePostOperationAdds audit metadata to newly created customer records after persistence, ensuring every creation event is traceable for security and compliance.• Stamp “createdBy”, “createdAt” values• Add system identifiers for correlation or traceability• Trigger downstream audit logging servicesMaintains a reliable and compliant audit trail across all customer creation events

transformRequest 

This hook transforms the request context before processing any operation. It is invoked early in the request lifecycle, allowing implementers to adjust the context for subsequent extensibility hooks.

Hook Method 

Map<String, Object> transformRequest(Map<String, Object> context)

Sample Apex Implementation 

1public Map<String, Object> transformRequest(Map<String, Object> context) {
2       if (context == null) {
3           return context;
4       }
5
6       Object idObj = context.get('id');
7       if (idObj == null || !(idObj instanceof String)) {
8           return context;
9       }
10
11       String idValue = (String)idObj;
12
13       // Check if the ID looks like a Salesforce ID (starts with 001, 003, etc.)
14       // If not, assume it's an AccountNumber and resolve it
15       if (idValue.length() == 15 || idValue.length() == 18) {
16           // Looks like a Salesforce ID, return context as-is
17           return context;
18       }
19
20       // Query Account objects where AccountNumber matches the provided ID
21       List<Account> accounts = [
22           SELECT Id, AccountNumber
23           FROM Account
24           WHERE AccountNumber = :idValue
25           LIMIT 1
26       ];
27
28       // If found, replace the ID in context with the Salesforce ID
29       if (!accounts.isEmpty() && accounts[0].AccountNumber != null) {
30           context.put('id', accounts[0].Id);
31       }
32
33       return context;
34   }

applyCustomValidations 

This hook validates custom business logic before creating a customer record. If validation fails, rejects the request and returns an error response.

The handler processes return values as follows.

Success:

  • Return a map with validationStatus set to any value other than “fail” (case-insensitive).
  • The API request continues normally.
  • Other fields in the map are logged but not used.

Failure:

  • Return a map with validationStatus set to “fail” (case-insensitive).
  • The API request is terminated immediately.
  • Raise a ValidationException containing
    • message: value from validationMessage key (or “Validation failed” by default)
    • details: value from validationDetails key (optional)
  • The client receives an HTTP 400 error response.

Hook Method 

Map<String, Object> applyCustomValidations(Map<String, Object> context)

Sample Apex Implementation 

1public Map<String, Object> applyCustomValidations(Map<String, Object> context) {
2        // Input context:
3        // {
4        //   api: 'CustomerManagement',
5        //   version: '4.0',
6        //   requestBody: { name: 'Acme Corp', type: 'Customer', status: 'Active' }
7        // }
8
9        Map<String, Object> requestBody = (Map<String, Object>) context.get('requestBody');
10        Map<String, Object> validationResult = new Map<String, Object>();
11
12        try {
13            String customerName = (String) requestBody.get('name');
14            String customerType = (String) requestBody.get('type');
15            String status = (String) requestBody.get('status');
16
17            // Validate required fields
18            if (String.isBlank(customerName)) {
19                validationResult.put('validationStatus', 'FAIL');
20                validationResult.put('validationMessage', 'Customer name is required');
21                return validationResult;
22            }
23
24            // Validate customer type
25            if (!isValidCustomerType(customerType)) {
26                validationResult.put('validationStatus', 'FAIL');
27                validationResult.put('validationMessage', 'Invalid customer type: ' + customerType);
28                validationResult.put('validationDetails', new Map<String, Object>{
29                    'allowedTypes' => new List<String>{'Customer', 'Prospect', 'Partner'}
30                });
31                return validationResult;
32            }
33
34            // Check for duplicate customer names
35            List<Account> existingAccounts = [SELECT Id FROM Account WHERE Name = :customerName LIMIT 1];
36            if (!existingAccounts.isEmpty()) {
37                validationResult.put('validationStatus', 'FAIL');
38                validationResult.put('validationMessage', 'Customer with name "' + customerName + '" already exists');
39                validationResult.put('validationDetails', new Map<String, Object>{
40                    'duplicateId' => existingAccounts[0].Id
41                });
42                return validationResult;
43            }
44
45            // PASS: All validations passed
46            validationResult.put('validationStatus', 'PASS');
47            validationResult.put('validationMessage', 'Customer creation validation passed');
48
49        } catch (Exception e) {
50            // FAIL: Error during validation
51            validationResult.put('validationStatus', 'FAIL');
52            validationResult.put('validationMessage', 'Validation error: ' + e.getMessage());
53        }
54
55        return validationResult;
56    }
57
58    private Boolean isValidCustomerType(String type) {
59        Set<String> validTypes = new Set<String>{'Customer', 'Prospect', 'Partner'};
60        return validTypes.contains(type);
61    }

customiseMutationPayload 

This hook modifies the mutation payload before creating a customer record. Use this to add computed fields, set defaults, or transform the payload based on business logic.

Hook Method 

Map<String, Object> customiseMutationPayload(Map<String, Object> request, Map<String, Object> context)

Sample Apex Implementation 

1/**
2     * Customizes the mutation payload before the GraphQL mutation is executed.
3     * Reads Description and Industry from the request body in context and adds them
4     * to the Account node at alias path "customerAccount".
5     *
6     * @param request The mutation request payload
7     * @param context Request context containing API details and request body
8     * @return Map containing mutation transformation instructions with nodes array:
9     *         - nodes[0]: Contains path='customerAccount' and addFields with Description and Industry
10     */
11    public Map<String, Object> customiseMutationPayload(Map<String, Object> request, Map<String, Object> context) {
12        // Return mutation transformation instructions: add Account fields at alias path "customerAccount"
13        Map<String, Object> nodes0 = new Map<String, Object>();
14        nodes0.put('path', 'customerAccount');
15        Map<String, Object> addFields = new Map<String, Object>();
16
17        // Read Description and Industry from request body in context
18        Map<String, Object> requestBody = (Map<String, Object>)context.get('requestBody');
19        if (requestBody != null) {
20            String description = (String)requestBody.get('description');
21            String industry = (String)requestBody.get('industry');
22
23            // Add Description to Account if present in request
24            if (description != null) {
25                addFields.put('Description', description);
26            }
27            // Add Industry to Account if present in request
28            if (industry != null) {
29                addFields.put('Industry', industry);
30            }
31        }
32
33        nodes0.put('addFields', addFields);
34
35        // Build nodes array containing the transformation instructions
36        List<Object> nodes = new List<Object>();
37        nodes.add(nodes0);
38
39        // Return result with nodes array
40        Map<String, Object> result = new Map<String, Object>();
41        result.put('nodes', nodes);
42        return result;
43    }

handlePostOperation 

This hook post-processes and transforms the API response after data is deleted.

Hook Method 

Map<String, Object> handlePostOperation(Map<String, Object> graphQLQueryResultAsMap, Map<String, Object> constructedTMFResponse, Map<String, Object> context)

Full Implementation Example 

1/**
2 * Complete Customer Management POST API Extension
3 * Demonstrates all three applicable lifecycle hooks with business logic
4 */
5public class CustomerManagementPOSTExtension implements comms_apex_ext.ICustomerManagementPOST {
6
7    /**
8     * Hook 1: Apply custom validations
9     */
10    public Map<String, Object> applyCustomValidations(Map<String, Object> context) {
11        Map<String, Object> requestBody = (Map<String, Object>) context.get('requestBody');
12        Map<String, Object> validationResult = new Map<String, Object>();
13
14        try {
15            String customerName = (String) requestBody.get('name');
16            String customerType = (String) requestBody.get('type');
17
18            // Validate required fields
19            if (String.isBlank(customerName)) {
20                validationResult.put('validationStatus', 'FAIL');
21                validationResult.put('validationMessage', 'Customer name is required');
22                return validationResult;
23            }
24
25            // Validate customer type
26            if (!isValidCustomerType(customerType)) {
27                validationResult.put('validationStatus', 'FAIL');
28                validationResult.put('validationMessage', 'Invalid customer type');
29                return validationResult;
30            }
31
32            // Check for duplicates
33            List<Account> existingAccounts = [SELECT Id FROM Account WHERE Name = :customerName LIMIT 1];
34            if (!existingAccounts.isEmpty()) {
35                validationResult.put('validationStatus', 'FAIL');
36                validationResult.put('validationMessage', 'Customer with this name already exists');
37                return validationResult;
38            }
39
40            validationResult.put('validationStatus', 'PASS');
41            validationResult.put('validationMessage', 'Validation passed');
42
43        } catch (Exception e) {
44            validationResult.put('validationStatus', 'FAIL');
45            validationResult.put('validationMessage', 'Validation error: ' + e.getMessage());
46        }
47
48        return validationResult;
49    }
50
51    /**
52     * Hook 2: Customize mutation payload
53     */
54    /**
55     * Customizes the mutation payload before the GraphQL mutation is executed.
56     * Reads Description and Industry from the request body in context and adds them
57     * to the Account node at alias path "customerAccount".
58     *
59     * @param request The mutation request payload
60     * @param context Request context containing API details and request body
61     * @return Map containing mutation transformation instructions with nodes array:
62     *         - nodes[0]: Contains path='customerAccount' and addFields with Description and Industry
63     */
64    public Map<String, Object> customiseMutationPayload(Map<String, Object> request, Map<String, Object> context) {
65        // Return mutation transformation instructions: add Account fields at alias path "customerAccount"
66        Map<String, Object> nodes0 = new Map<String, Object>();
67        nodes0.put('path', 'customerAccount');
68        Map<String, Object> addFields = new Map<String, Object>();
69
70        // Read Description and Industry from request body in context
71        Map<String, Object> requestBody = (Map<String, Object>)context.get('requestBody');
72        if (requestBody != null) {
73            String description = (String)requestBody.get('description');
74            String industry = (String)requestBody.get('industry');
75
76            // Add Description to Account if present in request
77            if (description != null) {
78                addFields.put('Description', description);
79            }
80            // Add Industry to Account if present in request
81            if (industry != null) {
82                addFields.put('Industry', industry);
83            }
84        }
85
86        nodes0.put('addFields', addFields);
87
88        // Build nodes array containing the transformation instructions
89        List<Object> nodes = new List<Object>();
90        nodes.add(nodes0);
91
92        // Return result with nodes array
93        Map<String, Object> result = new Map<String, Object>();
94        result.put('nodes', nodes);
95        return result;
96    }
97
98    /**
99     * Hook 3: Post-process response
100     */
101    public Map<String, Object> handlePostOperation(
102        Map<String, Object> graphQLQueryResultAsMap,
103        Map<String, Object> constructedTMFResponse,
104        Map<String, Object> context
105    ) {
106        return null;
107    }
108
109    /**
110     * NOT APPLICABLE: configureDefaultValidations is not used for Customer Management API
111     */
112    public Map<String, Boolean> configureDefaultValidations(
113        Map<String, Boolean> defaultValidationConfiguration,
114        Map<String, Object> context
115    ) {
116        return null;
117    }
118
119    private Boolean isValidCustomerType(String type) {
120        Set<String> validTypes = new Set<String>{'Customer', 'Prospect', 'Partner'};
121        return validTypes.contains(type);
122    }
123
124    private String determineCustomerSegment(String name) {
125        if (name.length() > 20) {
126            return 'Enterprise';
127        } else if (name.length() > 10) {
128            return 'Mid-Market';
129        } else {
130            return 'SMB';
131        }
132    }
133
134    **
135     * NOT APPLICABLE: customiseGraphQLQuery is not used for Customer Management API
136     */
137    public Map<String, Object> customiseGraphQLQuery(
138        Map<String, Object> graphQLAsMap,
139        Map<String, Object> context
140    ) {
141       return null;
142    }
143}

GraphQL Mutation - Create Operations 

Create mutations add new records and return the created record with the specified output fields.

Create Enhancements: Add Output Fields 

Original Mutation (GraphQL):

1mutation CreateCustomerAndRelatedEntities {
2uiapi {
3customerAccount: AccountCreate(input: {
4Account: { Name: "Acme Corp" }
5}) {
6Record {
7Id
8Name { value }
9}
10}
11}
12}

Transformation Instructions (JSON):

1{
2"nodes": [{
3"path": "customerAccount",
4"addFields": {
5"Industry": "Technology",
6"Revenue": 5000000,
7"Status": "Active"
8}
9}]
10}

Result (GraphQL):

1mutation CreateCustomerAndRelatedEntities {
2uiapi {
3customerAccount: AccountCreate(input: {
4Account: { Name: "Acme Corp" }
5}) {
6Record {
7Id
8Name { value }
9Industry
10Revenue
11Status
12}
13}
14}
15}

Create Enhancements: Modify Input Payload 

Original Mutation (GraphQL):

1mutation CreateCustomerAndRelatedEntities {
2  uiapi {
3    customerAccount: AccountCreate(input: {
4      Account: { Name: "Acme Corp" }
5    }) {
6      Record {
7        Id
8        Name { value }
9      }
10    }
11  }
12}

Transformation Instructions (JSON - Modify Input):

1{
2  "nodes": [{
3    "path": "customerAccount",
4    "modifyInput": {
5      "addInputFields": {
6        "Industry": "Technology",
7        "Status": "Active"
8      }
9    }
10  }]
11}

Result (GraphQL):

1mutation CreateCustomerAndRelatedEntities {
2  uiapi {
3    customerAccount: AccountCreate(input: {
4      Account: {
5        Name: "Acme Corp",
6        Industry: "Technology",
7        Status: "Active"
8      }
9    }) {
10      Record {
11        Id
12        Name { value }
13      }
14    }
15  }
16}

Using modifyInput with addInputFields adds new fields to the input payload. These fields are added to the Account object within the input wrapper and are sent to the mutation.

Note

Create Enhancements: Modify Existing Input Fields 

Original Mutation (GraphQL):

1mutation CreateCustomerAndRelatedEntities {
2  uiapi {
3    customerAccount: AccountCreate(input: {
4      Account: { Name: "Acme Corp" }
5    }) {
6      Record {
7        Id
8        Name { value }
9      }
10    }
11  }
12}

Transformation Instructions (JSON - Modify Existing Fields):

1{
2  "nodes": [{
3    "path": "customerAccount",
4    "modifyInput": {
5      "inputModifications": {
6        "Name": "Updated Company Name"
7      }
8    }
9  }]
10}

Result (GraphQL):

1mutation CreateCustomerAndRelatedEntities {
2  uiapi {
3    customerAccount: AccountCreate(input: {
4      Account: { Name: "Updated Company Name" }
5    }) {
6      Record {
7        Id
8        Name { value }
9      }
10    }
11  }
12}

The inputModifications replaces the values of existing input fields. The transformer identifies the wrapper key, for example Account and applies the changes at the correct nested level.

Note