---
schema_version: "1"
id: 986666a6dbf4b4afd01231b4d20c8ddd7abdd755c9f1182b620a3bea89f98724
title: ICustomerManagementDELETE Apex Interface
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/industries-integrations/comms/guides/Extensibility/ITroubleTicketManagementDELETE.md
canonical_url: https://developer.salesforce.com/docs/industries/communications/guide/ITroubleTicketManagementDELETE.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: ab07cf073c9c8d88b79321b4f782fb732476d7f566941e3bd9e3dfb382cd86c0
products:
  - Communications Cloud TM Forum API
  - Industries Integrations
---
# ICustomerManagementDELETE Apex Interface

The ITroubleTicketManagementDELETE 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.

- [transformRequest](#transformrequest)
- [applyCustomValidations](#applycustomvalidations)
- [customiseMutationPayload](#customisemutationpayload)
- [handlePostOperation](#handlepostoperation)
- [resolveUniqueIdentifiers](#resolveuniqueidentifiers)

**note:** These hooks are not used for the Trouble Ticket Management API. If implemented, the method may be invoked, but its return values are ignored.

- `configureDefaultValidations`
- `customiseGraphQLQuery`

## Deletion Lifecycle Use Cases and Hook Mapping

This table lists common use cases and the hooks required for the DELETE operation.

| **Use Case**                     | **Hook(s)**                                       | **Description** <div style="width:290px"></div>                                                                                                                                                           | **Example Scenarios** <div style="width:290px"></div>                                                                                                             | **Benefit** <div style="width:290px"></div>                                     |
| -------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| **Enforce Deletion Rules**       | `applyCustomValidations`                          | Validates 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 agreements | Prevents accidental deletion of critical records and preserves data integrity   |
| **Maintain Audit Trail**         | `customiseMutationPayload`, `handlePostOperation` | Enriches 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 deletion                          | Provides a complete audit trail for compliance and monitoring                   |
| **Support External Identifiers** | `resolveUniqueIdentifiers`                        | Allows 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 lookup       | Enables interoperability with external platforms and simplifies integrations    |
| **Clean Up Related Data**        | `handlePostOperation`                             | Performs 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 data       | Maintains data consistency and avoids accumulation of stale or orphaned records |
| **CaseNumber to Id Resolution**  | `transformRequest`                                | Resolve CaseNumber identifiers to Salesforce Ids for API compatibility.                                                                                                                                   | • An external system sends a CaseNumber (e.g., 00001027), which is resolved to a Salesforce record ID for API processing                                          | Support external systems that use CaseNumber instead of Salesforce Id           |

## 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

Resolve Salesforce ID to CaseNumber

```
public Map<String, Object> transformRequest(Map<String, Object> context) {
    if (context == null) {
        return context;
    }


    Object idObj = context.get('id');
    if (idObj == null || !(idObj instanceof String)) {
        return context;
    }


    String idValue = (String)idObj;


    // Check if the ID looks like a Salesforce ID (starts with 500, 15 or 18 characters)
    // If not, assume it's already a CaseNumber and return as-is
    if (idValue.length() == 15 || idValue.length() == 18) {
        // Looks like a Salesforce ID, resolve to CaseNumber
        List<Case> cases = [
            SELECT Id, CaseNumber
            FROM Case
            WHERE Id = :idValue
            LIMIT 1
        ];


        // If found, replace the ID in context with the CaseNumber
        if (!cases.isEmpty() && cases[0].CaseNumber != null) {
            context.put('id', cases[0].CaseNumber);
        }
    }


    return context;
}

```

## applyCustomValidations

This hook validates custom business logic before deleting a trouble ticket data. 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

Validate Trouble Ticket Deletion Request

```
public Map<String, Object> applyCustomValidations(Map<String, Object> context) {
    // Input context:
    // {
    //   api: 'TroubleTicketManagement',
    //   version: '5.0',
    //   id: '00001234' or '500xx000001ABCDEF'
    // }

    String troubleTicketId = (String) context.get('id');
    Map<String, Object> validationResult = new Map<String, Object>();

    try {
        // Resolve CaseNumber to Salesforce ID if needed
        String caseId = resolveCaseId(troubleTicketId);
        if (String.isBlank(caseId)) {
            validationResult.put('validationStatus', 'FAIL');
            validationResult.put('validationMessage', 'Trouble ticket not found');
            return validationResult;
        }

        // Query the Case to check its status
        List<Case> cases = [
            SELECT Id, Status, CaseNumber
            FROM Case
            WHERE Id = :caseId
            LIMIT 1
        ];

        if (cases.isEmpty()) {
            validationResult.put('validationStatus', 'FAIL');
            validationResult.put('validationMessage', 'Trouble ticket not found');
            return validationResult;
        }

        Case caseRecord = cases[0];

        // Prevent deletion of closed cases
        if (caseRecord.Status == 'Closed') {
            validationResult.put('validationStatus', 'FAIL');
            validationResult.put('validationMessage', 'Cannot delete closed trouble ticket');
            validationResult.put('validationDetails', new Map<String, Object>{
                'reason' => 'CASE_ALREADY_CLOSED',
                'status' => caseRecord.Status,
                'caseNumber' => caseRecord.CaseNumber
            });
            return validationResult;
        }

        // Prevent deletion of cases with open child cases
        List<Case> childCases = [
            SELECT Id FROM Case
            WHERE ParentId = :caseId
            AND Status != 'Closed'
            LIMIT 1
        ];

        if (!childCases.isEmpty()) {
            validationResult.put('validationStatus', 'FAIL');
            validationResult.put('validationMessage', 'Cannot delete trouble ticket with open child cases');
            validationResult.put('validationDetails', new Map<String, Object>{
                'reason' => 'OPEN_CHILD_CASES_EXIST'
            });
            return validationResult;
        }

        // Check user permissions
        if (!UserInfo.getProfileId().equals('00e000000000001')) { // Admin profile
            validationResult.put('validationStatus', 'FAIL');
            validationResult.put('validationMessage', 'User does not have permission to delete trouble tickets');
            validationResult.put('validationDetails', new Map<String, Object>{
                'requiredRole' => 'Administrator'
            });
            return validationResult;
        }

        // PASS: All validations passed
        validationResult.put('validationStatus', 'PASS');
        validationResult.put('validationMessage', 'Trouble ticket deletion validation passed');

    } catch (Exception e) {
        // FAIL: Error during validation
        validationResult.put('validationStatus', 'FAIL');
        validationResult.put('validationMessage', 'Validation error: ' + e.getMessage());
    }

    return validationResult;
}


/**
 * Helper method to resolve CaseNumber to Salesforce ID
 */
private String resolveCaseId(String identifier) {
    // If it's already a Salesforce ID (15 or 18 characters), return as-is
    if (identifier.length() == 15 || identifier.length() == 18) {
        return identifier;
    }

    // Otherwise, treat as CaseNumber and query for Salesforce ID
    List<Case> cases = [
        SELECT Id FROM Case
        WHERE CaseNumber = :identifier
        LIMIT 1
    ];

    return cases.isEmpty() ? null : cases[0].Id;
}


```

## customiseMutationPayload

This hook modifies the mutation payload before deleting a trouble ticket data. 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

Add Deletion Metadata and Clean Up Related Records

```
public Map<String, Object> customiseMutationPayload(
    Map<String, Object> request,
    Map<String, Object> context
) {
    String troubleTicketId = (String) context.get('id');
    String caseId = resolveCaseId(troubleTicketId);

    return new Map<String, Object>{
        'nodes' => new List<Object>{
            // Delete related CaseComments before deleting the Case
            new Map<String, Object>{
                'path' => 'deleteCaseComments',
                'insertGraphQlSnippet' => 'deleteCaseComments: CaseCommentDelete(input: { Id: "00a000000000001AAA" }) { Id }'
            },
            // Unlink Case from Account before deletion
            new Map<String, Object>{
                'path' => 'unlinkCase',
                'insertGraphQlSnippet' => 'unlinkCase: CaseUpdate(input: { Id: "' + caseId + '" Case: { AccountId: null, ContactId: null } }) { Record { Id AccountId { value } ContactId { value } } }'
            },
            // Delete the Case itself
            new Map<String, Object>{
                'path' => 'deleteCase',
                'insertGraphQlSnippet' => 'deleteCase: CaseDelete(input: { Id: "' + caseId + '" }) { Id }'
            }
        }
    };
}


```

## 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)`

## Sample Apex Implementation

```
public Map<String, Object> handlePostOperation(
    Map<String, Object> graphQLQueryResultAsMap,
    Map<String, Object> constructedTMFResponse,
    Map<String, Object> context
) {
    String troubleTicketId = (String) context.get('id');

    // Log deletion event
    System.debug('Trouble ticket deleted: ' + troubleTicketId);
    System.debug('Deleted by user: ' + UserInfo.getUserId());
    System.debug('Deleted at: ' + System.now());

    // You can also create audit records, send notifications, etc.
    // Note: Return value is ignored, so any processing here is for side effects only

    return null;
}
```

## resolveUniqueIdentifiers

This hook resolves external identifiers (for example, CaseNumber) to Salesforce record IDs and is invoked for batch resolution of multiple identifiers.

## Hook Method

`Map<String, String> resolveUniqueIdentifiers(List<String> ids)`

## Sample Apex Implementation

```
public Map<String, String> resolveUniqueIdentifiers(List<String> ids) {
    Map<String, String> resolvedIds = new Map<String, String>();

    try {
        // Query Case objects where CaseNumber matches any of the provided IDs
        List<Case> cases = [
            SELECT Id, CaseNumber
            FROM Case
            WHERE CaseNumber IN :ids
        ];

        // Build map of CaseNumber to Salesforce ID
        for (Case caseRecord : cases) {
            if (caseRecord.CaseNumber != null) {
                resolvedIds.put(caseRecord.CaseNumber, caseRecord.Id);
            }
        }

    } catch (Exception e) {
        System.debug('Error resolving IDs: ' + e.getMessage());
    }

    return resolvedIds;
}

```

## Full Implementation Example

Here's a complete sample Apex implementation using all supported hooks.

```
/**
 * Complete Trouble Ticket Management DELETE API Extension
 * Demonstrates all applicable lifecycle hooks with business logic
 */
public class TroubleTicketManagementDELETEExtension implements comms_apex_ext.ITroubleTicketManagementDELETE {

    /**
     * Hook 1: Transform request context
     */
    public Map<String, Object> transformRequest(Map<String, Object> context) {
        if (context == null) {
            return context;
        }

        Object idObj = context.get('id');
        if (idObj == null || !(idObj instanceof String)) {
            return context;
        }

        String idValue = (String)idObj;

        // If it's not a Salesforce ID format, treat as CaseNumber and resolve
        if (idValue.length() != 15 && idValue.length() != 18) {
            List<Case> cases = [
                SELECT Id, CaseNumber
                FROM Case
                WHERE CaseNumber = :idValue
                LIMIT 1
            ];

            if (!cases.isEmpty() && cases[0].Id != null) {
                context.put('id', cases[0].Id);
            }
        }

        return context;
    }

    /**
     * Hook 2: Apply custom validations
     */
    public Map<String, Object> applyCustomValidations(Map<String, Object> context) {
        String troubleTicketId = (String) context.get('id');
        Map<String, Object> validationResult = new Map<String, Object>();

        try {
            String caseId = resolveCaseId(troubleTicketId);
            if (String.isBlank(caseId)) {
                validationResult.put('validationStatus', 'FAIL');
                validationResult.put('validationMessage', 'Trouble ticket not found');
                return validationResult;
            }

            // Check if case is closed
            List<Case> cases = [
                SELECT Id, Status FROM Case
                WHERE Id = :caseId AND Status = 'Closed' LIMIT 1
            ];

            if (!cases.isEmpty()) {
                validationResult.put('validationStatus', 'FAIL');
                validationResult.put('validationMessage', 'Cannot delete closed trouble ticket');
                return validationResult;
            }

            validationResult.put('validationStatus', 'PASS');
            validationResult.put('validationMessage', 'Validation passed');

        } catch (Exception e) {
            validationResult.put('validationStatus', 'FAIL');
            validationResult.put('validationMessage', 'Validation error: ' + e.getMessage());
        }

        return validationResult;
    }

    /**
     * Hook 3: Customize mutation payload
     */
    public Map<String, Object> customiseMutationPayload(
        Map<String, Object> request,
        Map<String, Object> context
    ) {
        String troubleTicketId = (String) context.get('id');
        String caseId = resolveCaseId(troubleTicketId);

        return new Map<String, Object>{
            'nodes' => new List<Object>{
                new Map<String, Object>{
                    'path' => 'unlinkCase',
                    'insertGraphQlSnippet' => 'unlinkCase: CaseUpdate(input: { Id: "' + caseId + '" Case: { AccountId: null, ContactId: null } }) { Record { Id AccountId { value } ContactId { value } } }'
                },
                new Map<String, Object>{
                    'path' => 'deleteCase',
                    'insertGraphQlSnippet' => 'deleteCase: CaseDelete(input: { Id: "' + caseId + '" }) { Id }'
                }
            }
        };
    }

    /**
     * Hook 4: Resolve unique identifiers
     */
    public Map<String, String> resolveUniqueIdentifiers(List<String> ids) {
        Map<String, String> resolvedIds = new Map<String, String>();

        try {
            List<Case> cases = [
                SELECT Id, CaseNumber
                FROM Case
                WHERE CaseNumber IN :ids
            ];

            for (Case caseRecord : cases) {
                if (caseRecord.CaseNumber != null) {
                    resolvedIds.put(caseRecord.CaseNumber, caseRecord.Id);
                }
            }

        } catch (Exception e) {
            System.debug('Error resolving IDs: ' + e.getMessage());
        }

        return resolvedIds;
    }

    /**
     * Hook 5: Post-process response
     */
    public Map<String, Object> handlePostOperation(
        Map<String, Object> graphQLQueryResultAsMap,
        Map<String, Object> constructedTMFResponse,
        Map<String, Object> context
    ) {
       /*
      TODO Any post processing to be performed.
       */

        return null;
    }

    /**
     * Helper method to resolve CaseNumber to Salesforce ID
     */
    private String resolveCaseId(String identifier) {
        if (identifier.length() == 15 || identifier.length() == 18) {
            return identifier;
        }

        List<Case> cases = [
            SELECT Id FROM Case
            WHERE CaseNumber = :identifier
            LIMIT 1
        ];

        return cases.isEmpty() ? null : cases[0].Id;
    }

    /**
     * NOT APPLICABLE: configureDefaultValidations is not used for Trouble Ticket Management DELETE API
     */
    public Map<String, Boolean> configureDefaultValidations(
        Map<String, Boolean> defaultValidationConfiguration,
        Map<String, Object> context
    ) {
        return defaultValidationConfiguration;
    }

    /**
     * NOT APPLICABLE: customiseGraphQLQuery is not used for Trouble Ticket Management DELETE API
     */
    public Map<String, Object> customiseGraphQLQuery(Map<String, Object> graphQLAsMap, Map<String, Object> context) {
        return null;
    }
}


```

## 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):**

```
{
  "nodes": [
    {
      "path": "unlinkCase",
      "insertGraphQlSnippet": "unlinkCase: CaseUpdate(input: { Id: \"500xx000000bnecAAA\" Case: { ContactId: null, AccountId, null } }) { Record { Id ContactId { value } AccountId { value } } }"
    },
    {
      "path": "deleteCase",
      "insertGraphQlSnippet": "deleteCase: CaseDelete(input: { Id: \"500xx000000bnecAAA\" }) { Id }"
    }
  ]
}


```

**Result (GraphQL):**

```
mutation DeleteCase {
  uiapi(input: { allOrNone: true }) {
    unlinkCase: CaseUpdate(input: { Id: "500xx000000bnecAAA" Case: { ContactId: null, AccountId: null } }) {
      Record {
        Id
        ContactId {
          value
        }
        AccountId {
          value
        }
      }
    }
    deleteCase: CaseDelete(input: { Id: "500xx000000bnecAAA" }) {
      Id
    }
  }
}

```
