ICustomerManagementDELETE Apex Interface

The ICustomerManagementDELETE interface provides extensibility for DELETE operations through Apex-based validations, mutation-payload adjustments, and post-operation processing. Users can enforce deletion policies, apply soft-delete or metadata logic, and customize final responses without impacting TMF-compliant delete 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

Deletion Lifecycle Use Cases and Hook Mapping 

This table lists common use cases and the hooks required for the DELETE operation.

Use CaseHook(s)Description
Example Scenarios
Benefit
Enforce Deletion RulesapplyCustomValidationsValidates whether a record is eligible for deletion by checking dependencies, business constraints, and user permissions. Prevents unsafe deletions that may violate data integrity or operational rules.• Block deletion if active child records exist• Enforce that only admins can delete high-value accounts• Prevent deletion when linked to open cases or agreementsPrevents accidental deletion of critical records and preserves data integrity
Maintain Audit TrailcustomiseMutationPayload, handlePostOperationEnriches the deletion request with audit metadata and records post-delete events, enabling traceability for compliance and regulatory review. Supports soft-delete or archival strategies.• Capture who deleted the record and timestamp• Add soft-delete flags before actual deletion• Trigger audit-event logging after deletionProvides a complete audit trail for compliance and monitoring
Support External IdentifiersresolveUniqueIdentifiersAllows records to be deleted using external IDs instead of Salesforce IDs. Enables seamless integration with external or legacy customer systems.• Delete by external CRM customer ID• Resolve telecom subscriber ID to internal account before deletion• Accept partner-system identifier for record lookupEnables interoperability with external platforms and simplifies integrations
Clean Up Related DatahandlePostOperationPerforms cascading cleanup of related or dependent data after a record has been deleted. Ensures the system remains consistent and free of orphaned or stale records.• Archive or remove associated contact mediums• Delete orphaned child records after parent deletion• Trigger asynchronous cleanup workflow for related dataMaintains data consistency and avoids accumulation of stale or orphaned records

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 deleting 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        //   customerId: '001xx000003DHP'
7        // }
8
9        String customerId = (String) context.get('customerId');
10        Map<String, Object> validationResult = new Map<String, Object>();
11
12        try {
13            // Check if customer has active contracts
14            List<Contract> activeContracts = [
15                SELECT Id FROM Contract
16                WHERE AccountId = :customerId
17                AND Status = 'Active'
18                LIMIT 1
19            ];
20
21            if (!activeContracts.isEmpty()) {
22                validationResult.put('validationStatus', 'FAIL');
23                validationResult.put('validationMessage', 'Cannot delete customer with active contracts');
24                validationResult.put('validationDetails', new Map<String, Object>{
25                    'reason' => 'ACTIVE_CONTRACTS_EXIST',
26                    'contractCount' => activeContracts.size()
27                });
28                return validationResult;
29            }
30
31            // Check if customer has open opportunities
32            List<Opportunity> openOpportunities = [
33                SELECT Id FROM Opportunity
34                WHERE AccountId = :customerId
35                AND IsClosed = false
36                LIMIT 1
37            ];
38
39            if (!openOpportunities.isEmpty()) {
40                validationResult.put('validationStatus', 'FAIL');
41                validationResult.put('validationMessage', 'Cannot delete customer with open opportunities');
42                validationResult.put('validationDetails', new Map<String, Object>{
43                    'reason' => 'OPEN_OPPORTUNITIES_EXIST'
44                });
45                return validationResult;
46            }
47
48            // Check user permissions
49            if (!UserInfo.getProfileId().equals('00e000000000001')) { // Admin profile
50                validationResult.put('validationStatus', 'FAIL');
51                validationResult.put('validationMessage', 'User does not have permission to delete customers');
52                validationResult.put('validationDetails', new Map<String, Object>{
53                    'requiredRole' => 'Administrator'
54                });
55                return validationResult;
56            }
57
58            // PASS: All validations passed
59            validationResult.put('validationStatus', 'PASS');
60            validationResult.put('validationMessage', 'Customer deletion validation passed');
61
62        } catch (Exception e) {
63            // FAIL: Error during validation
64            validationResult.put('validationStatus', 'FAIL');
65            validationResult.put('validationMessage', 'Validation error: ' + e.getMessage());
66        }
67
68        return validationResult;
69    }

customiseMutationPayload 

This hook modifies the mutation payload before deleting a customer record. Use this to add deletion metadata, set soft-delete flags, or apply business logic transformations.

Hook Method 

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

Sample Apex Implementation 

1public Map<String, Object> customiseMutationPayload(
2        Map<String, Object> request,
3        Map<String, Object> context
4    ) {
5        return new Map<String, Object>{
6            'nodes' => new List<Object>{
7                new Map<String, Object>{
8                    'path' => 'unlinkCase',
9                    'insertGraphQlSnippet' => 'unlinkCase: CaseUpdate(input: { Id: "500LT00000DmBZaYAN" Case: { ContactId: null, AccountId: null } }) { Record { Id ContactId { value } AccountId { value } } }'
10                },
11                new Map<String, Object>{
12                    'path' => 'deleteCase',
13                    'insertGraphQlSnippet' => 'deleteCase: CaseDelete(input: { Id: "500xx000000bnecAAA" }) { Id }'
14                }
15            }
16        };
17    }

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 

Here’s a complete sample Apex implementation using all supported hooks.

1/**
2 * Complete Customer Management DELETE API Extension
3 * Demonstrates all applicable lifecycle hooks with business logic
4 */
5public class CustomerManagementDELETEExtension implements comms_apex_ext.ICustomerManagementDELETE {
6
7    /**
8     * Hook 1: Apply custom validations
9     */
10    public Map<String, Object> applyCustomValidations(Map<String, Object> context) {
11        String customerId = (String) context.get('customerId');
12        Map<String, Object> validationResult = new Map<String, Object>();
13
14        try {
15            // Check for active contracts
16            List<Contract> activeContracts = [
17                SELECT Id FROM Contract
18                WHERE AccountId = :customerId AND Status = 'Active' LIMIT 1
19            ];
20
21            if (!activeContracts.isEmpty()) {
22                validationResult.put('validationStatus', 'FAIL');
23                validationResult.put('validationMessage', 'Cannot delete customer with active contracts');
24                return validationResult;
25            }
26
27            validationResult.put('validationStatus', 'PASS');
28            validationResult.put('validationMessage', 'Validation passed');
29
30        } catch (Exception e) {
31            validationResult.put('validationStatus', 'FAIL');
32            validationResult.put('validationMessage', 'Validation error: ' + e.getMessage());
33        }
34
35        return validationResult;
36    }
37
38    /**
39     * Hook 2: Customize mutation payload
40     */
41    public Map<String, Object> customiseMutationPayload(
42        Map<String, Object> request,
43        Map<String, Object> context
44    ) {
45        return new Map<String, Object>{
46            'nodes' => new List<Object>{
47                new Map<String, Object>{
48                    'path' => 'unlinkCase',
49                    'insertGraphQlSnippet' => 'unlinkCase: CaseUpdate(input: { Id: "500xx000000bnecAAA" Case: { ContactId: null, AccountId: null } }) { Record { Id ContactId { value } AccountId { value } } }'
50                },
51                new Map<String, Object>{
52                    'path' => 'deleteCase',
53                    'insertGraphQlSnippet' => 'deleteCase: CaseDelete(input: { Id: "500xx000000bnecAAA" }) { Id }'
54                }
55            }
56        };
57    }
58
59    /**
60     * Hook 3: Resolve unique identifiers
61     */
62    public Map<String, String> resolveUniqueIdentifiers(List<String> ids) {
63        Map<String, String> resolvedIds = new Map<String, String>();
64
65        try {
66            List<Account> accounts = [
67                SELECT Id, ExternalId__c
68                FROM Account
69                WHERE ExternalId__c IN :ids
70            ];
71
72            for (Account acc : accounts) {
73                resolvedIds.put(acc.ExternalId__c, acc.Id);
74            }
75
76        } catch (Exception e) {
77            System.debug('Error resolving IDs: ' + e.getMessage());
78        }
79
80        return resolvedIds;
81    }
82
83    /**
84     * Hook 4: Post-process response
85     */
86    public Map<String, Object> handlePostOperation(
87        Map<String, Object> graphQLQueryResultAsMap,
88        Map<String, Object> constructedTMFResponse,
89        Map<String, Object> context
90    ) {
91       /*
92	  TODO Any post processing to be performed.
93       */
94
95        return null;
96    }
97
98    /**
99     * NOT APPLICABLE: configureDefaultValidations is not used for Customer Management DELETE API
100     */
101    public Map<String, Boolean> configureDefaultValidations(
102        Map<String, Boolean> defaultValidationConfiguration,
103        Map<String, Object> context
104    ) {
105        return defaultValidationConfiguration;
106    }
107
108    /**
109     * NOT APPLICABLE: customiseGraphQLQuery is not used for Customer Management DELETE API
110     */
111    public Map<String, Object> customiseGraphQLQuery(Map<String, Object> graphQLAsMap, Map<String, Object> context) {
112        return null;
113    }
114}

GraphQL Mutation - Delete Operations 

Delete mutations remove records. The delete transformation supports inserting GraphQL snippets to perform related operations, such as unlinking relationships before deletion or creating audit logs. The insertGraphQlSnippet instruction allows you to embed complete GraphQL operations within a mutation and serves as the primary mechanism for extending delete operations.

Delete Enhancements: Unlink Before Delete 

Transformation Instructions (JSON):

1{
2  "nodes": [
3    {
4      "path": "unlinkCase",
5      "insertGraphQlSnippet": "unlinkCase: CaseUpdate(input: { Id: \"500xx000000bnecAAA\" Case: { ContactId: null, AccountId, null } }) { Record { Id ContactId { value } AccountId { value } } }"
6    },
7    {
8      "path": "deleteCase",
9      "insertGraphQlSnippet": "deleteCase: CaseDelete(input: { Id: \"500xx000000bnecAAA\" }) { Id }"
10    }
11  ]
12}

Result (GraphQL):

1mutation DeleteCase {
2  uiapi(input: { allOrNone: true }) {
3    unlinkCase: CaseUpdate(input: { Id: "500xx000000bnecAAA" Case: { ContactId: null, AccountId: null } }) {
4      Record {
5        Id
6        ContactId {
7          value
8        }
9        AccountId {
10          value
11        }
12      }
13    }
14    deleteCase: CaseDelete(input: { Id: "500xx000000bnecAAA" }) {
15      Id
16    }
17  }
18}